diff options
Diffstat (limited to 'src')
46 files changed, 311 insertions, 256 deletions
diff --git a/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala b/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala index a13a778b2f..b8384851da 100644 --- a/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala +++ b/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala @@ -12,7 +12,7 @@ abstract class DefaultMacroCompiler extends Resolvers import treeInfo._ import definitions._ val runDefinitions = currentRun.runDefinitions - import runDefinitions.{Predef_???, _} + import runDefinitions.Predef_??? val typer: global.analyzer.Typer val context = typer.context diff --git a/src/compiler/scala/reflect/macros/compiler/Errors.scala b/src/compiler/scala/reflect/macros/compiler/Errors.scala index cc4508e696..98fd091e9c 100644 --- a/src/compiler/scala/reflect/macros/compiler/Errors.scala +++ b/src/compiler/scala/reflect/macros/compiler/Errors.scala @@ -11,7 +11,6 @@ trait Errors extends Traces { import analyzer._ import definitions._ import treeInfo._ - import typer.TyperErrorGen._ import typer.infer.InferErrorGen._ import runDefinitions._ def globalSettings = global.settings diff --git a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala index 4484c234aa..d3f49390ea 100644 --- a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala +++ b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala @@ -1,18 +1,12 @@ package scala.reflect.macros package compiler -import scala.reflect.internal.Flags._ -import scala.reflect.macros.TypecheckException - trait Resolvers { self: DefaultMacroCompiler => import global._ import analyzer._ - import definitions._ import treeInfo._ - import gen._ - import runDefinitions._ trait Resolver { self: MacroImplRefCompiler => diff --git a/src/compiler/scala/reflect/macros/compiler/Validators.scala b/src/compiler/scala/reflect/macros/compiler/Validators.scala index a146818ae3..fc932f2b18 100644 --- a/src/compiler/scala/reflect/macros/compiler/Validators.scala +++ b/src/compiler/scala/reflect/macros/compiler/Validators.scala @@ -9,7 +9,7 @@ trait Validators { import global._ import analyzer._ import definitions._ - import runDefinitions.{Predef_???, _} + import runDefinitions.Predef_??? trait Validator { self: MacroImplRefCompiler => diff --git a/src/compiler/scala/reflect/macros/util/Helpers.scala b/src/compiler/scala/reflect/macros/util/Helpers.scala index bddc42d1f9..961c41dab5 100644 --- a/src/compiler/scala/reflect/macros/util/Helpers.scala +++ b/src/compiler/scala/reflect/macros/util/Helpers.scala @@ -54,14 +54,10 @@ trait Helpers { * * @see Metalevels.scala for more information and examples about metalevels */ - def increaseMetalevel(pre: Type, tp: Type): Type = { - val runDefinitions = currentRun.runDefinitions - import runDefinitions._ - + def increaseMetalevel(pre: Type, tp: Type): Type = transparentShallowTransform(RepeatedParamClass, tp) { case tp => typeRef(pre, MacroContextExprClass, List(tp)) } - } /** Transforms c.Expr[T] types into c.Tree and leaves the rest unchanged. */ diff --git a/src/compiler/scala/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/reflect/quasiquotes/Parsers.scala index 007bac27da..97ec7dbfc3 100644 --- a/src/compiler/scala/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/reflect/quasiquotes/Parsers.scala @@ -90,7 +90,7 @@ trait Parsers { self: Quasiquotes => case _ => super.makePatDef(mods, pat, rhs) } } - import treeBuilder.{global => _, unit => _, _} + import treeBuilder.{global => _, unit => _} // q"def foo($x)" override def param(owner: Name, implicitmod: Int, caseParam: Boolean): ValDef = diff --git a/src/compiler/scala/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/reflect/quasiquotes/Reifiers.scala index 07becdc3c6..cc98717c4e 100644 --- a/src/compiler/scala/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/reflect/quasiquotes/Reifiers.scala @@ -8,7 +8,6 @@ import scala.reflect.internal.Flags._ trait Reifiers { self: Quasiquotes => import global._ import global.build._ - import global.treeInfo._ import global.definitions._ import Rank._ import universeTypes._ diff --git a/src/compiler/scala/reflect/reify/Reifier.scala b/src/compiler/scala/reflect/reify/Reifier.scala index b1cc797389..a3e0f02dcc 100644 --- a/src/compiler/scala/reflect/reify/Reifier.scala +++ b/src/compiler/scala/reflect/reify/Reifier.scala @@ -21,7 +21,6 @@ abstract class Reifier extends States import global._ import definitions._ private val runDefinitions = currentRun.runDefinitions - import runDefinitions._ val typer: global.analyzer.Typer val universe: Tree diff --git a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala index 4512b2cb6f..de9fec0df5 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala @@ -5,10 +5,6 @@ trait GenUtils { self: Reifier => import global._ - import treeInfo._ - import definitions._ - private val runDefinitions = currentRun.runDefinitions - import runDefinitions._ def reifyList(xs: List[Any]): Tree = mkList(xs map reify) diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index 0a356ed7b6..1a6843a249 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -8,7 +8,6 @@ package scala.tools.nsc import scala.reflect.internal.util.{ SourceFile, NoSourceFile, FreshNameCreator } import scala.collection.mutable import scala.collection.mutable.{ LinkedHashSet, ListBuffer } -import scala.tools.nsc.reporters.Reporter trait CompilationUnits { global: Global => diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 76d8b7e5ef..9cc9712b44 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -9,17 +9,16 @@ package nsc import java.io.{ File, FileOutputStream, PrintWriter, IOException, FileNotFoundException } import java.nio.charset.{ Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException } -import java.util.UUID._ import scala.compat.Platform.currentTime import scala.collection.{ mutable, immutable } import io.{ SourceReader, AbstractFile, Path } import reporters.{ Reporter, ConsoleReporter } -import util.{ ClassPath, MergedClassPath, StatisticsInfo, returning, stackTraceString } +import util.{ ClassPath, StatisticsInfo, returning, stackTraceString } import scala.reflect.ClassTag import scala.reflect.internal.util.{ OffsetPosition, SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile } import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat } import scala.reflect.io.VirtualFile -import symtab.{ Flags, SymbolTable, SymbolLoaders, SymbolTrackers } +import symtab.{ Flags, SymbolTable, SymbolTrackers } import symtab.classfile.Pickler import plugins.Plugins import ast._ @@ -28,7 +27,7 @@ import typechecker._ import transform.patmat.PatternMatching import transform._ import backend.icode.{ ICodes, GenICode, ICodeCheckers } -import backend.{ ScalaPrimitives, Platform, JavaPlatform } +import backend.{ ScalaPrimitives, JavaPlatform } import backend.jvm.GenBCode import backend.jvm.GenASM import backend.opt.{ Inliners, InlineExceptionHandlers, ConstantOptimization, ClosureElimination, DeadCodeElimination } diff --git a/src/compiler/scala/tools/nsc/Parsing.scala b/src/compiler/scala/tools/nsc/Parsing.scala index 4dd3c3f378..9e5999ce4f 100644 --- a/src/compiler/scala/tools/nsc/Parsing.scala +++ b/src/compiler/scala/tools/nsc/Parsing.scala @@ -7,7 +7,6 @@ package scala package tools.nsc import scala.reflect.internal.Positions -import scala.tools.nsc.reporters.Reporter /** Similar to Reporting: gather global functionality specific to parsing. */ diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index c9782de7c8..4d7e9e753f 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -7,7 +7,6 @@ package scala package tools package nsc -import reporters.{ Reporter, ConsoleReporter } import scala.collection.{ mutable, immutable } import scala.reflect.internal.util.StringOps.countElementsAsString diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala index 54d4a30553..328ec8a033 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala @@ -6,7 +6,6 @@ package scala.tools.nsc.backend.jvm import scala.tools.nsc.Global -import PartialFunction._ /** * This trait contains code shared between GenBCode and GenASM that depends on types defined in diff --git a/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala b/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala index 1fadcb8920..c6e699373b 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala @@ -7,7 +7,6 @@ package scala package tools.nsc package backend.opt -import scala.tools.nsc.backend.icode.analysis.LubException import scala.annotation.tailrec /** diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index 23611bb629..b4987e1240 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -229,7 +229,8 @@ class MutableSettings(val errorFn: String => Unit) def OutputSetting(outputDirs: OutputDirs, default: String) = add(new OutputSetting(outputDirs, default)) def PhasesSetting(name: String, descr: String, default: String = "") = add(new PhasesSetting(name, descr, default)) def StringSetting(name: String, arg: String, descr: String, default: String) = add(new StringSetting(name, arg, descr, default)) - def ScalaVersionSetting(name: String, arg: String, descr: String, default: ScalaVersion) = add(new ScalaVersionSetting(name, arg, descr, default)) + def ScalaVersionSetting(name: String, arg: String, descr: String, initial: ScalaVersion, default: Option[ScalaVersion] = None) = + add(new ScalaVersionSetting(name, arg, descr, initial, default)) def PathSetting(name: String, descr: String, default: String): PathSetting = { val prepend = StringSetting(name + "/p", "", "", "").internalOnly() val append = StringSetting(name + "/a", "", "", "").internalOnly() @@ -506,28 +507,35 @@ class MutableSettings(val errorFn: String => Unit) withHelpSyntax(name + " <" + arg + ">") } - /** A setting represented by a Scala version, (`default` unless set) */ + /** A setting represented by a Scala version. + * The `initial` value is used if the setting is not specified. + * The `default` value is used if the option is specified without argument (e.g., `-Xmigration`). + */ class ScalaVersionSetting private[nsc]( name: String, val arg: String, descr: String, - default: ScalaVersion) + initial: ScalaVersion, + default: Option[ScalaVersion]) extends Setting(name, descr) { type T = ScalaVersion - protected var v: T = NoScalaVersion + protected var v: T = initial + // This method is invoked if there are no colonated args. In this case the default value is + // used. No arguments are consumed. override def tryToSet(args: List[String]) = { - value = default + default match { + case Some(d) => value = d + case None => errorFn(s"$name requires an argument, the syntax is $helpSyntax") + } Some(args) } override def tryToSetColon(args: List[String]) = args match { - case Nil => value = default; Some(Nil) - case x :: xs => value = ScalaVersion(x, errorFn) ; Some(xs) + case x :: xs => value = ScalaVersion(x, errorFn); Some(xs) + case nil => Some(nil) } - override def tryToSetFromPropertyValue(s: String) = tryToSet(List(s)) - def unparse: List[String] = if (value == NoScalaVersion) Nil else List(s"${name}:${value.unparse}") withHelpSyntax(s"${name}:<${arg}>") diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 850534f2cc..c59d56d8f8 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -92,7 +92,8 @@ trait ScalaSettings extends AbsScalaSettings * The previous "-source" option is intended to be used mainly * though this helper. */ - lazy val isScala211: Boolean = (source.value >= ScalaVersion("2.11.0")) + def isScala211: Boolean = source.value >= ScalaVersion("2.11.0") + def isScala212: Boolean = source.value >= ScalaVersion("2.12.0") /** * -X "Advanced" settings @@ -111,7 +112,7 @@ trait ScalaSettings extends AbsScalaSettings val logFreeTerms = BooleanSetting ("-Xlog-free-terms", "Print a message when reification creates a free term.") val logFreeTypes = BooleanSetting ("-Xlog-free-types", "Print a message when reification resorts to generating a free type.") val maxClassfileName = IntSetting ("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, Some((72, 255)), _ => None) - val Xmigration = ScalaVersionSetting ("-Xmigration", "version", "Warn about constructs whose behavior may have changed since version.", AnyScalaVersion) + val Xmigration = ScalaVersionSetting ("-Xmigration", "version", "Warn about constructs whose behavior may have changed since version.", initial = NoScalaVersion, default = Some(AnyScalaVersion)) val nouescape = BooleanSetting ("-Xno-uescape", "Disable handling of \\u unicode escapes.") val Xnojline = BooleanSetting ("-Xnojline", "Do not use JLine for editing.") val Xverify = BooleanSetting ("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)") @@ -133,7 +134,7 @@ trait ScalaSettings extends AbsScalaSettings val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases.") val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "") val strictInference = BooleanSetting ("-Xstrict-inference", "Don't infer known-unsound types") - val source = ScalaVersionSetting ("-Xsource", "version", "Treat compiler input as Scala source for the specified version, see SI-8126.", ScalaVersion("2.11")) withPostSetHook ( _ => isScala211) + val source = ScalaVersionSetting ("-Xsource", "version", "Treat compiler input as Scala source for the specified version, see SI-8126.", initial = ScalaVersion("2.11")) val XnoPatmatAnalysis = BooleanSetting ("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.") val XfullLubs = BooleanSetting ("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.") diff --git a/src/compiler/scala/tools/nsc/settings/ScalaVersion.scala b/src/compiler/scala/tools/nsc/settings/ScalaVersion.scala index 4f45043c5e..43bdad5882 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaVersion.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaVersion.scala @@ -34,7 +34,7 @@ case object NoScalaVersion extends ScalaVersion { * to segregate builds */ case class SpecificScalaVersion(major: Int, minor: Int, rev: Int, build: ScalaBuild) extends ScalaVersion { - def unparse = s"${major}.${minor}.${rev}.${build.unparse}" + def unparse = s"${major}.${minor}.${rev}${build.unparse}" def compare(that: ScalaVersion): Int = that match { case SpecificScalaVersion(thatMajor, thatMinor, thatRev, thatBuild) => diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index 835d338ab3..f7b1021ea2 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -30,7 +30,6 @@ import scala.collection.mutable.LinkedHashMap abstract class Delambdafy extends Transform with TypingTransformers with ast.TreeDSL with TypeAdaptingTransformer { import global._ import definitions._ - import CODE._ val analyzer: global.analyzer.type = global.analyzer diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 3d8b2f02f3..db639d0868 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -98,7 +98,7 @@ abstract class Erasure extends AddInterfaces val len = sig.length val copy: Array[Char] = sig.toCharArray var changed = false - while (i < sig.length) { + while (i < len) { val ch = copy(i) if (ch == '.' && last != '>') { copy(i) = '$' @@ -410,7 +410,6 @@ abstract class Erasure extends AddInterfaces def fulldef(sym: Symbol) = if (sym == NoSymbol) sym.toString else s"$sym: ${sym.tpe} in ${sym.owner}" - var noclash = true val clashErrors = mutable.Buffer[(Position, String)]() def clashError(what: String) = { val pos = if (member.owner == root) member.pos else root.pos diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala index bbd11efa7e..c1c025ad48 100644 --- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala +++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package transform -import symtab.Flags._ import scala.reflect.internal.SymbolPairs /** A class that yields a kind of iterator (`Cursor`), diff --git a/src/compiler/scala/tools/nsc/transform/Statics.scala b/src/compiler/scala/tools/nsc/transform/Statics.scala index e2508b8d08..4673be6de7 100644 --- a/src/compiler/scala/tools/nsc/transform/Statics.scala +++ b/src/compiler/scala/tools/nsc/transform/Statics.scala @@ -1,9 +1,6 @@ package scala.tools.nsc package transform -import symtab._ -import Flags._ - import collection.mutable.Buffer abstract class Statics extends Transform with ast.TreeDSL { diff --git a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala index f83b6f857e..3b23306386 100644 --- a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala +++ b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala @@ -1,7 +1,6 @@ package scala.tools.nsc package transform -import scala.reflect.internal._ import scala.tools.nsc.ast.TreeDSL import scala.tools.nsc.Global diff --git a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala index 7fcfdf4868..8924394b72 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala @@ -122,7 +122,6 @@ trait ScalacPatternExpanders { case _ => sel } val patterns = newPatterns(args) - val isSeq = sel.symbol.name == nme.unapplySeq val isUnapply = sel.symbol.name == nme.unapply val extractor = sel.symbol.name match { diff --git a/src/compiler/scala/tools/nsc/typechecker/Tags.scala b/src/compiler/scala/tools/nsc/typechecker/Tags.scala index 90ec3a89b8..57dc74d2a0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Tags.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Tags.scala @@ -11,7 +11,6 @@ trait Tags { self: Typer => private val runDefinitions = currentRun.runDefinitions - import runDefinitions._ private def resolveTag(pos: Position, taggedTp: Type, allowMaterialization: Boolean) = enteringTyper { def wrapper (tree: => Tree): Tree = if (allowMaterialization) (context.withMacrosEnabled[Tree](tree)) else (context.withMacrosDisabled[Tree](tree)) @@ -66,7 +65,7 @@ trait Tags { // if someone requests a type tag, but scala-reflect.jar isn't on the library classpath, then bail if (pre == NoType && ApiUniverseClass == NoSymbol) EmptyTree else { - val tagSym = if (concrete) TypeTagClass else WeakTypeTagClass + val tagSym = if (concrete) runDefinitions.TypeTagClass else runDefinitions.WeakTypeTagClass val tagTp = if (pre == NoType) TypeRef(ApiUniverseClass.toTypeConstructor, tagSym, List(tp)) else singleType(pre, pre member tagSym.name) val taggedTp = appliedType(tagTp, List(tp)) resolveTag(pos, taggedTp, allowMaterialization) diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 1dac27639c..0f90c6a478 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -607,7 +607,7 @@ trait TypeDiagnostics { if (!c.owner.exists || c.owner.isClass || c.owner.isMethod || (c.owner.isType && !c.owner.isParameter)) c else enclClassOrMethodOrTypeMember(c.outer) - val tt = tparams.filter(_.name != typeNames.WILDCARD).foreach { tp => + tparams.filter(_.name != typeNames.WILDCARD).foreach { tp => // we don't care about type params shadowing other type params in the same declaration enclClassOrMethodOrTypeMember(context).outer.lookupSymbol(tp.name, s => s != tp.symbol && s.hasRawInfo && reallyExists(s)) match { case LookupSucceeded(_, sym2) => context.warning(tp.pos, diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index cc2d9141ce..fc1f45e358 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -142,17 +142,30 @@ trait Unapplies extends ast.TreeDSL { /** The unapply method corresponding to a case class */ def caseModuleUnapplyMeth(cdef: ClassDef): DefDef = { - val tparams = constrTparamsInvariant(cdef) - val method = constrParamss(cdef) match { + val tparams = constrTparamsInvariant(cdef) + val method = constrParamss(cdef) match { case xs :: _ if xs.nonEmpty && isRepeatedParamType(xs.last.tpt) => nme.unapplySeq case _ => nme.unapply } - val cparams = List(ValDef(Modifiers(PARAM | SYNTHETIC), unapplyParamName, classType(cdef, tparams), EmptyTree)) - val ifNull = if (constrParamss(cdef).head.isEmpty) FALSE else REF(NoneModule) - val body = nullSafe({ case Ident(x) => caseClassUnapplyReturnValue(x, cdef) }, ifNull)(Ident(unapplyParamName)) + val cparams = List(ValDef(Modifiers(PARAM | SYNTHETIC), unapplyParamName, classType(cdef, tparams), EmptyTree)) + val resultType = if (!settings.isScala212) TypeTree() else { // fix for SI-6541 under -Xsource:2.12 + def repeatedToSeq(tp: Tree) = tp match { + case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS_NAME), tps) => AppliedTypeTree(gen.rootScalaDot(tpnme.Seq), tps) + case _ => tp + } + constrParamss(cdef) match { + case Nil | Nil :: _ => + gen.rootScalaDot(tpnme.Boolean) + case params :: _ => + val constrParamTypes = params.map(param => repeatedToSeq(param.tpt)) + AppliedTypeTree(gen.rootScalaDot(tpnme.Option), List(treeBuilder.makeTupleType(constrParamTypes))) + } + } + val ifNull = if (constrParamss(cdef).head.isEmpty) FALSE else REF(NoneModule) + val body = nullSafe({ case Ident(x) => caseClassUnapplyReturnValue(x, cdef) }, ifNull)(Ident(unapplyParamName)) atPos(cdef.pos.focus)( - DefDef(caseMods, method, tparams, List(cparams), TypeTree(), body) + DefDef(caseMods, method, tparams, List(cparams), resultType, body) ) } diff --git a/src/compiler/scala/tools/util/Javap.scala b/src/compiler/scala/tools/util/Javap.scala deleted file mode 100644 index 3cfc1eb2a1..0000000000 --- a/src/compiler/scala/tools/util/Javap.scala +++ /dev/null @@ -1,32 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools -package util - -import scala.tools.nsc.util.ScalaClassLoader -import java.io.PrintWriter - -trait JpResult { - def isError: Boolean - def value: Any - def show(): Unit -} - -trait Javap { - def loader: ScalaClassLoader - def printWriter: PrintWriter - def apply(args: Seq[String]): List[JpResult] - def tryFile(path: String): Option[Array[Byte]] - def tryClass(path: String): Array[Byte] -} - -object NoJavap extends Javap { - def loader: ScalaClassLoader = getClass.getClassLoader - def printWriter: PrintWriter = new PrintWriter(System.err, true) - def apply(args: Seq[String]): List[JpResult] = Nil - def tryFile(path: String): Option[Array[Byte]] = None - def tryClass(path: String): Array[Byte] = Array() -} diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index aa9dec2761..9bfefc3de2 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -290,7 +290,6 @@ sealed abstract class List[+A] extends AbstractSeq[A] if (this eq Nil) Nil.asInstanceOf[That] else { var rest = this var h: ::[B] = null - var x: A = null.asInstanceOf[A] // Special case for first element do { val x: Any = pf.applyOrElse(rest.head, List.partialNotApplied) diff --git a/src/library/scala/collection/mutable/AnyRefMap.scala b/src/library/scala/collection/mutable/AnyRefMap.scala index 47fb66744e..fccc9d83e6 100644 --- a/src/library/scala/collection/mutable/AnyRefMap.scala +++ b/src/library/scala/collection/mutable/AnyRefMap.scala @@ -224,7 +224,7 @@ extends AbstractMap[K, V] override def put(key: K, value: V): Option[V] = { val h = hashOf(key) val k = key - var i = seekEntryOrOpen(h, k) + val i = seekEntryOrOpen(h, k) if (i < 0) { val j = i & IndexMask _hashes(j) = h @@ -251,7 +251,7 @@ extends AbstractMap[K, V] override def update(key: K, value: V): Unit = { val h = hashOf(key) val k = key - var i = seekEntryOrOpen(h, k) + val i = seekEntryOrOpen(h, k) if (i < 0) { val j = i & IndexMask _hashes(j) = h diff --git a/src/library/scala/collection/mutable/LongMap.scala b/src/library/scala/collection/mutable/LongMap.scala index ef488a3697..5fafe23d9f 100644 --- a/src/library/scala/collection/mutable/LongMap.scala +++ b/src/library/scala/collection/mutable/LongMap.scala @@ -81,7 +81,7 @@ extends AbstractMap[Long, V] private def toIndex(k: Long): Int = { // Part of the MurmurHash3 32 bit finalizer val h = ((k ^ (k >>> 32)) & 0xFFFFFFFFL).toInt - var x = (h ^ (h >>> 16)) * 0x85EBCA6B + val x = (h ^ (h >>> 16)) * 0x85EBCA6B (x ^ (x >>> 13)) & mask } @@ -311,7 +311,7 @@ extends AbstractMap[Long, V] } } else { - var i = seekEntryOrOpen(key) + val i = seekEntryOrOpen(key) if (i < 0) { val j = i & IndexMask _keys(j) = key diff --git a/src/library/scala/reflect/ClassTag.scala b/src/library/scala/reflect/ClassTag.scala index bced505273..2f4aa9cb84 100644 --- a/src/library/scala/reflect/ClassTag.scala +++ b/src/library/scala/reflect/ClassTag.scala @@ -2,8 +2,7 @@ package scala package reflect import java.lang.{ Class => jClass } -import scala.language.{implicitConversions, existentials} -import scala.runtime.ScalaRunTime.{ arrayClass, arrayElementClass } +import scala.runtime.ScalaRunTime.arrayElementClass /** * diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index fcef4dd6be..6863cdfd82 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -15,7 +15,6 @@ import scala.language.postfixOps /** AnnotationInfo and its helpers */ trait AnnotationInfos extends api.Annotations { self: SymbolTable => import definitions._ - import treeInfo._ // Common annotation code between Symbol and Type. // For methods altering the annotation list, on Symbol it mutates diff --git a/src/reflect/scala/reflect/internal/Internals.scala b/src/reflect/scala/reflect/internal/Internals.scala index 26f3bfd9d0..ad4cec5b4d 100644 --- a/src/reflect/scala/reflect/internal/Internals.scala +++ b/src/reflect/scala/reflect/internal/Internals.scala @@ -9,7 +9,6 @@ import scala.ref.WeakReference import scala.reflect.api.Universe import scala.reflect.macros.Attachments import scala.reflect.internal.util.FreshNameCreator -import scala.reflect.internal.Flags._ import scala.reflect.internal.util.ListOfNil trait Internals extends api.Internals { diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala index 0a7a33cdd8..c418321234 100644 --- a/src/reflect/scala/reflect/internal/ReificationSupport.scala +++ b/src/reflect/scala/reflect/internal/ReificationSupport.scala @@ -7,7 +7,6 @@ import util._ trait ReificationSupport { self: SymbolTable => import definitions._ - import internal._ class ReificationSupportImpl extends ReificationSupportApi { def selectType(owner: Symbol, name: String): TypeSymbol = diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 99ff6a10b4..f2517fff54 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -128,6 +128,7 @@ trait StdNames { final val AnyRef: NameType = "AnyRef" final val Array: NameType = "Array" final val List: NameType = "List" + final val Option: NameType = "Option" final val Seq: NameType = "Seq" final val Symbol: NameType = "Symbol" final val WeakTypeTag: NameType = "WeakTypeTag" diff --git a/src/reflect/scala/reflect/internal/SymbolPairs.scala b/src/reflect/scala/reflect/internal/SymbolPairs.scala index c088e8f57c..4763e77a34 100644 --- a/src/reflect/scala/reflect/internal/SymbolPairs.scala +++ b/src/reflect/scala/reflect/internal/SymbolPairs.scala @@ -8,7 +8,6 @@ package reflect package internal import scala.collection.mutable -import Flags._ import util.HashSet import scala.annotation.tailrec diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 9dc4baee32..2f07cef315 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -8,7 +8,6 @@ package reflect package internal import Flags._ -import pickling.PickleFormat._ import scala.collection.{ mutable, immutable } import util.Statistics diff --git a/src/reflect/scala/reflect/internal/transform/PostErasure.scala b/src/reflect/scala/reflect/internal/transform/PostErasure.scala index f0c7d0f050..dd4f044818 100644 --- a/src/reflect/scala/reflect/internal/transform/PostErasure.scala +++ b/src/reflect/scala/reflect/internal/transform/PostErasure.scala @@ -5,7 +5,6 @@ package transform trait PostErasure { val global: SymbolTable import global._ - import definitions._ object elimErasedValueType extends TypeMap { def apply(tp: Type) = tp match { diff --git a/src/reflect/scala/reflect/macros/Typers.scala b/src/reflect/scala/reflect/macros/Typers.scala index 990784c9e7..bd608601dc 100644 --- a/src/reflect/scala/reflect/macros/Typers.scala +++ b/src/reflect/scala/reflect/macros/Typers.scala @@ -2,8 +2,6 @@ package scala package reflect package macros -import scala.reflect.internal.{Mode => InternalMode} - /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala index fe39e1f245..7848753e69 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala @@ -5,7 +5,7 @@ package runtime import scala.reflect.internal.{TreeInfo, SomePhase} import scala.reflect.internal.{SymbolTable => InternalSymbolTable} import scala.reflect.runtime.{SymbolTable => RuntimeSymbolTable} -import scala.reflect.api.{TreeCreator, TypeCreator, Universe} +import scala.reflect.api.{TypeCreator, Universe} /** An implementation of [[scala.reflect.api.Universe]] for runtime reflection using JVM classloaders. * diff --git a/src/reflect/scala/reflect/runtime/SymbolTable.scala b/src/reflect/scala/reflect/runtime/SymbolTable.scala index 02155578f8..092bbd711f 100644 --- a/src/reflect/scala/reflect/runtime/SymbolTable.scala +++ b/src/reflect/scala/reflect/runtime/SymbolTable.scala @@ -2,8 +2,6 @@ package scala package reflect package runtime -import scala.reflect.internal.Flags._ - /** * This symbol table trait fills in the definitions so that class information is obtained by refection. * It can be used either from a reflexive universe (class scala.reflect.runtime.JavaUniverse), or else from diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala index f5e16c6640..6d0cb0df45 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala @@ -2,8 +2,7 @@ package scala package reflect package runtime -import scala.reflect.io.AbstractFile -import scala.collection.{ immutable, mutable } +import scala.collection.immutable import scala.reflect.internal.Flags._ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: SymbolTable => diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index 20b5a79aaa..3f4922a602 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -1190,6 +1190,8 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set finally isettings.unwrapStrings = saved } + def withoutTruncating[A](body: => A): A = reporter withoutTruncating body + def symbolDefString(sym: Symbol) = { TypeStrings.quieter( exitingTyper(sym.defString), diff --git a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala index 3cb6ba11c1..ebca3e7e62 100644 --- a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala +++ b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala @@ -9,7 +9,7 @@ package interpreter import java.lang.{ ClassLoader => JavaClassLoader, Iterable => JIterable } import scala.tools.nsc.util.ScalaClassLoader -import java.io.{ ByteArrayInputStream, CharArrayWriter, FileNotFoundException, PrintWriter, Writer } +import java.io.{ ByteArrayInputStream, CharArrayWriter, FileNotFoundException, PrintWriter, StringWriter, Writer } import java.util.{ Locale } import java.util.concurrent.ConcurrentLinkedQueue import javax.tools.{ Diagnostic, DiagnosticCollector, DiagnosticListener, @@ -18,39 +18,47 @@ import javax.tools.{ Diagnostic, DiagnosticCollector, DiagnosticListener, import scala.reflect.io.{ AbstractFile, Directory, File, Path } import scala.io.Source import scala.util.{ Try, Success, Failure } -import scala.util.Properties.lineSeparator +import scala.util.Properties.{ lineSeparator => EOL } import scala.util.matching.Regex -import scala.collection.JavaConverters +import scala.collection.JavaConverters._ import scala.collection.generic.Clearable import java.net.URL import scala.language.reflectiveCalls +import PartialFunction.{ cond => when } import Javap._ +/** Javap command implementation. Supports platform tool for Java 6 or 7+. + * Adds a few options for REPL world, to show bodies of `App` classes and closures. + */ class JavapClass( val loader: ScalaClassLoader, val printWriter: PrintWriter, intp: Option[IMain] = None -) extends scala.tools.util.Javap { +) extends Javap { import JavapTool.ToolArgs import JavapClass._ lazy val tool = JavapTool() - /** Run the tool. Option args start with "-". + /** Run the tool. Option args start with "-", except that "-" itself + * denotes the last REPL result. * The default options are "-protected -verbose". * Byte data for filename args is retrieved with findBytes. + * @return results for invoking JpResult.show() */ def apply(args: Seq[String]): List[JpResult] = { val (options, classes) = args partition (s => (s startsWith "-") && s.length > 1) - val (flags, upgraded) = upgrade(options) + val (flags, upgraded) = upgrade(options) import flags.{ app, fun, help, raw } + val targets = if (fun && !help) FunFinder(loader, intp).funs(classes) else classes + if (help || classes.isEmpty) List(JpResult(JavapTool.helper(printWriter))) else if (targets.isEmpty) - List(JpResult("No anonfuns found.")) + List(JpResult("No closures found.")) else - tool(raw, upgraded)(targets map (klass => targeted(klass, app))) + tool(raw, upgraded)(targets map (targeted(_, app))) // JavapTool.apply } /** Cull our tool options. */ @@ -79,8 +87,10 @@ class JavapClass( case s => s } val targetedBytes = if (app) findAppBody(req) else (path, findBytes(req)) - if (targetedBytes._2.isEmpty) throw new FileNotFoundException(s"Could not find class bytes for '$path'") - targetedBytes + targetedBytes match { + case (_, bytes) if bytes.isEmpty => throw new FileNotFoundException(s"Could not find class bytes for '$path'") + case ok => ok + } } private def findAppBody(path: String): (String, Array[Byte]) = { @@ -89,16 +99,12 @@ class JavapClass( // assumes only the first match is of interest (because only one endpoint is generated). def findNewStyle(bytes: Array[Byte]) = { import scala.tools.asm.ClassReader - import scala.tools.asm.tree.ClassNode - import PartialFunction.cond - import JavaConverters._ - val rdr = new ClassReader(bytes) - val nod = new ClassNode - rdr.accept(nod, 0) //foo/Bar.delayedEndpoint$foo$Bar$1 val endpoint = "delayedEndpoint".r.unanchored - def isEndPoint(s: String) = (s contains '$') && cond(s) { case endpoint() => true } - nod.methods.asScala collectFirst { case m if isEndPoint(m.name) => m.name } + def isEndPoint(s: String) = (s contains '$') && when(s) { case endpoint() => true } + new ClassReader(bytes) withMethods { methods => + methods collectFirst { case m if isEndPoint(m.name) => m.name } + } } // try new style, and add foo#delayedEndpoint$bar$1 to filter on the endpoint def asNewStyle(bytes: Array[Byte]) = Some(bytes) filter (_.nonEmpty) flatMap { bs => @@ -122,8 +128,7 @@ class JavapClass( def findBytes(path: String): Array[Byte] = tryFile(path) getOrElse tryClass(path) - /** Assume the string is a path and try to find the classfile - * it represents. + /** Assume the string is a path and try to find the classfile it represents. */ def tryFile(path: String): Option[Array[Byte]] = (Try (File(path.asClassResource)) filter (_.exists) map (_.toByteArray())).toOption @@ -202,55 +207,67 @@ class JavapClass( w } + def filterLines(target: String, text: String): String = { + // take Foo# as Foo#apply for purposes of filtering. Useful for -fun Foo#; + // if apply is added here, it's for other than -fun: javap Foo#, perhaps m#? + val filterOn = target.splitHashMember._2 map { s => if (s.isEmpty) "apply" else s } + var filtering = false // true if in region matching filter + // turn filtering on/off given the pattern of interest + def filterStatus(line: String, pattern: String) = { + def isSpecialized(method: String) = (method startsWith pattern+"$") && (method endsWith "$sp") + def isAnonymized(method: String) = (pattern == "$anonfun") && (method startsWith "$anonfun$") + // cheap heuristic, todo maybe parse for the java sig. + // method sigs end in paren semi + def isAnyMethod = line endsWith ");" + // take the method name between the space char and left paren. + // accept exact match or something that looks like what we might be asking for. + def isOurMethod = { + val lparen = line lastIndexOf '(' + val blank = line.lastIndexOf(' ', lparen) + if (blank < 0) false + else { + val method = line.substring(blank+1, lparen) + (method == pattern || isSpecialized(method) || isAnonymized(method)) + } + } + filtering = + if (filtering) { + // next blank line terminates section + // in non-verbose mode, next line is next method, more or less + line.trim.nonEmpty && (!isAnyMethod || isOurMethod) + } else { + isAnyMethod && isOurMethod + } + filtering + } + // do we output this line? + def checkFilter(line: String) = filterOn map (filterStatus(line, _)) getOrElse true + val sw = new StringWriter + val pw = new PrintWriter(sw) + for { + line <- Source.fromString(text).getLines() + if checkFilter(line) + } pw println line + pw.flush() + sw.toString + } + /** Create a Showable with output massage. * @param raw show ugly repl names * @param target attempt to filter output to show region of interest * @param preamble other messages to output */ - def showWithPreamble(raw: Boolean, target: String, preamble: String = ""): Showable = new Showable { - // ReplStrippingWriter clips and scrubs on write(String) - // circumvent it by write(mw, 0, mw.length) or wrap it in withoutUnwrapping - def show() = - if (raw && intp.isDefined) intp.get withoutUnwrapping { writeLines() } - else writeLines() - private def writeLines() { - // take Foo# as Foo#apply for purposes of filtering. Useful for -fun Foo#; - // if apply is added here, it's for other than -fun: javap Foo#, perhaps m#? - val filterOn = target.splitHashMember._2 map { s => if (s.isEmpty) "apply" else s } - var filtering = false // true if in region matching filter - // turn filtering on/off given the pattern of interest - def filterStatus(line: String, pattern: String) = { - // cheap heuristic, todo maybe parse for the java sig. - // method sigs end in paren semi - def isAnyMethod = line.endsWith(");") - def isOurMethod = { - val lparen = line.lastIndexOf('(') - val blank = line.lastIndexOf(' ', lparen) - if (blank < 0) false - else { - val method = line.substring(blank+1, lparen) - (method == pattern || ((method startsWith pattern+"$") && (method endsWith "$sp"))) - } - } - filtering = - if (filtering) { - // next blank line terminates section - // for -public, next line is next method, more or less - line.trim.nonEmpty && !isAnyMethod - } else { - isAnyMethod && isOurMethod - } - filtering - } - // do we output this line? - def checkFilter(line: String) = filterOn map (filterStatus(line, _)) getOrElse true - for { - line <- Source.fromString(preamble + written).getLines() - if checkFilter(line) - } printWriter write f"$line%n" - printWriter.flush() + def showWithPreamble(raw: Boolean, target: String, preamble: String = ""): Showable = + new Showable { + private def writeLines() = filterLines(target, preamble + written) + val output = writeLines() + + // ReplStrippingWriter clips and scrubs on write(String) + // circumvent it by write(mw, 0, mw.length) or wrap it in withoutUnwrapping + def show() = + if (raw && intp.isDefined) intp.get withoutUnwrapping { printWriter.write(output, 0, output.length) } + else intp.get withoutTruncating(printWriter write output) } - } } class JavapTool6 extends JavapTool { @@ -291,6 +308,7 @@ class JavapClass( } class JavapTool7 extends JavapTool { + import JavapTool._ type Task = { def call(): Boolean // true = ok //def run(args: Array[String]): Int // all args @@ -322,19 +340,14 @@ class JavapClass( /** All diagnostic messages. * @param locale Locale for diagnostic messages, null by default. */ - def messages(implicit locale: Locale = null) = { - import JavaConverters._ - diagnostics.asScala.map(_ getMessage locale).toList - } + def messages(implicit locale: Locale = null) = diagnostics.asScala.map(_ getMessage locale).toList + // don't filter this message if raw, since the names are likely to differ + private val container = "Binary file .* contains .*".r def reportable(raw: Boolean): String = { - // don't filter this message if raw, since the names are likely to differ - val container = "Binary file .* contains .*".r - val m = if (raw) messages - else messages filter (_ match { case container() => false case _ => true }) + val m = if (raw) messages else messages filterNot (when(_) { case container() => true }) clear() - if (m.nonEmpty) m mkString ("", lineSeparator, lineSeparator) - else "" + if (m.nonEmpty) m mkString ("", EOL, EOL) else "" } } val reporter = new JavaReporter @@ -396,7 +409,6 @@ class JavapClass( def task(options: Seq[String], classes: Seq[String], inputs: Seq[Input]): Task = { //ServiceLoader.load(classOf[javax.tools.DisassemblerTool]). //getTask(writer, fileManager, reporter, options.asJava, classes.asJava) - import JavaConverters.asJavaIterableConverter TaskCtor.newInstance(writer, fileManager(inputs), reporter, options.asJava, classes.asJava) .orFailed (throw new IllegalStateException) } @@ -476,7 +488,7 @@ class JavapClass( object ToolArgs { def fromArgs(args: Seq[String]): (ToolArgs, Seq[String]) = ((ToolArgs(), Seq[String]()) /: (args flatMap massage)) { case ((t,others), s) => s match { - case "-fun" => (t copy (fun=true), others) + case "-fun" => (t copy (fun=true), others :+ "-private") case "-app" => (t copy (app=true), others) case "-help" => (t copy (help=true), others) case "-raw" => (t copy (raw=true), others) @@ -542,24 +554,26 @@ class JavapClass( val DefaultOptions = List("-protected", "-verbose") - def isAvailable = Seq(Env, Tool) exists (cn => hasClass(loader, cn)) - private def hasClass(cl: ScalaClassLoader, cn: String) = cl.tryToInitializeClass[AnyRef](cn).isDefined - private def isTaskable(cl: ScalaClassLoader) = hasClass(cl, Tool) + def isAvailable = Seq(Env, Tool) exists (hasClass(loader, _)) /** Select the tool implementation for this platform. */ - def apply() = if (isTaskable(loader)) new JavapTool7 else new JavapTool6 + def apply() = if (hasClass(loader, Tool)) new JavapTool7 else new JavapTool6 } } object JavapClass { + import scala.tools.asm.ClassReader + import scala.tools.asm.tree.{ ClassNode, MethodNode } + def apply( loader: ScalaClassLoader = ScalaClassLoader.appLoader, printWriter: PrintWriter = new PrintWriter(System.out, true), intp: Option[IMain] = None ) = new JavapClass(loader, printWriter, intp) + /** Match foo#bar, both groups are optional (may be null). */ val HashSplit = "([^#]+)?(?:#(.+)?)?".r // We enjoy flexibility in specifying either a fully-qualified class name com.acme.Widget @@ -580,9 +594,9 @@ object JavapClass { else (s take i, Some(s drop i+1)) } } - implicit class ClassLoaderOps(val cl: ClassLoader) extends AnyVal { + implicit class ClassLoaderOps(val loader: ScalaClassLoader) extends AnyVal { private def parentsOf(x: ClassLoader): List[ClassLoader] = if (x == null) Nil else x :: parentsOf(x.getParent) - def parents: List[ClassLoader] = parentsOf(cl) + def parents: List[ClassLoader] = parentsOf(loader) /* all file locations */ def locations = { def alldirs = parents flatMap (_ match { @@ -596,7 +610,7 @@ object JavapClass { /* only the file location from which the given class is loaded */ def locate(k: String): Option[Path] = { Try { - val klass = try cl loadClass k catch { + val klass = try loader loadClass k catch { case _: NoClassDefFoundError => null // let it snow } // cf ScalaClassLoader.originOfClass @@ -608,44 +622,66 @@ object JavapClass { } } /* would classBytes succeed with a nonempty array */ - def resourceable(className: String): Boolean = cl.getResource(className.asClassResource) != null + def resourceable(className: String): Boolean = loader.getResource(className.asClassResource) != null + + /* class reader of class bytes */ + def classReader(resource: String): ClassReader = new ClassReader(loader classBytes resource) + } + implicit class `class reader convenience`(val reader: ClassReader) extends AnyVal { + def withMethods[A](f: Seq[MethodNode] => A): A = { + val cls = new ClassNode + reader.accept(cls, 0) + f(cls.methods.asScala) + } } implicit class PathOps(val p: Path) extends AnyVal { import scala.tools.nsc.io.Jar def isJar = Jar isJarOrZip p } + implicit class `fun with files`(val f: AbstractFile) extends AnyVal { + def descend(path: Seq[String]): Option[AbstractFile] = { + def lookup(f: AbstractFile, path: Seq[String]): Option[AbstractFile] = path match { + case p if p.isEmpty => Option(f) + case p => Option(f.lookupName(p.head, directory = true)) flatMap (lookup(_, p.tail)) + } + lookup(f, path) + } + } implicit class URLOps(val url: URL) extends AnyVal { def isFile: Boolean = url.getProtocol == "file" } object FunFinder { def apply(loader: ScalaClassLoader, intp: Option[IMain]) = new FunFinder(loader, intp) } + // FunFinder.funs(ks) finds anonfuns class FunFinder(loader: ScalaClassLoader, intp: Option[IMain]) { + // manglese for closure: typename, $anonfun or lamba, opt method, digits + val closure = """(.*)\$(\$anonfun|lambda)(?:\$+([^$]+))?\$(\d+)""".r + + // manglese for closure + val cleese = "(?:anonfun|lambda)" + // class k, candidate f without prefix - def isFunOfClass(k: String, f: String) = { - val p = (s"${Regex quote k}\\$$+anonfun").r - (p findPrefixOf f).nonEmpty - } + def isFunOfClass(k: String, f: String) = (s"${Regex quote k}\\$$+$cleese".r findPrefixOf f).nonEmpty + // class k, candidate f without prefix, method m - def isFunOfMethod(k: String, m: String, f: String) = { - val p = (s"${Regex quote k}\\$$+anonfun\\$$${Regex quote m}\\$$").r - (p findPrefixOf f).nonEmpty - } - def isFunOfTarget(k: String, m: Option[String], f: String) = - if (m.isEmpty) isFunOfClass(k, f) - else isFunOfMethod(k, m.get, f) - def listFunsInAbsFile(k: String, m: Option[String], d: AbstractFile) = { - for (f <- d; if !f.isDirectory && isFunOfTarget(k, m, f.name)) yield f.name - } - // path prefix p, class k, dir d - def listFunsInDir(p: String, k: String, m: Option[String])(d: Directory) = { - val subdir = Path(p) - for (f <- (d / subdir).toDirectory.list; if f.isFile && isFunOfTarget(k, m, f.name)) + def isFunOfMethod(k: String, m: String, f: String) = + (s"${Regex quote k}\\$$+$cleese\\$$+${Regex quote m}\\$$".r findPrefixOf f).nonEmpty + + def isFunOfTarget(target: Target, f: String) = + target.member map (isFunOfMethod(target.name, _, f)) getOrElse isFunOfClass(target.name, f) + + def listFunsInAbsFile(target: Target)(d: AbstractFile) = + for (f <- d; if !f.isDirectory && isFunOfTarget(target, f.name)) yield f.name + + def listFunsInDir(target: Target)(d: Directory) = { + val subdir = Path(target.prefix) + for (f <- (d / subdir).toDirectory.list; if f.isFile && isFunOfTarget(target, f.name)) yield f.name } - // path prefix p, class k, jar file f - def listFunsInJar(p: String, k: String, m: Option[String])(f: File) = { + + def listFunsInJar(target: Target)(f: File) = { import java.util.jar.JarEntry import scala.tools.nsc.io.Jar def maybe(e: JarEntry) = { @@ -654,78 +690,133 @@ object JavapClass { if (parts.length < 2) ("", e.getName) else (parts.init mkString "/", parts.last) } - if (path == p && isFunOfTarget(k, m, name)) Some(name) else None + if (path == target.prefix && isFunOfTarget(target, name)) Some(name) else None } (new Jar(f) map maybe).flatten } def loadable(name: String) = loader resourceable name - // translated class, optional member, opt member to filter on, whether it is repl output - def translate(s: String): (String, Option[String], Option[String], Boolean) = { + case class Target(path: String, member: Option[String], filter: Option[String], isRepl: Boolean, isModule: Boolean) { + val splat = path split "\\." + val name = splat.last + val prefix = if (splat.length > 1) splat.init mkString "/" else "" + val pkg = if (splat.length > 1) splat.init mkString "." else "" + val targetName = s"$name${ if (isModule) "$" else "" }" + } + // translated class, optional member, opt member to filter on, whether it is repl output and a module + def translate(s: String): Target = { val (k0, m0) = s.splitHashMember - val k = k0.asClassName + val isModule = k0 endsWith "$" + val k = (k0 stripSuffix "$").asClassName val member = m0 filter (_.nonEmpty) // take Foo# as no member, not "" val filter = m0 flatMap { case "" => Some("apply") case _ => None } // take Foo# as filter on apply // class is either something replish or available to loader // $line.$read$$etc$Foo#member - ((intp flatMap (_ translatePath k) filter (loadable) map ((_, member, filter, true))) + ((intp flatMap (_ translatePath k) filter (loadable) map (x => Target(x stripSuffix "$", member, filter, true, isModule))) // s = "f" and $line.$read$$etc$#f is what we're after, // ignoring any #member (except take # as filter on #apply) - orElse (intp flatMap (_ translateEnclosingClass k) map ((_, Some(k), filter, true))) - getOrElse ((k, member, filter, false))) + orElse (intp flatMap (_ translateEnclosingClass k) map (x => Target(x stripSuffix "$", Some(k), filter, true, isModule))) + getOrElse (Target(k, member, filter, false, isModule))) } /** Find the classnames of anonfuns associated with k, * where k may be an available class or a symbol in scope. */ - def funsOf(k0: String): Seq[String] = { + def funsOf(selection: String): Seq[String] = { // class is either something replish or available to loader - val (k, member, filter, isReplish) = translate(k0) - val splat = k split "\\." - val name = splat.last - val prefix = if (splat.length > 1) splat.init mkString "/" else "" - val pkg = if (splat.length > 1) splat.init mkString "." else "" + val target = translate(selection) + // reconstitute an anonfun with a package // if filtered, add the hash back, e.g. pkg.Foo#bar, pkg.Foo$anon$1#apply def packaged(s: String) = { - val p = if (pkg.isEmpty) s else s"$pkg.$s" - val pm = filter map (p + "#" + _) - pm getOrElse p + val p = if (target.pkg.isEmpty) s else s"${target.pkg}.$s" + target.filter map (p + "#" + _) getOrElse p } - // is this translated path in (usually virtual) repl outdir? or loadable from filesystem? - val fs = if (isReplish) { - def outed(d: AbstractFile, p: Seq[String]): Option[AbstractFile] = { - if (p.isEmpty) Option(d) - else Option(d.lookupName(p.head, directory = true)) flatMap (f => outed(f, p.tail)) - } - outed(intp.get.replOutput.dir, splat.init) map { d => - listFunsInAbsFile(name, member, d) map packaged - } - } else { - loader locate k map { w => - if (w.isDirectory) listFunsInDir(prefix, name, member)(w.toDirectory) map packaged - else if (w.isJar) listFunsInJar(prefix, name, member)(w.toFile) map packaged - else Nil + // find closure classes in repl outdir or try asking the classloader where to look + val fs = + if (target.isRepl) + (intp.get.replOutput.dir descend target.splat.init) map { d => + listFunsInAbsFile(target)(d) map (_.asClassName) map packaged + } + else + loader locate target.path map { + case d if d.isDirectory => listFunsInDir(target)(d.toDirectory) map packaged + case j if j.isJar => listFunsInJar(target)(j.toFile) map packaged + case _ => Nil + } + val res = fs map (_.to[Seq]) getOrElse Seq() + // on second thought, we don't care about lamba method classes, just the impl methods + val rev = + res flatMap { + case x @ closure(_, "lambda", _, _) => labdaMethod(x, target) + //target.member flatMap (_ => labdaMethod(x, target)) getOrElse s"${target.name}#$$anonfun" + case x => Some(x) + } + rev + } + // given C$lambda$$g$n for member g and n in 1..N, find the C.accessor$x + // and the C.$anonfun$x it forwards to. + def labdaMethod(lambda: String, target: Target): Option[String] = { + import scala.tools.asm.ClassReader + import scala.tools.asm.Opcodes.INVOKESTATIC + import scala.tools.asm.tree.{ ClassNode, MethodInsnNode } + // the accessor methods invoked statically by the apply of the given closure class + def accesses(s: String): Seq[(String, String)] = { + val accessor = """accessor\$\d+""".r + loader classReader s withMethods { ms => + ms filter (_.name == "apply") flatMap (_.instructions.toArray.collect { + case i: MethodInsnNode if i.getOpcode == INVOKESTATIC && when(i.name) { case accessor(_*) => true } => (i.owner, i.name) + }) } } - fs match { - case Some(xs) => xs.to[Seq] // maybe empty - case None => Seq() // nothing found, e.g., junk input + // get the k.$anonfun for the accessor k.m + def anonOf(k: String, m: String): String = { + val res = + loader classReader k withMethods { ms => + ms filter (_.name == m) flatMap (_.instructions.toArray.collect { + case i: MethodInsnNode if i.getOpcode == INVOKESTATIC && i.name.startsWith("$anonfun") => i.name + }) + } + assert(res.size == 1) + res.head + } + // the lambdas invoke accessors that call the anonfuns of interest. Filter k on the k#$anonfuns. + val ack = accesses(lambda) + assert(ack.size == 1) // There can be only one. + ack.head match { + case (k, _) if target.isModule && !(k endsWith "$") => None + case (k, m) => Some(s"${k}#${anonOf(k, m)}") } } - def funs(ks: Seq[String]) = ks flatMap funsOf _ + /** Translate the supplied targets to patterns for anonfuns. + * Pattern is typename $ label [[$]$func] $n where label is $anonfun or lamba, + * and lambda includes the extra dollar, func is a method name, and n is an int. + * The typename for a nested class is dollar notation, Betty$Bippy. + * + * If C has anonfun closure classes, then use C$$anonfun$f$1 (various names, C# filters on apply). + * If C has lambda closure classes, then use C#$anonfun (special-cased by output filter). + */ + def funs(ks: Seq[String]): Seq[String] = ks flatMap funsOf } } +trait Javap { + def loader: ScalaClassLoader + def printWriter: PrintWriter + def apply(args: Seq[String]): List[Javap.JpResult] + def tryFile(path: String): Option[Array[Byte]] + def tryClass(path: String): Array[Byte] +} + object Javap { def isAvailable(cl: ScalaClassLoader = ScalaClassLoader.appLoader) = JavapClass(cl).JavapTool.isAvailable def apply(path: String): Unit = apply(Seq(path)) def apply(args: Seq[String]): Unit = JavapClass() apply args foreach (_.show()) - trait Showable { + private[interpreter] trait Showable { def show(): Unit } - sealed trait JpResult extends scala.tools.util.JpResult { + sealed trait JpResult { type ResultType def isError: Boolean def value: ResultType @@ -751,8 +842,13 @@ object Javap { def isError = false def show() = value.show() // output to tool's PrintWriter } - implicit class Lastly[A](val t: Try[A]) extends AnyVal { - private def effect[X](last: =>Unit)(a: X): Try[A] = { last; t } - def lastly(last: =>Unit): Try[A] = t transform (effect(last) _, effect(last) _) - } +} + +object NoJavap extends Javap { + import Javap._ + def loader: ScalaClassLoader = getClass.getClassLoader + def printWriter: PrintWriter = new PrintWriter(System.err, true) + def apply(args: Seq[String]): List[JpResult] = Nil + def tryFile(path: String): Option[Array[Byte]] = None + def tryClass(path: String): Array[Byte] = Array() } diff --git a/src/repl/scala/tools/nsc/interpreter/package.scala b/src/repl/scala/tools/nsc/interpreter/package.scala index 079097d7a2..56f1e65376 100644 --- a/src/repl/scala/tools/nsc/interpreter/package.scala +++ b/src/repl/scala/tools/nsc/interpreter/package.scala @@ -11,6 +11,7 @@ import scala.reflect.runtime.{ universe => ru } import scala.reflect.{ClassTag, classTag} import scala.reflect.api.{Mirror, TypeCreator, Universe => ApiUniverse} import scala.util.control.Exception.catching +import scala.util.Try /** The main REPL related classes and values are as follows. * In addition to standard compiler classes Global and Settings, there are: @@ -196,4 +197,14 @@ package object interpreter extends ReplConfig with ReplStrings { } } } + + /* debug assist + private[nsc] implicit class `smart stringifier`(val sc: StringContext) extends AnyVal { + import StringContext._, runtime.ScalaRunTime.stringOf + def ss(args: Any*): String = sc.standardInterpolator(treatEscapes, args map stringOf) + } debug assist */ + private[nsc] implicit class `try lastly`[A](val t: Try[A]) extends AnyVal { + private def effect[X](last: =>Unit)(a: X): Try[A] = { last; t } + def lastly(last: =>Unit): Try[A] = t transform (effect(last) _, effect(last) _) + } } |