summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build/maven/scala-dist-pom.xml5
-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/backend/jvm/AsmUtils.scala8
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala96
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala718
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala96
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala139
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala25
-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/GenASM.scala7
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala11
-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/Mixin.scala2
-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/NamesDefaults.scala4
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala1
-rw-r--r--src/interactive/scala/tools/nsc/interactive/Main.scala7
-rwxr-xr-xsrc/library/scala/collection/IndexedSeqOptimized.scala2
-rw-r--r--src/library/scala/collection/Iterator.scala13
-rw-r--r--src/library/scala/collection/immutable/Stream.scala8
-rw-r--r--src/library/scala/collection/mutable/AVLTree.scala2
-rw-r--r--src/library/scala/concurrent/Channel.scala17
-rw-r--r--src/library/scala/concurrent/ExecutionContext.scala4
-rw-r--r--src/library/scala/concurrent/Lock.scala2
-rw-r--r--src/library/scala/concurrent/SyncVar.scala16
-rw-r--r--src/library/scala/concurrent/package.scala5
-rw-r--r--src/library/scala/util/Properties.scala7
-rw-r--r--src/reflect/scala/reflect/internal/Names.scala106
-rw-r--r--src/reflect/scala/reflect/internal/util/SourceFile.scala2
-rw-r--r--src/reflect/scala/reflect/runtime/package.scala3
-rw-r--r--src/repl/scala/tools/nsc/MainGenericRunner.scala128
-rwxr-xr-xsrc/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala12
38 files changed, 984 insertions, 1219 deletions
diff --git a/src/build/maven/scala-dist-pom.xml b/src/build/maven/scala-dist-pom.xml
index 22a24dea21..9477e14285 100644
--- a/src/build/maven/scala-dist-pom.xml
+++ b/src/build/maven/scala-dist-pom.xml
@@ -40,6 +40,11 @@
<version>@VERSION@</version>
</dependency>
<dependency>
+ <groupId>org.scala-lang</groupId>
+ <artifactId>scalap</artifactId>
+ <version>@VERSION@</version>
+ </dependency>
+ <dependency>
<groupId>org.scala-lang.plugins</groupId>
<!-- plugins are fully cross-versioned. But, we don't publish with 2.11.0-SNAPSHOT, instead use full version of the last non-snapshot version -->
<artifactId>scala-continuations-plugin_@SCALA_FULL_VERSION@</artifactId>
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/backend/jvm/AsmUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
index 856f85d9e3..2af2037fec 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
@@ -8,6 +8,7 @@ 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 {
@@ -50,4 +51,11 @@ object AsmUtils {
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 3d1b646069..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
@@ -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 3fdb38ce0e..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, false)
- 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
}
}
@@ -1112,17 +1114,17 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
// 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, false)
+ 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,12 @@ 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
)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
index f9f6e7c3ff..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)
}
}
@@ -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 a76fa4d7ba..effc68c5e3 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
@@ -25,6 +25,7 @@ import java.io.PrintWriter
*/
abstract class BCodeSkelBuilder extends BCodeHelpers {
import global._
+ import bTypes._
/*
* There's a dedicated PlainClassBuilder for each CompilationUnit,
@@ -133,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)
@@ -142,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
@@ -166,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)
@@ -261,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
)
@@ -397,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
}
@@ -531,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,
@@ -555,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)
@@ -685,7 +686,7 @@ 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
+ val jtype = asmMethodType(callee).descriptor
insnModB = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESPECIAL, jowner, jname, jtype, false)
}
@@ -694,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",
@@ -706,7 +707,7 @@ 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
+ 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)
@@ -723,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/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index 13a5c6413d..988c04f514 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -1401,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)
@@ -1446,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()
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
index 61cf76f524..9b292fee7f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
@@ -244,14 +244,9 @@ abstract class GenBCode extends BCodeSyncAndTry {
val beanC = if (bean == null) null else SubItem3(bean.name, getByteArray(bean))
if (AsmUtils.traceSerializedClassEnabled && plain.name.contains(AsmUtils.traceSerializedClassPattern)) {
- def readClass(bytes: Array[Byte]): asm.tree.ClassNode = {
- val node = new asm.tree.ClassNode()
- new asm.ClassReader(bytes).accept(node, 0)
- node
- }
- if (mirrorC != null) AsmUtils.traceClass(readClass(mirrorC.jclassBytes))
- AsmUtils.traceClass(readClass(plainC.jclassBytes))
- if (beanC != null) AsmUtils.traceClass(readClass(beanC.jclassBytes))
+ 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/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/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/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/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/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()
diff --git a/src/interactive/scala/tools/nsc/interactive/Main.scala b/src/interactive/scala/tools/nsc/interactive/Main.scala
index c838606f02..7796c65670 100644
--- a/src/interactive/scala/tools/nsc/interactive/Main.scala
+++ b/src/interactive/scala/tools/nsc/interactive/Main.scala
@@ -12,7 +12,7 @@ package interactive
*/
object Main extends nsc.MainClass {
override def processSettingsHook(): Boolean = {
- if (this.settings.Yidedebug) {
+ def run(): Unit = {
this.settings.Xprintpos.value = true
this.settings.Yrangepos.value = true
val compiler = new interactive.Global(this.settings, this.reporter)
@@ -27,8 +27,9 @@ object Main extends nsc.MainClass {
case None => reporter.reset() // Causes other compiler errors to be ignored
}
askShutdown
- false
}
- else true
+ super.processSettingsHook() && (
+ if (this.settings.Yidedebug) { run() ; false } else true
+ )
}
}
diff --git a/src/library/scala/collection/IndexedSeqOptimized.scala b/src/library/scala/collection/IndexedSeqOptimized.scala
index ade04e4de8..42cb37aa24 100755
--- a/src/library/scala/collection/IndexedSeqOptimized.scala
+++ b/src/library/scala/collection/IndexedSeqOptimized.scala
@@ -206,7 +206,7 @@ trait IndexedSeqOptimized[+A, +Repr] extends Any with IndexedSeqLike[A, Repr] {
override /*SeqLike*/
def lastIndexWhere(p: A => Boolean, end: Int): Int = {
- var i = end
+ var i = math.min(end, length - 1)
while (i >= 0 && !p(this(i))) i -= 1
i
}
diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala
index e321a6adba..f6f46e158f 100644
--- a/src/library/scala/collection/Iterator.scala
+++ b/src/library/scala/collection/Iterator.scala
@@ -922,6 +922,9 @@ trait Iterator[+A] extends TraversableOnce[A] {
/** For reasons which remain to be determined, calling
* self.take(n).toSeq cause an infinite loop, so we have
* a slight variation on take for local usage.
+ * NB: self.take.toSeq is slice.toStream, lazily built on self,
+ * so a subsequent self.hasNext would not test self after the
+ * group was consumed.
*/
private def takeDestructively(size: Int): Seq[A] = {
val buf = new ArrayBuffer[A]
@@ -945,12 +948,10 @@ trait Iterator[+A] extends TraversableOnce[A] {
// so the rest of the code can be oblivious
val xs: Seq[B] = {
val res = takeDestructively(count)
- // extra checks so we don't calculate length unless there's reason
- if (pad.isDefined && !self.hasNext) {
- val shortBy = count - res.length
- if (shortBy > 0) res ++ padding(shortBy) else res
- }
- else res
+ // was: extra checks so we don't calculate length unless there's reason
+ // but since we took the group eagerly, just use the fast length
+ val shortBy = count - res.length
+ if (shortBy > 0 && pad.isDefined) res ++ padding(shortBy) else res
}
lazy val len = xs.length
lazy val incomplete = len < count
diff --git a/src/library/scala/collection/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala
index 60de147477..d3ff5e8abf 100644
--- a/src/library/scala/collection/immutable/Stream.scala
+++ b/src/library/scala/collection/immutable/Stream.scala
@@ -97,6 +97,14 @@ import scala.language.implicitConversions
* If, on the other hand, there is nothing holding on to the head (e.g. we used
* `def` to define the `Stream`) then once it is no longer being used directly,
* it disappears.
+ *
+ * - Note that some operations, including [[drop]], [[dropWhile]],
+ * [[flatMap]] or [[collect]] may process a large number of intermediate
+ * elements before returning. These necessarily hold onto the head, since
+ * they are methods on `Stream`, and a stream holds its own head. For
+ * computations of this sort where memoization is not desired, use
+ * `Iterator` when possible.
+ *
* {{{
* // For example, let's build the natural numbers and do some silly iteration
* // over them.
diff --git a/src/library/scala/collection/mutable/AVLTree.scala b/src/library/scala/collection/mutable/AVLTree.scala
index de09bb2040..cc2acb74d4 100644
--- a/src/library/scala/collection/mutable/AVLTree.scala
+++ b/src/library/scala/collection/mutable/AVLTree.scala
@@ -14,8 +14,8 @@ package mutable
* An immutable AVL Tree implementation formerly used by mutable.TreeSet
*
* @author Lucien Pereira
- * @deprecated("AVLTree and its related classes are being removed from the standard library since they're not different enough from RedBlackTree to justify keeping them.", "2.11.0")
*/
+@deprecated("AVLTree and its related classes are being removed from the standard library since they're not different enough from RedBlackTree to justify keeping them.", "2.11.2")
private[mutable] sealed trait AVLTree[+A] extends Serializable {
def balance: Int
diff --git a/src/library/scala/concurrent/Channel.scala b/src/library/scala/concurrent/Channel.scala
index 067244bd1c..89ad7d8c0e 100644
--- a/src/library/scala/concurrent/Channel.scala
+++ b/src/library/scala/concurrent/Channel.scala
@@ -10,8 +10,10 @@
package scala.concurrent
-/** This class ...
+/** This class provides a simple FIFO queue of data objects,
+ * which are read by one or more reader threads.
*
+ * @tparam A type of data exchanged
* @author Martin Odersky
* @version 1.0, 10/03/2003
*/
@@ -20,11 +22,14 @@ class Channel[A] {
var elem: A = _
var next: LinkedList[A] = null
}
- private var written = new LinkedList[A] // FIFO buffer, realized through
+ private var written = new LinkedList[A] // FIFO queue, realized through
private var lastWritten = written // aliasing of a linked list
private var nreaders = 0
- /**
+ /** Append a value to the FIFO queue to be read by `read`.
+ * This operation is nonblocking and can be executed by any thread.
+ *
+ * @param x object to enqueue to this channel
*/
def write(x: A) = synchronized {
lastWritten.elem = x
@@ -33,6 +38,11 @@ class Channel[A] {
if (nreaders > 0) notify()
}
+ /** Retrieve the next waiting object from the FIFO queue,
+ * blocking if necessary until an object is available.
+ *
+ * @return next object dequeued from this channel
+ */
def read: A = synchronized {
while (written.next == null) {
try {
@@ -45,5 +55,4 @@ class Channel[A] {
written = written.next
x
}
-
}
diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala
index a1e94c8876..4674c9174b 100644
--- a/src/library/scala/concurrent/ExecutionContext.scala
+++ b/src/library/scala/concurrent/ExecutionContext.scala
@@ -77,12 +77,12 @@ trait ExecutionContext {
}
/**
- * Union interface since Java does not support union types
+ * An [[ExecutionContext]] that is also a Java [[Executor]].
*/
trait ExecutionContextExecutor extends ExecutionContext with Executor
/**
- * Union interface since Java does not support union types
+ * An [[ExecutionContext]] that is also a Java [[ExecutorService]].
*/
trait ExecutionContextExecutorService extends ExecutionContextExecutor with ExecutorService
diff --git a/src/library/scala/concurrent/Lock.scala b/src/library/scala/concurrent/Lock.scala
index 1c00c0e91f..8d18da2d38 100644
--- a/src/library/scala/concurrent/Lock.scala
+++ b/src/library/scala/concurrent/Lock.scala
@@ -14,8 +14,8 @@ package scala.concurrent
*
* @author Martin Odersky
* @version 1.0, 10/03/2003
- * @deprecated("Use java.util.concurrent.locks.Lock", "2.11.0")
*/
+@deprecated("Use java.util.concurrent.locks.Lock", "2.11.2")
class Lock {
var available = true
diff --git a/src/library/scala/concurrent/SyncVar.scala b/src/library/scala/concurrent/SyncVar.scala
index d5dc3d7e3f..494c955833 100644
--- a/src/library/scala/concurrent/SyncVar.scala
+++ b/src/library/scala/concurrent/SyncVar.scala
@@ -13,6 +13,7 @@ import java.util.concurrent.TimeUnit
/** A class to provide safe concurrent access to a mutable cell.
* All methods are synchronized.
*
+ * @tparam A type of the contained value
* @author Martin Odersky
* @version 1.0, 10/03/2003
*/
@@ -20,6 +21,12 @@ class SyncVar[A] {
private var isDefined: Boolean = false
private var value: Option[A] = None
+ /**
+ * Waits for this SyncVar to become defined and returns
+ * the result, without modifying the stored value.
+ *
+ * @return value that is held in this container
+ */
def get: A = synchronized {
while (!isDefined) wait()
value.get
@@ -57,8 +64,12 @@ class SyncVar[A] {
value
}
- /** Waits for this SyncVar to become defined and returns
- * the result */
+ /**
+ * Waits for this SyncVar to become defined and returns
+ * the result, unsetting the stored value before returning.
+ *
+ * @return value that was held in this container
+ */
def take(): A = synchronized {
try get
finally unsetVal()
@@ -129,4 +140,3 @@ class SyncVar[A] {
}
}
-
diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala
index cc1350f5a9..4d88253de4 100644
--- a/src/library/scala/concurrent/package.scala
+++ b/src/library/scala/concurrent/package.scala
@@ -55,6 +55,11 @@ package object concurrent {
}
package concurrent {
+ /**
+ * This marker trait is used by [[Await]] to ensure that [[Awaitable.ready]] and [[Awaitable.result]]
+ * are not directly called by user code. An implicit instance of this trait is only available when
+ * user code is currently calling the methods on [[Await]].
+ */
@implicitNotFound("Don't call `Awaitable` methods directly, use the `Await` object.")
sealed trait CanAwait
diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala
index d597feb898..2daa4de9a6 100644
--- a/src/library/scala/util/Properties.scala
+++ b/src/library/scala/util/Properties.scala
@@ -155,9 +155,12 @@ private[scala] trait PropertiesTrait {
// This is looking for javac, tools.jar, etc.
// Tries JDK_HOME first, then the more common but likely jre JAVA_HOME,
// and finally the system property based javaHome.
- def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome))
+ def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome))
- def versionMsg = "Scala %s %s -- %s".format(propCategory, versionString, copyrightString)
+ // private[scala] for 2.12
+ private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString"
+
+ def versionMsg = versionFor(propCategory)
def scalaCmd = if (isWin) "scala.bat" else "scala"
def scalacCmd = if (isWin) "scalac.bat" else "scalac"
diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala
index ae9f2da4e5..b50f324074 100644
--- a/src/reflect/scala/reflect/internal/Names.scala
+++ b/src/reflect/scala/reflect/internal/Names.scala
@@ -40,7 +40,10 @@ trait Names extends api.Names {
/** Hashtable for finding type names quickly. */
private val typeHashtable = new Array[TypeName](HASH_SIZE)
- /** The hashcode of a name. */
+ /**
+ * The hashcode of a name depends on the first, the last and the middle character,
+ * and the length of the name.
+ */
private def hashValue(cs: Array[Char], offset: Int, len: Int): Int =
if (len > 0)
(len * (41 * 41 * 41) +
@@ -104,10 +107,21 @@ trait Names extends api.Names {
// The logic order here is future-proofing against the possibility
// that name.toString will become an eager val, in which case the call
// to enterChars cannot follow the construction of the TermName.
- val ncStart = nc
- enterChars(cs, offset, len)
- if (cachedString ne null) new TermName_S(ncStart, len, h, cachedString)
- else new TermName_R(ncStart, len, h)
+ var startIndex = 0
+ if (cs == chrs) {
+ // Optimize for subName, the new name is already stored in chrs
+ startIndex = offset
+ } else {
+ startIndex = nc
+ enterChars(cs, offset, len)
+ }
+ val next = termHashtable(h)
+ val termName =
+ if (cachedString ne null) new TermName_S(startIndex, len, next, cachedString)
+ else new TermName_R(startIndex, len, next)
+ // Add the new termName to the hashtable only after it's been fully constructed
+ termHashtable(h) = termName
+ termName
}
}
if (synchronizeNames) nameLock.synchronized(body) else body
@@ -145,40 +159,20 @@ trait Names extends api.Names {
newTermName(bs, offset, len).toTypeName
/**
- * Used only by the GenBCode backend, to represent bytecode-level types in a way that makes equals() and hashCode() efficient.
- * For bytecode-level types of OBJECT sort, its internal name (not its descriptor) is stored.
- * For those of ARRAY sort, its descriptor is stored ie has a leading '['
- * For those of METHOD sort, its descriptor is stored ie has a leading '('
+ * Used by the GenBCode backend to lookup type names that are known to already exist. This method
+ * might be invoked in a multi-threaded setting. Invoking newTypeName instead might be unsafe.
*
- * can-multi-thread
- * TODO SI-6240 !!! JZ Really? the constructors TermName and TypeName publish unconstructed `this` references
- * into the hash tables; we could observe them here before the subclass constructor completes.
+ * can-multi-thread: names are added to the hash tables only after they are fully constructed.
*/
- final def lookupTypeName(cs: Array[Char]): TypeName = { lookupTypeNameIfExisting(cs, true) }
-
- final def lookupTypeNameIfExisting(cs: Array[Char], failOnNotFound: Boolean): TypeName = {
-
- val hterm = hashValue(cs, 0, cs.size) & HASH_MASK
- var nterm = termHashtable(hterm)
- while ((nterm ne null) && (nterm.length != cs.size || !equals(nterm.start, cs, 0, cs.size))) {
- nterm = nterm.next
- }
- if (nterm eq null) {
- if (failOnNotFound) { assert(false, "TermName not yet created: " + new String(cs)) }
- return null
- }
+ final def lookupTypeName(cs: Array[Char]): TypeName = {
+ val hash = hashValue(cs, 0, cs.length) & HASH_MASK
+ var typeName = typeHashtable(hash)
- val htype = hashValue(chrs, nterm.start, nterm.length) & HASH_MASK
- var ntype = typeHashtable(htype)
- while ((ntype ne null) && ntype.start != nterm.start) {
- ntype = ntype.next
+ while ((typeName ne null) && (typeName.length != cs.length || !equals(typeName.start, cs, 0, cs.length))) {
+ typeName = typeName.next
}
- if (ntype eq null) {
- if (failOnNotFound) { assert(false, "TypeName not yet created: " + new String(cs)) }
- return null
- }
-
- ntype
+ assert(typeName != null, s"TypeName ${new String(cs)} not yet created.")
+ typeName
}
// Classes ----------------------------------------------------------------------
@@ -515,43 +509,47 @@ trait Names extends api.Names {
/** TermName_S and TypeName_S have fields containing the string version of the name.
* TermName_R and TypeName_R recreate it each time toString is called.
*/
- private final class TermName_S(index0: Int, len0: Int, hash: Int, override val toString: String) extends TermName(index0, len0, hash) {
- protected def createCompanionName(h: Int): TypeName = new TypeName_S(index, len, h, toString)
+ private final class TermName_S(index0: Int, len0: Int, next0: TermName, override val toString: String) extends TermName(index0, len0, next0) {
+ protected def createCompanionName(next: TypeName): TypeName = new TypeName_S(index, len, next, toString)
override def newName(str: String): TermName = newTermNameCached(str)
}
- private final class TypeName_S(index0: Int, len0: Int, hash: Int, override val toString: String) extends TypeName(index0, len0, hash) {
- protected def createCompanionName(h: Int): TermName = new TermName_S(index, len, h, toString)
+ private final class TypeName_S(index0: Int, len0: Int, next0: TypeName, override val toString: String) extends TypeName(index0, len0, next0) {
override def newName(str: String): TypeName = newTypeNameCached(str)
}
- private final class TermName_R(index0: Int, len0: Int, hash: Int) extends TermName(index0, len0, hash) {
- protected def createCompanionName(h: Int): TypeName = new TypeName_R(index, len, h)
+ private final class TermName_R(index0: Int, len0: Int, next0: TermName) extends TermName(index0, len0, next0) {
+ protected def createCompanionName(next: TypeName): TypeName = new TypeName_R(index, len, next)
override def toString = new String(chrs, index, len)
}
- private final class TypeName_R(index0: Int, len0: Int, hash: Int) extends TypeName(index0, len0, hash) {
- protected def createCompanionName(h: Int): TermName = new TermName_R(index, len, h)
+ private final class TypeName_R(index0: Int, len0: Int, next0: TypeName) extends TypeName(index0, len0, next0) {
override def toString = new String(chrs, index, len)
}
// SYNCNOTE: caller to constructor must synchronize if `synchronizeNames` is enabled
- sealed abstract class TermName(index0: Int, len0: Int, hash: Int) extends Name(index0, len0) with TermNameApi {
+ sealed abstract class TermName(index0: Int, len0: Int, val next: TermName) extends Name(index0, len0) with TermNameApi {
type ThisNameType = TermName
protected[this] def thisName: TermName = this
- val next: TermName = termHashtable(hash)
- termHashtable(hash) = this
+
def isTermName: Boolean = true
def isTypeName: Boolean = false
def toTermName: TermName = this
def toTypeName: TypeName = {
def body = {
+ // Re-computing the hash saves a field for storing it in the TermName
val h = hashValue(chrs, index, len) & HASH_MASK
var n = typeHashtable(h)
while ((n ne null) && n.start != index)
n = n.next
if (n ne null) n
- else createCompanionName(h)
+ else {
+ val next = typeHashtable(h)
+ val typeName = createCompanionName(next)
+ // Add the new typeName to the hashtable only after it's been fully constructed
+ typeHashtable(h) = typeName
+ typeName
+ }
}
if (synchronizeNames) nameLock.synchronized(body) else body
}
@@ -562,7 +560,7 @@ trait Names extends api.Names {
def nameKind = "term"
/** SYNCNOTE: caller must synchronize if `synchronizeNames` is enabled */
- protected def createCompanionName(h: Int): TypeName
+ protected def createCompanionName(next: TypeName): TypeName
}
implicit val TermNameTag = ClassTag[TermName](classOf[TermName])
@@ -572,24 +570,22 @@ trait Names extends api.Names {
def unapply(name: TermName): Option[String] = Some(name.toString)
}
- sealed abstract class TypeName(index0: Int, len0: Int, hash: Int) extends Name(index0, len0) with TypeNameApi {
+ sealed abstract class TypeName(index0: Int, len0: Int, val next: TypeName) extends Name(index0, len0) with TypeNameApi {
type ThisNameType = TypeName
protected[this] def thisName: TypeName = this
- val next: TypeName = typeHashtable(hash)
- typeHashtable(hash) = this
-
def isTermName: Boolean = false
def isTypeName: Boolean = true
def toTermName: TermName = {
def body = {
+ // Re-computing the hash saves a field for storing it in the TypeName
val h = hashValue(chrs, index, len) & HASH_MASK
var n = termHashtable(h)
while ((n ne null) && n.start != index)
n = n.next
- if (n ne null) n
- else createCompanionName(h)
+ assert (n ne null, s"TypeName $this is missing its correspondent")
+ n
}
if (synchronizeNames) nameLock.synchronized(body) else body
}
@@ -601,8 +597,6 @@ trait Names extends api.Names {
def nameKind = "type"
override def decode = if (nameDebug) super.decode + "!" else super.decode
- /** SYNCNOTE: caller must synchronize if `synchronizeNames` is enabled */
- protected def createCompanionName(h: Int): TermName
}
implicit val TypeNameTag = ClassTag[TypeName](classOf[TypeName])
diff --git a/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala
index 4fccad74ac..a2642628a4 100644
--- a/src/reflect/scala/reflect/internal/util/SourceFile.scala
+++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala
@@ -40,7 +40,7 @@ abstract class SourceFile {
def lineToString(index: Int): String = {
val start = lineToOffset(index)
var end = start
- while (!isEndOfLine(end) && end <= length) end += 1
+ while (end < length && !isEndOfLine(end)) end += 1
new String(content, start, end - start)
}
diff --git a/src/reflect/scala/reflect/runtime/package.scala b/src/reflect/scala/reflect/runtime/package.scala
index 3c9bbccba3..e240bed0a7 100644
--- a/src/reflect/scala/reflect/runtime/package.scala
+++ b/src/reflect/scala/reflect/runtime/package.scala
@@ -30,7 +30,8 @@ package runtime {
import c.universe._
val runtimeClass = c.reifyEnclosingRuntimeClass
if (runtimeClass.isEmpty) c.abort(c.enclosingPosition, "call site does not have an enclosing class")
- val runtimeUniverse = Select(Select(Select(Ident(newTermName("scala")), newTermName("reflect")), newTermName("runtime")), newTermName("universe"))
+ val scalaPackage = Select(Ident(newTermName("_root_")), newTermName("scala"))
+ val runtimeUniverse = Select(Select(Select(scalaPackage, newTermName("reflect")), newTermName("runtime")), newTermName("universe"))
val currentMirror = Apply(Select(runtimeUniverse, newTermName("runtimeMirror")), List(Select(runtimeClass, newTermName("getClassLoader"))))
c.Expr[Nothing](currentMirror)(c.WeakTypeTag.Nothing)
}
diff --git a/src/repl/scala/tools/nsc/MainGenericRunner.scala b/src/repl/scala/tools/nsc/MainGenericRunner.scala
index 43f0ea1256..34057ed341 100644
--- a/src/repl/scala/tools/nsc/MainGenericRunner.scala
+++ b/src/repl/scala/tools/nsc/MainGenericRunner.scala
@@ -8,7 +8,6 @@ package tools.nsc
import io.{ File }
import util.{ ClassPath, ScalaClassLoader }
-import Properties.{ versionString, copyrightString }
import GenericRunnerCommand._
object JarRunner extends CommonRunner {
@@ -28,79 +27,78 @@ object JarRunner extends CommonRunner {
}
/** An object that runs Scala code. It has three possible
- * sources for the code to run: pre-compiled code, a script file,
- * or interactive entry.
- */
+ * sources for the code to run: pre-compiled code, a script file,
+ * or interactive entry.
+ */
class MainGenericRunner {
- def errorFn(ex: Throwable): Boolean = {
- ex.printStackTrace()
- false
- }
- def errorFn(str: String): Boolean = {
- Console.err println str
- false
+ def errorFn(str: String, e: Option[Throwable] = None, isFailure: Boolean = true): Boolean = {
+ if (str.nonEmpty) Console.err println str
+ e foreach (_.printStackTrace())
+ !isFailure
}
def process(args: Array[String]): Boolean = {
val command = new GenericRunnerCommand(args.toList, (x: String) => errorFn(x))
- import command.{ settings, howToRun, thingToRun }
- def sampleCompiler = new Global(settings) // def so its not created unless needed
-
- if (!command.ok) return errorFn("\n" + command.shortUsageMsg)
- else if (settings.version) return errorFn("Scala code runner %s -- %s".format(versionString, copyrightString))
- else if (command.shouldStopWithInfo) return errorFn(command getInfoMessage sampleCompiler)
-
- def isE = !settings.execute.isDefault
- def dashe = settings.execute.value
-
- def isI = !settings.loadfiles.isDefault
- def dashi = settings.loadfiles.value
-
- // Deadlocks on startup under -i unless we disable async.
- if (isI)
- settings.Yreplsync.value = true
-
- def combinedCode = {
- val files = if (isI) dashi map (file => File(file).slurp()) else Nil
- val str = if (isE) List(dashe) else Nil
-
- files ++ str mkString "\n\n"
- }
-
- def runTarget(): Either[Throwable, Boolean] = howToRun match {
- case AsObject =>
- ObjectRunner.runAndCatch(settings.classpathURLs, thingToRun, command.arguments)
- case AsScript =>
- ScriptRunner.runScriptAndCatch(settings, thingToRun, command.arguments)
- case AsJar =>
- JarRunner.runJar(settings, thingToRun, command.arguments)
- case Error =>
- Right(false)
- case _ =>
- // We start the repl when no arguments are given.
- Right(new interpreter.ILoop process settings)
+ import command.{ settings, howToRun, thingToRun, shortUsageMsg, shouldStopWithInfo }
+ def sampleCompiler = new Global(settings) // def so it's not created unless needed
+
+ def run(): Boolean = {
+ def isE = !settings.execute.isDefault
+ def dashe = settings.execute.value
+
+ def isI = !settings.loadfiles.isDefault
+ def dashi = settings.loadfiles.value
+
+ // Deadlocks on startup under -i unless we disable async.
+ if (isI)
+ settings.Yreplsync.value = true
+
+ def combinedCode = {
+ val files = if (isI) dashi map (file => File(file).slurp()) else Nil
+ val str = if (isE) List(dashe) else Nil
+
+ files ++ str mkString "\n\n"
+ }
+
+ def runTarget(): Either[Throwable, Boolean] = howToRun match {
+ case AsObject =>
+ ObjectRunner.runAndCatch(settings.classpathURLs, thingToRun, command.arguments)
+ case AsScript =>
+ ScriptRunner.runScriptAndCatch(settings, thingToRun, command.arguments)
+ case AsJar =>
+ JarRunner.runJar(settings, thingToRun, command.arguments)
+ case Error =>
+ Right(false)
+ case _ =>
+ // We start the repl when no arguments are given.
+ Right(new interpreter.ILoop process settings)
+ }
+
+ /** If -e and -i were both given, we want to execute the -e code after the
+ * -i files have been included, so they are read into strings and prepended to
+ * the code given in -e. The -i option is documented to only make sense
+ * interactively so this is a pretty reasonable assumption.
+ *
+ * This all needs a rewrite though.
+ */
+ if (isE) {
+ ScriptRunner.runCommand(settings, combinedCode, thingToRun +: command.arguments)
+ }
+ else runTarget() match {
+ case Left(ex) => errorFn("", Some(ex)) // there must be a useful message of hope to offer here
+ case Right(b) => b
+ }
}
- /** If -e and -i were both given, we want to execute the -e code after the
- * -i files have been included, so they are read into strings and prepended to
- * the code given in -e. The -i option is documented to only make sense
- * interactively so this is a pretty reasonable assumption.
- *
- * This all needs a rewrite though.
- */
- if (isE) {
- ScriptRunner.runCommand(settings, combinedCode, thingToRun +: command.arguments)
- }
- else runTarget() match {
- case Left(ex) => errorFn(ex)
- case Right(b) => b
- }
+ if (!command.ok)
+ errorFn(f"%n$shortUsageMsg")
+ else if (shouldStopWithInfo)
+ errorFn(command getInfoMessage sampleCompiler, isFailure = false)
+ else
+ run()
}
}
object MainGenericRunner extends MainGenericRunner {
- def main(args: Array[String]) {
- if (!process(args))
- sys.exit(1)
- }
+ def main(args: Array[String]): Unit = if (!process(args)) sys.exit(1)
}
diff --git a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala
index a933c35c99..19cc27b40b 100755
--- a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala
@@ -666,7 +666,7 @@ trait CommentFactoryBase { this: MemberLookupBase =>
}
def summary(): Inline = {
- val i = inline(check("."))
+ val i = inline(checkSentenceEnded())
Summary(
if (jump("."))
Chain(List(i, Text(".")))
@@ -785,6 +785,16 @@ trait CommentFactoryBase { this: MemberLookupBase =>
})
}
+ def checkSentenceEnded(): Boolean = {
+ (char == '.') && {
+ val poff = offset
+ nextChar() // read '.'
+ val ok = char == endOfText || char == endOfLine || isWhitespace(char)
+ offset = poff
+ ok
+ }
+ }
+
def reportError(pos: Position, message: String) {
reporter.warning(pos, message)
}