diff options
Diffstat (limited to 'src')
56 files changed, 1041 insertions, 1258 deletions
diff --git a/src/build/bnd/scala-actors.bnd b/src/build/bnd/scala-actors.bnd index 8d0555777f..69885fc2bf 100644 --- a/src/build/bnd/scala-actors.bnd +++ b/src/build/bnd/scala-actors.bnd @@ -3,3 +3,5 @@ Bundle-SymbolicName: org.scala-lang.scala-actors ver: @VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} +Import-Package: scala.*;version="${range;[==,=+);${ver}}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-compiler-doc.bnd b/src/build/bnd/scala-compiler-doc.bnd index 4910e5fcb0..9d6d0304d1 100644 --- a/src/build/bnd/scala-compiler-doc.bnd +++ b/src/build/bnd/scala-compiler-doc.bnd @@ -3,4 +3,5 @@ Bundle-SymbolicName: org.scala-lang.modules.scala-compiler-doc_@SCALA_BINARY_VER ver: @SCALA_COMPILER_DOC_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} -Import-Package: * +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-compiler-interactive.bnd b/src/build/bnd/scala-compiler-interactive.bnd index 34d2f2956d..07e3de35b0 100644 --- a/src/build/bnd/scala-compiler-interactive.bnd +++ b/src/build/bnd/scala-compiler-interactive.bnd @@ -3,4 +3,5 @@ Bundle-SymbolicName: org.scala-lang.modules.scala-compiler-interactive_@SCALA_BI ver: @SCALA_COMPILER_INTERACTIVE_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} -Import-Package: * +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-compiler.bnd b/src/build/bnd/scala-compiler.bnd index dc30513db4..2bd24d780d 100644 --- a/src/build/bnd/scala-compiler.bnd +++ b/src/build/bnd/scala-compiler.bnd @@ -5,4 +5,8 @@ Bundle-Version: ${ver} Export-Package: *;version=${ver} Import-Package: jline.*;resolution:=optional, \ org.apache.tools.ant.*;resolution:=optional, \ + scala.util.parsing.*;version="${range;[====,====];@PARSER_COMBINATORS_VERSION@}";resolution:=optional, \ + scala.xml.*;version="${range;[====,====];@XML_VERSION@}";resolution:=optional, \ + scala.*;version="${range;[==,=+);${ver}}", \ * +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-continuations-library.bnd b/src/build/bnd/scala-continuations-library.bnd index bb505b60a9..b36718cc5b 100644 --- a/src/build/bnd/scala-continuations-library.bnd +++ b/src/build/bnd/scala-continuations-library.bnd @@ -1,5 +1,7 @@ Bundle-Name: Scala Delimited Continuations Library Bundle-SymbolicName: org.scala-lang.plugins.scala-continuations-library -ver: @VERSION@ +ver: @CONTINUATIONS_LIBRARY_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-continuations-plugin.bnd b/src/build/bnd/scala-continuations-plugin.bnd index cd66614a22..2f2464b452 100644 --- a/src/build/bnd/scala-continuations-plugin.bnd +++ b/src/build/bnd/scala-continuations-plugin.bnd @@ -1,5 +1,7 @@ Bundle-Name: Scala Delimited Continuations Compiler Plugin Bundle-SymbolicName: org.scala-lang.plugins.scala-continuations-plugin -ver: @VERSION@ +ver: @CONTINUATIONS_PLUGIN_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-library.bnd b/src/build/bnd/scala-library.bnd index 03aff45672..7eb4fa4b2a 100644 --- a/src/build/bnd/scala-library.bnd +++ b/src/build/bnd/scala-library.bnd @@ -4,3 +4,4 @@ ver: @VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} Import-Package: sun.misc;resolution:=optional, * +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-parser-combinators.bnd b/src/build/bnd/scala-parser-combinators.bnd index 6ffc3b2760..ef8646cbd0 100644 --- a/src/build/bnd/scala-parser-combinators.bnd +++ b/src/build/bnd/scala-parser-combinators.bnd @@ -1,5 +1,7 @@ Bundle-Name: Scala Parser Combinators Library Bundle-SymbolicName: org.scala-lang.modules.scala-parser-combinators -ver: @VERSION@ +ver: @PARSER_COMBINATORS_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-reflect.bnd b/src/build/bnd/scala-reflect.bnd index 6cda346d3a..e4bc54e52e 100644 --- a/src/build/bnd/scala-reflect.bnd +++ b/src/build/bnd/scala-reflect.bnd @@ -3,4 +3,7 @@ Bundle-SymbolicName: org.scala-lang.scala-reflect ver: @VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} -Import-Package: scala.tools.nsc;resolution:=optional, * +Import-Package: scala.*;version="${range;[==,=+);${ver}}", \ + scala.tools.nsc;resolution:=optional;version="${range;[==,=+);${ver}}", \ + * +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-swing.bnd b/src/build/bnd/scala-swing.bnd index 7cccb1343b..f8b50baa91 100644 --- a/src/build/bnd/scala-swing.bnd +++ b/src/build/bnd/scala-swing.bnd @@ -1,5 +1,7 @@ Bundle-Name: Scala Swing Bundle-SymbolicName: org.scala-lang.modules.scala-swing -ver: @VERSION@ +ver: @SCALA_SWING_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6,JavaSE-1.7 diff --git a/src/build/bnd/scala-xml.bnd b/src/build/bnd/scala-xml.bnd index 5d64c05e65..01bf0144eb 100644 --- a/src/build/bnd/scala-xml.bnd +++ b/src/build/bnd/scala-xml.bnd @@ -1,5 +1,7 @@ Bundle-Name: Scala XML Library Bundle-SymbolicName: org.scala-lang.modules.scala-xml -ver: @VERSION@ +ver: @XML_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 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/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 0da66d43f7..0ad3c2c76b 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -1424,11 +1424,18 @@ abstract class GenICode extends SubComponent { def genZandOrZor(and: Boolean): Boolean = { val ctxInterm = ctx.newBlock() - val branchesReachable = if (and) genCond(lhs, ctx, ctxInterm, elseCtx) + val lhsBranchesReachable = if (and) genCond(lhs, ctx, ctxInterm, elseCtx) else genCond(lhs, ctx, thenCtx, ctxInterm) - ctxInterm.bb killUnless branchesReachable + // If lhs is known to throw, we can kill the just created ctxInterm. + ctxInterm.bb killUnless lhsBranchesReachable - genCond(rhs, ctxInterm, thenCtx, elseCtx) + val rhsBranchesReachable = genCond(rhs, ctxInterm, thenCtx, elseCtx) + + // Reachable means "it does not always throw", i.e. "it might not throw". + // In an expression (a && b) or (a || b), the b branch might not be evaluated. + // Such an expression is therefore known to throw only if both expressions throw. Or, + // successors are reachable if either of the two is reachable (SI-8625). + lhsBranchesReachable || rhsBranchesReachable } def genRefEq(isEq: Boolean) = { val f = genEqEqPrimitive(lhs, rhs, ctx) _ diff --git a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala 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/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index e036035397..2f2142027f 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -1050,20 +1050,18 @@ abstract class Erasure extends AddInterfaces } } - def isAccessible(sym: Symbol) = localTyper.context.isAccessible(sym, sym.owner.thisType) - if (!isAccessible(owner) && qual.tpe != null) { + def isJvmAccessible(sym: Symbol) = (sym.isClass && !sym.isJavaDefined) || localTyper.context.isAccessible(sym, sym.owner.thisType) + if (!isJvmAccessible(owner) && qual.tpe != null) { qual match { case Super(_, _) => - // Insert a cast here at your peril -- see SI-5162. Bail out if the target method is defined in - // Java, otherwise, we'd get an IllegalAccessError at runtime. If the target method is defined in - // Scala, however, we should have access. - if (owner.isJavaDefined) unit.error(tree.pos, s"Unable to access ${tree.symbol.fullLocationString} with a super reference.") + // Insert a cast here at your peril -- see SI-5162. + unit.error(tree.pos, s"Unable to access ${tree.symbol.fullLocationString} with a super reference.") tree case _ => // Todo: Figure out how qual.tpe could be null in the check above (it does appear in build where SwingWorker.this // has a null type). val qualSym = qual.tpe.widen.typeSymbol - if (isAccessible(qualSym) && !qualSym.isPackageClass && !qualSym.isPackageObjectClass) { + if (isJvmAccessible(qualSym) && !qualSym.isPackageClass && !qualSym.isPackageObjectClass) { // insert cast to prevent illegal access error (see #4283) // util.trace("insert erasure cast ") (*/ treeCopy.Select(tree, gen.mkAttributedCast(qual, qual.tpe.widen), name) //) diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 33e8e47c76..313d0a6e61 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -391,7 +391,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { else { sourceModule setPos sym.pos if (sourceModule.flags != MODULE) { - log("!!! Directly setting sourceModule flags from %s to MODULE".format(sourceModule.flagString)) + log(s"!!! Directly setting sourceModule flags for $sourceModule from ${sourceModule.flagString} to MODULE") sourceModule.flags = MODULE } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index d87090fa46..73c3e6f016 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1306,7 +1306,7 @@ trait Implicits { } def wrapResult(tree: Tree): SearchResult = - if (tree == EmptyTree) SearchFailure else new SearchResult(tree, EmptyTreeTypeSubstituter, Nil) + if (tree == EmptyTree) SearchFailure else new SearchResult(atPos(pos.focus)(tree), EmptyTreeTypeSubstituter, Nil) /** Materializes implicits of predefined types (currently, manifests and tags). * Will be replaced by implicit macros once we fix them. diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 4382a2c6f7..099031d536 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1040,10 +1040,10 @@ trait Namers extends MethodSynthesis { * so the resulting type is a valid external method type, it does not contain (references to) skolems. */ def thisMethodType(restpe: Type) = { - val checkDependencies = new DependentTypeChecker(context)(this) - checkDependencies check vparamSymss - // DEPMETTODO: check not needed when they become on by default - checkDependencies(restpe) + if (vparamSymss.lengthCompare(0) > 0) { // OPT fast path for methods of 0-1 parameter lists + val checkDependencies = new DependentTypeChecker(context)(this) + checkDependencies check vparamSymss + } val makeMethodType = (vparams: List[Symbol], restpe: Type) => { // TODODEPMET: check that we actually don't need to do anything here @@ -1177,7 +1177,13 @@ trait Namers extends MethodSynthesis { } } - addDefaultGetters(meth, ddef, vparamss, tparams, overriddenSymbol(methResTp)) + val overridden = { + val isConstr = meth.isConstructor + if (isConstr || !methOwner.isClass) NoSymbol else overriddenSymbol(methResTp) + } + val hasDefaults = mexists(vparamss)(_.symbol.hasDefault) || mexists(overridden.paramss)(_.hasDefault) + if (hasDefaults) + addDefaultGetters(meth, ddef, vparamss, tparams, overridden) // fast track macros, i.e. macros defined inside the compiler, are hardcoded // hence we make use of that and let them have whatever right-hand side they need @@ -1219,7 +1225,7 @@ trait Namers extends MethodSynthesis { * typechecked, the corresponding param would not yet have the "defaultparam" * flag. */ - private def addDefaultGetters(meth: Symbol, ddef: DefDef, vparamss: List[List[ValDef]], tparams: List[TypeDef], overriddenSymbol: => Symbol) { + private def addDefaultGetters(meth: Symbol, ddef: DefDef, vparamss: List[List[ValDef]], tparams: List[TypeDef], overridden: Symbol) { val DefDef(_, _, rtparams0, rvparamss0, _, _) = resetAttrs(ddef.duplicate) // having defs here is important to make sure that there's no sneaky tree sharing // in methods with multiple default parameters @@ -1227,7 +1233,6 @@ trait Namers extends MethodSynthesis { def rvparamss = rvparamss0.map(_.map(_.duplicate)) val methOwner = meth.owner val isConstr = meth.isConstructor - val overridden = if (isConstr || !methOwner.isClass) NoSymbol else overriddenSymbol val overrides = overridden != NoSymbol && !overridden.isOverloaded // value parameters of the base class (whose defaults might be overridden) var baseParamss = (vparamss, overridden.tpe.paramss) match { @@ -1745,7 +1750,6 @@ trait Namers extends MethodSynthesis { for (p <- vps) this(p.info) // can only refer to symbols in earlier parameter sections - // (if the extension is enabled) okParams ++= vps } } diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index dceb0a47d8..284ab2f6f9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -174,8 +174,8 @@ trait NamesDefaults { self: Analyzer => // assigning the correct method symbol, typedSelect will just assign the type. the reason // to still call 'typed' is to correctly infer singleton types, SI-5259. val selectPos = - if(qual.pos.isRange && baseFun.pos.isRange) qual.pos.union(baseFun.pos).withStart(Math.min(qual.pos.end, baseFun.pos.end)) - else baseFun.pos + if(qual.pos.isRange && baseFun1.pos.isRange) qual.pos.union(baseFun1.pos).withStart(Math.min(qual.pos.end, baseFun1.pos.end)) + else baseFun1.pos val f = blockTyper.typedOperator(Select(newQual, selected).setSymbol(baseFun1.symbol).setPos(selectPos)) if (funTargs.isEmpty) f else TypeApply(f, funTargs).setType(baseFun.tpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 9fe693ce2a..66b1c2d87a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3022,7 +3022,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper || (looker.hasAccessorFlag && !accessed.hasAccessorFlag && accessed.isPrivate) ) - def checkNoDoubleDefs(stats: List[Tree]): Unit = { + def checkNoDoubleDefs: Unit = { val scope = if (inBlock) context.scope else context.owner.info.decls var e = scope.elems while ((e ne null) && e.owner == scope) { @@ -3057,8 +3057,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // the corresponding synthetics to the package class, only to the package object class. def shouldAdd(sym: Symbol) = inBlock || !context.isInPackageObject(sym, context.owner) - for (sym <- scope if shouldAdd(sym)) - for (tree <- context.unit.synthetics get sym) { + for (sym <- scope) + for (tree <- context.unit.synthetics get sym if shouldAdd(sym)) { // OPT: shouldAdd is usually true. Call it here, rather than in the outer loop newStats += typedStat(tree) // might add even more synthetics to the scope context.unit.synthetics -= sym } @@ -3104,7 +3104,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val stats1 = stats mapConserve typedStat if (phase.erasedTypes) stats1 else { - checkNoDoubleDefs(stats1) + // As packages are open, it doesn't make sense to check double definitions here. Furthermore, + // it is expensive if the package is large. Instead, such double defininitions are checked in `Namers.enterInScope` + if (!context.owner.isPackageClass) + checkNoDoubleDefs addSynthetics(stats1) } } diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 3b12086cc7..923297bafb 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -141,6 +141,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => val run = new Run run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled + globalPhase = run.typerPhase // amazing... looks like phase and globalPhase are different things, so we need to set them separately currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions reporter.reset() 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 1b496383a3..f6f46e158f 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -922,11 +922,16 @@ 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] var i = 0 - while (self.hasNext && i < size) { + // The order of terms in the following condition is important + // here as self.hasNext could be blocking + while (i < size && self.hasNext) { buf += self.next i += 1 } @@ -943,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/HashSet.scala b/src/library/scala/collection/immutable/HashSet.scala index 726937efd9..e4b7371ed4 100644 --- a/src/library/scala/collection/immutable/HashSet.scala +++ b/src/library/scala/collection/immutable/HashSet.scala @@ -162,6 +162,13 @@ class HashSet[A] extends AbstractSet[A] def - (e: A): HashSet[A] = nullToEmpty(removed0(e, computeHash(e), 0)) + /** Returns this $coll as an immutable set. + * + * A new set will not be built; lazy collections will stay lazy. + */ + @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] + override def filter(p: A => Boolean) = { val buffer = new Array[HashSet[A]](bufferSize(size)) nullToEmpty(filter0(p, false, 0, buffer, 0)) diff --git a/src/library/scala/collection/immutable/ListSet.scala b/src/library/scala/collection/immutable/ListSet.scala index 1bb07eb02d..89d1a9640e 100644 --- a/src/library/scala/collection/immutable/ListSet.scala +++ b/src/library/scala/collection/immutable/ListSet.scala @@ -138,6 +138,13 @@ class ListSet[A] extends AbstractSet[A] override def stringPrefix = "ListSet" + /** Returns this $coll as an immutable set. + * + * A new set will not be built; lazy collections will stay lazy. + */ + @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] + /** Represents an entry in the `ListSet`. */ protected class Node(override val head: A) extends ListSet[A] with Serializable { diff --git a/src/library/scala/collection/immutable/Set.scala b/src/library/scala/collection/immutable/Set.scala index 0fbf7942d4..7725ad9ee3 100644 --- a/src/library/scala/collection/immutable/Set.scala +++ b/src/library/scala/collection/immutable/Set.scala @@ -35,12 +35,7 @@ trait Set[A] extends Iterable[A] override def companion: GenericCompanion[Set] = Set - /** Returns this $coll as an immutable map. - * - * A new map will not be built; lazy collections will stay lazy. - */ - @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") - override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] + override def toSet[B >: A]: Set[B] = to[({type l[a] = immutable.Set[B]})#l] // for bincompat; remove in dev override def seq: Set[A] = this protected override def parCombiner = ParSet.newCombiner[A] // if `immutable.SetLike` gets introduced, please move this there! @@ -62,6 +57,7 @@ object Set extends ImmutableSetFactory[Set] { def - (elem: Any): Set[Any] = this def iterator: Iterator[Any] = Iterator.empty override def foreach[U](f: Any => U): Unit = {} + override def toSet[B >: Any]: Set[B] = this.asInstanceOf[Set[B]] } private[collection] def emptyInstance: Set[Any] = EmptySet @@ -92,6 +88,8 @@ object Set extends ImmutableSetFactory[Set] { if (f(elem1)) Some(elem1) else None } + @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] } /** An optimized representation for immutable sets of size 2 */ @@ -123,6 +121,8 @@ object Set extends ImmutableSetFactory[Set] { else if (f(elem2)) Some(elem2) else None } + @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] } /** An optimized representation for immutable sets of size 3 */ @@ -156,6 +156,8 @@ object Set extends ImmutableSetFactory[Set] { else if (f(elem3)) Some(elem3) else None } + @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] } /** An optimized representation for immutable sets of size 4 */ @@ -191,6 +193,8 @@ object Set extends ImmutableSetFactory[Set] { else if (f(elem4)) Some(elem4) else None } + @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] } } 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/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index cf3f356daa..103f885ad4 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -48,22 +48,17 @@ trait Scopes extends api.Scopes { self: SymbolTable => * This is necessary because when run from reflection every scope needs to have a * SynchronizedScope as mixin. */ - class Scope protected[Scopes] (initElems: ScopeEntry = null, initFingerPrints: Long = 0L) extends ScopeApi with MemberScopeApi { + class Scope protected[Scopes]() extends ScopeApi with MemberScopeApi { - protected[Scopes] def this(base: Scope) = { - this(base.elems) - nestinglevel = base.nestinglevel + 1 - } - - private[scala] var elems: ScopeEntry = initElems + private[scala] var elems: ScopeEntry = _ /** The number of times this scope is nested in another */ - private var nestinglevel = 0 + private[Scopes] var nestinglevel = 0 /** the hash table */ - private var hashtable: Array[ScopeEntry] = null + private[Scopes] var hashtable: Array[ScopeEntry] = null /** a cache for all elements, to be used by symbol iterator. */ @@ -84,8 +79,6 @@ trait Scopes extends api.Scopes { self: SymbolTable => */ private val MIN_HASH = 8 - if (size >= MIN_HASH) createHash() - /** Returns a new scope with the same content as this one. */ def cloneScope: Scope = newScopeWith(this.toList: _*) @@ -435,7 +428,14 @@ trait Scopes extends api.Scopes { self: SymbolTable => } /** Create a new scope nested in another one with which it shares its elements */ - def newNestedScope(outer: Scope): Scope = new Scope(outer) + final def newNestedScope(outer: Scope): Scope = { + val nested = newScope // not `new Scope`, we must allow the runtime reflection universe to mixin SynchronizedScopes! + nested.elems = outer.elems + nested.nestinglevel = outer.nestinglevel + 1 + if (outer.hashtable ne null) + nested.hashtable = java.util.Arrays.copyOf(outer.hashtable, outer.hashtable.length) + nested + } /** Create a new scope with given initial elements */ def newScopeWith(elems: Symbol*): Scope = { 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/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index c56bc28d90..7ba68b8733 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -91,7 +91,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => // // Short of significantly changing SymbolLoaders I see no other way than just // to slap a global lock on materialization in runtime reflection. - class PackageScope(pkgClass: Symbol) extends Scope(initFingerPrints = -1L) // disable fingerprinting as we do not know entries beforehand + class PackageScope(pkgClass: Symbol) extends Scope with SynchronizedScope { assert(pkgClass.isType) diff --git a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala index c90901410a..4a8585d616 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala @@ -37,8 +37,7 @@ private[reflect] trait SynchronizedOps extends internal.SymbolTable // Scopes - override def newScope = new Scope() with SynchronizedScope - override def newNestedScope(outer: Scope): Scope = new Scope(outer) with SynchronizedScope + override def newScope = new Scope with SynchronizedScope trait SynchronizedScope extends Scope { // we can keep this lock fine-grained, because methods of Scope don't do anything extraordinary, which makes deadlocks impossible 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) } |