summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools')
-rw-r--r--src/compiler/scala/tools/nsc/CompilerCommand.scala11
-rw-r--r--src/compiler/scala/tools/nsc/Driver.scala21
-rw-r--r--src/compiler/scala/tools/nsc/GenericRunnerCommand.scala27
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala13
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala61
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala98
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala718
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala99
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala147
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala43
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala17
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala171
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala417
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala24
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala7
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala9
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala48
-rw-r--r--src/compiler/scala/tools/nsc/transform/Delambdafy.scala4
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala12
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala1
-rw-r--r--src/compiler/scala/tools/nsc/transform/TailCalls.scala7
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/Logic.scala25
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala18
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala20
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala13
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala1
34 files changed, 940 insertions, 1121 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala
index bab0768ca9..a1d0d52dcf 100644
--- a/src/compiler/scala/tools/nsc/CompilerCommand.scala
+++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala
@@ -20,9 +20,12 @@ class CompilerCommand(arguments: List[String], val settings: Settings) {
def ok = processArgumentsResult._1
def files = processArgumentsResult._2
- /** The name of the command */
+ /** The name of the command. */
def cmdName = "scalac"
+ /** A descriptive alias for version and help messages. */
+ def cmdDesc = "compiler"
+
private def explainAdvanced = "\n" + """
|-- Notes on option parsing --
|Boolean settings are always false unless set.
@@ -85,7 +88,11 @@ class CompilerCommand(arguments: List[String], val settings: Settings) {
def getInfoMessage(global: Global): String = {
import settings._
- if (help) usageMsg + global.pluginOptionsHelp
+ import Properties.{ versionString, copyrightString } //versionFor
+ def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString"
+
+ if (version) versionFor(cmdDesc)
+ else if (help) usageMsg + global.pluginOptionsHelp
else if (Xhelp) xusageMsg
else if (Yhelp) yusageMsg
else if (showPlugins) global.pluginDescriptions
diff --git a/src/compiler/scala/tools/nsc/Driver.scala b/src/compiler/scala/tools/nsc/Driver.scala
index 3ac27a42e8..6befa76b3f 100644
--- a/src/compiler/scala/tools/nsc/Driver.scala
+++ b/src/compiler/scala/tools/nsc/Driver.scala
@@ -2,26 +2,24 @@ package scala
package tools.nsc
import scala.tools.nsc.reporters.ConsoleReporter
-import Properties.{ versionString, copyrightString, residentPromptString }
+import Properties.{ versionMsg, residentPromptString }
import scala.reflect.internal.util.FakePos
abstract class Driver {
val prompt = residentPromptString
- val versionMsg = "Scala compiler " +
- versionString + " -- " +
- copyrightString
-
var reporter: ConsoleReporter = _
protected var command: CompilerCommand = _
protected var settings: Settings = _
- protected def scalacError(msg: String) {
+ protected def scalacError(msg: String): Unit = {
reporter.error(FakePos("scalac"), msg + "\n scalac -help gives more information")
}
- protected def processSettingsHook(): Boolean = true
+ protected def processSettingsHook(): Boolean = {
+ if (settings.version) { reporter echo versionMsg ; false } else true
+ }
protected def newCompiler(): Global
@@ -37,14 +35,12 @@ abstract class Driver {
}
def process(args: Array[String]) {
- val ss = new Settings(scalacError)
- reporter = new ConsoleReporter(ss)
+ val ss = new Settings(scalacError)
+ reporter = new ConsoleReporter(ss)
command = new CompilerCommand(args.toList, ss)
settings = command.settings
- if (settings.version) {
- reporter.echo(versionMsg)
- } else if (processSettingsHook()) {
+ if (processSettingsHook()) {
val compiler = newCompiler()
try {
if (reporter.hasErrors)
@@ -68,5 +64,4 @@ abstract class Driver {
process(args)
sys.exit(if (reporter.hasErrors) 1 else 0)
}
-
}
diff --git a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala
index e710222285..dbdeec809f 100644
--- a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala
+++ b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala
@@ -19,9 +19,10 @@ extends CompilerCommand(args, settings) {
def this(args: List[String]) =
this(args, str => Console.println("Error: " + str))
- /** name of the associated compiler command */
override def cmdName = "scala"
- def compCmdName = "scalac"
+ override def cmdDesc = "code runner"
+
+ def compCmdName = "scalac" // super.cmdName
// change CompilerCommand behavior
override def shouldProcessArguments: Boolean = false
@@ -50,17 +51,16 @@ extends CompilerCommand(args, settings) {
case Nil => AsRepl
case hd :: _ => waysToRun find (_.name == settings.howtorun.value) getOrElse guessHowToRun(hd)
}
- private def interpolate(s: String) = s.trim.replaceAll("@cmd@", cmdName).replaceAll("@compileCmd@", compCmdName) + "\n"
-
- def shortUsageMsg = interpolate("""
-Usage: @cmd@ <options> [<script|class|object|jar> <arguments>]
- or @cmd@ -help
-All options to @compileCmd@ (see @compileCmd@ -help) are also allowed.
-""")
+ def shortUsageMsg =
+s"""|Usage: $cmdName <options> [<script|class|object|jar> <arguments>]
+ | or $cmdName -help
+ |
+ |All options to $compCmdName (see $compCmdName -help) are also allowed.
+""".stripMargin
- override def usageMsg = shortUsageMsg + interpolate("""
-The first given argument other than options to @cmd@ designates
+ override def usageMsg = f"""$shortUsageMsg
+The first given argument other than options to $cmdName designates
what to run. Runnable targets are:
- a file containing scala source
@@ -68,7 +68,7 @@ what to run. Runnable targets are:
- a runnable jar file with a valid Main-Class attribute
- or if no argument is given, the repl (interactive shell) is started
-Options to @cmd@ which reach the java runtime:
+Options to $cmdName which reach the java runtime:
-Dname=prop passed directly to java to set system properties
-J<arg> -J is stripped and <arg> passed to java as-is
@@ -86,8 +86,7 @@ A file argument will be run as a scala script unless it contains only
self-contained compilation units (classes and objects) and exactly one
runnable main method. In that case the file will be compiled and the
main method invoked. This provides a bridge between scripts and standard
-scala source.
- """) + "\n"
+scala source.%n"""
}
object GenericRunnerCommand {
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 35eab94333..cb785de4b3 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -1169,8 +1169,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
if (option) reporter.warning(pos, msg)
else if (!(warnings contains pos)) warnings += ((pos, msg))
def summarize() =
- if (warnings.nonEmpty && (option.isDefault || settings.fatalWarnings))
- warning("there were %d %s warning(s); re-run with %s for details".format(warnings.size, what, option.name))
+ if (warnings.nonEmpty && (option.isDefault || settings.fatalWarnings)){
+ val warningEvent = if (warnings.size > 1) s"were ${ warnings.size } $what warnings" else s"was one $what warning"
+ warning(s"there $warningEvent; re-run with ${ option.name } for details")
+ }
}
def newSourceFile(code: String, filename: String = "<console>") =
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 0da66d43f7..0ad3c2c76b 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -1424,11 +1424,18 @@ abstract class GenICode extends SubComponent {
def genZandOrZor(and: Boolean): Boolean = {
val ctxInterm = ctx.newBlock()
- val branchesReachable = if (and) genCond(lhs, ctx, ctxInterm, elseCtx)
+ val lhsBranchesReachable = if (and) genCond(lhs, ctx, ctxInterm, elseCtx)
else genCond(lhs, ctx, thenCtx, ctxInterm)
- ctxInterm.bb killUnless branchesReachable
+ // If lhs is known to throw, we can kill the just created ctxInterm.
+ ctxInterm.bb killUnless lhsBranchesReachable
- genCond(rhs, ctxInterm, thenCtx, elseCtx)
+ val rhsBranchesReachable = genCond(rhs, ctxInterm, thenCtx, elseCtx)
+
+ // Reachable means "it does not always throw", i.e. "it might not throw".
+ // In an expression (a && b) or (a || b), the b branch might not be evaluated.
+ // Such an expression is therefore known to throw only if both expressions throw. Or,
+ // successors are reachable if either of the two is reachable (SI-8625).
+ lhsBranchesReachable || rhsBranchesReachable
}
def genRefEq(isEq: Boolean) = {
val f = genEqEqPrimitive(lhs, rhs, ctx) _
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
new file mode 100644
index 0000000000..2af2037fec
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
@@ -0,0 +1,61 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.tools.nsc.backend.jvm
+
+import scala.tools.asm.tree.{ClassNode, MethodNode}
+import java.io.PrintWriter
+import scala.tools.asm.util.{TraceClassVisitor, TraceMethodVisitor, Textifier}
+import scala.tools.asm.ClassReader
+
+object AsmUtils {
+
+ /**
+ * Print the bytecode of methods generated by GenBCode to the standard output. Only methods
+ * whose name contains `traceMethodPattern` are traced.
+ */
+ final val traceMethodEnabled = false
+ final val traceMethodPattern = ""
+
+ /**
+ * Print the bytecode of classes generated by GenBCode to the standard output.
+ */
+ final val traceClassEnabled = false
+ final val traceClassPattern = ""
+
+ /**
+ * Print the bytedcode of classes as they are serialized by the ASM library. The serialization
+ * performed by `asm.ClassWriter` can change the code generated by GenBCode. For example, it
+ * introduces stack map frames, it computes the maximal stack sizes, and it replaces dead
+ * code by NOPs (see also https://github.com/scala/scala/pull/3726#issuecomment-42861780).
+ */
+ final val traceSerializedClassEnabled = false
+ final val traceSerializedClassPattern = ""
+
+ def traceMethod(mnode: MethodNode): Unit = {
+ println(s"Bytecode for method ${mnode.name}")
+ val p = new Textifier
+ val tracer = new TraceMethodVisitor(p)
+ mnode.accept(tracer)
+ val w = new PrintWriter(System.out)
+ p.print(w)
+ w.flush()
+ }
+
+ def traceClass(cnode: ClassNode): Unit = {
+ println(s"Bytecode for class ${cnode.name}")
+ val w = new PrintWriter(System.out)
+ cnode.accept(new TraceClassVisitor(w))
+ w.flush()
+ }
+
+ def traceClass(bytes: Array[Byte]): Unit = traceClass(readClass(bytes))
+
+ def readClass(bytes: Array[Byte]): ClassNode = {
+ val node = new ClassNode()
+ new ClassReader(bytes).accept(node, 0)
+ node
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
index 92ebe5027a..bffa4bc51d 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
@@ -24,6 +24,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
import global._
import definitions._
import bCodeICodeCommon._
+ import bTypes._
/*
* Functionality to build the body of ASM MethodNode, except for `synchronized` and `try` expressions.
@@ -46,16 +47,16 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
def emit(opc: Int) { mnode.visitInsn(opc) }
def emitZeroOf(tk: BType) {
- (tk.sort: @switch) match {
- case asm.Type.BOOLEAN => bc.boolconst(false)
- case asm.Type.BYTE |
- asm.Type.SHORT |
- asm.Type.CHAR |
- asm.Type.INT => bc.iconst(0)
- case asm.Type.LONG => bc.lconst(0)
- case asm.Type.FLOAT => bc.fconst(0)
- case asm.Type.DOUBLE => bc.dconst(0)
- case asm.Type.VOID => ()
+ tk match {
+ case BOOL => bc.boolconst(false)
+ case BYTE |
+ SHORT |
+ CHAR |
+ INT => bc.iconst(0)
+ case LONG => bc.lconst(0)
+ case FLOAT => bc.fconst(0)
+ case DOUBLE => bc.dconst(0)
+ case UNIT => ()
case _ => emit(asm.Opcodes.ACONST_NULL)
}
}
@@ -166,7 +167,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
// load argument on stack
assert(args.length == 1, s"Too many arguments for array get operation: $tree");
genLoad(args.head, INT)
- generatedType = k.getComponentType
+ generatedType = k.asArrayBType.componentType
bc.aload(elementType)
}
else if (scalaPrimitives.isArraySet(code)) {
@@ -320,7 +321,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
mnode.visitVarInsn(asm.Opcodes.ALOAD, 0)
generatedType =
if (tree.symbol == ArrayClass) ObjectReference
- else brefType(thisName) // inner class (if any) for claszSymbol already tracked.
+ else ClassBType(thisName) // inner class (if any) for claszSymbol already tracked.
}
case Select(Ident(nme.EMPTY_PACKAGE_NAME), module) =>
@@ -418,7 +419,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
if (hostClass == null) internalName(field.owner)
else internalName(hostClass)
val fieldJName = field.javaSimpleName.toString
- val fieldDescr = symInfoTK(field).getDescriptor
+ val fieldDescr = symInfoTK(field).descriptor
val isStatic = field.isStaticMember
val opc =
if (isLoad) { if (isStatic) asm.Opcodes.GETSTATIC else asm.Opcodes.GETFIELD }
@@ -459,7 +460,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case ClazzTag =>
val toPush: BType = {
val kind = toTypeKind(const.typeValue)
- if (kind.isValueType) classLiteral(kind)
+ if (kind.isPrimitive) classLiteral(kind)
else kind
}
mnode.visitLdcInsn(toPush.toASMType)
@@ -468,7 +469,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val sym = const.symbolValue
val ownerName = internalName(sym.owner)
val fieldName = sym.javaSimpleName.toString
- val fieldDesc = toTypeKind(sym.tpe.underlying).getDescriptor
+ val fieldDesc = toTypeKind(sym.tpe.underlying).descriptor
mnode.visitFieldInsn(
asm.Opcodes.GETSTATIC,
ownerName,
@@ -540,26 +541,28 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
def genTypeApply(): BType = {
genLoadQualifier(fun)
- if (l.isValueType && r.isValueType)
+ // TODO @lry make pattern match
+ if (l.isPrimitive && r.isPrimitive)
genConversion(l, r, cast)
- else if (l.isValueType) {
+ else if (l.isPrimitive) {
bc drop l
if (cast) {
- mnode.visitTypeInsn(asm.Opcodes.NEW, classCastExceptionReference.getInternalName)
+ mnode.visitTypeInsn(asm.Opcodes.NEW, classCastExceptionReference.internalName)
bc dup ObjectReference
emit(asm.Opcodes.ATHROW)
} else {
bc boolconst false
}
}
- else if (r.isValueType && cast) {
+ else if (r.isPrimitive && cast) {
abort(s"Erasure should have added an unboxing operation to prevent this cast. Tree: $app")
}
- else if (r.isValueType) {
+ else if (r.isPrimitive) {
bc isInstance classLiteral(r)
}
else {
- genCast(r, cast)
+ assert(r.isRef, r) // ensure that it's not a method
+ genCast(r.asRefBType, cast)
}
if (cast) r else BOOL
@@ -579,7 +582,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
mnode.visitVarInsn(asm.Opcodes.ALOAD, 0)
genLoadArguments(args, paramTKs(app))
genCallMethod(fun.symbol, invokeStyle, pos = app.pos)
- generatedType = asmMethodType(fun.symbol).getReturnType
+ generatedType = asmMethodType(fun.symbol).returnType
// 'new' constructor call: Note: since constructors are
// thought to return an instance of what they construct,
@@ -590,13 +593,13 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
assert(ctor.isClassConstructor, s"'new' call to non-constructor: ${ctor.name}")
generatedType = tpeTK(tpt)
- assert(generatedType.isRefOrArrayType, s"Non reference type cannot be instantiated: $generatedType")
+ assert(generatedType.isRef, s"Non reference type cannot be instantiated: $generatedType")
generatedType match {
- case arr if generatedType.isArray =>
+ case arr @ ArrayBType(componentType) =>
genLoadArguments(args, paramTKs(app))
- val dims = arr.getDimensions
- var elemKind = arr.getElementType
+ val dims = arr.dimension
+ var elemKind = arr.elementType
val argsSize = args.length
if (argsSize > dims) {
cunit.error(app.pos, s"too many arguments for array constructor: found ${args.length} but array has only $dims dimension(s)")
@@ -606,18 +609,18 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
* elemKind = new BType(BType.ARRAY, arr.off + argsSize, arr.len - argsSize)
* however the above does not enter a TypeName for each nested arrays in chrs.
*/
- for (i <- args.length until dims) elemKind = arrayOf(elemKind)
+ for (i <- args.length until dims) elemKind = ArrayBType(elemKind)
}
(argsSize : @switch) match {
case 1 => bc newarray elemKind
case _ =>
- val descr = ('[' * argsSize) + elemKind.getDescriptor // denotes the same as: arrayN(elemKind, argsSize).getDescriptor
+ val descr = ('[' * argsSize) + elemKind.descriptor // denotes the same as: arrayN(elemKind, argsSize).descriptor
mnode.visitMultiANewArrayInsn(descr, argsSize)
}
- case rt if generatedType.hasObjectSort =>
+ case rt: ClassBType =>
assert(exemplar(ctor.owner).c == rt, s"Symbol ${ctor.owner.fullName} is different from $rt")
- mnode.visitTypeInsn(asm.Opcodes.NEW, rt.getInternalName)
+ mnode.visitTypeInsn(asm.Opcodes.NEW, rt.internalName)
bc dup generatedType
genLoadArguments(args, paramTKs(app))
genCallMethod(ctor, icodes.opcodes.Static(onInstance = true))
@@ -630,7 +633,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val nativeKind = tpeTK(expr)
genLoad(expr, nativeKind)
val MethodNameAndType(mname, mdesc) = asmBoxTo(nativeKind)
- bc.invokestatic(BoxesRunTime.getInternalName, mname, mdesc)
+ bc.invokestatic(BoxesRunTime.internalName, mname, mdesc)
generatedType = boxResultType(fun.symbol) // was toTypeKind(fun.symbol.tpe.resultType)
case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) =>
@@ -638,7 +641,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val boxType = unboxResultType(fun.symbol) // was toTypeKind(fun.symbol.owner.linkedClassOfClass.tpe)
generatedType = boxType
val MethodNameAndType(mname, mdesc) = asmUnboxTo(boxType)
- bc.invokestatic(BoxesRunTime.getInternalName, mname, mdesc)
+ bc.invokestatic(BoxesRunTime.internalName, mname, mdesc)
case app @ Apply(fun, args) =>
val sym = fun.symbol
@@ -683,7 +686,12 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case _ =>
}
if ((targetTypeKind != null) && (sym == definitions.Array_clone) && invokeStyle.isDynamic) {
- val target: String = targetTypeKind.getInternalName
+ // An invokevirtual points to a CONSTANT_Methodref_info which in turn points to a
+ // CONSTANT_Class_info of the receiver type.
+ // The JVMS is not explicit about this, but that receiver type may be an array type
+ // descriptor (instead of a class internal name):
+ // invokevirtual #2; //Method "[I".clone:()Ljava/lang/Object
+ val target: String = targetTypeKind.asRefBType.classOrArrayType
bc.invokevirtual(target, "clone", "()Ljava/lang/Object;")
}
else {
@@ -694,7 +702,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
genNormalMethodCall()
- generatedType = asmMethodType(sym).getReturnType
+ generatedType = asmMethodType(sym).returnType
}
}
@@ -706,7 +714,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val ArrayValue(tpt @ TypeTree(), elems) = av
val elmKind = tpeTK(tpt)
- val generatedType = arrayOf(elmKind)
+ val generatedType = ArrayBType(elmKind)
lineNumber(av)
bc iconst elems.length
@@ -808,7 +816,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
emit(asm.Opcodes.ATHROW) // ICode enters here into enterIgnoreMode, we'll rely instead on DCE at ClassNode level.
} else if (from.isNullType) {
bc drop from
- mnode.visitInsn(asm.Opcodes.ACONST_NULL)
+ emit(asm.Opcodes.ACONST_NULL)
}
else (from, to) match {
case (BYTE, LONG) | (SHORT, LONG) | (CHAR, LONG) | (INT, LONG) => bc.emitT2T(INT, LONG)
@@ -876,12 +884,12 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
if (claszSymbol == module.moduleClass && jMethodName != "readResolve" && !inStaticMethod) {
mnode.visitVarInsn(asm.Opcodes.ALOAD, 0)
} else {
- val mbt = symInfoTK(module)
+ val mbt = symInfoTK(module).asClassBType
mnode.visitFieldInsn(
asm.Opcodes.GETSTATIC,
- mbt.getInternalName /* + "$" */ ,
+ mbt.internalName /* + "$" */ ,
strMODULE_INSTANCE_FIELD,
- mbt.getDescriptor // for nostalgics: toTypeKind(module.tpe).getDescriptor
+ mbt.descriptor // for nostalgics: toTypeKind(module.tpe).descriptor
)
}
}
@@ -894,7 +902,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}
}
- def genCast(to: BType, cast: Boolean) {
+ def genCast(to: RefBType, cast: Boolean) {
if (cast) { bc checkCast to }
else { bc isInstance to }
}
@@ -959,10 +967,10 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
)
val receiver = if (useMethodOwner) methodOwner else hostSymbol
val bmOwner = asmClassType(receiver)
- val jowner = bmOwner.getInternalName
+ val jowner = bmOwner.internalName
val jname = method.javaSimpleName.toString
val bmType = asmMethodType(method)
- val mdescr = bmType.getDescriptor
+ val mdescr = bmType.descriptor
def initModule() {
// we initialize the MODULE$ field immediately after the super ctor
@@ -1025,7 +1033,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
private def genCJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType) {
if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
bc.emitIF_ICMP(op, success)
- } else if (tk.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_)
+ } else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
bc.emitIF_ACMP(op, success)
} else {
(tk: @unchecked) match {
@@ -1046,7 +1054,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
private def genCZJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType) {
if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
bc.emitIF(op, success)
- } else if (tk.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_)
+ } else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
// @unchecked because references aren't compared with GT, GE, LT, LE.
(op : @unchecked) match {
case icodes.EQ => bc emitIFNULL success
@@ -1131,7 +1139,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case ZOR => genZandOrZor(and = false)
case code =>
// TODO !!!!!!!!!! isReferenceType, in the sense of TypeKind? (ie non-array, non-boxed, non-nothing, may be null)
- if (scalaPrimitives.isUniversalEqualityOp(code) && tpeTK(lhs).hasObjectSort) {
+ if (scalaPrimitives.isUniversalEqualityOp(code) && tpeTK(lhs).isClass) {
// `lhs` has reference type
if (code == EQ) genEqEqPrimitive(lhs, rhs, success, failure)
else genEqEqPrimitive(lhs, rhs, failure, success)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala
deleted file mode 100644
index aa7e73a36b..0000000000
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala
+++ /dev/null
@@ -1,718 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2012 LAMP/EPFL
- * @author Martin Odersky
- */
-
-package scala
-package tools.nsc
-package backend.jvm
-
-import scala.tools.asm
-import scala.annotation.switch
-import scala.collection.{ immutable, mutable }
-
-/*
- * Immutable representations of bytecode-level types.
- *
- * @author Miguel Garcia, http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded
- * @version 1.0
- *
- */
-abstract class BCodeGlue extends SubComponent {
-
- import global._
-
- protected val bCodeICodeCommon: BCodeICodeCommon[global.type] = new BCodeICodeCommon(global)
-
- object BType {
-
- import global.chrs
-
- // ------------- sorts -------------
-
- val VOID : Int = 0
- val BOOLEAN: Int = 1
- val CHAR : Int = 2
- val BYTE : Int = 3
- val SHORT : Int = 4
- val INT : Int = 5
- val FLOAT : Int = 6
- val LONG : Int = 7
- val DOUBLE : Int = 8
- val ARRAY : Int = 9
- val OBJECT : Int = 10
- val METHOD : Int = 11
-
- // ------------- primitive types -------------
-
- val VOID_TYPE = new BType(VOID, ('V' << 24) | (5 << 16) | (0 << 8) | 0, 1)
- val BOOLEAN_TYPE = new BType(BOOLEAN, ('Z' << 24) | (0 << 16) | (5 << 8) | 1, 1)
- val CHAR_TYPE = new BType(CHAR, ('C' << 24) | (0 << 16) | (6 << 8) | 1, 1)
- val BYTE_TYPE = new BType(BYTE, ('B' << 24) | (0 << 16) | (5 << 8) | 1, 1)
- val SHORT_TYPE = new BType(SHORT, ('S' << 24) | (0 << 16) | (7 << 8) | 1, 1)
- val INT_TYPE = new BType(INT, ('I' << 24) | (0 << 16) | (0 << 8) | 1, 1)
- val FLOAT_TYPE = new BType(FLOAT, ('F' << 24) | (2 << 16) | (2 << 8) | 1, 1)
- val LONG_TYPE = new BType(LONG, ('J' << 24) | (1 << 16) | (1 << 8) | 2, 1)
- val DOUBLE_TYPE = new BType(DOUBLE, ('D' << 24) | (3 << 16) | (3 << 8) | 2, 1)
-
- /*
- * Returns the Java type corresponding to the given type descriptor.
- *
- * @param off the offset of this descriptor in the chrs buffer.
- * @return the Java type corresponding to the given type descriptor.
- *
- * can-multi-thread
- */
- def getType(off: Int): BType = {
- var len = 0
- chrs(off) match {
- case 'V' => VOID_TYPE
- case 'Z' => BOOLEAN_TYPE
- case 'C' => CHAR_TYPE
- case 'B' => BYTE_TYPE
- case 'S' => SHORT_TYPE
- case 'I' => INT_TYPE
- case 'F' => FLOAT_TYPE
- case 'J' => LONG_TYPE
- case 'D' => DOUBLE_TYPE
- case '[' =>
- len = 1
- while (chrs(off + len) == '[') {
- len += 1
- }
- if (chrs(off + len) == 'L') {
- len += 1
- while (chrs(off + len) != ';') {
- len += 1
- }
- }
- new BType(ARRAY, off, len + 1)
- case 'L' =>
- len = 1
- while (chrs(off + len) != ';') {
- len += 1
- }
- new BType(OBJECT, off + 1, len - 1)
- // case '(':
- case _ =>
- assert(chrs(off) == '(')
- var resPos = off + 1
- while (chrs(resPos) != ')') { resPos += 1 }
- val resType = getType(resPos + 1)
- val len = resPos - off + 1 + resType.len;
- new BType(
- METHOD,
- off,
- if (resType.hasObjectSort) {
- len + 2 // "+ 2" accounts for the "L ... ;" in a descriptor for a non-array reference.
- } else {
- len
- }
- )
- }
- }
-
- /* Params denote an internal name.
- * can-multi-thread
- */
- def getObjectType(index: Int, length: Int): BType = {
- val sort = if (chrs(index) == '[') ARRAY else OBJECT;
- new BType(sort, index, length)
- }
-
- /*
- * @param methodDescriptor a method descriptor.
- *
- * must-single-thread
- */
- def getMethodType(methodDescriptor: String): BType = {
- val n = global.newTypeName(methodDescriptor)
- new BType(BType.METHOD, n.start, n.length) // TODO assert isValidMethodDescriptor
- }
-
- /*
- * Returns the Java method type corresponding to the given argument and return types.
- *
- * @param returnType the return type of the method.
- * @param argumentTypes the argument types of the method.
- * @return the Java type corresponding to the given argument and return types.
- *
- * must-single-thread
- */
- def getMethodType(returnType: BType, argumentTypes: Array[BType]): BType = {
- val n = global.newTypeName(getMethodDescriptor(returnType, argumentTypes))
- new BType(BType.METHOD, n.start, n.length)
- }
-
- /*
- * Returns the Java types corresponding to the argument types of method descriptor whose first argument starts at idx0.
- *
- * @param idx0 index into chrs of the first argument.
- * @return the Java types corresponding to the argument types of the given method descriptor.
- *
- * can-multi-thread
- */
- private def getArgumentTypes(idx0: Int): Array[BType] = {
- assert(chrs(idx0 - 1) == '(', "doesn't look like a method descriptor.")
- val args = new Array[BType](getArgumentCount(idx0))
- var off = idx0
- var size = 0
- while (chrs(off) != ')') {
- args(size) = getType(off)
- off += args(size).len
- if (args(size).sort == OBJECT) { off += 2 }
- // debug: assert("LVZBSCIJFD[)".contains(chrs(off)))
- size += 1
- }
- // debug: var check = 0; while (check < args.length) { assert(args(check) != null); check += 1 }
- args
- }
-
- /*
- * Returns the number of argument types of this method type, whose first argument starts at idx0.
- *
- * @param idx0 index into chrs of the first argument.
- * @return the number of argument types of this method type.
- *
- * can-multi-thread
- */
- private def getArgumentCount(idx0: Int): Int = {
- assert(chrs(idx0 - 1) == '(', "doesn't look like a method descriptor.")
- var off = idx0
- var size = 0
- var keepGoing = true
- while (keepGoing) {
- val car = chrs(off)
- off += 1
- if (car == ')') {
- keepGoing = false
- } else if (car == 'L') {
- while (chrs(off) != ';') { off += 1 }
- off += 1
- size += 1
- } else if (car != '[') {
- size += 1
- }
- }
-
- size
- }
-
- /*
- * Returns the Java type corresponding to the return type of the given
- * method descriptor.
- *
- * @param methodDescriptor a method descriptor.
- * @return the Java type corresponding to the return type of the given method descriptor.
- *
- * must-single-thread
- */
- def getReturnType(methodDescriptor: String): BType = {
- val n = global.newTypeName(methodDescriptor)
- val delta = n.pos(')') // `delta` is relative to the Name's zero-based start position, not a valid index into chrs.
- assert(delta < n.length, s"not a valid method descriptor: $methodDescriptor")
- getType(n.start + delta + 1)
- }
-
- /*
- * Returns the descriptor corresponding to the given argument and return types.
- * Note: no BType is created here for the resulting method descriptor,
- * if that's desired the invoker is responsible for that.
- *
- * @param returnType the return type of the method.
- * @param argumentTypes the argument types of the method.
- * @return the descriptor corresponding to the given argument and return types.
- *
- * can-multi-thread
- */
- def getMethodDescriptor(
- returnType: BType,
- argumentTypes: Array[BType]): String =
- {
- val buf = new StringBuffer()
- buf.append('(')
- var i = 0
- while (i < argumentTypes.length) {
- argumentTypes(i).getDescriptor(buf)
- i += 1
- }
- buf.append(')')
- returnType.getDescriptor(buf)
- buf.toString()
- }
-
- } // end of object BType
-
- /*
- * Based on ASM's Type class. Namer's chrs is used in this class for the same purposes as the `buf` char array in asm.Type.
- *
- * All methods of this classs can-multi-thread
- */
- final class BType(val sort: Int, val off: Int, val len: Int) {
-
- import global.chrs
-
- /*
- * can-multi-thread
- */
- def toASMType: scala.tools.asm.Type = {
- import scala.tools.asm
- // using `asm.Type.SHORT` instead of `BType.SHORT` because otherwise "warning: could not emit switch for @switch annotated match"
- (sort: @switch) match {
- case asm.Type.VOID => asm.Type.VOID_TYPE
- case asm.Type.BOOLEAN => asm.Type.BOOLEAN_TYPE
- case asm.Type.CHAR => asm.Type.CHAR_TYPE
- case asm.Type.BYTE => asm.Type.BYTE_TYPE
- case asm.Type.SHORT => asm.Type.SHORT_TYPE
- case asm.Type.INT => asm.Type.INT_TYPE
- case asm.Type.FLOAT => asm.Type.FLOAT_TYPE
- case asm.Type.LONG => asm.Type.LONG_TYPE
- case asm.Type.DOUBLE => asm.Type.DOUBLE_TYPE
- case asm.Type.ARRAY |
- asm.Type.OBJECT => asm.Type.getObjectType(getInternalName)
- case asm.Type.METHOD => asm.Type.getMethodType(getDescriptor)
- }
- }
-
- /*
- * Unlike for ICode's REFERENCE, isBoxedType(t) implies isReferenceType(t)
- * Also, `isReferenceType(RT_NOTHING) == true` , similarly for RT_NULL.
- * Use isNullType() , isNothingType() to detect Nothing and Null.
- *
- * can-multi-thread
- */
- def hasObjectSort = (sort == BType.OBJECT)
-
- /*
- * Returns the number of dimensions of this array type. This method should
- * only be used for an array type.
- *
- * @return the number of dimensions of this array type.
- *
- * can-multi-thread
- */
- def getDimensions: Int = {
- var i = 1
- while (chrs(off + i) == '[') {
- i += 1
- }
- i
- }
-
- /*
- * Returns the (ultimate) element type of this array type.
- * This method should only be used for an array type.
- *
- * @return Returns the type of the elements of this array type.
- *
- * can-multi-thread
- */
- def getElementType: BType = {
- assert(isArray, s"Asked for the element type of a non-array type: $this")
- BType.getType(off + getDimensions)
- }
-
- /*
- * Returns the internal name of the class corresponding to this object or
- * array type. The internal name of a class is its fully qualified name (as
- * returned by Class.getName(), where '.' are replaced by '/'. This method
- * should only be used for an object or array type.
- *
- * @return the internal name of the class corresponding to this object type.
- *
- * can-multi-thread
- */
- def getInternalName: String = {
- new String(chrs, off, len)
- }
-
- /*
- * @return the suffix of the internal name until the last '/' (if '/' present), internal name otherwise.
- *
- * can-multi-thread
- */
- def getSimpleName: String = {
- assert(hasObjectSort, s"not of object sort: $toString")
- val iname = getInternalName
- val idx = iname.lastIndexOf('/')
- if (idx == -1) iname
- else iname.substring(idx + 1)
- }
-
- /*
- * Returns the argument types of methods of this type.
- * This method should only be used for method types.
- *
- * @return the argument types of methods of this type.
- *
- * can-multi-thread
- */
- def getArgumentTypes: Array[BType] = {
- BType.getArgumentTypes(off + 1)
- }
-
- /*
- * Returns the return type of methods of this type.
- * This method should only be used for method types.
- *
- * @return the return type of methods of this type.
- *
- * can-multi-thread
- */
- def getReturnType: BType = {
- assert(chrs(off) == '(', s"doesn't look like a method descriptor: $toString")
- var resPos = off + 1
- while (chrs(resPos) != ')') { resPos += 1 }
- BType.getType(resPos + 1)
- }
-
- // ------------------------------------------------------------------------
- // Inspector methods
- // ------------------------------------------------------------------------
-
- def isPrimitiveOrVoid = (sort < BType.ARRAY) // can-multi-thread
- def isValueType = (sort < BType.ARRAY) // can-multi-thread
- def isArray = (sort == BType.ARRAY) // can-multi-thread
- def isUnitType = (sort == BType.VOID) // can-multi-thread
-
- def isRefOrArrayType = { hasObjectSort || isArray } // can-multi-thread
- def isNonUnitValueType = { isValueType && !isUnitType } // can-multi-thread
-
- def isNonSpecial = { !isValueType && !isArray && !isPhantomType } // can-multi-thread
- def isNothingType = { (this == RT_NOTHING) || (this == CT_NOTHING) } // can-multi-thread
- def isNullType = { (this == RT_NULL) || (this == CT_NULL) } // can-multi-thread
- def isPhantomType = { isNothingType || isNullType } // can-multi-thread
-
- /*
- * can-multi-thread
- */
- def isBoxed = {
- this match {
- case BOXED_UNIT | BOXED_BOOLEAN | BOXED_CHAR |
- BOXED_BYTE | BOXED_SHORT | BOXED_INT |
- BOXED_FLOAT | BOXED_LONG | BOXED_DOUBLE
- => true
- case _
- => false
- }
- }
-
- /* On the JVM,
- * BOOL, BYTE, CHAR, SHORT, and INT
- * are like Ints for the purpose of lub calculation.
- *
- * can-multi-thread
- */
- def isIntSizedType = {
- (sort : @switch) match {
- case BType.BOOLEAN | BType.CHAR |
- BType.BYTE | BType.SHORT | BType.INT
- => true
- case _
- => false
- }
- }
-
- /* On the JVM, similar to isIntSizedType except that BOOL isn't integral while LONG is.
- *
- * can-multi-thread
- */
- def isIntegralType = {
- (sort : @switch) match {
- case BType.CHAR |
- BType.BYTE | BType.SHORT | BType.INT |
- BType.LONG
- => true
- case _
- => false
- }
- }
-
- /* On the JVM, FLOAT and DOUBLE.
- *
- * can-multi-thread
- */
- def isRealType = { (sort == BType.FLOAT ) || (sort == BType.DOUBLE) }
-
- def isNumericType = (isIntegralType || isRealType) // can-multi-thread
-
- /* Is this type a category 2 type in JVM terms? (ie, is it LONG or DOUBLE?)
- *
- * can-multi-thread
- */
- def isWideType = (getSize == 2)
-
- /*
- * Element vs. Component type of an array:
- * Quoting from the JVMS, Sec. 2.4 "Reference Types and Values"
- *
- * An array type consists of a component type with a single dimension (whose
- * length is not given by the type). The component type of an array type may itself be
- * an array type. If, starting from any array type, one considers its component type,
- * and then (if that is also an array type) the component type of that type, and so on,
- * eventually one must reach a component type that is not an array type; this is called
- * the element type of the array type. The element type of an array type is necessarily
- * either a primitive type, or a class type, or an interface type.
- *
- */
-
- /* The type of items this array holds.
- *
- * can-multi-thread
- */
- def getComponentType: BType = {
- assert(isArray, s"Asked for the component type of a non-array type: $this")
- BType.getType(off + 1)
- }
-
- // ------------------------------------------------------------------------
- // Conversion to type descriptors
- // ------------------------------------------------------------------------
-
- /*
- * @return the descriptor corresponding to this Java type.
- *
- * can-multi-thread
- */
- def getDescriptor: String = {
- val buf = new StringBuffer()
- getDescriptor(buf)
- buf.toString()
- }
-
- /*
- * Appends the descriptor corresponding to this Java type to the given string buffer.
- *
- * @param buf the string buffer to which the descriptor must be appended.
- *
- * can-multi-thread
- */
- private def getDescriptor(buf: StringBuffer) {
- if (isPrimitiveOrVoid) {
- // descriptor is in byte 3 of 'off' for primitive types (buf == null)
- buf.append(((off & 0xFF000000) >>> 24).asInstanceOf[Char])
- } else if (sort == BType.OBJECT) {
- buf.append('L')
- buf.append(chrs, off, len)
- buf.append(';')
- } else { // sort == ARRAY || sort == METHOD
- buf.append(chrs, off, len)
- }
- }
-
- // ------------------------------------------------------------------------
- // Corresponding size and opcodes
- // ------------------------------------------------------------------------
-
- /*
- * Returns the size of values of this type.
- * This method must not be used for method types.
- *
- * @return the size of values of this type, i.e., 2 for <tt>long</tt> and
- * <tt>double</tt>, 0 for <tt>void</tt> and 1 otherwise.
- *
- * can-multi-thread
- */
- def getSize: Int = {
- // the size is in byte 0 of 'off' for primitive types (buf == null)
- if (isPrimitiveOrVoid) (off & 0xFF) else 1
- }
-
- /*
- * Returns a JVM instruction opcode adapted to this Java type. This method
- * must not be used for method types.
- *
- * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD,
- * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL,
- * ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
- * @return an opcode that is similar to the given opcode, but adapted to
- * this Java type. For example, if this type is <tt>float</tt> and
- * <tt>opcode</tt> is IRETURN, this method returns FRETURN.
- *
- * can-multi-thread
- */
- def getOpcode(opcode: Int): Int = {
- import scala.tools.asm.Opcodes
- if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
- // the offset for IALOAD or IASTORE is in byte 1 of 'off' for
- // primitive types (buf == null)
- opcode + (if (isPrimitiveOrVoid) (off & 0xFF00) >> 8 else 4)
- } else {
- // the offset for other instructions is in byte 2 of 'off' for
- // primitive types (buf == null)
- opcode + (if (isPrimitiveOrVoid) (off & 0xFF0000) >> 16 else 4)
- }
- }
-
- // ------------------------------------------------------------------------
- // Equals, hashCode and toString
- // ------------------------------------------------------------------------
-
- /*
- * Tests if the given object is equal to this type.
- *
- * @param o the object to be compared to this type.
- * @return <tt>true</tt> if the given object is equal to this type.
- *
- * can-multi-thread
- */
- override def equals(o: Any): Boolean = {
- if (!(o.isInstanceOf[BType])) {
- return false
- }
- val t = o.asInstanceOf[BType]
- if (this eq t) {
- return true
- }
- if (sort != t.sort) {
- return false
- }
- if (sort >= BType.ARRAY) {
- if (len != t.len) {
- return false
- }
- // sort checked already
- if (off == t.off) {
- return true
- }
- var i = 0
- while (i < len) {
- if (chrs(off + i) != chrs(t.off + i)) {
- return false
- }
- i += 1
- }
- // If we reach here, we could update the largest of (this.off, t.off) to match the other, so as to simplify future == comparisons.
- // But that would require a var rather than val.
- }
- true
- }
-
- /*
- * @return a hash code value for this type.
- *
- * can-multi-thread
- */
- override def hashCode(): Int = {
- var hc = 13 * sort;
- if (sort >= BType.ARRAY) {
- var i = off
- val end = i + len
- while (i < end) {
- hc = 17 * (hc + chrs(i))
- i += 1
- }
- }
- hc
- }
-
- /*
- * @return the descriptor of this type.
- *
- * can-multi-thread
- */
- override def toString: String = { getDescriptor }
-
- }
-
- /*
- * Creates a TypeName and the BType token for it.
- * This method does not add to `innerClassBufferASM`, use `internalName()` or `asmType()` or `toTypeKind()` for that.
- *
- * must-single-thread
- */
- def brefType(iname: String): BType = { brefType(newTypeName(iname.toCharArray(), 0, iname.length())) }
-
- /*
- * Creates a BType token for the TypeName received as argument.
- * This method does not add to `innerClassBufferASM`, use `internalName()` or `asmType()` or `toTypeKind()` for that.
- *
- * can-multi-thread
- */
- def brefType(iname: TypeName): BType = { BType.getObjectType(iname.start, iname.length) }
-
- // due to keyboard economy only
- val UNIT = BType.VOID_TYPE
- val BOOL = BType.BOOLEAN_TYPE
- val CHAR = BType.CHAR_TYPE
- val BYTE = BType.BYTE_TYPE
- val SHORT = BType.SHORT_TYPE
- val INT = BType.INT_TYPE
- val LONG = BType.LONG_TYPE
- val FLOAT = BType.FLOAT_TYPE
- val DOUBLE = BType.DOUBLE_TYPE
-
- val BOXED_UNIT = brefType("java/lang/Void")
- val BOXED_BOOLEAN = brefType("java/lang/Boolean")
- val BOXED_BYTE = brefType("java/lang/Byte")
- val BOXED_SHORT = brefType("java/lang/Short")
- val BOXED_CHAR = brefType("java/lang/Character")
- val BOXED_INT = brefType("java/lang/Integer")
- val BOXED_LONG = brefType("java/lang/Long")
- val BOXED_FLOAT = brefType("java/lang/Float")
- val BOXED_DOUBLE = brefType("java/lang/Double")
-
- /*
- * RT_NOTHING and RT_NULL exist at run-time only.
- * They are the bytecode-level manifestation (in method signatures only) of what shows up as NothingClass resp. NullClass in Scala ASTs.
- * Therefore, when RT_NOTHING or RT_NULL are to be emitted,
- * a mapping is needed: the internal names of NothingClass and NullClass can't be emitted as-is.
- */
- val RT_NOTHING = brefType("scala/runtime/Nothing$")
- val RT_NULL = brefType("scala/runtime/Null$")
- val CT_NOTHING = brefType("scala/Nothing") // TODO needed?
- val CT_NULL = brefType("scala/Null") // TODO needed?
-
- val srBooleanRef = brefType("scala/runtime/BooleanRef")
- val srByteRef = brefType("scala/runtime/ByteRef")
- val srCharRef = brefType("scala/runtime/CharRef")
- val srIntRef = brefType("scala/runtime/IntRef")
- val srLongRef = brefType("scala/runtime/LongRef")
- val srFloatRef = brefType("scala/runtime/FloatRef")
- val srDoubleRef = brefType("scala/runtime/DoubleRef")
-
- /* Map from type kinds to the Java reference types.
- * Useful when pushing class literals onto the operand stack (ldc instruction taking a class literal).
- * @see Predef.classOf
- * @see genConstant()
- */
- val classLiteral = immutable.Map[BType, BType](
- UNIT -> BOXED_UNIT,
- BOOL -> BOXED_BOOLEAN,
- BYTE -> BOXED_BYTE,
- SHORT -> BOXED_SHORT,
- CHAR -> BOXED_CHAR,
- INT -> BOXED_INT,
- LONG -> BOXED_LONG,
- FLOAT -> BOXED_FLOAT,
- DOUBLE -> BOXED_DOUBLE
- )
-
- case class MethodNameAndType(mname: String, mdesc: String)
-
- val asmBoxTo: Map[BType, MethodNameAndType] = {
- Map(
- BOOL -> MethodNameAndType("boxToBoolean", "(Z)Ljava/lang/Boolean;" ) ,
- BYTE -> MethodNameAndType("boxToByte", "(B)Ljava/lang/Byte;" ) ,
- CHAR -> MethodNameAndType("boxToCharacter", "(C)Ljava/lang/Character;") ,
- SHORT -> MethodNameAndType("boxToShort", "(S)Ljava/lang/Short;" ) ,
- INT -> MethodNameAndType("boxToInteger", "(I)Ljava/lang/Integer;" ) ,
- LONG -> MethodNameAndType("boxToLong", "(J)Ljava/lang/Long;" ) ,
- FLOAT -> MethodNameAndType("boxToFloat", "(F)Ljava/lang/Float;" ) ,
- DOUBLE -> MethodNameAndType("boxToDouble", "(D)Ljava/lang/Double;" )
- )
- }
-
- val asmUnboxTo: Map[BType, MethodNameAndType] = {
- Map(
- BOOL -> MethodNameAndType("unboxToBoolean", "(Ljava/lang/Object;)Z") ,
- BYTE -> MethodNameAndType("unboxToByte", "(Ljava/lang/Object;)B") ,
- CHAR -> MethodNameAndType("unboxToChar", "(Ljava/lang/Object;)C") ,
- SHORT -> MethodNameAndType("unboxToShort", "(Ljava/lang/Object;)S") ,
- INT -> MethodNameAndType("unboxToInt", "(Ljava/lang/Object;)I") ,
- LONG -> MethodNameAndType("unboxToLong", "(Ljava/lang/Object;)J") ,
- FLOAT -> MethodNameAndType("unboxToFloat", "(Ljava/lang/Object;)F") ,
- DOUBLE -> MethodNameAndType("unboxToDouble", "(Ljava/lang/Object;)D")
- )
- }
-}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index f800dbf9cd..51bfdf0027 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -20,8 +20,8 @@ import scala.tools.nsc.io.AbstractFile
*
*/
abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
-
import global._
+ import bTypes._
/*
* must-single-thread
@@ -56,7 +56,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
/*
* can-multi-thread
*/
- def firstCommonSuffix(as: List[Tracked], bs: List[Tracked]): BType = {
+ def firstCommonSuffix(as: List[Tracked], bs: List[Tracked]): ClassBType = {
var chainA = as
var chainB = bs
var fcs: Tracked = null
@@ -77,17 +77,18 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*/
final class CClassWriter(flags: Int) extends asm.ClassWriter(flags) {
- /*
- * This method is thread re-entrant because chrs never grows during its operation (that's because all TypeNames being looked up have already been entered).
- * To stress this point, rather than using `newTypeName()` we use `lookupTypeName()`
+ /**
+ * This method is thread re-entrant because chrs never grows during its operation (that's
+ * because all TypeNames being looked up have already been entered).
+ * To stress this point, rather than using `newTypeName()` we use `lookupTypeName()`
*
- * can-multi-thread
+ * can-multi-thread
*/
override def getCommonSuperClass(inameA: String, inameB: String): String = {
- val a = brefType(lookupTypeName(inameA.toCharArray))
- val b = brefType(lookupTypeName(inameB.toCharArray))
+ val a = ClassBType(lookupTypeName(inameA.toCharArray))
+ val b = ClassBType(lookupTypeName(inameB.toCharArray))
val lca = jvmWiseLUB(a, b)
- val lcaName = lca.getInternalName // don't call javaName because that side-effects innerClassBuffer.
+ val lcaName = lca.internalName // don't call javaName because that side-effects innerClassBuffer.
assert(lcaName != "scala/Any")
lcaName // ASM caches the answer during the lifetime of a ClassWriter. We outlive that. Not sure whether caching on our side would improve things.
@@ -95,16 +96,17 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
}
- /*
- * Finding the least upper bound in agreement with the bytecode verifier (given two internal names handed out by ASM)
- * Background:
- * http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf
- * http://comments.gmane.org/gmane.comp.java.vm.languages/2293
- * https://issues.scala-lang.org/browse/SI-3872
+ /**
+ * Finding the least upper bound in agreement with the bytecode verifier (given two internal names
+ * handed out by ASM)
+ * Background:
+ * http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf
+ * http://comments.gmane.org/gmane.comp.java.vm.languages/2293
+ * https://issues.scala-lang.org/browse/SI-3872
*
- * can-multi-thread
+ * can-multi-thread
*/
- def jvmWiseLUB(a: BType, b: BType): BType = {
+ def jvmWiseLUB(a: ClassBType, b: ClassBType): ClassBType = {
assert(a.isNonSpecial, s"jvmWiseLUB() received a non-plain-class $a")
assert(b.isNonSpecial, s"jvmWiseLUB() received a non-plain-class $b")
@@ -401,14 +403,14 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*
* must-single-thread
*/
- final def internalName(sym: Symbol): String = { asmClassType(sym).getInternalName }
+ final def internalName(sym: Symbol): String = asmClassType(sym).internalName
/*
* Tracks (if needed) the inner class given by `sym`.
*
* must-single-thread
*/
- final def asmClassType(sym: Symbol): BType = {
+ final def asmClassType(sym: Symbol): ClassBType = {
assert(
hasInternalName(sym),
{
@@ -514,17 +516,18 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
case _: ConstantType => toTypeKind(t.underlying)
case TypeRef(_, sym, args) =>
- if (sym == ArrayClass) arrayOf(toTypeKind(args.head))
+ if (sym == ArrayClass) ArrayBType(toTypeKind(args.head))
else primitiveOrRefType2(sym)
case ClassInfoType(_, _, sym) =>
assert(sym != ArrayClass, "ClassInfoType to ArrayClass!")
primitiveOrRefType(sym)
+ // TODO @lry check below comments / todo's
// !!! Iulian says types which make no sense after erasure should not reach here, which includes the ExistentialType, AnnotatedType, RefinedType.
case ExistentialType(_, t) => toTypeKind(t) // TODO shouldn't get here but the following does: akka-actor/src/main/scala/akka/util/WildcardTree.scala
case AnnotatedType(_, w) => toTypeKind(w) // TODO test/files/jvm/annotations.scala causes an AnnotatedType to reach here.
- case RefinedType(parents, _) => parents map toTypeKind reduceLeft jvmWiseLUB
+ case RefinedType(parents, _) => parents.map(toTypeKind(_).asClassBType) reduceLeft jvmWiseLUB
// For sure WildcardTypes shouldn't reach here either, but when debugging such situations this may come in handy.
// case WildcardType => REFERENCE(ObjectClass)
@@ -538,12 +541,12 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
/*
* must-single-thread
*/
- def asmMethodType(msym: Symbol): BType = {
+ def asmMethodType(msym: Symbol): MethodBType = {
assert(msym.isMethod, s"not a method-symbol: $msym")
val resT: BType =
- if (msym.isClassConstructor || msym.isConstructor) BType.VOID_TYPE
- else toTypeKind(msym.tpe.resultType);
- BType.getMethodType( resT, mkArray(msym.tpe.paramTypes map toTypeKind) )
+ if (msym.isClassConstructor || msym.isConstructor) UNIT
+ else toTypeKind(msym.tpe.resultType)
+ MethodBType(msym.tpe.paramTypes map toTypeKind, resT)
}
/*
@@ -577,14 +580,14 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*
* must-single-thread
*/
- final def descriptor(t: Type): String = { toTypeKind(t).getDescriptor }
+ final def descriptor(t: Type): String = { toTypeKind(t).descriptor }
/*
* Tracks (if needed) the inner class given by `sym`.
*
* must-single-thread
*/
- final def descriptor(sym: Symbol): String = { asmClassType(sym).getDescriptor }
+ final def descriptor(sym: Symbol): String = { asmClassType(sym).descriptor }
} // end of trait BCInnerClassGen
@@ -800,7 +803,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
val thrownExceptions: List[String] = getExceptions(throws)
val jReturnType = toTypeKind(methodInfo.resultType)
- val mdesc = BType.getMethodType(jReturnType, mkArray(paramJavaTypes)).getDescriptor
+ val mdesc = MethodBType(paramJavaTypes, jReturnType).descriptor
val mirrorMethodName = m.javaSimpleName.toString
val mirrorMethod: asm.MethodVisitor = jclass.visitMethod(
flags,
@@ -819,13 +822,13 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
var index = 0
for(jparamType <- paramJavaTypes) {
- mirrorMethod.visitVarInsn(jparamType.getOpcode(asm.Opcodes.ILOAD), index)
- assert(jparamType.sort != BType.METHOD, jparamType)
- index += jparamType.getSize
+ mirrorMethod.visitVarInsn(jparamType.typedOpcode(asm.Opcodes.ILOAD), index)
+ assert(!jparamType.isInstanceOf[MethodBType], jparamType)
+ index += jparamType.size
}
- mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, asmMethodType(m).getDescriptor)
- mirrorMethod.visitInsn(jReturnType.getOpcode(asm.Opcodes.IRETURN))
+ mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, asmMethodType(m).descriptor, false)
+ mirrorMethod.visitInsn(jReturnType.typedOpcode(asm.Opcodes.IRETURN))
mirrorMethod.visitMaxs(0, 0) // just to follow protocol, dummy arguments
mirrorMethod.visitEnd()
@@ -993,7 +996,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
flags,
mirrorName,
null /* no java-generic-signature */,
- JAVA_LANG_OBJECT.getInternalName,
+ JAVA_LANG_OBJECT.internalName,
EMPTY_STRING_ARRAY
)
@@ -1085,12 +1088,11 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
EMPTY_STRING_ARRAY // no throwable exceptions
)
- val stringArrayJType: BType = arrayOf(JAVA_LANG_STRING)
- val conJType: BType =
- BType.getMethodType(
- BType.VOID_TYPE,
- Array(exemplar(definitions.ClassClass).c, stringArrayJType, stringArrayJType)
- )
+ val stringArrayJType: BType = ArrayBType(JAVA_LANG_STRING)
+ val conJType: BType = MethodBType(
+ exemplar(definitions.ClassClass).c :: stringArrayJType :: stringArrayJType :: Nil,
+ UNIT
+ )
def push(lst: List[String]) {
var fi = 0
@@ -1099,7 +1101,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
constructor.visitLdcInsn(new java.lang.Integer(fi))
if (f == null) { constructor.visitInsn(asm.Opcodes.ACONST_NULL) }
else { constructor.visitLdcInsn(f) }
- constructor.visitInsn(JAVA_LANG_STRING.getOpcode(asm.Opcodes.IASTORE))
+ constructor.visitInsn(JAVA_LANG_STRING.typedOpcode(asm.Opcodes.IASTORE))
fi += 1
}
}
@@ -1108,21 +1110,21 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
constructor.visitVarInsn(asm.Opcodes.ALOAD, 0)
// push the class
- constructor.visitLdcInsn(exemplar(cls).c)
+ constructor.visitLdcInsn(exemplar(cls).c.toASMType)
// push the string array of field information
constructor.visitLdcInsn(new java.lang.Integer(fieldList.length))
- constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, JAVA_LANG_STRING.getInternalName)
+ constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, JAVA_LANG_STRING.internalName)
push(fieldList)
// push the string array of method information
constructor.visitLdcInsn(new java.lang.Integer(methodList.length))
- constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, JAVA_LANG_STRING.getInternalName)
+ constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, JAVA_LANG_STRING.internalName)
push(methodList)
// invoke the superclass constructor, which will do the
// necessary java reflection and create Method objects.
- constructor.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, "scala/beans/ScalaBeanInfo", INSTANCE_CONSTRUCTOR_NAME, conJType.getDescriptor)
+ constructor.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, "scala/beans/ScalaBeanInfo", INSTANCE_CONSTRUCTOR_NAME, conJType.descriptor, false)
constructor.visitInsn(asm.Opcodes.RETURN)
constructor.visitMaxs(0, 0) // just to follow protocol, dummy arguments
@@ -1161,7 +1163,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
def legacyAddCreatorCode(clinit: asm.MethodVisitor, cnode: asm.tree.ClassNode, thisName: String) {
// this tracks the inner class in innerClassBufferASM, if needed.
val androidCreatorType = asmClassType(AndroidCreatorClass)
- val tdesc_creator = androidCreatorType.getDescriptor
+ val tdesc_creator = androidCreatorType.descriptor
cnode.visitField(
PublicStaticFinal,
@@ -1182,12 +1184,13 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
)
// INVOKEVIRTUAL `moduleName`.CREATOR() : android.os.Parcelable$Creator;
- val bt = BType.getMethodType(androidCreatorType, Array.empty[BType])
+ val bt = MethodBType(Nil, androidCreatorType)
clinit.visitMethodInsn(
asm.Opcodes.INVOKEVIRTUAL,
moduleName,
"CREATOR",
- bt.getDescriptor
+ bt.descriptor,
+ false
)
// PUTSTATIC `thisName`.CREATOR;
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
index c3492b79a9..9b7c975960 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
@@ -9,8 +9,7 @@ package backend.jvm
import scala.tools.asm
import scala.annotation.switch
-import scala.collection.{ immutable, mutable }
-import collection.convert.Wrappers.JListWrapper
+import scala.collection.mutable
/*
* A high-level facade to the ASM API for bytecode generation.
@@ -19,9 +18,17 @@ import collection.convert.Wrappers.JListWrapper
* @version 1.0
*
*/
-abstract class BCodeIdiomatic extends BCodeGlue {
+abstract class BCodeIdiomatic extends SubComponent {
+ protected val bCodeICodeCommon: BCodeICodeCommon[global.type] = new BCodeICodeCommon(global)
+
+ val bTypes = new BTypes[global.type](global) {
+ def chrs = global.chrs
+ override type BTypeName = global.TypeName
+ override def createNewName(s: String) = global.newTypeName(s)
+ }
import global._
+ import bTypes._
val classfileVersion: Int = settings.target.value match {
case "jvm-1.5" => asm.Opcodes.V1_5
@@ -44,12 +51,12 @@ abstract class BCodeIdiomatic extends BCodeGlue {
val CLASS_CONSTRUCTOR_NAME = "<clinit>"
val INSTANCE_CONSTRUCTOR_NAME = "<init>"
- val ObjectReference = brefType("java/lang/Object")
+ val ObjectReference = ClassBType("java/lang/Object")
val AnyRefReference = ObjectReference
- val objArrayReference = arrayOf(ObjectReference)
+ val objArrayReference = ArrayBType(ObjectReference)
val JAVA_LANG_OBJECT = ObjectReference
- val JAVA_LANG_STRING = brefType("java/lang/String")
+ val JAVA_LANG_STRING = ClassBType("java/lang/String")
var StringBuilderReference: BType = null
@@ -108,17 +115,6 @@ abstract class BCodeIdiomatic extends BCodeGlue {
a
}
- /*
- * The type of 1-dimensional arrays of `elem` type.
- * The invoker is responsible for tracking (if needed) the inner class given by the elem BType.
- *
- * must-single-thread
- */
- final def arrayOf(elem: BType): BType = {
- assert(!(elem.isUnitType), s"The element type of an array can't be: $elem")
- brefType("[" + elem.getDescriptor)
- }
-
/* Just a namespace for utilities that encapsulate MethodVisitor idioms.
* In the ASM world, org.objectweb.asm.commons.InstructionAdapter plays a similar role,
* but the methods here allow choosing when to transition from ICode to ASM types
@@ -242,12 +238,12 @@ abstract class BCodeIdiomatic extends BCodeGlue {
final def genStringConcat(el: BType) {
val jtype =
- if (el.isArray || el.hasObjectSort) JAVA_LANG_OBJECT
- else el;
+ if (el.isArray || el.isClass) JAVA_LANG_OBJECT
+ else el
- val bt = BType.getMethodType(StringBuilderReference, Array(jtype))
+ val bt = MethodBType(List(jtype), StringBuilderReference)
- invokevirtual(StringBuilderClassName, "append", bt.getDescriptor)
+ invokevirtual(StringBuilderClassName, "append", bt.descriptor)
}
/*
@@ -268,7 +264,7 @@ abstract class BCodeIdiomatic extends BCodeGlue {
final def emitT2T(from: BType, to: BType) {
assert(
- from.isNonUnitValueType && to.isNonUnitValueType,
+ from.isNonVoidPrimitiveType && to.isNonVoidPrimitiveType,
s"Cannot emit primitive conversion from $from to $to"
)
@@ -290,37 +286,37 @@ abstract class BCodeIdiomatic extends BCodeGlue {
assert(from != BOOL && to != BOOL, s"inconvertible types : $from -> $to")
// We're done with BOOL already
- (from.sort: @switch) match {
+ from match {
// using `asm.Type.SHORT` instead of `BType.SHORT` because otherwise "warning: could not emit switch for @switch annotated match"
- case asm.Type.BYTE => pickOne(JCodeMethodN.fromByteT2T)
- case asm.Type.SHORT => pickOne(JCodeMethodN.fromShortT2T)
- case asm.Type.CHAR => pickOne(JCodeMethodN.fromCharT2T)
- case asm.Type.INT => pickOne(JCodeMethodN.fromIntT2T)
+ case BYTE => pickOne(JCodeMethodN.fromByteT2T)
+ case SHORT => pickOne(JCodeMethodN.fromShortT2T)
+ case CHAR => pickOne(JCodeMethodN.fromCharT2T)
+ case INT => pickOne(JCodeMethodN.fromIntT2T)
- case asm.Type.FLOAT =>
+ case FLOAT =>
import asm.Opcodes.{ F2L, F2D, F2I }
- (to.sort: @switch) match {
- case asm.Type.LONG => emit(F2L)
- case asm.Type.DOUBLE => emit(F2D)
- case _ => emit(F2I); emitT2T(INT, to)
+ to match {
+ case LONG => emit(F2L)
+ case DOUBLE => emit(F2D)
+ case _ => emit(F2I); emitT2T(INT, to)
}
- case asm.Type.LONG =>
+ case LONG =>
import asm.Opcodes.{ L2F, L2D, L2I }
- (to.sort: @switch) match {
- case asm.Type.FLOAT => emit(L2F)
- case asm.Type.DOUBLE => emit(L2D)
- case _ => emit(L2I); emitT2T(INT, to)
+ to match {
+ case FLOAT => emit(L2F)
+ case DOUBLE => emit(L2D)
+ case _ => emit(L2I); emitT2T(INT, to)
}
- case asm.Type.DOUBLE =>
+ case DOUBLE =>
import asm.Opcodes.{ D2L, D2F, D2I }
- (to.sort: @switch) match {
- case asm.Type.FLOAT => emit(D2F)
- case asm.Type.LONG => emit(D2L)
- case _ => emit(D2I); emitT2T(INT, to)
+ to match {
+ case FLOAT => emit(D2F)
+ case LONG => emit(D2L)
+ case _ => emit(D2I); emitT2T(INT, to)
}
}
} // end of emitT2T()
@@ -372,24 +368,26 @@ abstract class BCodeIdiomatic extends BCodeGlue {
// can-multi-thread
final def newarray(elem: BType) {
- if (elem.isRefOrArrayType || elem.isPhantomType ) {
- /* phantom type at play in `Array(null)`, SI-1513. On the other hand, Array(()) has element type `scala.runtime.BoxedUnit` which hasObjectSort. */
- jmethod.visitTypeInsn(Opcodes.ANEWARRAY, elem.getInternalName)
- } else {
- val rand = {
- // using `asm.Type.SHORT` instead of `BType.SHORT` because otherwise "warning: could not emit switch for @switch annotated match"
- (elem.sort: @switch) match {
- case asm.Type.BOOLEAN => Opcodes.T_BOOLEAN
- case asm.Type.BYTE => Opcodes.T_BYTE
- case asm.Type.SHORT => Opcodes.T_SHORT
- case asm.Type.CHAR => Opcodes.T_CHAR
- case asm.Type.INT => Opcodes.T_INT
- case asm.Type.LONG => Opcodes.T_LONG
- case asm.Type.FLOAT => Opcodes.T_FLOAT
- case asm.Type.DOUBLE => Opcodes.T_DOUBLE
+ elem match {
+ case c: RefBType =>
+ /* phantom type at play in `Array(null)`, SI-1513. On the other hand, Array(()) has element type `scala.runtime.BoxedUnit` which isObject. */
+ jmethod.visitTypeInsn(Opcodes.ANEWARRAY, c.classOrArrayType)
+ case _ =>
+ assert(elem.isNonVoidPrimitiveType)
+ val rand = {
+ // using `asm.Type.SHORT` instead of `BType.SHORT` because otherwise "warning: could not emit switch for @switch annotated match"
+ elem match {
+ case BOOL => Opcodes.T_BOOLEAN
+ case BYTE => Opcodes.T_BYTE
+ case SHORT => Opcodes.T_SHORT
+ case CHAR => Opcodes.T_CHAR
+ case INT => Opcodes.T_INT
+ case LONG => Opcodes.T_LONG
+ case FLOAT => Opcodes.T_FLOAT
+ case DOUBLE => Opcodes.T_DOUBLE
+ }
}
- }
- jmethod.visitIntInsn(Opcodes.NEWARRAY, rand)
+ jmethod.visitIntInsn(Opcodes.NEWARRAY, rand)
}
}
@@ -409,19 +407,19 @@ abstract class BCodeIdiomatic extends BCodeGlue {
// can-multi-thread
final def invokespecial(owner: String, name: String, desc: String) {
- jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc)
+ jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, false)
}
// can-multi-thread
final def invokestatic(owner: String, name: String, desc: String) {
- jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc)
+ jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, false)
}
// can-multi-thread
final def invokeinterface(owner: String, name: String, desc: String) {
- jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc)
+ jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc, true)
}
// can-multi-thread
final def invokevirtual(owner: String, name: String, desc: String) {
- jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc)
+ jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false)
}
// can-multi-thread
@@ -529,7 +527,7 @@ abstract class BCodeIdiomatic extends BCodeGlue {
// can-multi-thread
final def emitVarInsn(opc: Int, idx: Int, tk: BType) {
assert((opc == Opcodes.ILOAD) || (opc == Opcodes.ISTORE), opc)
- jmethod.visitVarInsn(tk.getOpcode(opc), idx)
+ jmethod.visitVarInsn(tk.typedOpcode(opc), idx)
}
// ---------------- array load and store ----------------
@@ -538,7 +536,7 @@ abstract class BCodeIdiomatic extends BCodeGlue {
final def emitTypeBased(opcs: Array[Int], tk: BType) {
assert(tk != UNIT, tk)
val opc = {
- if (tk.isRefOrArrayType) { opcs(0) }
+ if (tk.isRef) { opcs(0) }
else if (tk.isIntSizedType) {
(tk: @unchecked) match {
case BOOL | BYTE => opcs(1)
@@ -563,11 +561,11 @@ abstract class BCodeIdiomatic extends BCodeGlue {
final def emitPrimitive(opcs: Array[Int], tk: BType) {
val opc = {
// using `asm.Type.SHORT` instead of `BType.SHORT` because otherwise "warning: could not emit switch for @switch annotated match"
- (tk.sort: @switch) match {
- case asm.Type.LONG => opcs(1)
- case asm.Type.FLOAT => opcs(2)
- case asm.Type.DOUBLE => opcs(3)
- case _ => opcs(0)
+ tk match {
+ case LONG => opcs(1)
+ case FLOAT => opcs(2)
+ case DOUBLE => opcs(3)
+ case _ => opcs(0)
}
}
emit(opc)
@@ -582,15 +580,14 @@ abstract class BCodeIdiomatic extends BCodeGlue {
// ---------------- type checks and casts ----------------
// can-multi-thread
- final def isInstance(tk: BType) {
- jmethod.visitTypeInsn(Opcodes.INSTANCEOF, tk.getInternalName)
+ final def isInstance(tk: RefBType): Unit = {
+ jmethod.visitTypeInsn(Opcodes.INSTANCEOF, tk.classOrArrayType)
}
// can-multi-thread
- final def checkCast(tk: BType) {
- assert(tk.isRefOrArrayType, s"checkcast on primitive type: $tk")
+ final def checkCast(tk: RefBType): Unit = {
// TODO ICode also requires: but that's too much, right? assert(!isBoxedType(tk), "checkcast on boxed type: " + tk)
- jmethod.visitTypeInsn(Opcodes.CHECKCAST, tk.getInternalName)
+ jmethod.visitTypeInsn(Opcodes.CHECKCAST, tk.classOrArrayType)
}
} // end of class JCodeMethodN
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
index 360ce58ecc..effc68c5e3 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
@@ -14,6 +14,8 @@ import scala.tools.nsc.symtab._
import scala.annotation.switch
import scala.tools.asm
+import scala.tools.asm.util.{TraceMethodVisitor, ASMifier}
+import java.io.PrintWriter
/*
*
@@ -23,6 +25,7 @@ import scala.tools.asm
*/
abstract class BCodeSkelBuilder extends BCodeHelpers {
import global._
+ import bTypes._
/*
* There's a dedicated PlainClassBuilder for each CompilationUnit,
@@ -114,9 +117,13 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
addClassFields()
innerClassBufferASM ++= trackMemberClasses(claszSymbol, Nil)
-
gen(cd.impl)
+ addInnerClassesASM(cnode, innerClassBufferASM.toList)
+
+ if (AsmUtils.traceClassEnabled && cnode.name.contains(AsmUtils.traceClassPattern))
+ AsmUtils.traceClass(cnode)
+ cnode.innerClasses
assert(cd.symbol == claszSymbol, "Someone messed up BCodePhase.claszSymbol during genPlainClass().")
} // end of method genPlainClass()
@@ -127,7 +134,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
private def initJClass(jclass: asm.ClassVisitor) {
val ps = claszSymbol.info.parents
- val superClass: String = if (ps.isEmpty) JAVA_LANG_OBJECT.getInternalName else internalName(ps.head.typeSymbol);
+ val superClass: String = if (ps.isEmpty) JAVA_LANG_OBJECT.internalName else internalName(ps.head.typeSymbol);
val ifaces: Array[String] = {
val arrIfacesTr: Array[Tracked] = exemplar(claszSymbol).ifaces
val arrIfaces = new Array[String](arrIfacesTr.length)
@@ -136,7 +143,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
val ifaceTr = arrIfacesTr(i)
val bt = ifaceTr.c
if (ifaceTr.isInnerClass) { innerClassBufferASM += bt }
- arrIfaces(i) = bt.getInternalName
+ arrIfaces(i) = bt.internalName
i += 1
}
arrIfaces
@@ -160,7 +167,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
val enclM = getEnclosingMethodAttribute(claszSymbol)
if (enclM != null) {
val EnclMethodEntry(className, methodName, methodType) = enclM
- cnode.visitOuterClass(className, methodName, methodType.getDescriptor)
+ cnode.visitOuterClass(className, methodName, methodType.descriptor)
}
val ssa = getAnnotPickle(thisName, claszSymbol)
@@ -228,7 +235,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
if (isCZStaticModule) {
clinit.visitTypeInsn(asm.Opcodes.NEW, thisName)
clinit.visitMethodInsn(asm.Opcodes.INVOKESPECIAL,
- thisName, INSTANCE_CONSTRUCTOR_NAME, "()V")
+ thisName, INSTANCE_CONSTRUCTOR_NAME, "()V", false)
}
if (isCZParcelable) { legacyAddCreatorCode(clinit, cnode, thisName) }
clinit.visitInsn(asm.Opcodes.RETURN)
@@ -255,7 +262,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
val jfield = new asm.tree.FieldNode(
flags,
f.javaSimpleName.toString,
- symInfoTK(f).getDescriptor,
+ symInfoTK(f).descriptor,
javagensig,
null // no initial value
)
@@ -391,8 +398,8 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
assert(nxtIdx != -1, "not a valid start index")
val loc = Local(tk, sym.javaSimpleName.toString, nxtIdx, sym.isSynthetic)
slots += (sym -> loc)
- assert(tk.getSize > 0, "makeLocal called for a symbol whose type is Unit.")
- nxtIdx += tk.getSize
+ assert(tk.size > 0, "makeLocal called for a symbol whose type is Unit.")
+ nxtIdx += tk.size
loc
}
@@ -525,7 +532,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
if (isMethSymStaticCtor) CLASS_CONSTRUCTOR_NAME
else jMethodName
- val mdesc = asmMethodType(methSymbol).getDescriptor
+ val mdesc = asmMethodType(methSymbol).descriptor
mnode = cnode.visitMethod(
flags,
bytecodeName,
@@ -549,7 +556,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
methSymbol = dd.symbol
jMethodName = methSymbol.javaSimpleName.toString
- returnType = asmMethodType(dd.symbol).getReturnType
+ returnType = asmMethodType(dd.symbol).returnType
isMethSymStaticCtor = methSymbol.isStaticConstructor
resetMethodBookkeeping(dd)
@@ -639,6 +646,10 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
// Note we don't invoke visitMax, thus there are no FrameNode among mnode.instructions.
// The only non-instruction nodes to be found are LabelNode and LineNumberNode.
}
+
+ if (AsmUtils.traceMethodEnabled && mnode.name.contains(AsmUtils.traceMethodPattern))
+ AsmUtils.traceMethod(mnode)
+
mnode = null
} // end of method genDefDef()
@@ -675,8 +686,8 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
val callee = methSymbol.enclClass.primaryConstructor
val jname = callee.javaSimpleName.toString
val jowner = internalName(callee.owner)
- val jtype = asmMethodType(callee).getDescriptor
- insnModB = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESPECIAL, jowner, jname, jtype)
+ val jtype = asmMethodType(callee).descriptor
+ insnModB = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESPECIAL, jowner, jname, jtype, false)
}
var insnParcA: asm.tree.AbstractInsnNode = null
@@ -684,7 +695,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
// android creator code
if (isCZParcelable) {
// add a static field ("CREATOR") to this class to cache android.os.Parcelable$Creator
- val andrFieldDescr = asmClassType(AndroidCreatorClass).getDescriptor
+ val andrFieldDescr = asmClassType(AndroidCreatorClass).descriptor
cnode.visitField(
asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL,
"CREATOR",
@@ -696,8 +707,8 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
val callee = definitions.getMember(claszSymbol.companionModule, androidFieldName)
val jowner = internalName(callee.owner)
val jname = callee.javaSimpleName.toString
- val jtype = asmMethodType(callee).getDescriptor
- insnParcA = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESTATIC, jowner, jname, jtype)
+ val jtype = asmMethodType(callee).descriptor
+ insnParcA = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESTATIC, jowner, jname, jtype, false)
// PUTSTATIC `thisName`.CREATOR;
insnParcB = new asm.tree.FieldInsnNode(asm.Opcodes.PUTSTATIC, thisName, "CREATOR", andrFieldDescr)
}
@@ -713,7 +724,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
def emitLocalVarScope(sym: Symbol, start: asm.Label, end: asm.Label, force: Boolean = false) {
val Local(tk, name, idx, isSynth) = locals(sym)
if (force || !isSynth) {
- mnode.visitLocalVariable(name, tk.getDescriptor, null, start, end, idx)
+ mnode.visitLocalVariable(name, tk.descriptor, null, start, end, idx)
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala
index 9ddb7a3ce8..c271e7b129 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala
@@ -9,9 +9,7 @@ package tools.nsc
package backend
package jvm
-import scala.collection.{ mutable, immutable }
-import scala.annotation.switch
-
+import scala.collection.immutable
import scala.tools.asm
/*
@@ -22,6 +20,7 @@ import scala.tools.asm
*/
abstract class BCodeSyncAndTry extends BCodeBodyBuilder {
import global._
+ import bTypes._
/*
@@ -184,7 +183,7 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder {
val caseHandlers: List[EHClause] =
for (CaseDef(pat, _, caseBody) <- catches) yield {
pat match {
- case Typed(Ident(nme.WILDCARD), tpt) => NamelessEH(tpeTK(tpt), caseBody)
+ case Typed(Ident(nme.WILDCARD), tpt) => NamelessEH(tpeTK(tpt).asClassBType, caseBody)
case Ident(nme.WILDCARD) => NamelessEH(ThrowableReference, caseBody)
case Bind(_, _) => BoundEH (pat.symbol, caseBody)
}
@@ -250,7 +249,7 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder {
// (2.a) emit case clause proper
val startHandler = currProgramPoint()
var endHandler: asm.Label = null
- var excType: BType = null
+ var excType: ClassBType = null
registerCleanup(finCleanup)
ch match {
case NamelessEH(typeToDrop, caseBody) =>
@@ -269,7 +268,7 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder {
nopIfNeeded(startHandler)
endHandler = currProgramPoint()
emitLocalVarScope(patSymbol, startHandler, endHandler)
- excType = patTK
+ excType = patTK.asClassBType
}
unregisterCleanup(finCleanup)
// (2.b) mark the try-body as protected by this case clause.
@@ -357,10 +356,10 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder {
}
}
- def protect(start: asm.Label, end: asm.Label, handler: asm.Label, excType: BType) {
+ def protect(start: asm.Label, end: asm.Label, handler: asm.Label, excType: ClassBType) {
val excInternalName: String =
if (excType == null) null
- else excType.getInternalName
+ else excType.internalName
assert(start != end, "protecting a range of zero instructions leads to illegal class format. Solution: add a NOP to that range.")
mnode.visitTryCatchBlock(start, end, handler, excInternalName)
}
@@ -387,7 +386,7 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder {
def mayCleanStack(tree: Tree): Boolean = tree exists { t => t.isInstanceOf[Try] }
trait EHClause
- case class NamelessEH(typeToDrop: BType, caseBody: Tree) extends EHClause
+ case class NamelessEH(typeToDrop: ClassBType, caseBody: Tree) extends EHClause
case class BoundEH (patSymbol: Symbol, caseBody: Tree) extends EHClause
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
index 1eca69936a..62dfb4917d 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
@@ -18,27 +18,25 @@ import scala.collection.{ immutable, mutable }
*
*/
abstract class BCodeTypes extends BCodeIdiomatic {
-
import global._
+ import bTypes._
// when compiling the Scala library, some assertions don't hold (e.g., scala.Boolean has null superClass although it's not an interface)
val isCompilingStdLib = !(settings.sourcepath.isDefault)
- val srBoxedUnit = brefType("scala/runtime/BoxedUnit")
-
// special names
- var StringReference : BType = null
- var ThrowableReference : BType = null
- var jlCloneableReference : BType = null // java/lang/Cloneable
- var jlNPEReference : BType = null // java/lang/NullPointerException
- var jioSerializableReference : BType = null // java/io/Serializable
- var scalaSerializableReference : BType = null // scala/Serializable
- var classCastExceptionReference : BType = null // java/lang/ClassCastException
+ var StringReference : ClassBType = null
+ var ThrowableReference : ClassBType = null
+ var jlCloneableReference : ClassBType = null // java/lang/Cloneable
+ var jlNPEReference : ClassBType = null // java/lang/NullPointerException
+ var jioSerializableReference : ClassBType = null // java/io/Serializable
+ var scalaSerializableReference : ClassBType = null // scala/Serializable
+ var classCastExceptionReference : ClassBType = null // java/lang/ClassCastException
/* A map from scala primitive type-symbols to BTypes */
var primitiveTypeMap: Map[Symbol, BType] = null
/* A map from scala type-symbols for Nothing and Null to (runtime version) BTypes */
- var phantomTypeMap: Map[Symbol, BType] = null
+ var phantomTypeMap: Map[Symbol, ClassBType] = null
/* Maps the method symbol for a box method to the boxed type of the result.
* For example, the method symbol for `Byte.box()`) is mapped to the BType `Ljava/lang/Integer;`. */
var boxResultType: Map[Symbol, BType] = null
@@ -63,10 +61,10 @@ abstract class BCodeTypes extends BCodeIdiomatic {
val AbstractFunctionReference = new Array[Tracked](definitions.MaxFunctionArity + 1)
val abstractFunctionArityMap = mutable.Map.empty[BType, Int]
- var PartialFunctionReference: BType = null // scala.PartialFunction
- var AbstractPartialFunctionReference: BType = null // scala.runtime.AbstractPartialFunction
+ var PartialFunctionReference: ClassBType = null // scala.PartialFunction
+ var AbstractPartialFunctionReference: ClassBType = null // scala.runtime.AbstractPartialFunction
- var BoxesRunTime: BType = null
+ var BoxesRunTime: ClassBType = null
/*
* must-single-thread
@@ -107,7 +105,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
// Other than that, they aren't needed there (e.g., `isSubtypeOf()` special-cases boxed classes, similarly for others).
val boxedClasses = List(BoxedBooleanClass, BoxedCharacterClass, BoxedByteClass, BoxedShortClass, BoxedIntClass, BoxedLongClass, BoxedFloatClass, BoxedDoubleClass)
for(csym <- boxedClasses) {
- val key = brefType(csym.javaBinaryName.toTypeName)
+ val key = ClassBType(csym.javaBinaryName.toTypeName)
val tr = buildExemplar(key, csym)
symExemplars.put(csym, tr)
exemplars.put(tr.c, tr)
@@ -147,16 +145,6 @@ abstract class BCodeTypes extends BCodeIdiomatic {
scalaSerializableReference = exemplar(SerializableClass).c
classCastExceptionReference = exemplar(ClassCastExceptionClass).c
- /*
- * The bytecode emitter special-cases String concatenation, in that three methods of `JCodeMethodN`
- * ( `genStartConcat()` , `genStringConcat()` , and `genEndConcat()` )
- * don't obtain the method descriptor of the callee via `asmMethodType()` (as normally done)
- * but directly emit callsites on StringBuilder using literal constant for method descriptors.
- * In order to make sure those method descriptors are available as BTypes, they are initialized here.
- */
- BType.getMethodType("()V") // necessary for JCodeMethodN.genStartConcat
- BType.getMethodType("()Ljava/lang/String;") // necessary for JCodeMethodN.genEndConcat
-
PartialFunctionReference = exemplar(PartialFunctionClass).c
for(idx <- 0 to definitions.MaxFunctionArity) {
FunctionReference(idx) = exemplar(FunctionClass(idx))
@@ -165,12 +153,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
AbstractPartialFunctionReference = exemplar(AbstractPartialFunctionClass).c
}
- // later a few analyses (e.g. refreshInnerClasses) will look up BTypes based on descriptors in instructions
- // we make sure those BTypes can be found via lookup as opposed to creating them on the fly.
- BoxesRunTime = brefType("scala/runtime/BoxesRunTime")
- asmBoxTo.values foreach { mnat: MethodNameAndType => BType.getMethodType(mnat.mdesc) }
- asmUnboxTo.values foreach { mnat: MethodNameAndType => BType.getMethodType(mnat.mdesc) }
-
+ BoxesRunTime = ClassBType("scala/runtime/BoxesRunTime")
}
/*
@@ -191,28 +174,41 @@ abstract class BCodeTypes extends BCodeIdiomatic {
// allowing answering `conforms()` without resorting to typer.
// ------------------------------------------------
- val exemplars = new java.util.concurrent.ConcurrentHashMap[BType, Tracked]
- val symExemplars = new java.util.concurrent.ConcurrentHashMap[Symbol, Tracked]
+ /**
+ * TODO @lry should probably be a map form ClassBType to Tracked
+ */
+ val exemplars = new java.util.concurrent.ConcurrentHashMap[BType, Tracked]
- /*
- * Typically, a question about a BType can be answered only by using the BType as lookup key in one or more maps.
- * A `Tracked` object saves time by holding together information required to answer those questions:
+ /**
+ * Maps class symbols to their corresponding `Tracked` instance.
+ */
+ val symExemplars = new java.util.concurrent.ConcurrentHashMap[Symbol, Tracked]
+
+ /**
+ * A `Tracked` instance stores information about a BType. This allows ansering type questions
+ * without resolving to the compiler, in a thread-safe manner, in particular isSubtypeOf.
*
- * - `sc` denotes the bytecode-level superclass if any, null otherwise
+ * @param c the BType described by this `Tracked`
+ * @param flags the java flags for the type, computed by BCodeTypes#javaFlags
+ * @param sc the bytecode-level superclass if any, null otherwise
+ * @param ifaces the interfaces explicitly declared. Not included are those transitively
+ * supported, but the utility method `allLeafIfaces()` can be used for that.
+ * @param innersChain the containing classes for a non-package-level class `c`, null otherwise.
*
- * - `ifaces` denotes the interfaces explicitly declared.
- * Not included are those transitively supported, but the utility method `allLeafIfaces()` can be used for that.
+ * Note: the optimizer may inline anonymous closures, thus eliding those inner classes (no
+ * physical class file is emitted for elided classes). Before committing `innersChain` to
+ * bytecode, cross-check with the list of elided classes (SI-6546).
*
- * - `innersChain` denotes the containing classes for a non-package-level class `c`, null otherwise.
- * Note: the optimizer may inline anonymous closures, thus eliding those inner classes
- * (no physical class file is emitted for elided classes).
- * Before committing `innersChain` to bytecode, cross-check with the list of elided classes (SI-6546).
+ * All methods of this class can-multi-thread
*
- * All methods of this class can-multi-thread
+ * TODO @lry c: ClassBType. rename to ClassBTypeInfo
*/
- case class Tracked(c: BType, flags: Int, sc: Tracked, ifaces: Array[Tracked], innersChain: Array[InnerClassEntry]) {
+ case class Tracked(c: ClassBType, flags: Int, sc: Tracked, ifaces: Array[Tracked], innersChain: Array[InnerClassEntry]) {
// not a case-field because we initialize it only for JVM classes we emit.
+ // TODO @lry make it an Option[List[BType]]
+ // TODO: this is currently not used. a commit in the optimizer branch uses this field to
+ // re-compute inner classes (ee4c185). leaving it in for now.
private var _directMemberClasses: List[BType] = null
def directMemberClasses: List[BType] = {
@@ -223,9 +219,9 @@ abstract class BCodeTypes extends BCodeIdiomatic {
def directMemberClasses_=(bs: List[BType]) {
if (_directMemberClasses != null) {
// TODO we enter here when both mirror class and plain class are emitted for the same ModuleClassSymbol.
- assert(_directMemberClasses == bs.sortBy(_.off))
+ assert(_directMemberClasses.sameElements(bs))
}
- _directMemberClasses = bs.sortBy(_.off)
+ _directMemberClasses = bs
}
/* `isCompilingStdLib` saves the day when compiling:
@@ -247,7 +243,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
def isInnerClass = { innersChain != null }
def isLambda = {
// ie isLCC || isTraditionalClosureClass
- isFinal && (c.getSimpleName.contains(tpnme.ANON_FUN_NAME.toString)) && isFunctionType(c)
+ isFinal && (c.simpleName.contains(tpnme.ANON_FUN_NAME.toString)) && isFunctionType(c)
}
/* can-multi-thread */
@@ -350,8 +346,8 @@ abstract class BCodeTypes extends BCodeIdiomatic {
val superInterfaces0: List[Symbol] = csym.mixinClasses
val superInterfaces = existingSymbols(superInterfaces0 ++ csym.annotations.map(newParentForAttr)).distinct
- assert(!superInterfaces.contains(NoSymbol), s"found NoSymbol among: ${superInterfaces.mkString}")
- assert(superInterfaces.forall(s => s.isInterface || s.isTrait), s"found non-interface among: ${superInterfaces.mkString}")
+ assert(!superInterfaces.contains(NoSymbol), s"found NoSymbol among: ${superInterfaces.mkString(", ")}")
+ assert(superInterfaces.forall(s => s.isInterface || s.isTrait), s"found non-interface among: ${superInterfaces.mkString(", ")}")
minimizeInterfaces(superInterfaces)
}
@@ -380,8 +376,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
if (opt != null) {
return opt
}
-
- val key = brefType(csym.javaBinaryName.toTypeName)
+ val key = new ClassBType(csym.javaBinaryName.toTypeName)
assert(key.isNonSpecial || isCompilingStdLib, s"Not a class to track: ${csym.fullName}")
// TODO accomodate the fix for SI-5031 of https://github.com/scala/scala/commit/0527b2549bcada2fda2201daa630369b377d0877
@@ -395,12 +390,10 @@ abstract class BCodeTypes extends BCodeIdiomatic {
tr
}
- val EMPTY_TRACKED_ARRAY = Array.empty[Tracked]
-
/*
* must-single-thread
*/
- private def buildExemplar(key: BType, csym: Symbol): Tracked = {
+ private def buildExemplar(key: ClassBType, csym: Symbol): Tracked = {
val sc =
if (csym.isImplClass) definitions.ObjectClass
else csym.superClass
@@ -413,14 +406,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
((sc != NoSymbol) && !sc.isInterface) || isCompilingStdLib,
"superClass out of order"
)
- val ifaces = getSuperInterfaces(csym) map exemplar;
- val ifacesArr =
- if (ifaces.isEmpty) EMPTY_TRACKED_ARRAY
- else {
- val arr = new Array[Tracked](ifaces.size)
- ifaces.copyToArray(arr)
- arr
- }
+ val ifacesArr = getSuperInterfaces(csym).map(exemplar).toArray
val flags = mkFlags(
javaFlags(csym),
@@ -476,29 +462,30 @@ abstract class BCodeTypes extends BCodeIdiomatic {
if ((b == jlCloneableReference) ||
(b == jioSerializableReference) ||
(b == AnyRefReference)) { true }
- else if (b.isArray) { conforms(a.getComponentType, b.getComponentType) }
+ else if (b.isArray) { conforms(a.asArrayBType.componentType, // TODO @lry change to pattern match, get rid of casts
+ b.asArrayBType.componentType) }
else { false }
}
else if (a.isBoxed) { // may be null
if (b.isBoxed) { a == b }
else if (b == AnyRefReference) { true }
- else if (!(b.hasObjectSort)) { false }
+ else if (!(b.isClass)) { false }
else { exemplars.get(a).isSubtypeOf(b) } // e.g., java/lang/Double conforms to java/lang/Number
}
else if (a.isNullType) { // known to be null
if (b.isNothingType) { false }
- else if (b.isValueType) { false }
+ else if (b.isPrimitive) { false }
else { true }
}
else if (a.isNothingType) { // known to be Nothing
true
}
- else if (a.isUnitType) {
- b.isUnitType
+ else if (a == UNIT) {
+ b == UNIT
}
- else if (a.hasObjectSort) { // may be null
+ else if (a.isClass) { // may be null
if (a.isNothingType) { true }
- else if (b.hasObjectSort) { exemplars.get(a).isSubtypeOf(b) }
+ else if (b.isClass) { exemplars.get(a).isSubtypeOf(b) }
else if (b.isArray) { a.isNullType } // documentation only, because `if(a.isNullType)` (above) covers this case already.
else { false }
}
@@ -506,8 +493,8 @@ abstract class BCodeTypes extends BCodeIdiomatic {
def msg = s"(a: $a, b: $b)"
- assert(a.isNonUnitValueType, s"a isn't a non-Unit value type. $msg")
- assert(b.isValueType, s"b isn't a value type. $msg")
+ assert(a.isNonVoidPrimitiveType, s"a isn't a non-Unit value type. $msg")
+ assert(b.isPrimitive, s"b isn't a value type. $msg")
(a eq b) || (a match {
case BOOL | BYTE | SHORT | CHAR => b == INT || b == LONG // TODO Actually, BOOL does NOT conform to LONG. Even with adapt().
@@ -521,7 +508,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
* can-multi-thread
*/
def maxValueType(a: BType, other: BType): BType = {
- assert(a.isValueType, "maxValueType() is defined only for 1st arg valuetypes (2nd arg doesn't matter).")
+ assert(a.isPrimitive, "maxValueType() is defined only for 1st arg valuetypes (2nd arg doesn't matter).")
def uncomparable: Nothing = {
abort(s"Uncomparable BTypes: $a with $other")
@@ -537,30 +524,30 @@ abstract class BCodeTypes extends BCodeIdiomatic {
case BOOL => uncomparable
case BYTE =>
- if (other == CHAR) INT
+ if (other == CHAR) INT
else if (other.isNumericType) other
else uncomparable
case SHORT =>
other match {
- case BYTE => SHORT
- case CHAR => INT
+ case BYTE => SHORT
+ case CHAR => INT
case INT | LONG | FLOAT | DOUBLE => other
- case _ => uncomparable
+ case _ => uncomparable
}
case CHAR =>
other match {
- case BYTE | SHORT => INT
+ case BYTE | SHORT => INT
case INT | LONG | FLOAT | DOUBLE => other
- case _ => uncomparable
+ case _ => uncomparable
}
case INT =>
other match {
case BYTE | SHORT | CHAR => INT
case LONG | FLOAT | DOUBLE => other
- case _ => uncomparable
+ case _ => uncomparable
}
case LONG =>
@@ -569,7 +556,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
else uncomparable
case FLOAT =>
- if (other == DOUBLE) DOUBLE
+ if (other == DOUBLE) DOUBLE
else if (other.isNumericType) FLOAT
else uncomparable
@@ -586,18 +573,18 @@ abstract class BCodeTypes extends BCodeIdiomatic {
* can-multi-thread
*/
final def maxType(a: BType, other: BType): BType = {
- if (a.isValueType) { maxValueType(a, other) }
+ if (a.isPrimitive) { maxValueType(a, other) }
else {
if (a.isNothingType) return other;
if (other.isNothingType) return a;
if (a == other) return a;
// Approximate `lub`. The common type of two references is always AnyRef.
// For 'real' least upper bound wrt to subclassing use method 'lub'.
- assert(a.isArray || a.isBoxed || a.hasObjectSort, s"This is not a valuetype and it's not something else, what is it? $a")
+ assert(a.isArray || a.isBoxed || a.isClass, s"This is not a valuetype and it's not something else, what is it? $a")
// TODO For some reason, ICode thinks `REFERENCE(...).maxType(BOXED(whatever))` is `uncomparable`. Here, that has maxType AnyRefReference.
// BTW, when swapping arguments, ICode says BOXED(whatever).maxType(REFERENCE(...)) == AnyRefReference, so I guess the above was an oversight in REFERENCE.maxType()
- if (other.isRefOrArrayType) { AnyRefReference }
- else { abort(s"Uncomparable BTypes: $a with $other") }
+ if (other.isRef) { AnyRefReference }
+ else { abort(s"Uncomparable BTypes: $a with $other") }
}
}
@@ -609,7 +596,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
* can-multi-thread
*/
def isPartialFunctionType(t: BType): Boolean = {
- (t.hasObjectSort) && exemplars.get(t).isSubtypeOf(PartialFunctionReference)
+ (t.isClass) && exemplars.get(t).isSubtypeOf(PartialFunctionReference)
}
/*
@@ -618,7 +605,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
* can-multi-thread
*/
def isFunctionType(t: BType): Boolean = {
- if (!t.hasObjectSort) return false
+ if (!t.isClass) return false
var idx = 0
val et: Tracked = exemplars.get(t)
while (idx <= definitions.MaxFunctionArity) {
@@ -724,14 +711,8 @@ abstract class BCodeTypes extends BCodeIdiomatic {
}
}
- // now that we have all of `ics` , `csym` , and soon the inner-classes-chain, it's too tempting not to cache.
- if (chain.isEmpty) { null }
- else {
- val arr = new Array[InnerClassEntry](chain.size)
- (chain map toInnerClassEntry).copyToArray(arr)
-
- arr
- }
+ if (chain.isEmpty) null
+ else chain.map(toInnerClassEntry).toArray
}
/*
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
new file mode 100644
index 0000000000..5b0fa6f7a8
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -0,0 +1,417 @@
+package scala.tools.nsc
+package backend.jvm
+
+import scala.collection.immutable
+import scala.annotation.switch
+import scala.tools.asm
+import asm.Opcodes
+import scala.collection.mutable.ListBuffer
+
+/**
+ * BTypes is a backend component that defines the class BType, a number of basic instances and
+ * some utilities.
+ *
+ * A BType is essentially an slice of the array `chrs` denoting the name of the type, and a field
+ * denoting the kind (object, array, method, or one of the primitive types).
+ *
+ * BTypes depends on Global just because it re-uses hash-consing of Name. It would be cleaner to
+ * create an interface for BTypeName and extend it in scala.reflect.internal.Names#Name, that
+ * would simplify testing BTypes (no Global needed).
+ */
+abstract class BTypes[G <: Global](val __global_dont_use: G) {
+ def chrs: Array[Char]
+
+ /**
+ * Interface for names stored in `chrs`
+ */
+ type BTypeName <: __global_dont_use.Name
+
+ /**
+ * Create a new name in `chrs`. Names are assumed to be hash-consed. Equality on BType will use
+ * reference equality to compare the names.
+ */
+ def createNewName(s: String): BTypeName
+
+ /*sealed*/ trait BType { // Not sealed for now due to SI-8546
+ final override def toString: String = this match {
+ case UNIT => "V"
+ case BOOL => "Z"
+ case CHAR => "C"
+ case BYTE => "B"
+ case SHORT => "S"
+ case INT => "I"
+ case FLOAT => "F"
+ case LONG => "J"
+ case DOUBLE => "D"
+ case c @ ClassBType(_, _) => "L" + c.internalName + ";"
+ case ArrayBType(component) => "[" + component
+ case MethodBType(args, res) => "(" + args.mkString + ")" + res
+ }
+
+ /**
+ * @return The Java descriptor of this type. Examples:
+ * - int: I
+ * - java.lang.String: Ljava/lang/String;
+ * - int[]: [I
+ * - Object m(String s, double d): (Ljava/lang/String;D)Ljava/lang/Object;
+ */
+ final def descriptor = toString
+
+ /**
+ * @return 0 for void, 2 for long and double, 1 otherwise
+ */
+ final def size: Int = this match {
+ case UNIT => 0
+ case LONG | DOUBLE => 2
+ case _ => 1
+ }
+
+ final def isPrimitive: Boolean = this.isInstanceOf[PrimitiveBType]
+ final def isRef: Boolean = this.isInstanceOf[RefBType]
+ final def isArray: Boolean = this.isInstanceOf[ArrayBType]
+ final def isClass: Boolean = this.isInstanceOf[ClassBType]
+ final def isMethod: Boolean = this.isInstanceOf[MethodBType]
+
+ final def isNonVoidPrimitiveType = isPrimitive && this != UNIT
+ // TODO @lry should also include !isMethod in isNonSpecial? in this case it would be equivalent to isClass, so we could get rid of it.
+ final def isNonSpecial = !isPrimitive && !isArray && !isPhantomType
+ final def isNullType = this == RT_NULL || this == CT_NULL
+ final def isNothingType = this == RT_NOTHING || this == CT_NOTHING
+ final def isPhantomType = isNullType || isNothingType
+
+ final def isBoxed = this match {
+ case BOXED_UNIT | BOXED_BOOLEAN | BOXED_CHAR |
+ BOXED_BYTE | BOXED_SHORT | BOXED_INT |
+ BOXED_FLOAT | BOXED_LONG | BOXED_DOUBLE => true
+ case _ => false
+ }
+
+ final def isIntSizedType = this == BOOL || this == CHAR || this == BYTE ||
+ this == SHORT || this == INT
+ final def isIntegralType = this == INT || this == BYTE || this == LONG ||
+ this == CHAR || this == SHORT
+ final def isRealType = this == FLOAT || this == DOUBLE
+ final def isNumericType = isIntegralType || isRealType
+ final def isWideType = size == 2
+
+ /**
+ * See documentation of [[typedOpcode]].
+ * The numbers are taken from asm.Type.VOID_TYPE ff., the values are those shifted by << 8.
+ */
+ private def loadStoreOpcodeOffset: Int = this match {
+ case UNIT | INT => 0
+ case BOOL | BYTE => 5
+ case CHAR => 6
+ case SHORT => 7
+ case FLOAT => 2
+ case LONG => 1
+ case DOUBLE => 3
+ case _ => 4
+ }
+
+ /**
+ * See documentation of [[typedOpcode]].
+ * The numbers are taken from asm.Type.VOID_TYPE ff., the values are those shifted by << 16.
+ */
+ private def typedOpcodeOffset: Int = this match {
+ case UNIT => 5
+ case BOOL | CHAR | BYTE | SHORT | INT => 0
+ case FLOAT => 2
+ case LONG => 1
+ case DOUBLE => 3
+ case _ => 4
+ }
+
+ /**
+ * Some JVM opcodes have typed variants. This method returns the correct opcode according to
+ * the type.
+ *
+ * @param opcode A JVM instruction opcode. This opcode must be one of ILOAD, ISTORE, IALOAD,
+ * IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, ISHR, IUSHR, IAND, IOR
+ * IXOR and IRETURN.
+ * @return The opcode adapted to this java type. For example, if this type is `float` and
+ * `opcode` is `IRETURN`, this method returns `FRETURN`.
+ */
+ final def typedOpcode(opcode: Int): Int = {
+ if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE)
+ opcode + loadStoreOpcodeOffset
+ else
+ opcode + typedOpcodeOffset
+ }
+
+ /**
+ * The asm.Type corresponding to this BType.
+ *
+ * Note about asm.Type.getObjectType (*): For class types, the method expects the internal
+ * name, i.e. without the surrounding 'L' and ';'. For array types on the other hand, the
+ * method expects a full descriptor, for example "[Ljava/lang/String;".
+ *
+ * See method asm.Type.getType that creates a asm.Type from a type descriptor
+ * - for an OBJECT type, the 'L' and ';' are not part of the range of the created Type
+ * - for an ARRAY type, the full descriptor is part of the range
+ */
+ def toASMType: asm.Type = this match {
+ case UNIT => asm.Type.VOID_TYPE
+ case BOOL => asm.Type.BOOLEAN_TYPE
+ case CHAR => asm.Type.CHAR_TYPE
+ case BYTE => asm.Type.BYTE_TYPE
+ case SHORT => asm.Type.SHORT_TYPE
+ case INT => asm.Type.INT_TYPE
+ case FLOAT => asm.Type.FLOAT_TYPE
+ case LONG => asm.Type.LONG_TYPE
+ case DOUBLE => asm.Type.DOUBLE_TYPE
+ case c @ ClassBType(_, _) => asm.Type.getObjectType(c.internalName) // (*)
+ case a @ ArrayBType(_) => asm.Type.getObjectType(a.descriptor)
+ case m @ MethodBType(_, _) => asm.Type.getMethodType(m.descriptor)
+ }
+
+ def asRefBType : RefBType = this.asInstanceOf[RefBType]
+ def asArrayBType: ArrayBType = this.asInstanceOf[ArrayBType]
+ def asClassBType: ClassBType = this.asInstanceOf[ClassBType]
+ }
+
+ object BType {
+ /**
+ * @param chars The character array containing the descriptor
+ * @param start The position where the descriptor starts
+ * @return The BType and the index of the first character after the consumed descriptor
+ */
+ private[BTypes] def fromNonMethodDescriptor(chars: Array[Char], start: Int): (BType, Int) = {
+ chars(start) match {
+ case 'L' =>
+ var i = start
+ while (chars(i) != ';') { i += 1 }
+ // Example: chars = "IILpkg/Cls;I"
+ // ^ ^
+ // start=2 i=10
+ // `start + 1` to exclude the 'L', `i - start - 1` excludes the ';'
+ (new ClassBType(new String(chars, start + 1, i - start - 1)), i + 1)
+ case '[' =>
+ val (res, next) = fromNonMethodDescriptor(chars, start + 1)
+ (ArrayBType(res), next)
+ case 'V' => (UNIT, start + 1)
+ case 'Z' => (BOOL, start + 1)
+ case 'C' => (CHAR, start + 1)
+ case 'B' => (BYTE, start + 1)
+ case 'S' => (SHORT, start + 1)
+ case 'I' => (INT, start + 1)
+ case 'F' => (FLOAT, start + 1)
+ case 'J' => (LONG, start + 1)
+ case 'D' => (DOUBLE, start + 1)
+ }
+ }
+ }
+
+ sealed trait PrimitiveBType extends BType
+
+ case object UNIT extends PrimitiveBType
+ case object BOOL extends PrimitiveBType
+ case object CHAR extends PrimitiveBType
+ case object BYTE extends PrimitiveBType
+ case object SHORT extends PrimitiveBType
+ case object INT extends PrimitiveBType
+ case object FLOAT extends PrimitiveBType
+ case object LONG extends PrimitiveBType
+ case object DOUBLE extends PrimitiveBType
+
+ sealed trait RefBType extends BType {
+ /**
+ * The class or array type of this reference type. Used for ANEWARRAY, MULTIANEWARRAY,
+ * INSTANCEOF and CHECKCAST instructions. Also used for emitting invokevirtual calls to
+ * (a: Array[T]).clone() for any T, see genApply.
+ *
+ * In contrast to the descriptor, this string does not contain the surrounding 'L' and ';' for
+ * class types, for example "java/lang/String".
+ * However, for array types, the full descriptor is used, for example "[Ljava/lang/String;".
+ *
+ * This can be verified for example using javap or ASMifier.
+ */
+ def classOrArrayType: String = this match {
+ case c: ClassBType => c.internalName
+ case a: ArrayBType => a.descriptor
+ }
+ }
+
+ /**
+ * Class or Interface type.
+ *
+ * Classes are represented using their name as a slice of the `chrs` array. This representation is
+ * efficient because the JVM class name is initially created using `classSymbol.javaBinaryName`.
+ * This already adds the necessary string to the `chrs` array, so it makes sense to reuse the same
+ * name table in the backend.
+ *
+ * Not a case class because that would expose the (Int, Int) constructor (didn't find a way to
+ * make it private, also the factory in the companion).
+ */
+ class ClassBType private(val offset: Int, val length: Int) extends RefBType {
+ /**
+ * Construct a ClassBType for a given (intenred) class name.
+ *
+ * @param n The class name as a slice of the `chrs` array, without the surrounding 'L' and ';'.
+ * Note that `classSymbol.javaBinaryName` returns exactly such a name.
+ */
+ def this(n: BTypeName) = this(n.start, n.length)
+
+ /**
+ * Construct a ClassBType for a given java class name.
+ *
+ * @param s A class name of the form "java/lang/String", without the surrounding 'L' and ';'.
+ */
+ def this(s: String) = this({
+ assert(!(s.head == 'L' && s.last == ';'), s"Descriptor instead of internal name: $s")
+ createNewName(s)
+ })
+
+ /**
+ * The internal name of a class is the string returned by java.lang.Class.getName, with all '.'
+ * replaced by '/'. For example "java/lang/String".
+ */
+ def internalName: String = new String(chrs, offset, length)
+
+ /**
+ * @return The class name without the package prefix
+ */
+ def simpleName: String = internalName.split("/").last
+
+ /**
+ * Custom equals / hashCode are needed because this is not a case class.
+ */
+ override def equals(o: Any): Boolean = (this eq o.asInstanceOf[Object]) || (o match {
+ case ClassBType(`offset`, `length`) => true
+ case _ => false
+ })
+
+ override def hashCode: Int = {
+ import scala.runtime.Statics
+ var acc: Int = -889275714
+ acc = Statics.mix(acc, offset)
+ acc = Statics.mix(acc, length)
+ Statics.finalizeHash(acc, 2)
+ }
+ }
+
+ object ClassBType {
+ def apply(n: BTypeName): ClassBType = new ClassBType(n)
+ def apply(s: String): ClassBType = new ClassBType(s)
+
+ def unapply(c: ClassBType): Option[(Int, Int)] =
+ if (c == null) None
+ else Some((c.offset, c.length))
+ }
+
+ case class ArrayBType(componentType: BType) extends RefBType {
+ def dimension: Int = componentType match {
+ case a: ArrayBType => 1 + a.dimension
+ case _ => 1
+ }
+
+ def elementType: BType = componentType match {
+ case a: ArrayBType => a.elementType
+ case t => t
+ }
+ }
+
+ case class MethodBType(argumentTypes: List[BType], returnType: BType) extends BType {
+ private def this(types: (List[BType], BType)) = this(types._1, types._2)
+ def this(descriptor: String) = this(MethodBType.decomposeMethodDescriptor(descriptor))
+ }
+
+ object MethodBType {
+ private def decomposeMethodDescriptor(descriptor: String): (List[BType], BType) = {
+ val chars = descriptor.toCharArray
+ assert(chars(0) == '(', s"Not a valid method descriptor: $descriptor")
+ var i = 1
+ val argTypes = new ListBuffer[BType]
+ while (chars(i) != ')') {
+ val (argType, next) = BType.fromNonMethodDescriptor(chars, i)
+ argTypes += argType
+ i = next
+ }
+ val (resType, _) = BType.fromNonMethodDescriptor(chars, i + 1) // `i + 1` to skip the ')'
+ (argTypes.toList, resType)
+ }
+ def apply(descriptor: String) = {
+ val (argTypes, resType) = decomposeMethodDescriptor(descriptor)
+ new MethodBType(argTypes, resType)
+ }
+ }
+
+ val BOXED_UNIT = ClassBType("java/lang/Void")
+ val BOXED_BOOLEAN = ClassBType("java/lang/Boolean")
+ val BOXED_BYTE = ClassBType("java/lang/Byte")
+ val BOXED_SHORT = ClassBType("java/lang/Short")
+ val BOXED_CHAR = ClassBType("java/lang/Character")
+ val BOXED_INT = ClassBType("java/lang/Integer")
+ val BOXED_LONG = ClassBType("java/lang/Long")
+ val BOXED_FLOAT = ClassBType("java/lang/Float")
+ val BOXED_DOUBLE = ClassBType("java/lang/Double")
+
+ /*
+ * RT_NOTHING and RT_NULL exist at run-time only. They are the bytecode-level manifestation (in
+ * method signatures only) of what shows up as NothingClass resp. NullClass in Scala ASTs.
+ *
+ * Therefore, when RT_NOTHING or RT_NULL are to be emitted, a mapping is needed: the internal
+ * names of NothingClass and NullClass can't be emitted as-is.
+ */
+ val RT_NOTHING = ClassBType("scala/runtime/Nothing$")
+ val RT_NULL = ClassBType("scala/runtime/Null$")
+ val CT_NOTHING = ClassBType("scala/Nothing")
+ val CT_NULL = ClassBType("scala/Null")
+
+ val srBooleanRef = ClassBType("scala/runtime/BooleanRef")
+ val srByteRef = ClassBType("scala/runtime/ByteRef")
+ val srCharRef = ClassBType("scala/runtime/CharRef")
+ val srIntRef = ClassBType("scala/runtime/IntRef")
+ val srLongRef = ClassBType("scala/runtime/LongRef")
+ val srFloatRef = ClassBType("scala/runtime/FloatRef")
+ val srDoubleRef = ClassBType("scala/runtime/DoubleRef")
+
+ /**
+ * Map from type kinds to the Java reference types.
+ * Useful when pushing class literals onto the operand stack (ldc instruction taking a class
+ * literal).
+ * @see Predef.classOf
+ * @see genConstant()
+ *
+ * TODO @lry rename to "boxedClassOfPrimitive" or so, check usages
+ */
+ val classLiteral = immutable.Map[BType, ClassBType](
+ UNIT -> BOXED_UNIT,
+ BOOL -> BOXED_BOOLEAN,
+ BYTE -> BOXED_BYTE,
+ SHORT -> BOXED_SHORT,
+ CHAR -> BOXED_CHAR,
+ INT -> BOXED_INT,
+ LONG -> BOXED_LONG,
+ FLOAT -> BOXED_FLOAT,
+ DOUBLE -> BOXED_DOUBLE
+ )
+
+ case class MethodNameAndType(name: String, descriptor: String)
+
+ val asmBoxTo: immutable.Map[BType, MethodNameAndType] = {
+ Map(
+ BOOL -> MethodNameAndType("boxToBoolean", "(Z)Ljava/lang/Boolean;" ) ,
+ BYTE -> MethodNameAndType("boxToByte", "(B)Ljava/lang/Byte;" ) ,
+ CHAR -> MethodNameAndType("boxToCharacter", "(C)Ljava/lang/Character;") ,
+ SHORT -> MethodNameAndType("boxToShort", "(S)Ljava/lang/Short;" ) ,
+ INT -> MethodNameAndType("boxToInteger", "(I)Ljava/lang/Integer;" ) ,
+ LONG -> MethodNameAndType("boxToLong", "(J)Ljava/lang/Long;" ) ,
+ FLOAT -> MethodNameAndType("boxToFloat", "(F)Ljava/lang/Float;" ) ,
+ DOUBLE -> MethodNameAndType("boxToDouble", "(D)Ljava/lang/Double;" )
+ )
+ }
+
+ val asmUnboxTo: immutable.Map[BType, MethodNameAndType] = {
+ Map(
+ BOOL -> MethodNameAndType("unboxToBoolean", "(Ljava/lang/Object;)Z") ,
+ BYTE -> MethodNameAndType("unboxToByte", "(Ljava/lang/Object;)B") ,
+ CHAR -> MethodNameAndType("unboxToChar", "(Ljava/lang/Object;)C") ,
+ SHORT -> MethodNameAndType("unboxToShort", "(Ljava/lang/Object;)S") ,
+ INT -> MethodNameAndType("unboxToInt", "(Ljava/lang/Object;)I") ,
+ LONG -> MethodNameAndType("unboxToLong", "(Ljava/lang/Object;)J") ,
+ FLOAT -> MethodNameAndType("unboxToFloat", "(Ljava/lang/Object;)F") ,
+ DOUBLE -> MethodNameAndType("unboxToDouble", "(Ljava/lang/Object;)D")
+ )
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala
index 8e6c09213f..1d29fdee10 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala
@@ -1,6 +1,6 @@
/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
- * @author Paul Phillips
+ * @author Martin Odersky
*/
package scala.tools.nsc
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index b7f9b30e19..988c04f514 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -975,7 +975,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
index += jparamType.getSize()
}
- mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, javaType(m).getDescriptor)
+ mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, javaType(m).getDescriptor, false)
mirrorMethod.visitInsn(jReturnType.getOpcode(asm.Opcodes.IRETURN))
mirrorMethod.visitMaxs(0, 0) // just to follow protocol, dummy arguments
@@ -1061,7 +1061,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
asm.Opcodes.INVOKEVIRTUAL,
moduleName,
androidFieldName.toString,
- asm.Type.getMethodDescriptor(creatorType, Array.empty[asm.Type]: _*)
+ asm.Type.getMethodDescriptor(creatorType, Array.empty[asm.Type]: _*),
+ false
)
// PUTSTATIC `thisName`.CREATOR;
@@ -1400,7 +1401,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
// TODO param names: (m.params map (p => javaName(p.sym)))
- // typestate: entering mode with valid call sequences:
+ // typestate: entering mode with valid call sequences: (see ASM Guide, 3.2.1)
// [ visitAnnotationDefault ] ( visitAnnotation | visitParameterAnnotation | visitAttribute )*
emitAnnotations(jmethod, others)
@@ -1445,7 +1446,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
val hasStaticBitSet = ((flags & asm.Opcodes.ACC_STATIC) != 0)
genCode(m, emitVars, hasStaticBitSet)
- jmethod.visitMaxs(0, 0) // just to follow protocol, dummy arguments
+ // visitMaxs needs to be called according to the protocol. The arguments will be ignored
+ // since maximums (and stack map frames) are computed. See ASM Guide, Section 3.2.1,
+ // section "ClassWriter options"
+ jmethod.visitMaxs(0, 0)
}
jmethod.visitEnd()
@@ -1521,7 +1525,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
if (isStaticModule(clasz.symbol)) {
clinit.visitTypeInsn(asm.Opcodes.NEW, thisName)
clinit.visitMethodInsn(asm.Opcodes.INVOKESPECIAL,
- thisName, INSTANCE_CONSTRUCTOR_NAME, mdesc_arglessvoid)
+ thisName, INSTANCE_CONSTRUCTOR_NAME, mdesc_arglessvoid, false)
}
if (isParcelableClass) { legacyAddCreatorCode(clinit) }
@@ -1665,16 +1669,16 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
def rem(tk: TypeKind) { emitPrimitive(remOpcodes, tk) }
def invokespecial(owner: String, name: String, desc: String) {
- jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc)
+ jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, false)
}
def invokestatic(owner: String, name: String, desc: String) {
- jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc)
+ jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, false)
}
def invokeinterface(owner: String, name: String, desc: String) {
- jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc)
+ jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc, true)
}
def invokevirtual(owner: String, name: String, desc: String) {
- jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc)
+ jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false)
}
def goTo(label: asm.Label) { jmethod.visitJumpInsn(Opcodes.GOTO, label) }
@@ -2924,7 +2928,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
// invoke the superclass constructor, which will do the
// necessary java reflection and create Method objects.
- constructor.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, "scala/beans/ScalaBeanInfo", INSTANCE_CONSTRUCTOR_NAME, conJType.getDescriptor)
+ constructor.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, "scala/beans/ScalaBeanInfo", INSTANCE_CONSTRUCTOR_NAME, conJType.getDescriptor, false)
constructor.visitInsn(asm.Opcodes.RETURN)
constructor.visitMaxs(0, 0) // just to follow protocol, dummy arguments
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
index 193100474c..9b292fee7f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
@@ -243,6 +243,12 @@ abstract class GenBCode extends BCodeSyncAndTry {
val plainC = SubItem3(plain.name, getByteArray(plain))
val beanC = if (bean == null) null else SubItem3(bean.name, getByteArray(bean))
+ if (AsmUtils.traceSerializedClassEnabled && plain.name.contains(AsmUtils.traceSerializedClassPattern)) {
+ if (mirrorC != null) AsmUtils.traceClass(mirrorC.jclassBytes)
+ AsmUtils.traceClass(plainC.jclassBytes)
+ if (beanC != null) AsmUtils.traceClass(beanC.jclassBytes)
+ }
+
q3 add Item3(arrivalPos, mirrorC, plainC, beanC, outFolder)
}
diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala
index c49f23852f..a866173a88 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala
@@ -56,11 +56,8 @@ abstract class ClosureElimination extends SubComponent {
case (BOX(t1), UNBOX(t2)) if (t1 == t2) =>
Some(Nil)
- case (LOAD_FIELD(sym, isStatic), DROP(_)) if !sym.hasAnnotation(definitions.VolatileAttr) =>
- if (isStatic)
- Some(Nil)
- else
- Some(DROP(REFERENCE(definitions.ObjectClass)) :: Nil)
+ case (LOAD_FIELD(sym, /* isStatic */false), DROP(_)) if !sym.hasAnnotation(definitions.VolatileAttr) && inliner.isClosureClass(sym.owner) =>
+ Some(DROP(REFERENCE(definitions.ObjectClass)) :: Nil)
case _ => None
}
diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
index 90c37ba0b3..4b419b210c 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
@@ -169,9 +169,14 @@ abstract class DeadCodeElimination extends SubComponent {
case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) |
THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) |
- LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) =>
+ LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) | CREATE_ARRAY(_, _) =>
moveToWorkList()
+ case LOAD_FIELD(sym, isStatic) if isStatic || !inliner.isClosureClass(sym.owner) =>
+ // static load may trigger static initization.
+ // non-static load can throw NPE (but we know closure fields can't be accessed via a
+ // null reference.
+ moveToWorkList()
case CALL_METHOD(m1, _) if isSideEffecting(m1) =>
moveToWorkList()
@@ -193,6 +198,8 @@ abstract class DeadCodeElimination extends SubComponent {
moveToWorkListIf(necessary)
case LOAD_MODULE(sym) if isLoadNeeded(sym) =>
moveToWorkList() // SI-4859 Module initialization might side-effect.
+ case CALL_PRIMITIVE(Arithmetic(DIV | REM, INT | LONG) | ArrayLength(_)) =>
+ moveToWorkList() // SI-8601 Might divide by zero
case _ => ()
moveToWorkListIf(cond = false)
}
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 20ccc30ff6..d22dcacad6 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -42,7 +42,7 @@ trait ScalaSettings extends AbsScalaSettings
def optimiseSettings = List[BooleanSetting](inline, inlineHandlers, Xcloselim, Xdce, YconstOptimization)
/** If any of these settings is enabled, the compiler should print a message and exit. */
- def infoSettings = List[Setting](help, Xhelp, Yhelp, showPlugins, showPhases, genPhaseGraph)
+ def infoSettings = List[Setting](version, help, Xhelp, Yhelp, showPlugins, showPhases, genPhaseGraph)
/** Is an info setting set? */
def isInfo = infoSettings exists (_.isSetByUser)
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
index 6ca2205881..149b4fe446 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
@@ -780,32 +780,40 @@ abstract class ICodeReader extends ClassfileParser {
bb = otherBlock
// Console.println("\t> entering bb: " + bb)
}
- instr match {
- case LJUMP(target) =>
- otherBlock = blocks(target)
- bb.emitOnly(JUMP(otherBlock))
- case LCJUMP(success, failure, cond, kind) =>
- otherBlock = blocks(success)
- val failBlock = blocks(failure)
- bb.emitOnly(CJUMP(otherBlock, failBlock, cond, kind))
+ if (bb.closed) {
+ // the basic block is closed, i.e. the previous instruction was a jump, return or throw,
+ // but the next instruction is not a jump target. this means that the next instruction is
+ // dead code. we can therefore advance until the next jump target.
+ debuglog(s"ICode reader skipping dead instruction $instr in classfile $instanceCode")
+ } else {
+ instr match {
+ case LJUMP(target) =>
+ otherBlock = blocks(target)
+ bb.emitOnly(JUMP(otherBlock))
+
+ case LCJUMP(success, failure, cond, kind) =>
+ otherBlock = blocks(success)
+ val failBlock = blocks(failure)
+ bb.emitOnly(CJUMP(otherBlock, failBlock, cond, kind))
- case LCZJUMP(success, failure, cond, kind) =>
- otherBlock = blocks(success)
- val failBlock = blocks(failure)
- bb.emitOnly(CZJUMP(otherBlock, failBlock, cond, kind))
+ case LCZJUMP(success, failure, cond, kind) =>
+ otherBlock = blocks(success)
+ val failBlock = blocks(failure)
+ bb.emitOnly(CZJUMP(otherBlock, failBlock, cond, kind))
- case LSWITCH(tags, targets) =>
- bb.emitOnly(SWITCH(tags, targets map blocks))
+ case LSWITCH(tags, targets) =>
+ bb.emitOnly(SWITCH(tags, targets map blocks))
- case RETURN(_) =>
- bb emitOnly instr
+ case RETURN(_) =>
+ bb emitOnly instr
- case THROW(clasz) =>
- bb emitOnly instr
+ case THROW(clasz) =>
+ bb emitOnly instr
- case _ =>
- bb emit instr
+ case _ =>
+ bb emit instr
+ }
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
index 1468680fe0..1f8ab7e887 100644
--- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
+++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
@@ -132,7 +132,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
}
val params = ((optionSymbol(thisProxy) map {proxy:Symbol => ValDef(proxy)}) ++ (target.paramss.flatten map ValDef.apply)).toList
- val methSym = oldClass.newMethod(unit.freshTermName(nme.accessor.toString()), target.pos, FINAL | BRIDGE | SYNTHETIC | PROTECTED | STATIC)
+ val methSym = oldClass.newMethod(unit.freshTermName(nme.accessor.toString() + "$"), target.pos, FINAL | BRIDGE | SYNTHETIC | PROTECTED | STATIC)
val paramSyms = params map {param => methSym.newSyntheticValueParam(param.symbol.tpe, param.name) }
@@ -238,7 +238,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
// - reinstate the assertion in `Erasure.resolveAnonymousBridgeClash`
val suffix = "$lambda$" + (
if (funOwner.isPrimaryConstructor) ""
- else "$" + funOwner.name
+ else "$" + funOwner.name + "$"
)
val name = unit.freshTypeName(s"${oldClass.name.decode}$suffix")
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index e036035397..2f2142027f 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -1050,20 +1050,18 @@ abstract class Erasure extends AddInterfaces
}
}
- def isAccessible(sym: Symbol) = localTyper.context.isAccessible(sym, sym.owner.thisType)
- if (!isAccessible(owner) && qual.tpe != null) {
+ def isJvmAccessible(sym: Symbol) = (sym.isClass && !sym.isJavaDefined) || localTyper.context.isAccessible(sym, sym.owner.thisType)
+ if (!isJvmAccessible(owner) && qual.tpe != null) {
qual match {
case Super(_, _) =>
- // Insert a cast here at your peril -- see SI-5162. Bail out if the target method is defined in
- // Java, otherwise, we'd get an IllegalAccessError at runtime. If the target method is defined in
- // Scala, however, we should have access.
- if (owner.isJavaDefined) unit.error(tree.pos, s"Unable to access ${tree.symbol.fullLocationString} with a super reference.")
+ // Insert a cast here at your peril -- see SI-5162.
+ unit.error(tree.pos, s"Unable to access ${tree.symbol.fullLocationString} with a super reference.")
tree
case _ =>
// Todo: Figure out how qual.tpe could be null in the check above (it does appear in build where SwingWorker.this
// has a null type).
val qualSym = qual.tpe.widen.typeSymbol
- if (isAccessible(qualSym) && !qualSym.isPackageClass && !qualSym.isPackageObjectClass) {
+ if (isJvmAccessible(qualSym) && !qualSym.isPackageClass && !qualSym.isPackageObjectClass) {
// insert cast to prevent illegal access error (see #4283)
// util.trace("insert erasure cast ") (*/
treeCopy.Select(tree, gen.mkAttributedCast(qual, qual.tpe.widen), name) //)
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index 33e8e47c76..313d0a6e61 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -391,7 +391,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
else {
sourceModule setPos sym.pos
if (sourceModule.flags != MODULE) {
- log("!!! Directly setting sourceModule flags from %s to MODULE".format(sourceModule.flagString))
+ log(s"!!! Directly setting sourceModule flags for $sourceModule from ${sourceModule.flagString} to MODULE")
sourceModule.flags = MODULE
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index 02e55241b3..908aa69310 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -538,6 +538,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
bytecodeClazz.info
val sClass = clazz.owner.newClass(clazzName, clazz.pos, (clazz.flags | SPECIALIZED) & ~CASE)
+ sClass.setAnnotations(clazz.annotations) // SI-8574 important that the subclass picks up @SerialVersionUID, @strictfp, etc.
def cloneInSpecializedClass(member: Symbol, flagFn: Long => Long, newName: Name = null) =
member.cloneSymbol(sClass, flagFn(member.flags | SPECIALIZED), newName)
diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
index 714f189ead..d9d1192772 100644
--- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala
+++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
@@ -328,11 +328,14 @@ abstract class TailCalls extends Transform {
)
case CaseDef(pat, guard, body) =>
+ // CaseDefs are already translated and guards were moved into the body.
+ // If this was not the case, guards would have to be transformed here as well.
+ assert(guard.isEmpty)
deriveCaseDef(tree)(transform)
case If(cond, thenp, elsep) =>
treeCopy.If(tree,
- cond,
+ noTailTransform(cond),
transform(thenp),
transform(elsep)
)
@@ -363,7 +366,7 @@ abstract class TailCalls extends Transform {
rewriteApply(tapply, fun, targs, vargs)
case Apply(fun, args) if fun.symbol == Boolean_or || fun.symbol == Boolean_and =>
- treeCopy.Apply(tree, fun, transformTrees(args))
+ treeCopy.Apply(tree, noTailTransform(fun), transformTrees(args))
// this is to detect tailcalls in translated matches
// it's a one-argument call to a label that is in a tailposition and that looks like label(x) {x}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
index fde0aca584..0899507bab 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
@@ -505,7 +505,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
}
- import global.{ConstantType, Constant, SingletonType, Literal, Ident, singleType}
+ import global.{ConstantType, Constant, EmptyScope, SingletonType, Literal, Ident, refinedType, singleType, TypeBounds, NoSymbol}
import global.definitions._
@@ -538,23 +538,30 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
private val trees = mutable.HashSet.empty[Tree]
// hashconsing trees (modulo value-equality)
- private[TreesAndTypesDomain] def uniqueTpForTree(t: Tree): Type =
- // a new type for every unstable symbol -- only stable value are uniqued
- // technically, an unreachable value may change between cases
- // thus, the failure of a case that matches on a mutable value does not exclude the next case succeeding
- // (and thuuuuus, the latter case must be considered reachable)
- if (!t.symbol.isStable) t.tpe.narrow
+ private[TreesAndTypesDomain] def uniqueTpForTree(t: Tree): Type = {
+ def freshExistentialSubtype(tp: Type): Type = {
+ // SI-8611 tp.narrow is tempting, but unsuitable. See `testRefinedTypeSI8611` for an explanation.
+ NoSymbol.freshExistential("").setInfo(TypeBounds.upper(tp)).tpe
+ }
+
+ if (!t.symbol.isStable) {
+ // Create a fresh type for each unstable value, since we can never correlate it to another value.
+ // For example `case X => case X =>` should not complaing about the second case being unreachable,
+ // if X is mutable.
+ freshExistentialSubtype(t.tpe)
+ }
else trees find (a => a.correspondsStructure(t)(sameValue)) match {
case Some(orig) =>
- debug.patmat("unique tp for tree: "+ ((orig, orig.tpe)))
+ debug.patmat("unique tp for tree: " + ((orig, orig.tpe)))
orig.tpe
case _ =>
// duplicate, don't mutate old tree (TODO: use a map tree -> type instead?)
- val treeWithNarrowedType = t.duplicate setType t.tpe.narrow
+ val treeWithNarrowedType = t.duplicate setType freshExistentialSubtype(t.tpe)
debug.patmat("uniqued: "+ ((t, t.tpe, treeWithNarrowedType.tpe)))
trees += treeWithNarrowedType
treeWithNarrowedType.tpe
}
+ }
}
sealed abstract class Const {
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
index 894f959319..e1a663ea41 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
@@ -104,11 +104,18 @@ trait TreeAndTypeAnalysis extends Debugging {
// TODO case _ if tp.isTupleType => // recurse into component types
case modSym: ModuleClassSymbol =>
Some(List(tp))
+ case sym: RefinementClassSymbol =>
+ val parentSubtypes: List[Option[List[Type]]] = tp.parents.map(parent => enumerateSubtypes(parent))
+ if (parentSubtypes exists (_.isDefined))
+ // If any of the parents is enumerable, then the refinement type is enumerable.
+ Some(
+ // We must only include subtypes of the parents that conform to `tp`.
+ // See neg/virtpatmat_exhaust_compound.scala for an example.
+ parentSubtypes flatMap (_.getOrElse(Nil)) filter (_ <:< tp)
+ )
+ else None
// make sure it's not a primitive, else (5: Byte) match { case 5 => ... } sees no Byte
- case sym if !sym.isSealed || isPrimitiveValueClass(sym) =>
- debug.patmat("enum unsealed "+ ((tp, sym, sym.isSealed, isPrimitiveValueClass(sym))))
- None
- case sym =>
+ case sym if sym.isSealed =>
val subclasses = debug.patmatResult(s"enum $sym sealed, subclasses")(
// symbols which are both sealed and abstract need not be covered themselves, because
// all of their children must be and they cannot otherwise be created.
@@ -136,6 +143,9 @@ trait TreeAndTypeAnalysis extends Debugging {
else None
}
})
+ case sym =>
+ debug.patmat("enum unsealed "+ ((tp, sym, sym.isSealed, isPrimitiveValueClass(sym))))
+ None
}
// approximate a type to the static type that is fully checkable at run time,
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index d87090fa46..73c3e6f016 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -1306,7 +1306,7 @@ trait Implicits {
}
def wrapResult(tree: Tree): SearchResult =
- if (tree == EmptyTree) SearchFailure else new SearchResult(tree, EmptyTreeTypeSubstituter, Nil)
+ if (tree == EmptyTree) SearchFailure else new SearchResult(atPos(pos.focus)(tree), EmptyTreeTypeSubstituter, Nil)
/** Materializes implicits of predefined types (currently, manifests and tags).
* Will be replaced by implicit macros once we fix them.
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index aa7a570937..ef74beec62 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -422,9 +422,10 @@ trait Macros extends MacroRuntimes with Traces with Helpers {
val wrappedArgs = mapWithIndex(args)((arg, j) => {
val fingerprint = implParams(min(j, implParams.length - 1))
+ val duplicatedArg = duplicateAndKeepPositions(arg)
fingerprint match {
- case LiftedTyped => context.Expr[Nothing](arg.duplicate)(TypeTag.Nothing) // TODO: SI-5752
- case LiftedUntyped => arg.duplicate
+ case LiftedTyped => context.Expr[Nothing](duplicatedArg)(TypeTag.Nothing) // TODO: SI-5752
+ case LiftedUntyped => duplicatedArg
case _ => abort(s"unexpected fingerprint $fingerprint in $binding with paramss being $paramss " +
s"corresponding to arg $arg in $argss")
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 4382a2c6f7..099031d536 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -1040,10 +1040,10 @@ trait Namers extends MethodSynthesis {
* so the resulting type is a valid external method type, it does not contain (references to) skolems.
*/
def thisMethodType(restpe: Type) = {
- val checkDependencies = new DependentTypeChecker(context)(this)
- checkDependencies check vparamSymss
- // DEPMETTODO: check not needed when they become on by default
- checkDependencies(restpe)
+ if (vparamSymss.lengthCompare(0) > 0) { // OPT fast path for methods of 0-1 parameter lists
+ val checkDependencies = new DependentTypeChecker(context)(this)
+ checkDependencies check vparamSymss
+ }
val makeMethodType = (vparams: List[Symbol], restpe: Type) => {
// TODODEPMET: check that we actually don't need to do anything here
@@ -1177,7 +1177,13 @@ trait Namers extends MethodSynthesis {
}
}
- addDefaultGetters(meth, ddef, vparamss, tparams, overriddenSymbol(methResTp))
+ val overridden = {
+ val isConstr = meth.isConstructor
+ if (isConstr || !methOwner.isClass) NoSymbol else overriddenSymbol(methResTp)
+ }
+ val hasDefaults = mexists(vparamss)(_.symbol.hasDefault) || mexists(overridden.paramss)(_.hasDefault)
+ if (hasDefaults)
+ addDefaultGetters(meth, ddef, vparamss, tparams, overridden)
// fast track macros, i.e. macros defined inside the compiler, are hardcoded
// hence we make use of that and let them have whatever right-hand side they need
@@ -1219,7 +1225,7 @@ trait Namers extends MethodSynthesis {
* typechecked, the corresponding param would not yet have the "defaultparam"
* flag.
*/
- private def addDefaultGetters(meth: Symbol, ddef: DefDef, vparamss: List[List[ValDef]], tparams: List[TypeDef], overriddenSymbol: => Symbol) {
+ private def addDefaultGetters(meth: Symbol, ddef: DefDef, vparamss: List[List[ValDef]], tparams: List[TypeDef], overridden: Symbol) {
val DefDef(_, _, rtparams0, rvparamss0, _, _) = resetAttrs(ddef.duplicate)
// having defs here is important to make sure that there's no sneaky tree sharing
// in methods with multiple default parameters
@@ -1227,7 +1233,6 @@ trait Namers extends MethodSynthesis {
def rvparamss = rvparamss0.map(_.map(_.duplicate))
val methOwner = meth.owner
val isConstr = meth.isConstructor
- val overridden = if (isConstr || !methOwner.isClass) NoSymbol else overriddenSymbol
val overrides = overridden != NoSymbol && !overridden.isOverloaded
// value parameters of the base class (whose defaults might be overridden)
var baseParamss = (vparamss, overridden.tpe.paramss) match {
@@ -1745,7 +1750,6 @@ trait Namers extends MethodSynthesis {
for (p <- vps)
this(p.info)
// can only refer to symbols in earlier parameter sections
- // (if the extension is enabled)
okParams ++= vps
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
index dceb0a47d8..284ab2f6f9 100644
--- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
@@ -174,8 +174,8 @@ trait NamesDefaults { self: Analyzer =>
// assigning the correct method symbol, typedSelect will just assign the type. the reason
// to still call 'typed' is to correctly infer singleton types, SI-5259.
val selectPos =
- if(qual.pos.isRange && baseFun.pos.isRange) qual.pos.union(baseFun.pos).withStart(Math.min(qual.pos.end, baseFun.pos.end))
- else baseFun.pos
+ if(qual.pos.isRange && baseFun1.pos.isRange) qual.pos.union(baseFun1.pos).withStart(Math.min(qual.pos.end, baseFun1.pos.end))
+ else baseFun1.pos
val f = blockTyper.typedOperator(Select(newQual, selected).setSymbol(baseFun1.symbol).setPos(selectPos))
if (funTargs.isEmpty) f
else TypeApply(f, funTargs).setType(baseFun.tpe)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index bf98c0e3dc..66b1c2d87a 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1687,7 +1687,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val sameSourceFile = context.unit.source.file == psym.sourceFile
- if (psym.hasDeprecatedInheritanceAnnotation && !sameSourceFile) {
+ if (!isPastTyper && psym.hasDeprecatedInheritanceAnnotation && !sameSourceFile) {
val suffix = psym.deprecatedInheritanceMessage map (": " + _) getOrElse ""
val msg = s"inheritance from ${psym.fullLocationString} is deprecated$suffix"
unit.deprecationWarning(parent.pos, msg)
@@ -3022,7 +3022,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
|| (looker.hasAccessorFlag && !accessed.hasAccessorFlag && accessed.isPrivate)
)
- def checkNoDoubleDefs(stats: List[Tree]): Unit = {
+ def checkNoDoubleDefs: Unit = {
val scope = if (inBlock) context.scope else context.owner.info.decls
var e = scope.elems
while ((e ne null) && e.owner == scope) {
@@ -3057,8 +3057,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// the corresponding synthetics to the package class, only to the package object class.
def shouldAdd(sym: Symbol) =
inBlock || !context.isInPackageObject(sym, context.owner)
- for (sym <- scope if shouldAdd(sym))
- for (tree <- context.unit.synthetics get sym) {
+ for (sym <- scope)
+ for (tree <- context.unit.synthetics get sym if shouldAdd(sym)) { // OPT: shouldAdd is usually true. Call it here, rather than in the outer loop
newStats += typedStat(tree) // might add even more synthetics to the scope
context.unit.synthetics -= sym
}
@@ -3104,7 +3104,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val stats1 = stats mapConserve typedStat
if (phase.erasedTypes) stats1
else {
- checkNoDoubleDefs(stats1)
+ // As packages are open, it doesn't make sense to check double definitions here. Furthermore,
+ // it is expensive if the package is large. Instead, such double defininitions are checked in `Namers.enterInScope`
+ if (!context.owner.isPackageClass)
+ checkNoDoubleDefs
addSynthetics(stats1)
}
}
diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
index 3b12086cc7..923297bafb 100644
--- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
+++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
@@ -141,6 +141,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
val run = new Run
run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works
phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled
+ globalPhase = run.typerPhase // amazing... looks like phase and globalPhase are different things, so we need to set them separately
currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions
reporter.reset()