summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/reflect/macros/contexts/Infrastructure.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ClassPathMemoryConsumptionTester.scala77
-rw-r--r--src/compiler/scala/tools/nsc/GenericRunnerSettings.scala5
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala54
-rw-r--r--src/compiler/scala/tools/nsc/ObjectRunner.scala4
-rw-r--r--src/compiler/scala/tools/nsc/PhaseAssembly.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ScriptRunner.scala9
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala7
-rw-r--r--src/compiler/scala/tools/nsc/backend/JavaPlatform.scala15
-rw-r--r--src/compiler/scala/tools/nsc/backend/Platform.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/ICodes.scala3
-rw-r--r--src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala125
-rw-r--r--src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala55
-rw-r--r--src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala162
-rw-r--r--src/compiler/scala/tools/nsc/classpath/FileUtils.scala68
-rw-r--r--src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala101
-rw-r--r--src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala38
-rw-r--r--src/compiler/scala/tools/nsc/classpath/PackageNameUtils.scala26
-rw-r--r--src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala180
-rw-r--r--src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala67
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugins.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala8
-rw-r--r--src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala53
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala13
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/AddInterfaces.scala41
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala7
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala6
-rw-r--r--src/compiler/scala/tools/nsc/util/ClassFileLookup.scala57
-rw-r--r--src/compiler/scala/tools/nsc/util/ClassPath.scala119
-rw-r--r--src/compiler/scala/tools/reflect/ReflectMain.scala8
-rw-r--r--src/compiler/scala/tools/util/PathResolver.scala102
-rw-r--r--src/interactive/scala/tools/nsc/interactive/Global.scala4
-rwxr-xr-xsrc/library/scala/collection/JavaConverters.scala4
-rw-r--r--src/library/scala/collection/LinearSeq.scala7
-rw-r--r--src/library/scala/collection/LinearSeqLike.scala30
-rwxr-xr-xsrc/library/scala/collection/LinearSeqOptimized.scala35
-rw-r--r--src/library/scala/collection/SeqLike.scala10
-rw-r--r--src/library/scala/collection/SeqViewLike.scala1
-rw-r--r--src/library/scala/collection/immutable/PagedSeq.scala8
-rw-r--r--src/library/scala/collection/mutable/MultiMap.scala7
-rw-r--r--src/library/scala/collection/mutable/PriorityQueue.scala7
-rw-r--r--src/library/scala/math/BigDecimal.scala4
-rw-r--r--src/partest-extras/scala/tools/partest/BytecodeTest.scala6
-rw-r--r--src/reflect/scala/reflect/api/Constants.scala4
-rw-r--r--src/reflect/scala/reflect/api/Exprs.scala2
-rw-r--r--src/reflect/scala/reflect/api/FlagSets.scala6
-rw-r--r--src/reflect/scala/reflect/api/Mirror.scala2
-rw-r--r--src/reflect/scala/reflect/api/Mirrors.scala2
-rw-r--r--src/reflect/scala/reflect/api/Names.scala8
-rw-r--r--src/reflect/scala/reflect/api/Printers.scala30
-rw-r--r--src/reflect/scala/reflect/api/StandardDefinitions.scala6
-rw-r--r--src/reflect/scala/reflect/api/Symbols.scala2
-rw-r--r--src/reflect/scala/reflect/api/Trees.scala8
-rw-r--r--src/reflect/scala/reflect/internal/Depth.scala16
-rw-r--r--src/reflect/scala/reflect/internal/SymbolTable.scala1
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala14
-rw-r--r--src/reflect/scala/reflect/internal/pickling/UnPickler.scala19
-rw-r--r--src/reflect/scala/reflect/internal/settings/MutableSettings.scala3
-rw-r--r--src/reflect/scala/reflect/io/VirtualFile.scala4
-rw-r--r--src/reflect/scala/reflect/io/ZipArchive.scala34
-rw-r--r--src/repl/scala/tools/nsc/interpreter/ILoop.scala63
-rw-r--r--src/repl/scala/tools/nsc/interpreter/IMain.scala12
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala20
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala4
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css12
-rw-r--r--src/scalap/scala/tools/scalap/Arguments.scala22
-rw-r--r--src/scalap/scala/tools/scalap/Main.scala197
71 files changed, 1618 insertions, 430 deletions
diff --git a/src/compiler/scala/reflect/macros/contexts/Infrastructure.scala b/src/compiler/scala/reflect/macros/contexts/Infrastructure.scala
index df7aa4d2be..7088058145 100644
--- a/src/compiler/scala/reflect/macros/contexts/Infrastructure.scala
+++ b/src/compiler/scala/reflect/macros/contexts/Infrastructure.scala
@@ -12,5 +12,5 @@ trait Infrastructure {
def compilerSettings: List[String] = universe.settings.recreateArgs
- def classPath: List[java.net.URL] = global.classPath.asURLs
+ def classPath: List[java.net.URL] = global.classPath.asURLs.toList
}
diff --git a/src/compiler/scala/tools/nsc/ClassPathMemoryConsumptionTester.scala b/src/compiler/scala/tools/nsc/ClassPathMemoryConsumptionTester.scala
new file mode 100644
index 0000000000..2faf6c6272
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/ClassPathMemoryConsumptionTester.scala
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc
+
+import scala.io.StdIn.readLine
+
+/**
+ * Simple application to check out amount of memory used by chosen classpath representation.
+ * It allows us to create many scalac-like calls based on specified parameters, where each main retains Global.
+ * And we need additional tool (e.g. profiler) to measure memory consumption itself.
+ */
+object ClassPathMemoryConsumptionTester {
+
+ private class TestSettings extends Settings {
+ val requiredInstances = IntSetting("-requiredInstances",
+ "Determine how many times classpath should be loaded", 10, Some((1, 10000)), (_: String) => None)
+ }
+
+ private class MainRetainsGlobal extends scala.tools.nsc.MainClass {
+ var retainedGlobal: Global = _
+ override def doCompile(compiler: Global) {
+ retainedGlobal = compiler
+ super.doCompile(compiler)
+ }
+ }
+
+ def main(args: Array[String]): Unit = {
+ if (args contains "-help") usage()
+ else doTest(args)
+ }
+
+ private def doTest(args: Array[String]) = {
+ val settings = loadSettings(args.toList)
+
+ val mains = (1 to settings.requiredInstances.value) map (_ => new MainRetainsGlobal)
+
+ // we need original settings without additional params to be able to use them later
+ val baseArgs = argsWithoutRequiredInstances(args)
+
+ println(s"Loading classpath ${settings.requiredInstances.value} times")
+ val startTime = System.currentTimeMillis()
+
+ mains map (_.process(baseArgs))
+
+ val elapsed = System.currentTimeMillis() - startTime
+ println(s"Operation finished - elapsed $elapsed ms")
+ println("Memory consumption can be now measured")
+
+ var textFromStdIn = ""
+ while (textFromStdIn.toLowerCase != "exit")
+ textFromStdIn = readLine("Type 'exit' to close application: ")
+ }
+
+ /**
+ * Prints usage information
+ */
+ private def usage(): Unit =
+ println( """Use classpath and sourcepath options like in the case of e.g. 'scala' command.
+ | There's also one additional option:
+ | -requiredInstances <int value> Determine how many times classpath should be loaded
+ """.stripMargin.trim)
+
+ private def loadSettings(args: List[String]) = {
+ val settings = new TestSettings()
+ settings.processArguments(args, processAll = true)
+ if (settings.classpath.isDefault)
+ settings.classpath.value = sys.props("java.class.path")
+ settings
+ }
+
+ private def argsWithoutRequiredInstances(args: Array[String]) = {
+ val instancesIndex = args.indexOf("-requiredInstances")
+ if (instancesIndex == -1) args
+ else args.dropRight(args.length - instancesIndex) ++ args.drop(instancesIndex + 2)
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala
index ad75d02bff..1289d55c37 100644
--- a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala
+++ b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala
@@ -5,10 +5,11 @@
package scala.tools.nsc
-import scala.tools.util.PathResolver
+import java.net.URL
+import scala.tools.util.PathResolverFactory
class GenericRunnerSettings(error: String => Unit) extends Settings(error) {
- def classpathURLs = new PathResolver(this).asURLs
+ def classpathURLs: Seq[URL] = PathResolverFactory.create(this).resultAsURLs
val howtorun =
ChoiceSetting(
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 430424d0f8..733664c30a 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -14,11 +14,10 @@ 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.{ ClassFileLookup, ClassPath, MergedClassPath, StatisticsInfo, returning }
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 scala.reflect.internal.util.{ SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile }
+import scala.reflect.internal.pickling.PickleBuffer
import symtab.{ Flags, SymbolTable, SymbolTrackers }
import symtab.classfile.Pickler
import plugins.Plugins
@@ -35,6 +34,8 @@ import backend.opt.{ Inliners, InlineExceptionHandlers, ConstantOptimization, Cl
import backend.icode.analysis._
import scala.language.postfixOps
import scala.tools.nsc.ast.{TreeGen => AstTreeGen}
+import scala.tools.nsc.classpath.FlatClassPath
+import scala.tools.nsc.settings.ClassPathRepresentationType
class Global(var currentSettings: Settings, var reporter: Reporter)
extends SymbolTable
@@ -58,7 +59,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
class GlobalMirror extends Roots(NoSymbol) {
val universe: self.type = self
- def rootLoader: LazyType = new loaders.PackageLoader(classPath)
+ def rootLoader: LazyType = {
+ settings.YclasspathImpl.value match {
+ case ClassPathRepresentationType.Flat => new loaders.PackageLoaderUsingFlatClassPath(FlatClassPath.RootPackage, flatClassPath)
+ case ClassPathRepresentationType.Recursive => new loaders.PackageLoader(recursiveClassPath)
+ }
+ }
override def toString = "compiler mirror"
}
implicit val MirrorTag: ClassTag[Mirror] = ClassTag[Mirror](classOf[GlobalMirror])
@@ -104,7 +110,14 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
type PlatformClassPath = ClassPath[AbstractFile]
type OptClassPath = Option[PlatformClassPath]
- def classPath: PlatformClassPath = platform.classPath
+ def classPath: ClassFileLookup[AbstractFile] = settings.YclasspathImpl.value match {
+ case ClassPathRepresentationType.Flat => flatClassPath
+ case ClassPathRepresentationType.Recursive => recursiveClassPath
+ }
+
+ private def recursiveClassPath: ClassPath[AbstractFile] = platform.classPath
+
+ private def flatClassPath: FlatClassPath = platform.flatClassPath
// sub-components --------------------------------------------------
@@ -319,7 +332,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
None
}
- val charset = ( if (settings.encoding.isSetByUser) Some(settings.encoding.value) else None ) flatMap loadCharset getOrElse {
+ val charset = settings.encoding.valueSetByUser flatMap loadCharset getOrElse {
settings.encoding.value = defaultEncoding // A mandatory charset
Charset.forName(defaultEncoding)
}
@@ -334,16 +347,16 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
}
}
- ( if (settings.sourceReader.isSetByUser) Some(settings.sourceReader.value) else None ) flatMap loadReader getOrElse {
+ settings.sourceReader.valueSetByUser flatMap loadReader getOrElse {
new SourceReader(charset.newDecoder(), reporter)
}
}
- if (settings.verbose || settings.Ylogcp) {
+ if (settings.verbose || settings.Ylogcp)
reporter.echo(
- s"[search path for source files: ${classPath.sourcepaths.mkString(",")}]\n"+
- s"[search path for class files: ${classPath.asClasspathString}")
- }
+ s"[search path for source files: ${classPath.asSourcePathString}]\n" +
+ s"[search path for class files: ${classPath.asClassPathString}]"
+ )
// The current division between scala.reflect.* and scala.tools.nsc.* is pretty
// clunky. It is often difficult to have a setting influence something without having
@@ -846,6 +859,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
/** Extend classpath of `platform` and rescan updated packages. */
def extendCompilerClassPath(urls: URL*): Unit = {
+ if (settings.YclasspathImpl.value == ClassPathRepresentationType.Flat)
+ throw new UnsupportedOperationException("Flat classpath doesn't support extending the compiler classpath")
+
val newClassPath = platform.classPath.mergeUrlsIntoClassPath(urls: _*)
platform.currentClassPath = Some(newClassPath)
// Reload all specified jars into this compiler instance
@@ -881,8 +897,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
* entries on the classpath.
*/
def invalidateClassPathEntries(paths: String*): Unit = {
+ if (settings.YclasspathImpl.value == ClassPathRepresentationType.Flat)
+ throw new UnsupportedOperationException("Flat classpath doesn't support the classpath invalidation")
+
implicit object ClassPathOrdering extends Ordering[PlatformClassPath] {
- def compare(a:PlatformClassPath, b:PlatformClassPath) = a.asClasspathString compare b.asClasspathString
+ def compare(a:PlatformClassPath, b:PlatformClassPath) = a.asClassPathString compare b.asClassPathString
}
val invalidated, failed = new mutable.ListBuffer[ClassSymbol]
classPath match {
@@ -910,10 +929,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
informProgress(s"classpath updated on entries [${subst.keys mkString ","}]")
def mkClassPath(elems: Iterable[PlatformClassPath]): PlatformClassPath =
if (elems.size == 1) elems.head
- else new MergedClassPath(elems, classPath.context)
+ else new MergedClassPath(elems, recursiveClassPath.context)
val oldEntries = mkClassPath(subst.keys)
val newEntries = mkClassPath(subst.values)
- mergeNewEntries(newEntries, RootClass, Some(classPath), Some(oldEntries), invalidated, failed)
+ mergeNewEntries(newEntries, RootClass, Some(recursiveClassPath), Some(oldEntries), invalidated, failed)
}
}
def show(msg: String, syms: scala.collection.Traversable[Symbol]) =
@@ -1590,10 +1609,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
}
}
- /** Reset package class to state at typer (not sure what this
- * is needed for?)
+ /** Reset package class to state at typer (not sure what this is needed for?)
*/
- private def resetPackageClass(pclazz: Symbol) {
+ private def resetPackageClass(pclazz: Symbol): Unit = if (typerPhase != NoPhase) {
enteringPhase(firstPhase) {
pclazz.setInfo(enteringPhase(typerPhase)(pclazz.info))
}
diff --git a/src/compiler/scala/tools/nsc/ObjectRunner.scala b/src/compiler/scala/tools/nsc/ObjectRunner.scala
index 95264aeda6..7c14f4943f 100644
--- a/src/compiler/scala/tools/nsc/ObjectRunner.scala
+++ b/src/compiler/scala/tools/nsc/ObjectRunner.scala
@@ -18,14 +18,14 @@ trait CommonRunner {
* @throws NoSuchMethodException
* @throws InvocationTargetException
*/
- def run(urls: List[URL], objectName: String, arguments: Seq[String]) {
+ def run(urls: Seq[URL], objectName: String, arguments: Seq[String]) {
(ScalaClassLoader fromURLs urls).run(objectName, arguments)
}
/** Catches exceptions enumerated by run (in the case of InvocationTargetException,
* unwrapping it) and returns it any thrown in Left(x).
*/
- def runAndCatch(urls: List[URL], objectName: String, arguments: Seq[String]): Either[Throwable, Boolean] = {
+ def runAndCatch(urls: Seq[URL], objectName: String, arguments: Seq[String]): Either[Throwable, Boolean] = {
try { run(urls, objectName, arguments) ; Right(true) }
catch { case e: Throwable => Left(unwrap(e)) }
}
diff --git a/src/compiler/scala/tools/nsc/PhaseAssembly.scala b/src/compiler/scala/tools/nsc/PhaseAssembly.scala
index cfb4cd23a1..1eb6c9da2c 100644
--- a/src/compiler/scala/tools/nsc/PhaseAssembly.scala
+++ b/src/compiler/scala/tools/nsc/PhaseAssembly.scala
@@ -199,7 +199,7 @@ trait PhaseAssembly {
// Add all phases in the set to the graph
val graph = phasesSetToDepGraph(phasesSet)
- val dot = if (settings.genPhaseGraph.isSetByUser) Some(settings.genPhaseGraph.value) else None
+ val dot = settings.genPhaseGraph.valueSetByUser
// Output the phase dependency graph at this stage
def dump(stage: Int) = dot foreach (n => graphToDotFile(graph, s"$n-$stage.dot"))
diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala
index 7d5c6f6fff..6d24b31531 100644
--- a/src/compiler/scala/tools/nsc/ScriptRunner.scala
+++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala
@@ -8,7 +8,10 @@ package tools.nsc
import io.{ AbstractFile, Directory, File, Path }
import java.io.IOException
+import scala.tools.nsc.classpath.DirectoryFlatClassPath
import scala.tools.nsc.reporters.{Reporter,ConsoleReporter}
+import scala.tools.nsc.settings.ClassPathRepresentationType
+import scala.tools.nsc.util.ClassPath.DefaultJavaContext
import util.Exceptional.unwrap
/** An object that runs Scala code in script files.
@@ -112,8 +115,10 @@ class ScriptRunner extends HasCompileSocket {
}
def hasClassToRun(d: Directory): Boolean = {
- import util.ClassPath.{ DefaultJavaContext => ctx }
- val cp = ctx.newClassPath(AbstractFile.getDirectory(d))
+ val cp = settings.YclasspathImpl.value match {
+ case ClassPathRepresentationType.Recursive => DefaultJavaContext.newClassPath(AbstractFile.getDirectory(d))
+ case ClassPathRepresentationType.Flat => DirectoryFlatClassPath(d.jfile)
+ }
cp.findClass(mainClass).isDefined
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
index d3f495f280..f1517e56a0 100755
--- a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
@@ -346,12 +346,11 @@ trait MarkupParsers {
// parse more XML ?
if (charComingAfter(xSpaceOpt()) == '<') {
- xSpaceOpt()
- while (ch == '<') {
+ do {
+ xSpaceOpt()
nextch()
ts append element
- xSpaceOpt()
- }
+ } while (charComingAfter(xSpaceOpt()) == '<')
handle.makeXMLseq(r2p(start, start, curOffset), ts)
}
else {
diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
index 4877bd9b80..6bd123c51f 100644
--- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
+++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
@@ -7,7 +7,10 @@ package scala.tools.nsc
package backend
import io.AbstractFile
-import util.{ClassPath,MergedClassPath,DeltaClassPath}
+import scala.tools.nsc.classpath.FlatClassPath
+import scala.tools.nsc.settings.ClassPathRepresentationType
+import scala.tools.nsc.util.{ ClassPath, DeltaClassPath, MergedClassPath }
+import scala.tools.util.FlatClassPathResolver
import scala.tools.util.PathResolver
trait JavaPlatform extends Platform {
@@ -19,10 +22,20 @@ trait JavaPlatform extends Platform {
private[nsc] var currentClassPath: Option[MergedClassPath[AbstractFile]] = None
def classPath: ClassPath[AbstractFile] = {
+ assert(settings.YclasspathImpl.value == ClassPathRepresentationType.Recursive,
+ "To use recursive classpath representation you must enable it with -YclasspathImpl:recursive compiler option.")
+
if (currentClassPath.isEmpty) currentClassPath = Some(new PathResolver(settings).result)
currentClassPath.get
}
+ private[nsc] lazy val flatClassPath: FlatClassPath = {
+ assert(settings.YclasspathImpl.value == ClassPathRepresentationType.Flat,
+ "To use flat classpath representation you must enable it with -YclasspathImpl:flat compiler option.")
+
+ new FlatClassPathResolver(settings).result
+ }
+
/** Update classpath with a substituted subentry */
def updateClassPath(subst: Map[ClassPath[AbstractFile], ClassPath[AbstractFile]]) =
currentClassPath = Some(new DeltaClassPath(currentClassPath.get, subst))
diff --git a/src/compiler/scala/tools/nsc/backend/Platform.scala b/src/compiler/scala/tools/nsc/backend/Platform.scala
index 439cc1efb8..c3bc213be1 100644
--- a/src/compiler/scala/tools/nsc/backend/Platform.scala
+++ b/src/compiler/scala/tools/nsc/backend/Platform.scala
@@ -8,6 +8,7 @@ package backend
import util.ClassPath
import io.AbstractFile
+import scala.tools.nsc.classpath.FlatClassPath
/** The platform dependent pieces of Global.
*/
@@ -15,9 +16,12 @@ trait Platform {
val symbolTable: symtab.SymbolTable
import symbolTable._
- /** The compiler classpath. */
+ /** The old, recursive implementation of compiler classpath. */
def classPath: ClassPath[AbstractFile]
+ /** The new implementation of compiler classpath. */
+ private[nsc] def flatClassPath: FlatClassPath
+
/** Update classpath with a substitution that maps entries to entries */
def updateClassPath(subst: Map[ClassPath[AbstractFile], ClassPath[AbstractFile]])
diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala
index bc35a9e7de..10f0c6ee00 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala
@@ -113,7 +113,8 @@ abstract class ICodes extends AnyRef
global.loaders.lookupMemberAtTyperPhaseIfPossible(sym, name)
lazy val symbolTable: global.type = global
lazy val loaders: global.loaders.type = global.loaders
- def classPath: util.ClassPath[AbstractFile] = ICodes.this.global.platform.classPath
+
+ def classFileLookup: util.ClassFileLookup[AbstractFile] = global.classPath
}
/** A phase which works on icode. */
diff --git a/src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala
new file mode 100644
index 0000000000..3f06264e3c
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc.classpath
+
+import java.net.URL
+import scala.annotation.tailrec
+import scala.collection.mutable.ArrayBuffer
+import scala.reflect.io.AbstractFile
+import scala.tools.nsc.util.ClassPath
+import scala.tools.nsc.util.ClassRepresentation
+
+/**
+ * A classpath unifying multiple class- and sourcepath entries.
+ * Flat classpath can obtain entries for classes and sources independently
+ * so it tries to do operations quite optimally - iterating only these collections
+ * which are needed in the given moment and only as far as it's necessary.
+ * @param aggregates classpath instances containing entries which this class processes
+ */
+case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatClassPath {
+
+ override def findClassFile(className: String): Option[AbstractFile] = {
+ @tailrec
+ def find(aggregates: Seq[FlatClassPath]): Option[AbstractFile] =
+ if (aggregates.nonEmpty) {
+ val classFile = aggregates.head.findClassFile(className)
+ if (classFile.isDefined) classFile
+ else find(aggregates.tail)
+ } else None
+
+ find(aggregates)
+ }
+
+ override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = {
+ val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className)
+
+ @tailrec
+ def findEntry[T <: ClassRepClassPathEntry](aggregates: Seq[FlatClassPath], getEntries: FlatClassPath => Seq[T]): Option[T] =
+ if (aggregates.nonEmpty) {
+ val entry = getEntries(aggregates.head)
+ .find(_.name == simpleClassName)
+ if (entry.isDefined) entry
+ else findEntry(aggregates.tail, getEntries)
+ } else None
+
+ val classEntry = findEntry(aggregates, classesGetter(pkg))
+ val sourceEntry = findEntry(aggregates, sourcesGetter(pkg))
+
+ mergeClassesAndSources(classEntry.toList, sourceEntry.toList).headOption
+ }
+
+ override def asURLs: Seq[URL] = aggregates.flatMap(_.asURLs)
+
+ override def asClassPathStrings: Seq[String] = aggregates.map(_.asClassPathString).distinct
+
+ override def asSourcePathString: String = ClassPath.join(aggregates map (_.asSourcePathString): _*)
+
+ override private[nsc] def packages(inPackage: String): Seq[PackageEntry] = {
+ val aggregatedPackages = aggregates.flatMap(_.packages(inPackage)).distinct
+ aggregatedPackages
+ }
+
+ override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] =
+ getDistinctEntries(classesGetter(inPackage))
+
+ override private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] =
+ getDistinctEntries(sourcesGetter(inPackage))
+
+ override private[nsc] def list(inPackage: String): FlatClassPathEntries = {
+ val (packages, classesAndSources) = aggregates.map(_.list(inPackage)).unzip
+ val distinctPackages = packages.flatten.distinct
+ val distinctClassesAndSources = mergeClassesAndSources(classesAndSources: _*)
+ FlatClassPathEntries(distinctPackages, distinctClassesAndSources)
+ }
+
+ /**
+ * Returns only one entry for each name. If there's both a source and a class entry, it
+ * creates an entry containing both of them. If there would be more than one class or source
+ * entries for the same class it always would use the first entry of each type found on a classpath.
+ */
+ private def mergeClassesAndSources(entries: Seq[ClassRepClassPathEntry]*): Seq[ClassRepClassPathEntry] = {
+ // based on the implementation from MergedClassPath
+ var count = 0
+ val indices = collection.mutable.HashMap[String, Int]()
+ val mergedEntries = new ArrayBuffer[ClassRepClassPathEntry](1024)
+
+ for {
+ partOfEntries <- entries
+ entry <- partOfEntries
+ } {
+ val name = entry.name
+ if (indices contains name) {
+ val index = indices(name)
+ val existing = mergedEntries(index)
+
+ if (existing.binary.isEmpty && entry.binary.isDefined)
+ mergedEntries(index) = ClassAndSourceFilesEntry(entry.binary.get, existing.source.get)
+ if (existing.source.isEmpty && entry.source.isDefined)
+ mergedEntries(index) = ClassAndSourceFilesEntry(existing.binary.get, entry.source.get)
+ }
+ else {
+ indices(name) = count
+ mergedEntries += entry
+ count += 1
+ }
+ }
+ mergedEntries.toIndexedSeq
+ }
+
+ private def getDistinctEntries[EntryType <: ClassRepClassPathEntry](getEntries: FlatClassPath => Seq[EntryType]): Seq[EntryType] = {
+ val seenNames = collection.mutable.HashSet[String]()
+ val entriesBuffer = new ArrayBuffer[EntryType](1024)
+ for {
+ cp <- aggregates
+ entry <- getEntries(cp) if !seenNames.contains(entry.name)
+ } {
+ entriesBuffer += entry
+ seenNames += entry.name
+ }
+ entriesBuffer.toIndexedSeq
+ }
+
+ private def classesGetter(pkg: String) = (cp: FlatClassPath) => cp.classes(pkg)
+ private def sourcesGetter(pkg: String) = (cp: FlatClassPath) => cp.sources(pkg)
+}
diff --git a/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala b/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala
new file mode 100644
index 0000000000..9bf4e3f779
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc.classpath
+
+import scala.reflect.io.AbstractFile
+import scala.tools.nsc.util.ClassPath
+
+/**
+ * A trait that contains factory methods for classpath elements of type T.
+ *
+ * The logic has been abstracted from ClassPath#ClassPathContext so it's possible
+ * to have common trait that supports both recursive and flat classpath representations.
+ *
+ * Therefore, we expect that T will be either ClassPath[U] or FlatClassPath.
+ */
+trait ClassPathFactory[T] {
+
+ /**
+ * Create a new classpath based on the abstract file.
+ */
+ def newClassPath(file: AbstractFile): T
+
+ /**
+ * Creators for sub classpaths which preserve this context.
+ */
+ def sourcesInPath(path: String): List[T]
+
+ def expandPath(path: String, expandStar: Boolean = true): List[String] = ClassPath.expandPath(path, expandStar)
+
+ def expandDir(extdir: String): List[String] = ClassPath.expandDir(extdir)
+
+ def contentsOfDirsInPath(path: String): List[T] =
+ for {
+ dir <- expandPath(path, expandStar = false)
+ name <- expandDir(dir)
+ entry <- Option(AbstractFile.getDirectory(name))
+ } yield newClassPath(entry)
+
+ def classesInExpandedPath(path: String): IndexedSeq[T] =
+ classesInPathImpl(path, expand = true).toIndexedSeq
+
+ def classesInPath(path: String) = classesInPathImpl(path, expand = false)
+
+ def classesInManifest(useManifestClassPath: Boolean) =
+ if (useManifestClassPath) ClassPath.manifests.map(url => newClassPath(AbstractFile getResources url))
+ else Nil
+
+ // Internal
+ protected def classesInPathImpl(path: String, expand: Boolean) =
+ for {
+ file <- expandPath(path, expand)
+ dir <- Option(AbstractFile.getDirectory(file))
+ } yield newClassPath(dir)
+}
diff --git a/src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala
new file mode 100644
index 0000000000..81d2f7320f
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc.classpath
+
+import java.io.File
+import java.io.FileFilter
+import java.net.URL
+import scala.reflect.io.AbstractFile
+import scala.reflect.io.PlainFile
+import scala.tools.nsc.util.ClassRepresentation
+import FileUtils._
+
+/**
+ * A trait allowing to look for classpath entries of given type in directories.
+ * It provides common logic for classes handling class and source files.
+ * It makes use of the fact that in the case of nested directories it's easy to find a file
+ * when we have a name of a package.
+ */
+trait DirectoryFileLookup[FileEntryType <: ClassRepClassPathEntry] extends FlatClassPath {
+ val dir: File
+ assert(dir != null, "Directory file in DirectoryFileLookup cannot be null")
+
+ override def asURLs: Seq[URL] = Seq(dir.toURI.toURL)
+ override def asClassPathStrings: Seq[String] = Seq(dir.getPath)
+
+ import FlatClassPath.RootPackage
+ private def getDirectory(forPackage: String): Option[File] = {
+ if (forPackage == RootPackage) {
+ Some(dir)
+ } else {
+ val packageDirName = FileUtils.dirPath(forPackage)
+ val packageDir = new File(dir, packageDirName)
+ if (packageDir.exists && packageDir.isDirectory) {
+ Some(packageDir)
+ } else None
+ }
+ }
+
+ override private[nsc] def packages(inPackage: String): Seq[PackageEntry] = {
+ val dirForPackage = getDirectory(inPackage)
+ val nestedDirs: Array[File] = dirForPackage match {
+ case None => Array.empty
+ case Some(directory) => directory.listFiles(DirectoryFileLookup.packageDirectoryFileFilter)
+ }
+ val prefix = PackageNameUtils.packagePrefix(inPackage)
+ val entries = nestedDirs map { file =>
+ PackageEntryImpl(prefix + file.getName)
+ }
+ entries
+ }
+
+ protected def files(inPackage: String): Seq[FileEntryType] = {
+ val dirForPackage = getDirectory(inPackage)
+ val files: Array[File] = dirForPackage match {
+ case None => Array.empty
+ case Some(directory) => directory.listFiles(fileFilter)
+ }
+ val entries = files map { file =>
+ val wrappedFile = new scala.reflect.io.File(file)
+ createFileEntry(new PlainFile(wrappedFile))
+ }
+ entries
+ }
+
+ override private[nsc] def list(inPackage: String): FlatClassPathEntries = {
+ val dirForPackage = getDirectory(inPackage)
+ val files: Array[File] = dirForPackage match {
+ case None => Array.empty
+ case Some(directory) => directory.listFiles()
+ }
+ val packagePrefix = PackageNameUtils.packagePrefix(inPackage)
+ val packageBuf = collection.mutable.ArrayBuffer.empty[PackageEntry]
+ val fileBuf = collection.mutable.ArrayBuffer.empty[FileEntryType]
+ for (file <- files) {
+ if (file.isPackage) {
+ val pkgEntry = PackageEntryImpl(packagePrefix + file.getName)
+ packageBuf += pkgEntry
+ } else if (fileFilter.accept(file)) {
+ val wrappedFile = new scala.reflect.io.File(file)
+ val abstractFile = new PlainFile(wrappedFile)
+ fileBuf += createFileEntry(abstractFile)
+ }
+ }
+ FlatClassPathEntries(packageBuf, fileBuf)
+ }
+
+ protected def createFileEntry(file: AbstractFile): FileEntryType
+ protected def fileFilter: FileFilter
+}
+
+object DirectoryFileLookup {
+
+ private[classpath] object packageDirectoryFileFilter extends FileFilter {
+ override def accept(pathname: File): Boolean = pathname.isPackage
+ }
+}
+
+case class DirectoryFlatClassPath(dir: File)
+ extends DirectoryFileLookup[ClassFileEntryImpl]
+ with NoSourcePaths {
+
+ override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = findClassFile(className) map ClassFileEntryImpl
+
+ override def findClassFile(className: String): Option[AbstractFile] = {
+ val relativePath = FileUtils.dirPath(className)
+ val classFile = new File(s"$dir/$relativePath.class")
+ if (classFile.exists) {
+ val wrappedClassFile = new scala.reflect.io.File(classFile)
+ val abstractClassFile = new PlainFile(wrappedClassFile)
+ Some(abstractClassFile)
+ } else None
+ }
+
+ override protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file)
+ override protected def fileFilter: FileFilter = DirectoryFlatClassPath.classFileFilter
+
+ override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage)
+}
+
+object DirectoryFlatClassPath {
+
+ private val classFileFilter = new FileFilter {
+ override def accept(pathname: File): Boolean = pathname.isClass
+ }
+}
+
+case class DirectoryFlatSourcePath(dir: File)
+ extends DirectoryFileLookup[SourceFileEntryImpl]
+ with NoClassPaths {
+
+ override def asSourcePathString: String = asClassPathString
+
+ override protected def createFileEntry(file: AbstractFile): SourceFileEntryImpl = SourceFileEntryImpl(file)
+ override protected def fileFilter: FileFilter = DirectoryFlatSourcePath.sourceFileFilter
+
+ override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = {
+ findSourceFile(className) map SourceFileEntryImpl
+ }
+
+ private def findSourceFile(className: String): Option[AbstractFile] = {
+ val relativePath = FileUtils.dirPath(className)
+ val sourceFile = Stream("scala", "java")
+ .map(ext => new File(s"$dir/$relativePath.$ext"))
+ .collectFirst { case file if file.exists() => file }
+
+ sourceFile.map { file =>
+ val wrappedSourceFile = new scala.reflect.io.File(file)
+ val abstractSourceFile = new PlainFile(wrappedSourceFile)
+ abstractSourceFile
+ }
+ }
+
+ override private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = files(inPackage)
+}
+
+object DirectoryFlatSourcePath {
+
+ private val sourceFileFilter = new FileFilter {
+ override def accept(pathname: File): Boolean = endsScalaOrJava(pathname.getName)
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/classpath/FileUtils.scala b/src/compiler/scala/tools/nsc/classpath/FileUtils.scala
new file mode 100644
index 0000000000..ee2528e15c
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/classpath/FileUtils.scala
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc.classpath
+
+import java.io.{ File => JFile }
+import java.net.URL
+import scala.reflect.internal.FatalError
+import scala.reflect.io.AbstractFile
+
+/**
+ * Common methods related to Java files and abstract files used in the context of classpath
+ */
+object FileUtils {
+ implicit class AbstractFileOps(val file: AbstractFile) extends AnyVal {
+ def isPackage: Boolean = file.isDirectory && mayBeValidPackage(file.name)
+
+ def isClass: Boolean = !file.isDirectory && file.hasExtension("class")
+
+ def isScalaOrJavaSource: Boolean = !file.isDirectory && (file.hasExtension("scala") || file.hasExtension("java"))
+
+ // TODO do we need to check also other files using ZipMagicNumber like in scala.tools.nsc.io.Jar.isJarOrZip?
+ def isJarOrZip: Boolean = file.hasExtension("jar") || file.hasExtension("zip")
+
+ /**
+ * Safe method returning a sequence containing one URL representing this file, when underlying file exists,
+ * and returning given default value in other case
+ */
+ def toURLs(default: => Seq[URL] = Seq.empty): Seq[URL] = if (file.file == null) default else Seq(file.toURL)
+ }
+
+ implicit class FileOps(val file: JFile) extends AnyVal {
+ def isPackage: Boolean = file.isDirectory && mayBeValidPackage(file.getName)
+
+ def isClass: Boolean = file.isFile && file.getName.endsWith(".class")
+ }
+
+ def stripSourceExtension(fileName: String): String = {
+ if (endsScala(fileName)) stripClassExtension(fileName)
+ else if (endsJava(fileName)) stripJavaExtension(fileName)
+ else throw new FatalError("Unexpected source file ending: " + fileName)
+ }
+
+ def dirPath(forPackage: String) = forPackage.replace('.', '/')
+
+ def endsClass(fileName: String): Boolean =
+ fileName.length > 6 && fileName.substring(fileName.length - 6) == ".class"
+
+ def endsScalaOrJava(fileName: String): Boolean =
+ endsScala(fileName) || endsJava(fileName)
+
+ def endsJava(fileName: String): Boolean =
+ fileName.length > 5 && fileName.substring(fileName.length - 5) == ".java"
+
+ def endsScala(fileName: String): Boolean =
+ fileName.length > 6 && fileName.substring(fileName.length - 6) == ".scala"
+
+ def stripClassExtension(fileName: String): String =
+ fileName.substring(0, fileName.length - 6) // equivalent of fileName.length - ".class".length
+
+ def stripJavaExtension(fileName: String): String =
+ fileName.substring(0, fileName.length - 5)
+
+ // probably it should match a pattern like [a-z_]{1}[a-z0-9_]* but it cannot be changed
+ // because then some tests in partest don't pass
+ private def mayBeValidPackage(dirName: String): Boolean =
+ (dirName != "META-INF") && (dirName != "") && (dirName.charAt(0) != '.')
+}
diff --git a/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala
new file mode 100644
index 0000000000..26b5429e23
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc.classpath
+
+import scala.reflect.io.AbstractFile
+import scala.tools.nsc.util.{ ClassFileLookup, ClassPath, ClassRepresentation }
+
+/**
+ * A base trait for the particular flat classpath representation implementations.
+ *
+ * We call this variant of a classpath representation flat because it's possible to
+ * query the whole classpath using just single instance extending this trait.
+ *
+ * This is an alternative design compared to scala.tools.nsc.util.ClassPath
+ */
+trait FlatClassPath extends ClassFileLookup[AbstractFile] {
+ /** Empty string represents root package */
+ private[nsc] def packages(inPackage: String): Seq[PackageEntry]
+ private[nsc] def classes(inPackage: String): Seq[ClassFileEntry]
+ private[nsc] def sources(inPackage: String): Seq[SourceFileEntry]
+
+ /** Allows to get entries for packages and classes merged with sources possibly in one pass. */
+ private[nsc] def list(inPackage: String): FlatClassPathEntries
+
+ // A default implementation which should be overriden, if we can create more efficient
+ // solution for given type of FlatClassPath
+ override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = {
+ val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className)
+
+ val foundClassFromClassFiles = classes(pkg)
+ .find(_.name == simpleClassName)
+
+ def findClassInSources = sources(pkg)
+ .find(_.name == simpleClassName)
+
+ foundClassFromClassFiles orElse findClassInSources
+ }
+
+ override def asClassPathString: String = ClassPath.join(asClassPathStrings: _*)
+ def asClassPathStrings: Seq[String]
+}
+
+object FlatClassPath {
+ val RootPackage = ""
+}
+
+case class FlatClassPathEntries(packages: Seq[PackageEntry], classesAndSources: Seq[ClassRepClassPathEntry])
+
+object FlatClassPathEntries {
+ import scala.language.implicitConversions
+ // to have working unzip method
+ implicit def entry2Tuple(entry: FlatClassPathEntries) = (entry.packages, entry.classesAndSources)
+}
+
+sealed trait ClassRepClassPathEntry extends ClassRepresentation[AbstractFile]
+
+trait ClassFileEntry extends ClassRepClassPathEntry {
+ def file: AbstractFile
+}
+
+trait SourceFileEntry extends ClassRepClassPathEntry {
+ def file: AbstractFile
+}
+
+trait PackageEntry {
+ def name: String
+}
+
+private[nsc] case class ClassFileEntryImpl(file: AbstractFile) extends ClassFileEntry {
+ override def name = FileUtils.stripClassExtension(file.name) // class name
+
+ override def binary: Option[AbstractFile] = Some(file)
+ override def source: Option[AbstractFile] = None
+}
+
+private[nsc] case class SourceFileEntryImpl(file: AbstractFile) extends SourceFileEntry {
+ override def name = FileUtils.stripSourceExtension(file.name)
+
+ override def binary: Option[AbstractFile] = None
+ override def source: Option[AbstractFile] = Some(file)
+}
+
+private[nsc] case class ClassAndSourceFilesEntry(classFile: AbstractFile, srcFile: AbstractFile) extends ClassRepClassPathEntry {
+ override def name = FileUtils.stripClassExtension(classFile.name)
+
+ override def binary: Option[AbstractFile] = Some(classFile)
+ override def source: Option[AbstractFile] = Some(srcFile)
+}
+
+private[nsc] case class PackageEntryImpl(name: String) extends PackageEntry
+
+private[nsc] trait NoSourcePaths {
+ def asSourcePathString: String = ""
+ private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = Seq.empty
+}
+
+private[nsc] trait NoClassPaths {
+ def findClassFile(className: String): Option[AbstractFile] = None
+ private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = Seq.empty
+}
diff --git a/src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala b/src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala
new file mode 100644
index 0000000000..7f67381d4d
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc.classpath
+
+import scala.tools.nsc.Settings
+import scala.tools.nsc.io.AbstractFile
+import scala.tools.nsc.util.ClassPath
+import FileUtils.AbstractFileOps
+
+/**
+ * Provides factory methods for flat classpath. When creating classpath instances for a given path,
+ * it uses proper type of classpath depending on a types of particular files containing sources or classes.
+ */
+class FlatClassPathFactory(settings: Settings) extends ClassPathFactory[FlatClassPath] {
+
+ override def newClassPath(file: AbstractFile): FlatClassPath =
+ if (file.isJarOrZip)
+ ZipAndJarFlatClassPathFactory.create(file, settings)
+ else if (file.isDirectory)
+ new DirectoryFlatClassPath(file.file)
+ else
+ sys.error(s"Unsupported classpath element: $file")
+
+ override def sourcesInPath(path: String): List[FlatClassPath] =
+ for {
+ file <- expandPath(path, expandStar = false)
+ dir <- Option(AbstractFile getDirectory file)
+ } yield createSourcePath(dir)
+
+ private def createSourcePath(file: AbstractFile): FlatClassPath =
+ if (file.isJarOrZip)
+ ZipAndJarFlatSourcePathFactory.create(file, settings)
+ else if (file.isDirectory)
+ new DirectoryFlatSourcePath(file.file)
+ else
+ sys.error(s"Unsupported sourcepath element: $file")
+}
diff --git a/src/compiler/scala/tools/nsc/classpath/PackageNameUtils.scala b/src/compiler/scala/tools/nsc/classpath/PackageNameUtils.scala
new file mode 100644
index 0000000000..c907d565d2
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/classpath/PackageNameUtils.scala
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc.classpath
+
+import scala.tools.nsc.classpath.FlatClassPath.RootPackage
+
+/**
+ * Common methods related to package names represented as String
+ */
+object PackageNameUtils {
+
+ /**
+ * @param fullClassName full class name with package
+ * @return (package, simple class name)
+ */
+ def separatePkgAndClassNames(fullClassName: String): (String, String) = {
+ val lastDotIndex = fullClassName.lastIndexOf('.')
+ if (lastDotIndex == -1)
+ (RootPackage, fullClassName)
+ else
+ (fullClassName.substring(0, lastDotIndex), fullClassName.substring(lastDotIndex + 1))
+ }
+
+ def packagePrefix(inPackage: String): String = if (inPackage == RootPackage) "" else inPackage + "."
+}
diff --git a/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala b/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala
new file mode 100644
index 0000000000..84e21a3ccd
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc.classpath
+
+import java.io.File
+import java.net.URL
+import scala.annotation.tailrec
+import scala.reflect.io.{ AbstractFile, FileZipArchive, ManifestResources }
+import scala.tools.nsc.Settings
+import FileUtils._
+
+/**
+ * A trait providing an optional cache for classpath entries obtained from zip and jar files.
+ * It's possible to create such a cache assuming that entries in such files won't change (at
+ * least will be the same each time we'll load classpath during the lifetime of JVM process)
+ * - unlike class and source files in directories, which can be modified and recompiled.
+ * It allows us to e.g. reduce significantly memory used by PresentationCompilers in Scala IDE
+ * when there are a lot of projects having a lot of common dependencies.
+ */
+sealed trait ZipAndJarFileLookupFactory {
+
+ private val cache = collection.mutable.Map.empty[AbstractFile, FlatClassPath]
+
+ def create(zipFile: AbstractFile, settings: Settings): FlatClassPath = {
+ if (settings.YdisableFlatCpCaching) createForZipFile(zipFile)
+ else createUsingCache(zipFile, settings)
+ }
+
+ protected def createForZipFile(zipFile: AbstractFile): FlatClassPath
+
+ private def createUsingCache(zipFile: AbstractFile, settings: Settings): FlatClassPath = cache.synchronized {
+ def newClassPathInstance = {
+ if (settings.verbose || settings.Ylogcp)
+ println(s"$zipFile is not yet in the classpath cache")
+ createForZipFile(zipFile)
+ }
+ cache.getOrElseUpdate(zipFile, newClassPathInstance)
+ }
+}
+
+/**
+ * Manages creation of flat classpath for class files placed in zip and jar files.
+ * It should be the only way of creating them as it provides caching.
+ */
+object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory {
+
+ private case class ZipArchiveFlatClassPath(zipFile: File)
+ extends ZipArchiveFileLookup[ClassFileEntryImpl]
+ with NoSourcePaths {
+
+ override def findClassFile(className: String): Option[AbstractFile] = {
+ val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className)
+ classes(pkg).find(_.name == simpleClassName).map(_.file)
+ }
+
+ override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage)
+
+ override protected def createFileEntry(file: FileZipArchive#Entry): ClassFileEntryImpl = ClassFileEntryImpl(file)
+ override protected def isRequiredFileType(file: AbstractFile): Boolean = file.isClass
+ }
+
+ /**
+ * This type of classpath is closly related to the support for JSR-223.
+ * Its usage can be observed e.g. when running:
+ * jrunscript -classpath scala-compiler.jar;scala-reflect.jar;scala-library.jar -l scala
+ * with a particularly prepared scala-library.jar. It should have all classes listed in the manifest like e.g. this entry:
+ * Name: scala/Function2$mcFJD$sp.class
+ */
+ private case class ManifestResourcesFlatClassPath(file: ManifestResources)
+ extends FlatClassPath
+ with NoSourcePaths {
+
+ override def findClassFile(className: String): Option[AbstractFile] = {
+ val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className)
+ classes(pkg).find(_.name == simpleClassName).map(_.file)
+ }
+
+ override def asClassPathStrings: Seq[String] = Seq(file.path)
+
+ override def asURLs: Seq[URL] = file.toURLs()
+
+ import ManifestResourcesFlatClassPath.PackageFileInfo
+ import ManifestResourcesFlatClassPath.PackageInfo
+
+ /**
+ * A cache mapping package name to abstract file for package directory and subpackages of given package.
+ *
+ * ManifestResources can iterate through the collections of entries from e.g. remote jar file.
+ * We can't just specify the path to the concrete directory etc. so we can't just 'jump' into
+ * given package, when it's needed. On the other hand we can iterate over entries to get
+ * AbstractFiles, iterate over entries of these files etc.
+ *
+ * Instead of traversing a tree of AbstractFiles once and caching all entries or traversing each time,
+ * when we need subpackages of a given package or its classes, we traverse once and cache only packages.
+ * Classes for given package can be then easily loaded when they are needed.
+ */
+ private lazy val cachedPackages: collection.mutable.HashMap[String, PackageFileInfo] = {
+ val packages = collection.mutable.HashMap[String, PackageFileInfo]()
+
+ def getSubpackages(dir: AbstractFile): List[AbstractFile] =
+ (for (file <- dir if file.isPackage) yield file)(collection.breakOut)
+
+ @tailrec
+ def traverse(packagePrefix: String,
+ filesForPrefix: List[AbstractFile],
+ subpackagesQueue: collection.mutable.Queue[PackageInfo]): Unit = filesForPrefix match {
+ case pkgFile :: remainingFiles =>
+ val subpackages = getSubpackages(pkgFile)
+ val fullPkgName = packagePrefix + pkgFile.name
+ packages.put(fullPkgName, PackageFileInfo(pkgFile, subpackages))
+ val newPackagePrefix = fullPkgName + "."
+ subpackagesQueue.enqueue(PackageInfo(newPackagePrefix, subpackages))
+ traverse(packagePrefix, remainingFiles, subpackagesQueue)
+ case Nil if subpackagesQueue.nonEmpty =>
+ val PackageInfo(packagePrefix, filesForPrefix) = subpackagesQueue.dequeue()
+ traverse(packagePrefix, filesForPrefix, subpackagesQueue)
+ case _ =>
+ }
+
+ val subpackages = getSubpackages(file)
+ packages.put(FlatClassPath.RootPackage, PackageFileInfo(file, subpackages))
+ traverse(FlatClassPath.RootPackage, subpackages, collection.mutable.Queue())
+ packages
+ }
+
+ override private[nsc] def packages(inPackage: String): Seq[PackageEntry] = cachedPackages.get(inPackage) match {
+ case None => Seq.empty
+ case Some(PackageFileInfo(_, subpackages)) =>
+ val prefix = PackageNameUtils.packagePrefix(inPackage)
+ subpackages.map(packageFile => PackageEntryImpl(prefix + packageFile.name))
+ }
+
+ override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = cachedPackages.get(inPackage) match {
+ case None => Seq.empty
+ case Some(PackageFileInfo(pkg, _)) =>
+ (for (file <- pkg if file.isClass) yield ClassFileEntryImpl(file))(collection.breakOut)
+ }
+
+ override private[nsc] def list(inPackage: String): FlatClassPathEntries = FlatClassPathEntries(packages(inPackage), classes(inPackage))
+ }
+
+ private object ManifestResourcesFlatClassPath {
+ case class PackageFileInfo(packageFile: AbstractFile, subpackages: Seq[AbstractFile])
+ case class PackageInfo(packageName: String, subpackages: List[AbstractFile])
+ }
+
+ override protected def createForZipFile(zipFile: AbstractFile): FlatClassPath =
+ if (zipFile.file == null) createWithoutUnderlyingFile(zipFile)
+ else ZipArchiveFlatClassPath(zipFile.file)
+
+ private def createWithoutUnderlyingFile(zipFile: AbstractFile) = zipFile match {
+ case manifestRes: ManifestResources =>
+ ManifestResourcesFlatClassPath(manifestRes)
+ case _ =>
+ val errorMsg = s"Abstract files which don't have an underlying file and are not ManifestResources are not supported. There was $zipFile"
+ throw new IllegalArgumentException(errorMsg)
+ }
+}
+
+/**
+ * Manages creation of flat classpath for source files placed in zip and jar files.
+ * It should be the only way of creating them as it provides caching.
+ */
+object ZipAndJarFlatSourcePathFactory extends ZipAndJarFileLookupFactory {
+
+ private case class ZipArchiveFlatSourcePath(zipFile: File)
+ extends ZipArchiveFileLookup[SourceFileEntryImpl]
+ with NoClassPaths {
+
+ override def asSourcePathString: String = asClassPathString
+
+ override private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = files(inPackage)
+
+ override protected def createFileEntry(file: FileZipArchive#Entry): SourceFileEntryImpl = SourceFileEntryImpl(file)
+ override protected def isRequiredFileType(file: AbstractFile): Boolean = file.isScalaOrJavaSource
+ }
+
+ override protected def createForZipFile(zipFile: AbstractFile): FlatClassPath = ZipArchiveFlatSourcePath(zipFile.file)
+}
diff --git a/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala b/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala
new file mode 100644
index 0000000000..1d0de57779
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc.classpath
+
+import java.io.File
+import java.net.URL
+import scala.collection.Seq
+import scala.reflect.io.AbstractFile
+import scala.reflect.io.FileZipArchive
+import FileUtils.AbstractFileOps
+
+/**
+ * A trait allowing to look for classpath entries of given type in zip and jar files.
+ * It provides common logic for classes handling class and source files.
+ * It's aware of things like e.g. META-INF directory which is correctly skipped.
+ */
+trait ZipArchiveFileLookup[FileEntryType <: ClassRepClassPathEntry] extends FlatClassPath {
+ val zipFile: File
+
+ assert(zipFile != null, "Zip file in ZipArchiveFileLookup cannot be null")
+
+ override def asURLs: Seq[URL] = Seq(zipFile.toURI.toURL)
+ override def asClassPathStrings: Seq[String] = Seq(zipFile.getPath)
+
+ private val archive = new FileZipArchive(zipFile)
+
+ override private[nsc] def packages(inPackage: String): Seq[PackageEntry] = {
+ val prefix = PackageNameUtils.packagePrefix(inPackage)
+ for {
+ dirEntry <- findDirEntry(inPackage).toSeq
+ entry <- dirEntry.iterator if entry.isPackage
+ } yield PackageEntryImpl(prefix + entry.name)
+ }
+
+ protected def files(inPackage: String): Seq[FileEntryType] =
+ for {
+ dirEntry <- findDirEntry(inPackage).toSeq
+ entry <- dirEntry.iterator if isRequiredFileType(entry)
+ } yield createFileEntry(entry)
+
+ override private[nsc] def list(inPackage: String): FlatClassPathEntries = {
+ val foundDirEntry = findDirEntry(inPackage)
+
+ foundDirEntry map { dirEntry =>
+ val pkgBuf = collection.mutable.ArrayBuffer.empty[PackageEntry]
+ val fileBuf = collection.mutable.ArrayBuffer.empty[FileEntryType]
+ val prefix = PackageNameUtils.packagePrefix(inPackage)
+
+ for (entry <- dirEntry.iterator) {
+ if (entry.isPackage)
+ pkgBuf += PackageEntryImpl(prefix + entry.name)
+ else if (isRequiredFileType(entry))
+ fileBuf += createFileEntry(entry)
+ }
+ FlatClassPathEntries(pkgBuf, fileBuf)
+ } getOrElse FlatClassPathEntries(Seq.empty, Seq.empty)
+ }
+
+ private def findDirEntry(pkg: String) = {
+ val dirName = s"${FileUtils.dirPath(pkg)}/"
+ archive.allDirs.get(dirName)
+ }
+
+ protected def createFileEntry(file: FileZipArchive#Entry): FileEntryType
+ protected def isRequiredFileType(file: AbstractFile): Boolean
+}
diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala
index 6e3d013e52..4b1805479d 100644
--- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala
+++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala
@@ -7,7 +7,7 @@
package scala.tools.nsc
package plugins
-import scala.reflect.io.{ File, Path }
+import scala.reflect.io.Path
import scala.tools.nsc.util.ClassPath
import scala.tools.util.PathResolver.Defaults
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index d6650595eb..18e639b81c 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -202,6 +202,9 @@ trait ScalaSettings extends AbsScalaSettings
val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overriden methods.")
val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.").withDeprecationMessage(removalIn212)
val inferByName = BooleanSetting ("-Yinfer-by-name", "Allow inference of by-name types. This is a temporary option to ease transition. See SI-7899.").withDeprecationMessage(removalIn212)
+ val YclasspathImpl = ChoiceSetting ("-YclasspathImpl", "implementation", "Choose classpath scanning method.", List(ClassPathRepresentationType.Recursive, ClassPathRepresentationType.Flat), ClassPathRepresentationType.Recursive)
+ val YdisableFlatCpCaching = BooleanSetting ("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.")
+
val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes")
val YdisableUnreachablePrevention = BooleanSetting("-Ydisable-unreachable-prevention", "Disable the prevention of unreachable blocks in code generation.")
val YnoLoadImplClass = BooleanSetting ("-Yno-load-impl-class", "Do not load $class.class files.")
@@ -329,3 +332,8 @@ trait ScalaSettings extends AbsScalaSettings
val Discard = "discard"
}
}
+
+object ClassPathRepresentationType {
+ val Flat = "flat"
+ val Recursive = "recursive"
+}
diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
index 82c2a4d6ed..9af3efbece 100644
--- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
+++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
@@ -6,13 +6,15 @@
package scala.tools.nsc
package symtab
+import classfile.ClassfileParser
import java.io.IOException
import scala.compat.Platform.currentTime
-import scala.tools.nsc.util.{ ClassPath }
-import classfile.ClassfileParser
import scala.reflect.internal.MissingRequirementError
import scala.reflect.internal.util.Statistics
import scala.reflect.io.{ AbstractFile, NoAbstractFile }
+import scala.tools.nsc.classpath.FlatClassPath
+import scala.tools.nsc.settings.ClassPathRepresentationType
+import scala.tools.nsc.util.{ ClassPath, ClassRepresentation }
/** This class ...
*
@@ -86,8 +88,7 @@ abstract class SymbolLoaders {
// require yjp.jar at runtime. See SI-2089.
if (settings.termConflict.isDefault)
throw new TypeError(
- root+" contains object and package with same name: "+
- name+"\none of them needs to be removed from classpath"
+ s"$root contains object and package with same name: $name\none of them needs to be removed from classpath"
)
else if (settings.termConflict.value == "package") {
warning(
@@ -154,7 +155,7 @@ abstract class SymbolLoaders {
/** Initialize toplevel class and module symbols in `owner` from class path representation `classRep`
*/
- def initializeFromClassPath(owner: Symbol, classRep: ClassPath[AbstractFile]#ClassRep) {
+ def initializeFromClassPath(owner: Symbol, classRep: ClassRepresentation[AbstractFile]) {
((classRep.binary, classRep.source) : @unchecked) match {
case (Some(bin), Some(src))
if platform.needCompile(bin, src) && !binaryOnly(owner, classRep.name) =>
@@ -250,7 +251,7 @@ abstract class SymbolLoaders {
* Load contents of a package
*/
class PackageLoader(classpath: ClassPath[AbstractFile]) extends SymbolLoader with FlagAgnosticCompleter {
- protected def description = "package loader "+ classpath.name
+ protected def description = s"package loader ${classpath.name}"
protected def doComplete(root: Symbol) {
assert(root.isPackageClass, root)
@@ -276,6 +277,39 @@ abstract class SymbolLoaders {
}
}
+ /**
+ * Loads contents of a package
+ */
+ class PackageLoaderUsingFlatClassPath(packageName: String, classPath: FlatClassPath) extends SymbolLoader with FlagAgnosticCompleter {
+ protected def description = {
+ val shownPackageName = if (packageName == FlatClassPath.RootPackage) "<root package>" else packageName
+ s"package loader $shownPackageName"
+ }
+
+ protected def doComplete(root: Symbol) {
+ assert(root.isPackageClass, root)
+ root.setInfo(new PackageClassInfoType(newScope, root))
+
+ val classPathEntries = classPath.list(packageName)
+
+ if (!root.isRoot)
+ for (entry <- classPathEntries.classesAndSources) initializeFromClassPath(root, entry)
+ if (!root.isEmptyPackageClass) {
+ for (pkg <- classPathEntries.packages) {
+ val fullName = pkg.name
+
+ val name =
+ if (packageName == FlatClassPath.RootPackage) fullName
+ else fullName.substring(packageName.length + 1)
+ val packageLoader = new PackageLoaderUsingFlatClassPath(fullName, classPath)
+ enterPackage(root, name, packageLoader)
+ }
+
+ openPackageModule(root)
+ }
+ }
+ }
+
class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader with FlagAssigningCompleter {
private object classfileParser extends {
val symbolTable: SymbolLoaders.this.symbolTable.type = SymbolLoaders.this.symbolTable
@@ -293,8 +327,13 @@ abstract class SymbolLoaders {
*
*/
private type SymbolLoadersRefined = SymbolLoaders { val symbolTable: classfileParser.symbolTable.type }
+
val loaders = SymbolLoaders.this.asInstanceOf[SymbolLoadersRefined]
- val classPath = platform.classPath
+
+ override def classFileLookup: util.ClassFileLookup[AbstractFile] = settings.YclasspathImpl.value match {
+ case ClassPathRepresentationType.Recursive => platform.classPath
+ case ClassPathRepresentationType.Flat => platform.flatClassPath
+ }
}
protected def description = "class file "+ classfile.toString
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
index 14be8374b9..1abbdb50b0 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -16,8 +16,7 @@ import scala.annotation.switch
import scala.reflect.internal.{ JavaAccFlags }
import scala.reflect.internal.pickling.{PickleBuffer, ByteCodecs}
import scala.tools.nsc.io.AbstractFile
-
-import util.ClassPath
+import scala.tools.nsc.util.ClassFileLookup
/** This abstract class implements a class file parser.
*
@@ -43,8 +42,8 @@ abstract class ClassfileParser {
*/
protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol
- /** The compiler classpath. */
- def classPath: ClassPath[AbstractFile]
+ /** The way of the class file lookup used by the compiler. */
+ def classFileLookup: ClassFileLookup[AbstractFile]
import definitions._
import scala.reflect.internal.ClassfileConstants._
@@ -352,7 +351,7 @@ abstract class ClassfileParser {
}
private def loadClassSymbol(name: Name): Symbol = {
- val file = classPath findClassFile ("" +name) getOrElse {
+ val file = classFileLookup findClassFile name.toString getOrElse {
// SI-5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented
// therefore, it will rummage through the classpath triggering errors whenever it encounters package objects
// that are not in their correct place (see bug for details)
@@ -1047,8 +1046,8 @@ abstract class ClassfileParser {
for (entry <- innerClasses.entries) {
// create a new class member for immediate inner classes
if (entry.outerName == currentClass) {
- val file = classPath.findClassFile(entry.externalName.toString) getOrElse {
- throw new AssertionError(entry.externalName)
+ val file = classFileLookup.findClassFile(entry.externalName.toString) getOrElse {
+ throw new AssertionError(s"Class file for ${entry.externalName} not found")
}
enterClassAndModule(entry, file)
}
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
index cbe427775a..bd1fa4e707 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
@@ -130,7 +130,7 @@ abstract class ICodeReader extends ClassfileParser {
log("ICodeReader reading " + cls)
val name = cls.javaClassName
- classPath.findClassFile(name) match {
+ classFileLookup.findClassFile(name) match {
case Some(classFile) => parse(classFile, cls)
case _ => MissingRequirementError.notFound("Could not find bytecode for " + cls)
}
diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
index 2b7c6cca2c..f786ffb8f3 100644
--- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
+++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
@@ -8,6 +8,7 @@ package transform
import symtab._
import Flags._
+import scala.tools.nsc.util.ClassPath
abstract class AddInterfaces extends InfoTransform { self: Erasure =>
import global._ // the global environment
@@ -67,25 +68,30 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
val implName = tpnme.implClassName(iface.name)
val implFlags = (iface.flags & ~(INTERFACE | lateINTERFACE)) | IMPLCLASS
- val impl0 = (
+ val impl0 = {
if (!inClass) NoSymbol
- else iface.owner.info.decl(implName) match {
- case NoSymbol => NoSymbol
- case implSym =>
- // Unlink a pre-existing symbol only if the implementation class is
- // visible on the compilation classpath. In general this is true under
- // -optimise and not otherwise, but the classpath can use arbitrary
- // logic so the classpath must be queried.
- if (classPath.context.isValidName(implName + ".class")) {
- iface.owner.info.decls unlink implSym
- NoSymbol
- }
- else {
- log(s"not unlinking $iface's existing implClass ${implSym.name} because it is not on the classpath.")
- implSym
- }
+ else {
+ val typeInfo = iface.owner.info
+ typeInfo.decl(implName) match {
+ case NoSymbol => NoSymbol
+ case implSym =>
+ // Unlink a pre-existing symbol only if the implementation class is
+ // visible on the compilation classpath. In general this is true under
+ // -optimise and not otherwise, but the classpath can use arbitrary
+ // logic so the classpath must be queried.
+ // TODO this is not taken into account by flat classpath yet
+ classPath match {
+ case cp: ClassPath[_] if !cp.context.isValidName(implName + ".class") =>
+ log(s"not unlinking $iface's existing implClass ${implSym.name} because it is not on the classpath.")
+ implSym
+ case _ =>
+ typeInfo.decls unlink implSym
+ NoSymbol
+ }
+ }
}
- )
+ }
+
val impl = impl0 orElse {
val impl = iface.owner.newImplClass(implName, iface.pos, implFlags)
if (iface.thisSym != iface) {
@@ -345,6 +351,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
while (owner != sym && owner != impl) owner = owner.owner;
if (owner == impl) This(impl) setPos tree.pos
else tree
+ //TODO what about this commented out code?
/* !!!
case Super(qual, mix) =>
val mix1 = mix
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
index d862805a07..22661d6ccf 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
@@ -544,10 +544,17 @@ trait MatchTranslation {
// wrong when isSeq, and resultInMonad should always be correct since it comes
// directly from the extractor's result type
val binder = freshSym(pos, pureType(resultInMonad))
+ val potentiallyMutableBinders: Set[Symbol] =
+ if (extractorApply.tpe.typeSymbol.isNonBottomSubClass(OptionClass) && !aligner.isSeq)
+ Set.empty
+ else
+ // Ensures we capture unstable bound variables eagerly. These can arise under name based patmat or by indexing into mutable Seqs. See run t9003.scala
+ subPatBinders.toSet
ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder)(
subPatBinders,
subPatRefs(binder),
+ potentiallyMutableBinders,
aligner.isBool,
checkedLength,
patBinderOrCasted,
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
index 3abec521df..3fd9ce76f8 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
@@ -192,13 +192,14 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol)(
val subPatBinders: List[Symbol],
val subPatRefs: List[Tree],
+ val potentiallyMutableBinders: Set[Symbol],
extractorReturnsBoolean: Boolean,
val checkedLength: Option[Int],
val prevBinder: Symbol,
val ignoredSubPatBinders: Set[Symbol]
) extends FunTreeMaker with PreserveSubPatBinders {
- def extraStoredBinders: Set[Symbol] = Set()
+ def extraStoredBinders: Set[Symbol] = potentiallyMutableBinders
debug.patmat(s"""
|ExtractorTreeMaker($extractor, $extraCond, $nextBinder) {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index b13f9e94cc..a7d0d32c6f 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -104,7 +104,7 @@ trait Contexts { self: Analyzer =>
// there must be a scala.xml package when xml literals were parsed in this unit
if (unit.hasXml && ScalaXmlPackage == NoSymbol)
- reporter.error(unit.firstXmlPos, "To compile XML syntax, the scala.xml package must be on the classpath.\nPlease see http://docs.scala-lang.org/overviews/core/scala-2.11.html#scala-xml.")
+ reporter.error(unit.firstXmlPos, "To compile XML syntax, the scala.xml package must be on the classpath.\nPlease see https://github.com/scala/scala-xml for details.")
// scala-xml needs `scala.xml.TopScope` to be in scope globally as `$scope`
// We detect `scala-xml` by looking for `scala.xml.TopScope` and
@@ -480,7 +480,8 @@ trait Contexts { self: Analyzer =>
// SI-8245 `isLazy` need to skip lazy getters to ensure `return` binds to the right place
c.enclMethod = if (isDefDef && !owner.isLazy) c else enclMethod
- if (tree != outer.tree) c(TypeConstructorAllowed) = false
+ if (tree != outer.tree)
+ c(TypeConstructorAllowed) = false
registerContext(c.asInstanceOf[analyzer.Context])
debuglog("[context] ++ " + c.unit + " / " + tree.summaryString)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index a1de5e303b..0bb94be636 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -584,7 +584,7 @@ trait Namers extends MethodSynthesis {
// more than one hidden name, the second will not be warned.
// So it is the position of the actual hidden name.
//
- // Note: java imports have precence over definitions in the same package
+ // Note: java imports have precedence over definitions in the same package
// so don't warn for them. There is a corresponding special treatment
// in the shadowing rules in typedIdent to (SI-7232). In any case,
// we shouldn't be emitting warnings for .java source files.
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 4d9a6a47ef..aaa75b5ee1 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -5207,7 +5207,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def typedExistentialTypeTree(tree: ExistentialTypeTree) = {
val tree1 = typerWithLocalContext(context.makeNewScope(tree, context.owner)){
- _.typedExistentialTypeTree(tree, mode)
+ typer =>
+ if (context.inTypeConstructorAllowed)
+ typer.context.withinTypeConstructorAllowed(typer.typedExistentialTypeTree(tree, mode))
+ else
+ typer.typedExistentialTypeTree(tree, mode)
}
checkExistentialsFeature(tree1.pos, tree1.tpe, "the existential type")
tree1
diff --git a/src/compiler/scala/tools/nsc/util/ClassFileLookup.scala b/src/compiler/scala/tools/nsc/util/ClassFileLookup.scala
new file mode 100644
index 0000000000..4451651229
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/util/ClassFileLookup.scala
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc.util
+
+import scala.tools.nsc.io.AbstractFile
+import java.net.URL
+
+/**
+ * Simple interface that allows us to abstract over how class file lookup is performed
+ * in different classpath representations.
+ */
+// TODO at the end, after the possible removal of the old classpath representation, this class shouldn't be generic
+// T should be just changed to AbstractFile
+trait ClassFileLookup[T] {
+ def findClassFile(name: String): Option[AbstractFile]
+
+ /**
+ * It returns both classes from class file and source files (as our base ClassRepresentation).
+ * So note that it's not so strictly related to findClassFile.
+ */
+ def findClass(name: String): Option[ClassRepresentation[T]]
+
+ /**
+ * A sequence of URLs representing this classpath.
+ */
+ def asURLs: Seq[URL]
+
+ /** The whole classpath in the form of one String.
+ */
+ def asClassPathString: String
+
+ // for compatibility purposes
+ @deprecated("Use asClassPathString instead of this one", "2.11.5")
+ def asClasspathString: String = asClassPathString
+
+ /** The whole sourcepath in the form of one String.
+ */
+ def asSourcePathString: String
+}
+
+/**
+ * Represents classes which can be loaded with a ClassfileLoader and/or SourcefileLoader.
+ */
+// TODO at the end, after the possible removal of the old classpath implementation, this class shouldn't be generic
+// T should be just changed to AbstractFile
+trait ClassRepresentation[T] {
+ def binary: Option[T]
+ def source: Option[AbstractFile]
+
+ def name: String
+}
+
+object ClassRepresentation {
+ def unapply[T](classRep: ClassRepresentation[T]): Option[(Option[T], Option[AbstractFile])] =
+ Some((classRep.binary, classRep.source))
+}
diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala
index e78dee5eee..8d4d07759f 100644
--- a/src/compiler/scala/tools/nsc/util/ClassPath.scala
+++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala
@@ -7,16 +7,18 @@
package scala.tools.nsc
package util
+import io.{ AbstractFile, Directory, File, Jar }
+import java.net.MalformedURLException
import java.net.URL
+import java.util.regex.PatternSyntaxException
import scala.collection.{ mutable, immutable }
-import io.{ File, Directory, Path, Jar, AbstractFile }
import scala.reflect.internal.util.StringOps.splitWhere
-import Jar.isJarOrZip
+import scala.tools.nsc.classpath.FileUtils
+
import File.pathSeparator
-import scala.collection.convert.WrapAsScala.enumerationAsScalaIterator
-import java.net.MalformedURLException
-import java.util.regex.PatternSyntaxException
-import scala.reflect.runtime.ReflectionUtils
+import FileUtils.endsClass
+import FileUtils.endsScalaOrJava
+import Jar.isJarOrZip
/** <p>
* This module provides star expansion of '-classpath' option arguments, behaves the same as
@@ -89,7 +91,7 @@ object ClassPath {
/** A class modeling aspects of a ClassPath which should be
* propagated to any classpaths it creates.
*/
- abstract class ClassPathContext[T] {
+ abstract class ClassPathContext[T] extends classpath.ClassPathFactory[ClassPath[T]] {
/** A filter which can be used to exclude entities from the classpath
* based on their name.
*/
@@ -99,75 +101,47 @@ object ClassPath {
*/
def validClassFile(name: String) = endsClass(name) && isValidName(name)
def validPackage(name: String) = (name != "META-INF") && (name != "") && (name.charAt(0) != '.')
- def validSourceFile(name: String) = endsScala(name) || endsJava(name)
+ def validSourceFile(name: String) = endsScalaOrJava(name)
/** From the representation to its identifier.
*/
def toBinaryName(rep: T): String
- /** Create a new classpath based on the abstract file.
- */
- def newClassPath(file: AbstractFile): ClassPath[T]
-
- /** Creators for sub classpaths which preserve this context.
- */
def sourcesInPath(path: String): List[ClassPath[T]] =
for (file <- expandPath(path, expandStar = false) ; dir <- Option(AbstractFile getDirectory file)) yield
new SourcePath[T](dir, this)
-
- def contentsOfDirsInPath(path: String): List[ClassPath[T]] =
- for (dir <- expandPath(path, expandStar = false) ; name <- expandDir(dir) ; entry <- Option(AbstractFile getDirectory name)) yield
- newClassPath(entry)
-
- def classesInExpandedPath(path: String): IndexedSeq[ClassPath[T]] =
- classesInPathImpl(path, expand = true).toIndexedSeq
-
- def classesInPath(path: String) = classesInPathImpl(path, expand = false)
-
- // Internal
- private def classesInPathImpl(path: String, expand: Boolean) =
- for (file <- expandPath(path, expand) ; dir <- Option(AbstractFile getDirectory file)) yield
- newClassPath(dir)
-
- def classesInManifest(used: Boolean) =
- if (used) for (url <- manifests) yield newClassPath(AbstractFile getResources url) else Nil
}
- def manifests = Thread.currentThread().getContextClassLoader().getResources("META-INF/MANIFEST.MF").filter(_.getProtocol() == "jar").toList
+ def manifests: List[java.net.URL] = {
+ import scala.collection.convert.WrapAsScala.enumerationAsScalaIterator
+ Thread.currentThread().getContextClassLoader()
+ .getResources("META-INF/MANIFEST.MF")
+ .filter(_.getProtocol == "jar").toList
+ }
class JavaContext extends ClassPathContext[AbstractFile] {
def toBinaryName(rep: AbstractFile) = {
val name = rep.name
assert(endsClass(name), name)
- name.substring(0, name.length - 6)
+ FileUtils.stripClassExtension(name)
}
+
def newClassPath(dir: AbstractFile) = new DirectoryClassPath(dir, this)
}
object DefaultJavaContext extends JavaContext
- private def endsClass(s: String) = s.length > 6 && s.substring(s.length - 6) == ".class"
- private def endsScala(s: String) = s.length > 6 && s.substring(s.length - 6) == ".scala"
- private def endsJava(s: String) = s.length > 5 && s.substring(s.length - 5) == ".java"
-
/** From the source file to its identifier.
*/
- def toSourceName(f: AbstractFile): String = {
- val name = f.name
-
- if (endsScala(name)) name.substring(0, name.length - 6)
- else if (endsJava(name)) name.substring(0, name.length - 5)
- else throw new FatalError("Unexpected source file ending: " + name)
- }
+ def toSourceName(f: AbstractFile): String = FileUtils.stripSourceExtension(f.name)
}
+
import ClassPath._
/**
* Represents a package which contains classes and other packages
*/
-abstract class ClassPath[T] {
- type AnyClassRep = ClassPath[T]#ClassRep
-
+abstract class ClassPath[T] extends ClassFileLookup[T] {
/**
* The short name of the package (without prefix)
*/
@@ -179,21 +153,13 @@ abstract class ClassPath[T] {
*/
def origin: Option[String] = None
- /** A list of URLs representing this classpath.
- */
- def asURLs: List[URL]
-
- /** The whole classpath in the form of one String.
- */
- def asClasspathString: String
-
/** Info which should be propagated to any sub-classpaths.
*/
def context: ClassPathContext[T]
/** Lists of entities.
*/
- def classes: IndexedSeq[AnyClassRep]
+ def classes: IndexedSeq[ClassRepresentation[T]]
def packages: IndexedSeq[ClassPath[T]]
def sourcepaths: IndexedSeq[AbstractFile]
@@ -217,7 +183,7 @@ abstract class ClassPath[T] {
/**
* Represents classes which can be loaded with a ClassfileLoader and/or SourcefileLoader.
*/
- case class ClassRep(binary: Option[T], source: Option[AbstractFile]) {
+ case class ClassRep(binary: Option[T], source: Option[AbstractFile]) extends ClassRepresentation[T] {
def name: String = binary match {
case Some(x) => context.toBinaryName(x)
case _ =>
@@ -236,25 +202,27 @@ abstract class ClassPath[T] {
* Find a ClassRep given a class name of the form "package.subpackage.ClassName".
* Does not support nested classes on .NET
*/
- def findClass(name: String): Option[AnyClassRep] =
+ override def findClass(name: String): Option[ClassRepresentation[T]] =
splitWhere(name, _ == '.', doDropIndex = true) match {
case Some((pkg, rest)) =>
val rep = packages find (_.name == pkg) flatMap (_ findClass rest)
rep map {
- case x: ClassRep => x
+ case x: ClassRepresentation[T] => x
case x => throw new FatalError("Unexpected ClassRep '%s' found searching for name '%s'".format(x, name))
}
case _ =>
classes find (_.name == name)
}
- def findClassFile(name: String): Option[AbstractFile] =
+ override def findClassFile(name: String): Option[AbstractFile] =
findClass(name) match {
- case Some(ClassRep(Some(x: AbstractFile), _)) => Some(x)
+ case Some(ClassRepresentation(Some(x: AbstractFile), _)) => Some(x)
case _ => None
}
- def sortString = join(split(asClasspathString).sorted: _*)
+ override def asSourcePathString: String = sourcepaths.mkString(pathSeparator)
+
+ def sortString = join(split(asClassPathString).sorted: _*)
override def equals(that: Any) = that match {
case x: ClassPath[_] => this.sortString == x.sortString
case _ => false
@@ -266,10 +234,12 @@ abstract class ClassPath[T] {
* A Classpath containing source files
*/
class SourcePath[T](dir: AbstractFile, val context: ClassPathContext[T]) extends ClassPath[T] {
+ import FileUtils.AbstractFileOps
+
def name = dir.name
override def origin = dir.underlyingSource map (_.path)
- def asURLs = if (dir.file == null) Nil else List(dir.toURL)
- def asClasspathString = dir.path
+ def asURLs = dir.toURLs()
+ def asClassPathString = dir.path
val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq(dir)
private def traverse() = {
@@ -292,10 +262,12 @@ class SourcePath[T](dir: AbstractFile, val context: ClassPathContext[T]) extends
* A directory (or a .jar file) containing classfiles and packages
*/
class DirectoryClassPath(val dir: AbstractFile, val context: ClassPathContext[AbstractFile]) extends ClassPath[AbstractFile] {
+ import FileUtils.AbstractFileOps
+
def name = dir.name
override def origin = dir.underlyingSource map (_.path)
- def asURLs = if (dir.file == null) List(new URL(name)) else List(dir.toURL)
- def asClasspathString = dir.path
+ def asURLs = dir.toURLs(default = Seq(new URL(name)))
+ def asClassPathString = dir.path
val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq()
// calculates (packages, classes) in one traversal.
@@ -342,6 +314,7 @@ class MergedClassPath[T](
override val entries: IndexedSeq[ClassPath[T]],
val context: ClassPathContext[T])
extends ClassPath[T] {
+
def this(entries: TraversableOnce[ClassPath[T]], context: ClassPathContext[T]) =
this(entries.toIndexedSeq, context)
@@ -350,12 +323,12 @@ extends ClassPath[T] {
lazy val sourcepaths: IndexedSeq[AbstractFile] = entries flatMap (_.sourcepaths)
override def origin = Some(entries map (x => x.origin getOrElse x.name) mkString ("Merged(", ", ", ")"))
- override def asClasspathString: String = join(entries map (_.asClasspathString) : _*)
+ override def asClassPathString: String = join(entries map (_.asClassPathString) : _*)
- lazy val classes: IndexedSeq[AnyClassRep] = {
+ lazy val classes: IndexedSeq[ClassRepresentation[T]] = {
var count = 0
val indices = mutable.HashMap[String, Int]()
- val cls = new mutable.ArrayBuffer[AnyClassRep](1024)
+ val cls = new mutable.ArrayBuffer[ClassRepresentation[T]](1024)
for (e <- entries; c <- e.classes) {
val name = c.name
@@ -364,9 +337,9 @@ extends ClassPath[T] {
val existing = cls(idx)
if (existing.binary.isEmpty && c.binary.isDefined)
- cls(idx) = existing.copy(binary = c.binary)
+ cls(idx) = ClassRep(binary = c.binary, source = existing.source)
if (existing.source.isEmpty && c.source.isDefined)
- cls(idx) = existing.copy(source = c.source)
+ cls(idx) = ClassRep(binary = existing.binary, source = c.source)
}
else {
indices(name) = count
@@ -404,10 +377,12 @@ extends ClassPath[T] {
}
new MergedClassPath[T](newEntries, context)
}
+
def show() {
println("ClassPath %s has %d entries and results in:\n".format(name, entries.size))
- asClasspathString split ':' foreach (x => println(" " + x))
+ asClassPathString split ':' foreach (x => println(" " + x))
}
+
override def toString() = "merged classpath "+ entries.mkString("(", "\n", ")")
}
diff --git a/src/compiler/scala/tools/reflect/ReflectMain.scala b/src/compiler/scala/tools/reflect/ReflectMain.scala
index 3ae21b6b98..8d8418945a 100644
--- a/src/compiler/scala/tools/reflect/ReflectMain.scala
+++ b/src/compiler/scala/tools/reflect/ReflectMain.scala
@@ -1,17 +1,17 @@
package scala.tools
package reflect
+import scala.reflect.internal.util.ScalaClassLoader
import scala.tools.nsc.Driver
import scala.tools.nsc.Global
import scala.tools.nsc.Settings
-import scala.tools.nsc.util.ScalaClassLoader
-import scala.tools.util.PathResolver
+import scala.tools.util.PathResolverFactory
object ReflectMain extends Driver {
private def classloaderFromSettings(settings: Settings) = {
- val classpath = new PathResolver(settings).result
- ScalaClassLoader.fromURLs(classpath.asURLs, getClass.getClassLoader)
+ val classPathURLs = PathResolverFactory.create(settings).resultAsURLs
+ ScalaClassLoader.fromURLs(classPathURLs, getClass.getClassLoader)
}
override def newCompiler(): Global = new ReflectGlobal(settings, reporter, classloaderFromSettings(settings))
diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala
index 5526660509..8e5b1e0a5c 100644
--- a/src/compiler/scala/tools/util/PathResolver.scala
+++ b/src/compiler/scala/tools/util/PathResolver.scala
@@ -7,14 +7,17 @@ package scala
package tools
package util
+import java.net.URL
import scala.tools.reflect.WrappedProperties.AccessControl
-import scala.tools.nsc.{ Settings }
-import scala.tools.nsc.util.{ ClassPath, JavaClassPath }
+import scala.tools.nsc.Settings
+import scala.tools.nsc.util.{ ClassFileLookup, ClassPath, JavaClassPath }
import scala.reflect.io.{ File, Directory, Path, AbstractFile }
import scala.reflect.runtime.ReflectionUtils
import ClassPath.{ JavaContext, DefaultJavaContext, join, split }
import PartialFunction.condOpt
import scala.language.postfixOps
+import scala.tools.nsc.classpath.{ AggregateFlatClassPath, ClassPathFactory, FlatClassPath, FlatClassPathFactory }
+import scala.tools.nsc.settings.ClassPathRepresentationType
// Loosely based on the draft specification at:
// https://wiki.scala-lang.org/display/SIW/Classpath
@@ -48,9 +51,8 @@ object PathResolver {
/** Values found solely by inspecting environment or property variables.
*/
object Environment {
- private def searchForBootClasspath = (
+ private def searchForBootClasspath =
systemProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse ""
- )
/** Environment variables which java pays attention to so it
* seems we do as well.
@@ -104,7 +106,7 @@ object PathResolver {
else if (scalaLibAsDir.isDirectory) scalaLibAsDir.path
else ""
- // XXX It must be time for someone to figure out what all these things
+ // TODO It must be time for someone to figure out what all these things
// are intended to do. This is disabled here because it was causing all
// the scala jars to end up on the classpath twice: one on the boot
// classpath as set up by the runner (or regular classpath under -nobootcp)
@@ -170,39 +172,48 @@ object PathResolver {
!ReflectionUtils.scalacShouldntLoadClassfile(name)
}
- // called from scalap
+ @deprecated("This method is no longer used be scalap and will be deleted", "2.11.5")
def fromPathString(path: String, context: JavaContext = DefaultJavaContext): JavaClassPath = {
val s = new Settings()
s.classpath.value = path
- new PathResolver(s, context) result
+ new PathResolver(s, context).result
}
/** With no arguments, show the interesting values in Environment and Defaults.
* If there are arguments, show those in Calculated as if those options had been
* given to a scala runner.
*/
- def main(args: Array[String]): Unit = {
+ def main(args: Array[String]): Unit =
if (args.isEmpty) {
println(Environment)
println(Defaults)
- }
- else {
+ } else {
val settings = new Settings()
val rest = settings.processArguments(args.toList, processAll = false)._2
- val pr = new PathResolver(settings)
- println(" COMMAND: 'scala %s'".format(args.mkString(" ")))
+ val pr = PathResolverFactory.create(settings)
+ println("COMMAND: 'scala %s'".format(args.mkString(" ")))
println("RESIDUAL: 'scala %s'\n".format(rest.mkString(" ")))
- pr.result.show()
+
+ pr.result match {
+ case cp: JavaClassPath =>
+ cp.show()
+ case cp: AggregateFlatClassPath =>
+ println(s"ClassPath has ${cp.aggregates.size} entries and results in:\n${cp.asClassPathStrings}")
+ }
}
- }
}
-class PathResolver(settings: Settings, context: JavaContext) {
- import PathResolver.{ Defaults, Environment, AsLines, MkLines, ppcp }
+trait PathResolverResult {
+ def result: ClassFileLookup[AbstractFile]
- def this(settings: Settings) = this(settings,
- if (settings.YnoLoadImplClass) PathResolver.NoImplClassJavaContext
- else DefaultJavaContext)
+ def resultAsURLs: Seq[URL] = result.asURLs
+}
+
+abstract class PathResolverBase[BaseClassPathType <: ClassFileLookup[AbstractFile], ResultClassPathType <: BaseClassPathType]
+(settings: Settings, classPathFactory: ClassPathFactory[BaseClassPathType])
+ extends PathResolverResult {
+
+ import PathResolver.{ AsLines, Defaults, ppcp }
private def cmdLineOrElse(name: String, alt: String) = {
(commandLineFor(name) match {
@@ -232,6 +243,7 @@ class PathResolver(settings: Settings, context: JavaContext) {
def javaUserClassPath = if (useJavaClassPath) Defaults.javaUserClassPath else ""
def scalaBootClassPath = cmdLineOrElse("bootclasspath", Defaults.scalaBootClassPath)
def scalaExtDirs = cmdLineOrElse("extdirs", Defaults.scalaExtDirs)
+
/** Scaladoc doesn't need any bootstrapping, otherwise will create errors such as:
* [scaladoc] ../scala-trunk/src/reflect/scala/reflect/macros/Reifiers.scala:89: error: object api is not a member of package reflect
* [scaladoc] case class ReificationException(val pos: reflect.api.PositionApi, val msg: String) extends Throwable(msg)
@@ -250,16 +262,14 @@ class PathResolver(settings: Settings, context: JavaContext) {
* - Otherwise, if CLASSPATH is set, it is that
* - If neither of those, then "." is used.
*/
- def userClassPath = (
- if (!settings.classpath.isDefault)
- settings.classpath.value
+ def userClassPath =
+ if (!settings.classpath.isDefault) settings.classpath.value
else sys.env.getOrElse("CLASSPATH", ".")
- )
- import context._
+ import classPathFactory._
// Assemble the elements!
- def basis = List[Traversable[ClassPath[AbstractFile]]](
+ def basis = List[Traversable[BaseClassPathType]](
classesInPath(javaBootClassPath), // 1. The Java bootstrap class path.
contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path.
classesInExpandedPath(javaUserClassPath), // 3. The Java application class path.
@@ -278,7 +288,7 @@ class PathResolver(settings: Settings, context: JavaContext) {
| javaBootClassPath = ${ppcp(javaBootClassPath)}
| javaExtDirs = ${ppcp(javaExtDirs)}
| javaUserClassPath = ${ppcp(javaUserClassPath)}
- | useJavaClassPath = $useJavaClassPath
+ | useJavaClassPath = $useJavaClassPath
| scalaBootClassPath = ${ppcp(scalaBootClassPath)}
| scalaExtDirs = ${ppcp(scalaExtDirs)}
| userClassPath = ${ppcp(userClassPath)}
@@ -288,8 +298,10 @@ class PathResolver(settings: Settings, context: JavaContext) {
def containers = Calculated.containers
- lazy val result = {
- val cp = new JavaClassPath(containers.toIndexedSeq, context)
+ import PathResolver.MkLines
+
+ def result: ResultClassPathType = {
+ val cp = computeResult()
if (settings.Ylogcp) {
Console print f"Classpath built from ${settings.toConciseString} %n"
Console print s"Defaults: ${PathResolver.Defaults}"
@@ -301,5 +313,37 @@ class PathResolver(settings: Settings, context: JavaContext) {
cp
}
- def asURLs = result.asURLs
+ @deprecated("Use resultAsURLs instead of this one", "2.11.5")
+ def asURLs: List[URL] = resultAsURLs.toList
+
+ protected def computeResult(): ResultClassPathType
+}
+
+class PathResolver(settings: Settings, context: JavaContext)
+ extends PathResolverBase[ClassPath[AbstractFile], JavaClassPath](settings, context) {
+
+ def this(settings: Settings) =
+ this(settings,
+ if (settings.YnoLoadImplClass) PathResolver.NoImplClassJavaContext
+ else DefaultJavaContext)
+
+ override protected def computeResult(): JavaClassPath =
+ new JavaClassPath(containers.toIndexedSeq, context)
+}
+
+class FlatClassPathResolver(settings: Settings, flatClassPathFactory: ClassPathFactory[FlatClassPath])
+ extends PathResolverBase[FlatClassPath, AggregateFlatClassPath](settings, flatClassPathFactory) {
+
+ def this(settings: Settings) = this(settings, new FlatClassPathFactory(settings))
+
+ override protected def computeResult(): AggregateFlatClassPath = AggregateFlatClassPath(containers.toIndexedSeq)
+}
+
+object PathResolverFactory {
+
+ def create(settings: Settings): PathResolverResult =
+ settings.YclasspathImpl.value match {
+ case ClassPathRepresentationType.Flat => new FlatClassPathResolver(settings)
+ case ClassPathRepresentationType.Recursive => new PathResolver(settings)
+ }
}
diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala
index 7df809b6ff..5d00141e6a 100644
--- a/src/interactive/scala/tools/nsc/interactive/Global.scala
+++ b/src/interactive/scala/tools/nsc/interactive/Global.scala
@@ -128,8 +128,8 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
else NullLogger
import log.logreplay
- debugLog("logger: " + log.getClass + " writing to " + (new java.io.File(logName)).getAbsolutePath)
- debugLog("classpath: "+classPath)
+ debugLog(s"logger: ${log.getClass} writing to ${(new java.io.File(logName)).getAbsolutePath}")
+ debugLog(s"classpath: $classPath")
private var curTime = System.nanoTime
private def timeStep = {
diff --git a/src/library/scala/collection/JavaConverters.scala b/src/library/scala/collection/JavaConverters.scala
index a4fa58b13c..875f6e1c02 100755
--- a/src/library/scala/collection/JavaConverters.scala
+++ b/src/library/scala/collection/JavaConverters.scala
@@ -37,8 +37,8 @@ import convert._
* val sl2 : scala.collection.mutable.Buffer[Int] = jl.asScala
* assert(sl eq sl2)
* }}}
- * The following conversions also are supported, but the
- * direction Scala to Java is done my a more specifically named method:
+ * The following conversions are also supported, but the
+ * direction from Scala to Java is done by the more specifically named methods:
* `asJavaCollection`, `asJavaEnumeration`, `asJavaDictionary`.
*
* - `scala.collection.Iterable` <=> `java.util.Collection`
diff --git a/src/library/scala/collection/LinearSeq.scala b/src/library/scala/collection/LinearSeq.scala
index 49fbb902ab..5a7bb5891e 100644
--- a/src/library/scala/collection/LinearSeq.scala
+++ b/src/library/scala/collection/LinearSeq.scala
@@ -15,7 +15,14 @@ import generic._
import mutable.Builder
/** A base trait for linear sequences.
+ *
* $linearSeqInfo
+ *
+ * @define linearSeqInfo
+ * Linear sequences have reasonably efficient `head`, `tail`, and `isEmpty` methods.
+ * If these methods provide the fastest way to traverse the collection, a
+ * collection `Coll` that extends this trait should also extend
+ * `LinearSeqOptimized[A, Coll[A]]`.
*/
trait LinearSeq[+A] extends Seq[A]
with GenericTraversableTemplate[A, LinearSeq]
diff --git a/src/library/scala/collection/LinearSeqLike.scala b/src/library/scala/collection/LinearSeqLike.scala
index ff7985bf0d..96e2135fd1 100644
--- a/src/library/scala/collection/LinearSeqLike.scala
+++ b/src/library/scala/collection/LinearSeqLike.scala
@@ -14,22 +14,10 @@ import scala.annotation.tailrec
/** A template trait for linear sequences of type `LinearSeq[A]`.
*
- * $linearSeqInfo
- *
- * This trait just implements `iterator` in terms of `isEmpty, ``head`, and `tail`.
- * However, see `LinearSeqOptimized` for an implementation trait that overrides operations
+ * This trait just implements `iterator` and `corresponds` in terms of `isEmpty, ``head`, and `tail`.
+ * However, see `LinearSeqOptimized` for an implementation trait that overrides many more operations
* to make them run faster under the assumption of fast linear access with `head` and `tail`.
*
- * @define linearSeqInfo
- * Linear sequences are defined in terms of three abstract methods, which are assumed
- * to have efficient implementations. These are:
- * {{{
- * def isEmpty: Boolean
- * def head: A
- * def tail: Repr
- * }}}
- * Here, `A` is the type of the sequence elements and `Repr` is the type of the sequence itself.
- *
* Linear sequences do not add any new methods to `Seq`, but promise efficient implementations
* of linear access patterns.
* @author Martin Odersky
@@ -58,12 +46,18 @@ trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]] extends SeqLike[A, Repr
val result = these.head; these = these.tail; result
} else Iterator.empty.next()
- /** Have to clear `these` so the iterator is exhausted like
- * it would be without the optimization.
- */
override def toList: List[A] = {
+ /* Have to clear `these` so the iterator is exhausted like
+ * it would be without the optimization.
+ *
+ * Calling "newBuilder.result()" in toList method
+ * prevents original seq from garbage collection,
+ * so we use these.take(0) here.
+ *
+ * Check SI-8924 for details
+ */
val xs = these.toList
- these = newBuilder.result()
+ these = these.take(0)
xs
}
}
diff --git a/src/library/scala/collection/LinearSeqOptimized.scala b/src/library/scala/collection/LinearSeqOptimized.scala
index a28d796d5b..64248aa755 100755
--- a/src/library/scala/collection/LinearSeqOptimized.scala
+++ b/src/library/scala/collection/LinearSeqOptimized.scala
@@ -13,10 +13,24 @@ import mutable.ListBuffer
import immutable.List
import scala.annotation.tailrec
-/** A template trait for linear sequences of type `LinearSeq[A]` which optimizes
- * the implementation of several methods under the assumption of fast linear access.
+/** A template trait for linear sequences of type `LinearSeq[A]` which optimizes
+ * the implementation of various methods under the assumption of fast linear access.
+ *
+ * $linearSeqOptim
+ *
+ * @define linearSeqOptim
+ * Linear-optimized sequences implement most operations in in terms of three methods,
+ * which are assumed to have efficient implementations. These are:
+ * {{{
+ * def isEmpty: Boolean
+ * def head: A
+ * def tail: Repr
+ * }}}
+ * Here, `A` is the type of the sequence elements and `Repr` is the type of the sequence itself.
+ * Note that default implementations are provided via inheritance, but these
+ * should be overridden for performance.
+ *
*
- * $linearSeqInfo
*/
trait LinearSeqOptimized[+A, +Repr <: LinearSeqOptimized[A, Repr]] extends LinearSeqLike[A, Repr] { self: Repr =>
@@ -235,13 +249,16 @@ trait LinearSeqOptimized[+A, +Repr <: LinearSeqOptimized[A, Repr]] extends Linea
override /*IterableLike*/
def sameElements[B >: A](that: GenIterable[B]): Boolean = that match {
case that1: LinearSeq[_] =>
- var these = this
- var those = that1
- while (!these.isEmpty && !those.isEmpty && these.head == those.head) {
- these = these.tail
- those = those.tail
+ // Probably immutable, so check reference identity first (it's quick anyway)
+ (this eq that1) || {
+ var these = this
+ var those = that1
+ while (!these.isEmpty && !those.isEmpty && these.head == those.head) {
+ these = these.tail
+ those = those.tail
+ }
+ these.isEmpty && those.isEmpty
}
- these.isEmpty && those.isEmpty
case _ =>
super.sameElements(that)
}
diff --git a/src/library/scala/collection/SeqLike.scala b/src/library/scala/collection/SeqLike.scala
index fdfb1f2efc..329273df5b 100644
--- a/src/library/scala/collection/SeqLike.scala
+++ b/src/library/scala/collection/SeqLike.scala
@@ -140,7 +140,15 @@ trait SeqLike[+A, +Repr] extends Any with IterableLike[A, Repr] with GenSeqLike[
if (isEmpty) Iterator(repr)
else new PermutationsItr
- /** Iterates over combinations.
+ /** Iterates over combinations. A _combination_ of length `n` is a subsequence of
+ * the original sequence, with the elements taken in order. Thus, `"xy"` and `"yy"`
+ * are both length-2 combinations of `"xyy"`, but `"yx"` is not. If there is
+ * more than one way to generate the same subsequence, only one will be returned.
+ *
+ * For example, `"xyyy"` has three different ways to generate `"xy"` depending on
+ * whether the first, second, or third `"y"` is selected. However, since all are
+ * identical, only one will be chosen. Which of the three will be taken is an
+ * implementation detail that is not defined.
*
* @return An Iterator which traverses the possible n-element combinations of this $coll.
* @example `"abbbc".combinations(2) = Iterator(ab, ac, bb, bc)`
diff --git a/src/library/scala/collection/SeqViewLike.scala b/src/library/scala/collection/SeqViewLike.scala
index e719f19c78..ef6d2272cb 100644
--- a/src/library/scala/collection/SeqViewLike.scala
+++ b/src/library/scala/collection/SeqViewLike.scala
@@ -83,6 +83,7 @@ trait SeqViewLike[+A,
}
def length = index(self.length)
def apply(idx: Int) = {
+ if (idx < 0 || idx >= self.length) throw new IndexOutOfBoundsException(idx.toString)
val row = findRow(idx, 0, self.length - 1)
mapping(self(row)).seq.toSeq(idx - index(row))
}
diff --git a/src/library/scala/collection/immutable/PagedSeq.scala b/src/library/scala/collection/immutable/PagedSeq.scala
index 3a64820be6..f11217d26a 100644
--- a/src/library/scala/collection/immutable/PagedSeq.scala
+++ b/src/library/scala/collection/immutable/PagedSeq.scala
@@ -158,7 +158,7 @@ extends scala.collection.AbstractSeq[T]
* @note Calling this method will force the entire sequence to be read.
*/
def length: Int = {
- while (!latest.isLast) addMore()
+ while (!latest.isLast && latest.end < end) addMore()
(latest.end min end) - start
}
@@ -175,7 +175,8 @@ extends scala.collection.AbstractSeq[T]
*/
override def isDefinedAt(index: Int) =
index >= 0 && index < end - start && {
- val p = page(index + start); index + start < p.end
+ val absidx = index + start
+ absidx >= 0 && absidx < page(absidx).end
}
/** The subsequence from index `start` up to `end -1` if `end`
@@ -192,6 +193,9 @@ extends scala.collection.AbstractSeq[T]
if (f.next eq null) f.addMore(more)
f = f.next
}
+ // Warning -- not refining `more` means that slices can freely request and obtain
+ // data outside of their slice. This is part of the design of PagedSeq
+ // (to read pages!) but can be surprising.
new PagedSeq(more, f, s, e)
}
diff --git a/src/library/scala/collection/mutable/MultiMap.scala b/src/library/scala/collection/mutable/MultiMap.scala
index 78dfc35268..ac2ebf31d8 100644
--- a/src/library/scala/collection/mutable/MultiMap.scala
+++ b/src/library/scala/collection/mutable/MultiMap.scala
@@ -65,10 +65,9 @@ trait MultiMap[A, B] extends Map[A, Set[B]] {
*/
protected def makeSet: Set[B] = new HashSet[B]
- /** Assigns the specified `value` to a specified `key`, replacing
- * the existing value assigned to that `key` if it is equal to
- * the specified value. Otherwise, simply adds another binding to
- * the `key`.
+ /** Assigns the specified `value` to a specified `key`. If the key
+ * already has a binding to equal to `value`, nothing is changed;
+ * otherwise a new binding is added for that `key`.
*
* @param key The key to which to bind the new value.
* @param value The value to bind to the key.
diff --git a/src/library/scala/collection/mutable/PriorityQueue.scala b/src/library/scala/collection/mutable/PriorityQueue.scala
index 4a9a5d4008..d3c4161e3b 100644
--- a/src/library/scala/collection/mutable/PriorityQueue.scala
+++ b/src/library/scala/collection/mutable/PriorityQueue.scala
@@ -247,13 +247,6 @@ class PriorityQueue[A](implicit val ord: Ordering[A])
* @return a priority queue with the same elements.
*/
override def clone(): PriorityQueue[A] = new PriorityQueue[A] ++= this.iterator
-
- // def printstate() {
- // println("-----------------------")
- // println("Size: " + resarr.p_size0)
- // println("Internal array: " + resarr.p_array.toList)
- // println(toString)
- // }
}
diff --git a/src/library/scala/math/BigDecimal.scala b/src/library/scala/math/BigDecimal.scala
index 5a81710986..74a174ea74 100644
--- a/src/library/scala/math/BigDecimal.scala
+++ b/src/library/scala/math/BigDecimal.scala
@@ -417,7 +417,7 @@ extends ScalaNumber with ScalaNumericConversions with Serializable {
private final def computeHashCode(): Unit = {
computedHashCode =
if (isWhole && (precision - scale) < BigDecimal.maximumHashScale) toBigInt.hashCode
- else if (isValidDouble) doubleValue.##
+ else if (isDecimalDouble) doubleValue.##
else {
val temp = bigDecimal.stripTrailingZeros
scala.util.hashing.MurmurHash3.mixLast( temp.scaleByPowerOfTen(temp.scale).toBigInteger.hashCode, temp.scale )
@@ -477,7 +477,7 @@ extends ScalaNumber with ScalaNumericConversions with Serializable {
* `isExactDouble`, `isBinaryDouble`, or `isDecimalDouble`, depending on the intended meaning.
* By default, `decimal` creation is used, so `isDecimalDouble` is probably what you want.
*/
- @deprecated("Validity has two distinct meanings. Use `isExactBinaryDouble` or `equivalentToDouble` instead.", "2.11")
+ @deprecated("Validity has distinct meanings. Use `isExactDouble`, `isBinaryDouble`, or `isDecimalDouble` instead.", "2.11")
def isValidDouble = {
val d = toDouble
!d.isInfinity && bigDecimal.compareTo(new BigDec(d)) == 0
diff --git a/src/partest-extras/scala/tools/partest/BytecodeTest.scala b/src/partest-extras/scala/tools/partest/BytecodeTest.scala
index 3261cada37..37ef4684ef 100644
--- a/src/partest-extras/scala/tools/partest/BytecodeTest.scala
+++ b/src/partest-extras/scala/tools/partest/BytecodeTest.scala
@@ -116,10 +116,8 @@ abstract class BytecodeTest {
sys.error(s"Didn't find method '$name' in class '${classNode.name}'")
protected def loadClassNode(name: String, skipDebugInfo: Boolean = true): ClassNode = {
- val classBytes: InputStream = (for {
- classRep <- classpath.findClass(name)
- binary <- classRep.binary
- } yield binary.input) getOrElse sys.error(s"failed to load class '$name'; classpath = $classpath")
+ val classBytes: InputStream = classpath.findClassFile(name).map(_.input)
+ .getOrElse(sys.error(s"failed to load class '$name'; classpath = $classpath"))
val cr = new ClassReader(classBytes)
val cn = new ClassNode()
diff --git a/src/reflect/scala/reflect/api/Constants.scala b/src/reflect/scala/reflect/api/Constants.scala
index e73c5ffa91..fbcf7f3e4f 100644
--- a/src/reflect/scala/reflect/api/Constants.scala
+++ b/src/reflect/scala/reflect/api/Constants.scala
@@ -60,7 +60,7 @@ package api
*
* object Test extends App {
* val jann = typeOf[JavaAnnottee].typeSymbol.annotations(0).javaArgs
- * def jarg(name: String) = jann(newTermName(name)).asInstanceOf[LiteralArgument].value
+ * def jarg(name: String) = jann(TermName(name)).asInstanceOf[LiteralArgument].value
*
* val classRef = jarg("classRef").typeValue
* println(showRaw(classRef)) // TypeRef(ThisType(<empty>), JavaAnnottee, List())
@@ -150,7 +150,7 @@ trait Constants {
*
* object Test extends App {
* val jann = typeOf[JavaAnnottee].typeSymbol.annotations(0).javaArgs
- * def jarg(name: String) = jann(newTermName(name)) match {
+ * def jarg(name: String) = jann(TermName(name)) match {
* // Constant is always wrapped into a Literal or LiteralArgument tree node
* case LiteralArgument(ct: Constant) => value
* case _ => sys.error("Not a constant")
diff --git a/src/reflect/scala/reflect/api/Exprs.scala b/src/reflect/scala/reflect/api/Exprs.scala
index 3230fdbc67..ad03718898 100644
--- a/src/reflect/scala/reflect/api/Exprs.scala
+++ b/src/reflect/scala/reflect/api/Exprs.scala
@@ -84,7 +84,7 @@ trait Exprs { self: Universe =>
*
* It is equivalent to
* {{{
- * Select( expr.tree, newTermName("foo") )
+ * Select( expr.tree, TermName("foo") )
* }}}
*
* The following example code however does not compile
diff --git a/src/reflect/scala/reflect/api/FlagSets.scala b/src/reflect/scala/reflect/api/FlagSets.scala
index bf4d6353df..bcad84a3f0 100644
--- a/src/reflect/scala/reflect/api/FlagSets.scala
+++ b/src/reflect/scala/reflect/api/FlagSets.scala
@@ -20,20 +20,20 @@ import scala.language.implicitConversions
*
* For example, to create a class named `C` one would write something like:
* {{{
- * ClassDef(Modifiers(NoFlags), newTypeName("C"), Nil, ...)
+ * ClassDef(Modifiers(NoFlags), TypeName("C"), Nil, ...)
* }}}
*
* Here, the flag set is empty.
*
* To make `C` private, one would write something like:
* {{{
- * ClassDef(Modifiers(PRIVATE), newTypeName("C"), Nil, ...)
+ * ClassDef(Modifiers(PRIVATE), TypeName("C"), Nil, ...)
* }}}
*
* Flags can also be combined with the vertical bar operator (`|`).
* For example, a private final class is written something like:
* {{{
- * ClassDef(Modifiers(PRIVATE | FINAL), newTypeName("C"), Nil, ...)
+ * ClassDef(Modifiers(PRIVATE | FINAL), TypeName("C"), Nil, ...)
* }}}
*
* The list of all available flags is defined in [[scala.reflect.api.FlagSets#FlagValues]], available via
diff --git a/src/reflect/scala/reflect/api/Mirror.scala b/src/reflect/scala/reflect/api/Mirror.scala
index 318fdb369a..96aab48e75 100644
--- a/src/reflect/scala/reflect/api/Mirror.scala
+++ b/src/reflect/scala/reflect/api/Mirror.scala
@@ -58,7 +58,7 @@ abstract class Mirror[U <: Universe with Singleton] {
* scala> cm.staticPackage("scala")
* res2: scala.reflect.runtime.universe.ModuleSymbol = package scala
*
- * scala> res2.moduleClass.info member newTypeName("List")
+ * scala> res2.moduleClass.info member TypeName("List")
* res3: scala.reflect.runtime.universe.Symbol = type List
*
* scala> res3.fullName
diff --git a/src/reflect/scala/reflect/api/Mirrors.scala b/src/reflect/scala/reflect/api/Mirrors.scala
index ec420d184c..773c6b6fb4 100644
--- a/src/reflect/scala/reflect/api/Mirrors.scala
+++ b/src/reflect/scala/reflect/api/Mirrors.scala
@@ -292,7 +292,7 @@ trait Mirrors { self: Universe =>
* that can be used to create instances of the class, inspect its companion object or perform further reflections.
*
* To get a class symbol by the name of the class you would like to reflect,
- * use `<this mirror>.symbol.info.member(newTypeName(<name of the class>)).asClass`.
+ * use `<this mirror>.symbol.info.member(TypeName(<name of the class>)).asClass`.
* For further information about member lookup refer to `Symbol.info`.
*
* The input symbol can be either private or non-private (Scala reflection transparently deals with visibility).
diff --git a/src/reflect/scala/reflect/api/Names.scala b/src/reflect/scala/reflect/api/Names.scala
index fe5f47c25d..03f7b2d218 100644
--- a/src/reflect/scala/reflect/api/Names.scala
+++ b/src/reflect/scala/reflect/api/Names.scala
@@ -17,11 +17,11 @@ import scala.language.implicitConversions
* To search for the `map` method (which is a term) declared in the `List` class, one can do:
*
* {{{
- * scala> typeOf[List[_]].member(newTermName("map"))
+ * scala> typeOf[List[_]].member(TermName("map"))
* res0: reflect.runtime.universe.Symbol = method map
* }}}
*
- * To search for a type member, one can follow the same procedure, using `newTypeName` instead.
+ * To search for a type member, one can follow the same procedure, using `TypeName` instead.
*
* For more information about creating and using `Name`s, see the [[http://docs.scala-lang.org/overviews/reflection/annotations-names-scopes.html Reflection Guide: Annotations, Names, Scopes, and More]]
*
@@ -30,14 +30,14 @@ import scala.language.implicitConversions
*/
trait Names {
/** An implicit conversion from String to TermName.
- * Enables an alternative notation `"map": TermName` as opposed to `newTermName("map")`.
+ * Enables an alternative notation `"map": TermName` as opposed to `TermName("map")`.
* @group Names
*/
@deprecated("Use explicit `TermName(s)` instead", "2.11.0")
implicit def stringToTermName(s: String): TermName = TermName(s)
/** An implicit conversion from String to TypeName.
- * Enables an alternative notation `"List": TypeName` as opposed to `newTypeName("List")`.
+ * Enables an alternative notation `"List": TypeName` as opposed to `TypeName("List")`.
* @group Names
*/
@deprecated("Use explicit `TypeName(s)` instead", "2.11.0")
diff --git a/src/reflect/scala/reflect/api/Printers.scala b/src/reflect/scala/reflect/api/Printers.scala
index 92ae6d8b44..01b9759c70 100644
--- a/src/reflect/scala/reflect/api/Printers.scala
+++ b/src/reflect/scala/reflect/api/Printers.scala
@@ -46,15 +46,15 @@ import java.io.{ PrintWriter, StringWriter }
* {{{
* scala> showRaw(tree)
* res1: String = Block(List(
- * ClassDef(Modifiers(FINAL), newTypeName("C"), List(), Template(
- * List(Ident(newTypeName("AnyRef"))),
+ * ClassDef(Modifiers(FINAL), TypeName("C"), List(), Template(
+ * List(Ident(TypeName("AnyRef"))),
* noSelfType,
* List(
* DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(),
* Block(List(
* Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())),
* Literal(Constant(())))),
- * DefDef(Modifiers(), newTermName("x"), List(), List(), TypeTree(),
+ * DefDef(Modifiers(), TermName("x"), List(), List(), TypeTree(),
* Literal(Constant(2))))))),
* Literal(Constant(())))
* }}}
@@ -70,23 +70,23 @@ import java.io.{ PrintWriter, StringWriter }
*
* scala> showRaw(cm.mkToolBox().typecheck(tree), printTypes = true)
* res2: String = Block[1](List(
- * ClassDef[2](Modifiers(FINAL), newTypeName("C"), List(), Template[3](
- * List(Ident[4](newTypeName("AnyRef"))),
+ * ClassDef[2](Modifiers(FINAL), TypeName("C"), List(), Template[3](
+ * List(Ident[4](TypeName("AnyRef"))),
* noSelfType,
* List(
* DefDef[2](Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree[3](),
* Block[1](List(
- * Apply[4](Select[5](Super[6](This[3](newTypeName("C")), tpnme.EMPTY), ...))),
+ * Apply[4](Select[5](Super[6](This[3](TypeName("C")), tpnme.EMPTY), ...))),
* Literal[1](Constant(())))),
- * DefDef[2](Modifiers(), newTermName("x"), List(), List(), TypeTree[7](),
+ * DefDef[2](Modifiers(), TermName("x"), List(), List(), TypeTree[7](),
* Literal[8](Constant(2))))))),
* Literal[1](Constant(())))
* [1] TypeRef(ThisType(scala), scala.Unit, List())
* [2] NoType
- * [3] TypeRef(NoPrefix, newTypeName("C"), List())
+ * [3] TypeRef(NoPrefix, TypeName("C"), List())
* [4] TypeRef(ThisType(java.lang), java.lang.Object, List())
* [5] MethodType(List(), TypeRef(ThisType(java.lang), java.lang.Object, List()))
- * [6] SuperType(ThisType(newTypeName("C")), TypeRef(... java.lang.Object ...))
+ * [6] SuperType(ThisType(TypeName("C")), TypeRef(... java.lang.Object ...))
* [7] TypeRef(ThisType(scala), scala.Int, List())
* [8] ConstantType(Constant(2))
* }}}
@@ -112,10 +112,10 @@ import java.io.{ PrintWriter, StringWriter }
* // showRaw has already been discussed above
* scala> showRaw(tpe)
* res1: String = RefinedType(
- * List(TypeRef(ThisType(scala), newTypeName("AnyRef"), List())),
+ * List(TypeRef(ThisType(scala), TypeName("AnyRef"), List())),
* Scope(
- * newTermName("x"),
- * newTermName("y")))
+ * TermName("x"),
+ * TermName("y")))
* }}}
*
* `printIds` and/or `printKinds` can additionally be supplied as arguments in a call to
@@ -124,10 +124,10 @@ import java.io.{ PrintWriter, StringWriter }
* {{{
* scala> showRaw(tpe, printIds = true, printKinds = true)
* res2: String = RefinedType(
- * List(TypeRef(ThisType(scala#2043#PK), newTypeName("AnyRef")#691#TPE, List())),
+ * List(TypeRef(ThisType(scala#2043#PK), TypeName("AnyRef")#691#TPE, List())),
* Scope(
- * newTermName("x")#2540#METH,
- * newTermName("y")#2541#GET))
+ * TermName("x")#2540#METH,
+ * TermName("y")#2541#GET))
* }}}
*
* For more details about `Printer`s and other aspects of Scala reflection, see the
diff --git a/src/reflect/scala/reflect/api/StandardDefinitions.scala b/src/reflect/scala/reflect/api/StandardDefinitions.scala
index 524b7ea14b..bf9cf5e334 100644
--- a/src/reflect/scala/reflect/api/StandardDefinitions.scala
+++ b/src/reflect/scala/reflect/api/StandardDefinitions.scala
@@ -128,7 +128,7 @@ trait StandardDefinitions {
* scala> import scala.reflect.runtime.universe._
* import scala.reflect.runtime.universe._
*
- * scala> val m = typeOf[C].member(newTermName("m")).asMethod
+ * scala> val m = typeOf[C].member(TermName("m")).asMethod
* m: reflect.runtime.universe.MethodSymbol = method m
*
* scala> m.params(0)(0).info
@@ -156,7 +156,7 @@ trait StandardDefinitions {
* scala> import scala.reflect.runtime.universe._
* import scala.reflect.runtime.universe._
*
- * scala> val m = typeOf[C].member(newTermName("m")).asMethod
+ * scala> val m = typeOf[C].member(TermName("m")).asMethod
* m: reflect.runtime.universe.MethodSymbol = method m
*
* scala> m.params(0)(0).info
@@ -181,7 +181,7 @@ trait StandardDefinitions {
* scala> import scala.reflect.runtime.universe._
* import scala.reflect.runtime.universe._
*
- * scala> val m = typeOf[C].member(newTermName("m")).asMethod
+ * scala> val m = typeOf[C].member(TermName("m")).asMethod
* m: reflect.runtime.universe.MethodSymbol = method m
*
* scala> m.params(0)(0).info
diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala
index 42cf600c85..18d185067e 100644
--- a/src/reflect/scala/reflect/api/Symbols.scala
+++ b/src/reflect/scala/reflect/api/Symbols.scala
@@ -27,7 +27,7 @@ package api
* scala> class C[T] { def test[U](x: T)(y: U): Int = ??? }
* defined class C
*
- * scala> val test = typeOf[C[Int]].member(newTermName("test")).asMethod
+ * scala> val test = typeOf[C[Int]].member(TermName("test")).asMethod
* test: reflect.runtime.universe.MethodSymbol = method test
*
* scala> test.info
diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala
index ff8926651b..aeaa38c317 100644
--- a/src/reflect/scala/reflect/api/Trees.scala
+++ b/src/reflect/scala/reflect/api/Trees.scala
@@ -33,7 +33,7 @@ package api
*
* The following creates an AST representing `print("Hello World")`:
* {{{
- * Apply(Select(Select(This(newTypeName("scala")), newTermName("Predef")), newTermName("print")), List(Literal(Constant("Hello World"))))
+ * Apply(Select(Select(This(TypeName("scala")), TermName("Predef")), TermName("print")), List(Literal(Constant("Hello World"))))
* }}}
*
* The following creates an AST from a literal 5, and then uses `showRaw` to print it in a readable format.
@@ -1098,11 +1098,11 @@ trait Trees { self: Universe =>
* // a dummy node that carries the type of unapplication to patmat
* // the <unapply-selector> here doesn't have an underlying symbol
* // it only has a type assigned, therefore after `untypecheck` this tree is no longer typeable
- * Apply(Select(Ident(Foo), newTermName("unapply")), List(Ident(newTermName("<unapply-selector>")))),
+ * Apply(Select(Ident(Foo), TermName("unapply")), List(Ident(TermName("<unapply-selector>")))),
* // arguments of the unapply => nothing synthetic here
- * List(Bind(newTermName("x"), Ident(nme.WILDCARD)))),
+ * List(Bind(TermName("x"), Ident(nme.WILDCARD)))),
* EmptyTree,
- * Ident(newTermName("x")))))
+ * Ident(TermName("x")))))
* }}}
*
* Introduced by typer. Eliminated by compiler phases patmat (in the new pattern matcher of 2.10) or explicitouter (in the old pre-2.10 pattern matcher).
diff --git a/src/reflect/scala/reflect/internal/Depth.scala b/src/reflect/scala/reflect/internal/Depth.scala
index 357abf765f..a330e0accb 100644
--- a/src/reflect/scala/reflect/internal/Depth.scala
+++ b/src/reflect/scala/reflect/internal/Depth.scala
@@ -21,8 +21,20 @@ final class Depth private (val depth: Int) extends AnyVal with Ordered[Depth] {
object Depth {
// A don't care value for the depth parameter in lubs/glbs and related operations.
- final val AnyDepth = new Depth(Int.MinValue)
+ // When passed this value, the recursion budget will be inferred from the shape of
+ // the `typeDepth` of the list of types.
+ final val AnyDepthValue = -3
+ final val AnyDepth = new Depth(AnyDepthValue)
+
final val Zero = new Depth(0)
- @inline final def apply(depth: Int): Depth = if (depth < 0) AnyDepth else new Depth(depth)
+ // SI-9018: A negative depth is used to signal that we have breached the recursion limit.
+ // The LUB/GLB implementation will then truncate to Any/Nothing.
+ //
+ // We only really need one of these, but we allow representation of Depth(-1) and Depth(-2)
+ // to mimic the historical choice of 2.10.4.
+ @inline final def apply(depth: Int): Depth = {
+ if (depth < AnyDepthValue) AnyDepth
+ else new Depth(depth)
+ }
}
diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala
index ed5c68fe82..01e4cdf367 100644
--- a/src/reflect/scala/reflect/internal/SymbolTable.scala
+++ b/src/reflect/scala/reflect/internal/SymbolTable.scala
@@ -338,7 +338,6 @@ abstract class SymbolTable extends macros.Universe
case _ => false
}
if (pkgModule.isModule && !fromSource) {
- // println("open "+pkgModule)//DEBUG
openPackageModule(pkgModule, pkgClass)
}
}
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index b2f176e894..8e86e6fad9 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -181,7 +181,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
type AccessBoundaryType = Symbol
type AnnotationType = AnnotationInfo
- // TODO - don't allow names to be renamed in this unstructured a fashion.
+ // TODO - don't allow names to be renamed in this unstructured fashion.
// Rename as little as possible. Enforce invariants on all renames.
type TypeOfClonedSymbol >: Null <: Symbol { type NameType = Symbol.this.NameType }
@@ -1461,11 +1461,9 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def info: Type = try {
var cnt = 0
while (validTo == NoPeriod) {
- //if (settings.debug.value) System.out.println("completing " + this);//DEBUG
assert(infos ne null, this.name)
assert(infos.prev eq null, this.name)
val tp = infos.info
- //if (settings.debug.value) System.out.println("completing " + this.rawname + tp.getClass());//debug
if ((_rawflags & LOCKED) != 0L) { // rolled out once for performance
lock {
@@ -1474,6 +1472,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
}
} else {
_rawflags |= LOCKED
+ // TODO another commented out lines - this should be solved in one way or another
// activeLocks += 1
// lockedSyms += this
}
@@ -3432,10 +3431,11 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
trait StubSymbol extends Symbol {
devWarning("creating stub symbol to defer error: " + missingMessage)
- protected def missingMessage: String
+ def missingMessage: String
/** Fail the stub by throwing a [[scala.reflect.internal.MissingRequirementError]]. */
- override final def failIfStub() = {MissingRequirementError.signal(missingMessage)} //
+ override final def failIfStub() =
+ MissingRequirementError.signal(missingMessage)
/** Fail the stub by reporting an error to the reporter, setting the IS_ERROR flag
* on this symbol, and returning the dummy value `alt`.
@@ -3460,8 +3460,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
override def rawInfo = fail(NoType)
override def companionSymbol = fail(NoSymbol)
}
- class StubClassSymbol(owner0: Symbol, name0: TypeName, protected val missingMessage: String) extends ClassSymbol(owner0, owner0.pos, name0) with StubSymbol
- class StubTermSymbol(owner0: Symbol, name0: TermName, protected val missingMessage: String) extends TermSymbol(owner0, owner0.pos, name0) with StubSymbol
+ class StubClassSymbol(owner0: Symbol, name0: TypeName, val missingMessage: String) extends ClassSymbol(owner0, owner0.pos, name0) with StubSymbol
+ class StubTermSymbol(owner0: Symbol, name0: TermName, val missingMessage: String) extends TermSymbol(owner0, owner0.pos, name0) with StubSymbol
trait FreeSymbol extends Symbol {
def origin: String
diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
index a35620a994..1fc7aebab0 100644
--- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
+++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
@@ -211,7 +211,12 @@ abstract class UnPickler {
def fromName(name: Name) = name.toTermName match {
case nme.ROOT => loadingMirror.RootClass
case nme.ROOTPKG => loadingMirror.RootPackage
- case _ => adjust(owner.info.decl(name))
+ case _ =>
+ val decl = owner match {
+ case stub: StubSymbol => NoSymbol // SI-8502 Don't call .info and fail the stub
+ case _ => owner.info.decl(name)
+ }
+ adjust(decl)
}
def nestedObjectSymbol: Symbol = {
// If the owner is overloaded (i.e. a method), it's not possible to select the
@@ -389,13 +394,23 @@ abstract class UnPickler {
case CLASSINFOtpe => ClassInfoType(parents, symScope(clazz), clazz)
}
+ def readThisType(): Type = {
+ val sym = readSymbolRef() match {
+ case stub: StubSymbol if !stub.isClass =>
+ // SI-8502 This allows us to create a stub for a unpickled reference to `missingPackage.Foo`.
+ stub.owner.newStubSymbol(stub.name.toTypeName, stub.missingMessage)
+ case sym => sym
+ }
+ ThisType(sym)
+ }
+
// We're stuck with the order types are pickled in, but with judicious use
// of named parameters we can recapture a declarative flavor in a few cases.
// But it's still a rat's nest of adhockery.
(tag: @switch) match {
case NOtpe => NoType
case NOPREFIXtpe => NoPrefix
- case THIStpe => ThisType(readSymbolRef())
+ case THIStpe => readThisType()
case SINGLEtpe => SingleType(readTypeRef(), readSymbolRef().filter(_.isStable)) // SI-7596 account for overloading
case SUPERtpe => SuperType(readTypeRef(), readTypeRef())
case CONSTANTtpe => ConstantType(readConstantRef())
diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala
index a494c7f0d0..38893d8db3 100644
--- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala
+++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala
@@ -31,6 +31,9 @@ abstract class MutableSettings extends AbsSettings {
v = arg
postSetHook()
}
+
+ /** Returns Some(value) in the case of a value set by user and None otherwise. */
+ def valueSetByUser: Option[T] = if (isSetByUser) Some(value) else None
}
def Xexperimental: BooleanSetting
diff --git a/src/reflect/scala/reflect/io/VirtualFile.scala b/src/reflect/scala/reflect/io/VirtualFile.scala
index 45f38db745..1cb4f2fe6f 100644
--- a/src/reflect/scala/reflect/io/VirtualFile.scala
+++ b/src/reflect/scala/reflect/io/VirtualFile.scala
@@ -75,10 +75,10 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF
}
/** Does this abstract file denote an existing file? */
- def create() { unsupported() }
+ def create(): Unit = unsupported()
/** Delete the underlying file or directory (recursively). */
- def delete() { unsupported() }
+ def delete(): Unit = unsupported()
/**
* Returns the abstract file in this abstract directory with the
diff --git a/src/reflect/scala/reflect/io/ZipArchive.scala b/src/reflect/scala/reflect/io/ZipArchive.scala
index 8260189459..0c63acb86c 100644
--- a/src/reflect/scala/reflect/io/ZipArchive.scala
+++ b/src/reflect/scala/reflect/io/ZipArchive.scala
@@ -74,12 +74,6 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq
def container = unsupported()
def absolute = unsupported()
- private def walkIterator(its: Iterator[AbstractFile]): Iterator[AbstractFile] = {
- its flatMap { f =>
- if (f.isDirectory) walkIterator(f.iterator)
- else Iterator(f)
- }
- }
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */
sealed abstract class Entry(path: String) extends VirtualFile(baseName(path), path) {
// have to keep this name for compat with sbt's compiler-interface
@@ -87,6 +81,7 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq
override def underlyingSource = Some(self)
override def toString = self.path + "(" + path + ")"
}
+
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */
class DirEntry(path: String) extends Entry(path) {
val entries = mutable.HashMap[String, Entry]()
@@ -125,14 +120,15 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq
}
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */
final class FileZipArchive(file: JFile) extends ZipArchive(file) {
- def iterator: Iterator[Entry] = {
+ lazy val (root, allDirs) = {
+ val root = new DirEntry("/")
+ val dirs = mutable.HashMap[String, DirEntry]("/" -> root)
val zipFile = try {
new ZipFile(file)
} catch {
case ioe: IOException => throw new IOException("Error accessing " + file.getPath, ioe)
}
- val root = new DirEntry("/")
- val dirs = mutable.HashMap[String, DirEntry]("/" -> root)
+
val enum = zipFile.entries()
while (enum.hasMoreElements) {
@@ -150,11 +146,11 @@ final class FileZipArchive(file: JFile) extends ZipArchive(file) {
dir.entries(f.name) = f
}
}
-
- try root.iterator
- finally dirs.clear()
+ (root, dirs)
}
+ def iterator: Iterator[Entry] = root.iterator
+
def name = file.getName
def path = file.getPath
def input = File(file).inputStream()
@@ -244,11 +240,9 @@ final class ManifestResources(val url: URL) extends ZipArchive(null) {
val manifest = new Manifest(input)
val iter = manifest.getEntries().keySet().iterator().filter(_.endsWith(".class")).map(new ZipEntry(_))
- while (iter.hasNext) {
- val zipEntry = iter.next()
+ for (zipEntry <- iter) {
val dir = getDir(dirs, zipEntry)
- if (zipEntry.isDirectory) dir
- else {
+ if (!zipEntry.isDirectory) {
class FileEntry() extends Entry(zipEntry.getName) {
override def lastModified = zipEntry.getTime()
override def input = resourceInputStream(path)
@@ -284,14 +278,14 @@ final class ManifestResources(val url: URL) extends ZipArchive(null) {
private def resourceInputStream(path: String): InputStream = {
new FilterInputStream(null) {
override def read(): Int = {
- if(in == null) in = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
+ if(in == null) in = Thread.currentThread().getContextClassLoader().getResourceAsStream(path)
if(in == null) throw new RuntimeException(path + " not found")
- super.read();
+ super.read()
}
override def close(): Unit = {
- super.close();
- in = null;
+ super.close()
+ in = null
}
}
}
diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala
index 8bef424e2b..4fd5768b79 100644
--- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala
@@ -126,22 +126,18 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
}
/** print a friendly help message */
- def helpCommand(line: String): Result = {
- if (line == "") helpSummary()
- else uniqueCommand(line) match {
- case Some(lc) => echo("\n" + lc.help)
- case _ => ambiguousError(line)
- }
+ def helpCommand(line: String): Result = line match {
+ case "" => helpSummary()
+ case CommandMatch(cmd) => echo(f"%n${cmd.help}")
+ case _ => ambiguousError(line)
}
private def helpSummary() = {
- val usageWidth = commands map (_.usageMsg.length) max
- val formatStr = "%-" + usageWidth + "s %s"
+ val usageWidth = commands map (_.usageMsg.length) max
+ val formatStr = s"%-${usageWidth}s %s"
- echo("All commands can be abbreviated, e.g. :he instead of :help.")
+ echo("All commands can be abbreviated, e.g., :he instead of :help.")
- commands foreach { cmd =>
- echo(formatStr.format(cmd.usageMsg, cmd.help))
- }
+ for (cmd <- commands) echo(formatStr.format(cmd.usageMsg, cmd.help))
}
private def ambiguousError(cmd: String): Result = {
matchingCommands(cmd) match {
@@ -150,14 +146,14 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
}
Result(keepRunning = true, None)
}
+ // this lets us add commands willy-nilly and only requires enough command to disambiguate
private def matchingCommands(cmd: String) = commands filter (_.name startsWith cmd)
- private def uniqueCommand(cmd: String): Option[LoopCommand] = {
- // this lets us add commands willy-nilly and only requires enough command to disambiguate
- matchingCommands(cmd) match {
- case List(x) => Some(x)
- // exact match OK even if otherwise appears ambiguous
- case xs => xs find (_.name == cmd)
- }
+ private object CommandMatch {
+ def unapply(name: String): Option[LoopCommand] =
+ matchingCommands(name) match {
+ case x :: Nil => Some(x)
+ case xs => xs find (_.name == name) // accept an exact match
+ }
}
/** Show the history */
@@ -622,8 +618,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
if (f.exists) {
addedClasspath = ClassPath.join(addedClasspath, f.path)
intp.addUrlsToClassPath(f.toURI.toURL)
- echo("Added '%s' to classpath.".format(f.path, intp.global.classPath.asClasspathString))
- repldbg("Added '%s'. Your new classpath is:\n\"%s\"".format(f.path, intp.global.classPath.asClasspathString))
+ echo("Added '%s' to classpath.".format(f.path, intp.global.classPath.asClassPathString))
+ repldbg("Added '%s'. Your new classpath is:\n\"%s\"".format(f.path, intp.global.classPath.asClassPathString))
}
else echo("The path '" + f + "' doesn't seem to exist.")
}
@@ -667,8 +663,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
else {
addedClasspath = ClassPath.join(addedClasspath, f.path)
intp.addUrlsToClassPath(f.toURI.toURL)
- echo("Added '%s' to classpath.".format(f.path, intp.global.classPath.asClasspathString))
- repldbg("Added '%s'. Your new classpath is:\n\"%s\"".format(f.path, intp.global.classPath.asClasspathString))
+ echo("Added '%s' to classpath.".format(f.path, intp.global.classPath.asClassPathString))
+ repldbg("Added '%s'. Your new classpath is:\n\"%s\"".format(f.path, intp.global.classPath.asClassPathString))
}
}
@@ -701,20 +697,23 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
}
/** Run one command submitted by the user. Two values are returned:
- * (1) whether to keep running, (2) the line to record for replay,
- * if any. */
+ * (1) whether to keep running, (2) the line to record for replay, if any.
+ */
def command(line: String): Result = {
- if (line startsWith ":") {
- val cmd = line.tail takeWhile (x => !x.isWhitespace)
- uniqueCommand(cmd) match {
- case Some(lc) => lc(line.tail stripPrefix cmd dropWhile (_.isWhitespace))
- case _ => ambiguousError(cmd)
- }
- }
+ if (line startsWith ":") colonCommand(line.tail)
else if (intp.global == null) Result(keepRunning = false, None) // Notice failure to create compiler
else Result(keepRunning = true, interpretStartingWith(line))
}
+ private val commandish = """(\S+)(?:\s+)?(.*)""".r
+
+ private def colonCommand(line: String): Result = line.trim match {
+ case "" => helpSummary()
+ case commandish(CommandMatch(cmd), rest) => cmd(rest)
+ case commandish(name, _) => ambiguousError(name)
+ case _ => echo("?")
+ }
+
private def readWhile(cond: String => Boolean) = {
Iterator continually in.readLine("") takeWhile (x => x != null && cond(x))
}
diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala
index b990e401ec..f9f7388363 100644
--- a/src/repl/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala
@@ -15,7 +15,7 @@ import scala.concurrent.{ Future, ExecutionContext }
import scala.reflect.runtime.{ universe => ru }
import scala.reflect.{ ClassTag, classTag }
import scala.reflect.internal.util.{ BatchSourceFile, SourceFile }
-import scala.tools.util.PathResolver
+import scala.tools.util.PathResolverFactory
import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.typechecker.{ TypeStrings, StructuredTypeStrings }
import scala.tools.nsc.util.{ ScalaClassLoader, stringFromReader, stringFromWriter, StackTraceOps, ClassPath, MergedClassPath }
@@ -90,7 +90,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
def compilerClasspath: Seq[java.net.URL] = (
if (isInitializeComplete) global.classPath.asURLs
- else new PathResolver(settings).result.asURLs // the compiler's classpath
+ else PathResolverFactory.create(settings).resultAsURLs // the compiler's classpath
)
def settings = initialSettings
// Run the code body with the given boolean settings flipped to true.
@@ -1282,9 +1282,11 @@ object IMain {
def getProgram(statements: String*): String = null
- def getScriptEngine: ScriptEngine = new IMain(this, new Settings() {
- usemanifestcp.value = true
- })
+ def getScriptEngine: ScriptEngine = {
+ val settings = new Settings()
+ settings.usemanifestcp.value = true
+ new IMain(this, settings)
+ }
}
// The two name forms this is catching are the two sides of this assignment:
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala
index 295bae5bef..3738e79ffe 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala
@@ -228,6 +228,26 @@ abstract class HtmlPage extends Page { thisPage =>
</a>
</span>
+ def companionAndPackage(tpl: DocTemplateEntity): Elem =
+ <span class="morelinks">{
+ tpl.companion match {
+ case Some(companionTpl) =>
+ val objClassTrait =
+ if (companionTpl.isObject) s"object ${tpl.name}"
+ else if (companionTpl.isTrait) s"trait ${companionTpl.name}"
+ else s"class ${companionTpl.name}"
+ <div>
+ Related Docs:
+ <a href={relativeLinkTo(tpl.companion.get)} title="See companion">{objClassTrait}</a>
+ | {templateToHtml(tpl.inTemplate, s"package ${tpl.inTemplate.name}")}
+ </div>
+ case None =>
+ <div>Related Doc:
+ {templateToHtml(tpl.inTemplate, s"package ${tpl.inTemplate.name}")}
+ </div>
+ }
+ }</span>
+
def memberToUrl(template: Entity, isSelf: Boolean = true): String = {
val (signature: Option[String], containingTemplate: TemplateEntity) = template match {
case dte: DocTemplateEntity if (!isSelf) => (Some(dte.signature), dte.inTemplate)
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala
index f3df2b0bb4..eda52c5fbf 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala
@@ -110,7 +110,9 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
<img src={ relativeLinkTo(List(docEntityKindToBigImage(tpl), "lib")) }/>
}}
{ owner }
- <h1>{ displayName }</h1> { permalink(tpl) }
+ <h1>{ displayName }</h1>{
+ if (tpl.isPackage) NodeSeq.Empty else <h3>{companionAndPackage(tpl)}</h3>
+ }{ permalink(tpl) }
</div>
{ signature(tpl, isSelf = true) }
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css
index 35f66cd5df..e129e6cf6a 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css
+++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css
@@ -397,6 +397,18 @@ div.members > ol > li:last-child {
margin-bottom: 5px;
}
+#definition .morelinks {
+ text-align: right;
+ position: absolute;
+ top: 40px;
+ right: 10px;
+ width: 450px;
+}
+
+#definition .morelinks a {
+ color: #EBEBEB;
+}
+
#template .members li .permalink {
position: absolute;
top: 5px;
diff --git a/src/scalap/scala/tools/scalap/Arguments.scala b/src/scalap/scala/tools/scalap/Arguments.scala
index c375a5bac4..de9c30b8af 100644
--- a/src/scalap/scala/tools/scalap/Arguments.scala
+++ b/src/scalap/scala/tools/scalap/Arguments.scala
@@ -9,7 +9,7 @@
package scala.tools.scalap
import scala.collection.mutable
-import mutable.{ Buffer, ListBuffer }
+import mutable.ListBuffer
object Arguments {
case class Parser(optionPrefix: Char) {
@@ -47,7 +47,7 @@ object Arguments {
}
def parseBinding(str: String, separator: Char): (String, String) = (str indexOf separator) match {
- case -1 => argumentError("missing '" + separator + "' in binding '" + str + "'") ; ("", "")
+ case -1 => argumentError(s"missing '$separator' in binding '$str'") ; ("", "")
case idx => ((str take idx).trim, (str drop (idx + 1)).trim)
}
@@ -71,7 +71,7 @@ object Arguments {
i += 1
} else if (optionalArgs contains args(i)) {
if ((i + 1) == args.length) {
- argumentError("missing argument for '" + args(i) + "'")
+ argumentError(s"missing argument for '${args(i)}'")
i += 1
} else {
res.addArgument(args(i), args(i + 1))
@@ -79,11 +79,11 @@ object Arguments {
}
} else if (optionalBindings contains args(i)) {
if ((i + 1) == args.length) {
- argumentError("missing argument for '" + args(i) + "'")
+ argumentError(s"missing argument for '${args(i)}'")
i += 1
} else {
res.addBinding(args(i),
- parseBinding(args(i + 1), optionalBindings(args(i))));
+ parseBinding(args(i + 1), optionalBindings(args(i))))
i += 2
}
} else {
@@ -92,23 +92,23 @@ object Arguments {
while ((i == j) && iter.hasNext) {
val prefix = iter.next
if (args(i) startsWith prefix) {
- res.addPrefixed(prefix, args(i).substring(prefix.length()).trim());
+ res.addPrefixed(prefix, args(i).substring(prefix.length()).trim())
i += 1
}
}
if (i == j) {
- val iter = prefixedBindings.keysIterator;
+ val iter = prefixedBindings.keysIterator
while ((i == j) && iter.hasNext) {
val prefix = iter.next
if (args(i) startsWith prefix) {
val arg = args(i).substring(prefix.length()).trim()
i = i + 1
res.addBinding(prefix,
- parseBinding(arg, prefixedBindings(prefix)));
+ parseBinding(arg, prefixedBindings(prefix)))
}
}
if (i == j) {
- argumentError("unknown option '" + args(i) + "'")
+ argumentError(s"unknown option '${args(i)}'")
i = i + 1
}
}
@@ -119,7 +119,7 @@ object Arguments {
def parse(options: String*)(args: Array[String]): Arguments = {
val parser = new Parser('-')
- options foreach (parser withOption _)
+ options foreach parser.withOption
parser parse args
}
}
@@ -142,7 +142,7 @@ class Arguments {
if (key.length > 0)
bindings.getOrElseUpdate(tag, new mutable.HashMap)(key) = value
- def addBinding(tag: String, binding: Tuple2[String, String]): Unit =
+ def addBinding(tag: String, binding: (String, String)): Unit =
addBinding(tag, binding._1, binding._2)
def addOther(arg: String): Unit = others += arg
diff --git a/src/scalap/scala/tools/scalap/Main.scala b/src/scalap/scala/tools/scalap/Main.scala
index c72f416a89..7c554d196c 100644
--- a/src/scalap/scala/tools/scalap/Main.scala
+++ b/src/scalap/scala/tools/scalap/Main.scala
@@ -10,11 +10,16 @@ package tools.scalap
import java.io.{ PrintStream, OutputStreamWriter, ByteArrayOutputStream }
import scala.reflect.NameTransformer
-import scalax.rules.scalasig._
-import scala.tools.nsc.util.{ ClassPath, JavaClassPath }
-import scala.tools.util.PathResolver
-import ClassPath.DefaultJavaContext
+import scala.tools.nsc.Settings
+import scala.tools.nsc.classpath.AggregateFlatClassPath
+import scala.tools.nsc.classpath.FlatClassPathFactory
import scala.tools.nsc.io.AbstractFile
+import scala.tools.nsc.settings.ClassPathRepresentationType
+import scala.tools.nsc.util.ClassFileLookup
+import scala.tools.nsc.util.ClassPath.DefaultJavaContext
+import scala.tools.nsc.util.JavaClassPath
+import scala.tools.util.PathResolverFactory
+import scalax.rules.scalasig._
/**The main object used to execute scalap on the command-line.
*
@@ -42,12 +47,12 @@ class Main {
*
* @param clazz the class file to be processed.
*/
- def processJavaClassFile(clazz: Classfile) {
+ def processJavaClassFile(clazz: Classfile): Unit = {
// construct a new output stream writer
val out = new OutputStreamWriter(Console.out)
val writer = new JavaWriter(clazz, out)
// print the class
- writer.printClass
+ writer.printClass()
out.flush()
}
@@ -60,21 +65,20 @@ class Main {
syms.head.parent match {
// Partial match
- case Some(p) if (p.name != "<empty>") => {
+ case Some(p) if p.name != "<empty>" =>
val path = p.path
if (!isPackageObject) {
- stream.print("package ");
- stream.print(path);
+ stream.print("package ")
+ stream.print(path)
stream.print("\n")
} else {
val i = path.lastIndexOf(".")
if (i > 0) {
- stream.print("package ");
+ stream.print("package ")
stream.print(path.substring(0, i))
stream.print("\n")
}
}
- }
case _ =>
}
// Print classes
@@ -96,7 +100,7 @@ class Main {
/** Executes scalap with the given arguments and classpath for the
* class denoted by `classname`.
*/
- def process(args: Arguments, path: ClassPath[AbstractFile])(classname: String): Unit = {
+ def process(args: Arguments, path: ClassFileLookup[AbstractFile])(classname: String): Unit = {
// find the classfile
val encName = classname match {
case "scala.AnyRef" => "java.lang.Object"
@@ -106,92 +110,115 @@ class Main {
// we can afford allocations because this is not a performance critical code
classname.split('.').map(NameTransformer.encode).mkString(".")
}
- val cls = path.findClass(encName)
- if (cls.isDefined && cls.get.binary.isDefined) {
- val cfile = cls.get.binary.get
- if (verbose) {
- Console.println(Console.BOLD + "FILENAME" + Console.RESET + " = " + cfile.path)
- }
- val bytes = cfile.toByteArray
- if (isScalaFile(bytes)) {
- Console.println(decompileScala(bytes, isPackageObjectFile(encName)))
- } else {
- // construct a reader for the classfile content
- val reader = new ByteArrayReader(cfile.toByteArray)
- // parse the classfile
- val clazz = new Classfile(reader)
- processJavaClassFile(clazz)
- }
- // if the class corresponds to the artificial class scala.Any.
- // (see member list in class scala.tool.nsc.symtab.Definitions)
- }
- else
- Console.println("class/object " + classname + " not found.")
- }
- object EmptyClasspath extends ClassPath[AbstractFile] {
- /**
- * The short name of the package (without prefix)
- */
- def name = ""
- def asURLs = Nil
- def asClasspathString = ""
-
- val context = DefaultJavaContext
- val classes = IndexedSeq()
- val packages = IndexedSeq()
- val sourcepaths = IndexedSeq()
+ path.findClassFile(encName) match {
+ case Some(classFile) =>
+ if (verbose) {
+ Console.println(Console.BOLD + "FILENAME" + Console.RESET + " = " + classFile.path)
+ }
+ val bytes = classFile.toByteArray
+ if (isScalaFile(bytes)) {
+ Console.println(decompileScala(bytes, isPackageObjectFile(encName)))
+ } else {
+ // construct a reader for the classfile content
+ val reader = new ByteArrayReader(classFile.toByteArray)
+ // parse the classfile
+ val clazz = new Classfile(reader)
+ processJavaClassFile(clazz)
+ }
+ // if the class corresponds to the artificial class scala.Any.
+ // (see member list in class scala.tool.nsc.symtab.Definitions)
+ case _ =>
+ Console.println(s"class/object $classname not found.")
+ }
}
}
object Main extends Main {
+
+ private object opts {
+ val cp = "-cp"
+ val help = "-help"
+ val classpath = "-classpath"
+ val showPrivateDefs = "-private"
+ val verbose = "-verbose"
+ val version = "-version"
+
+ val classPathImplType = "-YclasspathImpl"
+ val disableFlatClassPathCaching = "-YdisableFlatCpCaching"
+ val logClassPath = "-Ylog-classpath"
+ }
+
/** Prints usage information for scalap. */
- def usage() {
- Console println """
+ def usage(): Unit = {
+ Console println s"""
|Usage: scalap {<option>} <name>
|where <name> is fully-qualified class name or <package_name>.package for package objects
|and <option> is
- | -private print private definitions
- | -verbose print out additional information
- | -version print out the version number of scalap
- | -help display this usage message
- | -classpath <path> specify where to find user class files
- | -cp <path> specify where to find user class files
+ | ${opts.showPrivateDefs} print private definitions
+ | ${opts.verbose} print out additional information
+ | ${opts.version} print out the version number of scalap
+ | ${opts.help} display this usage message
+ | ${opts.classpath} <path> specify where to find user class files
+ | ${opts.cp} <path> specify where to find user class files
""".stripMargin.trim
}
- def main(args: Array[String]) {
- // print usage information if there is no command-line argument
- if (args.isEmpty)
- return usage()
-
- val arguments = Arguments.Parser('-')
- .withOption("-private")
- .withOption("-verbose")
- .withOption("-version")
- .withOption("-help")
- .withOptionalArg("-classpath")
- .withOptionalArg("-cp")
- .parse(args);
-
- if (arguments contains "-version")
- Console.println(versionMsg)
- if (arguments contains "-help")
- usage()
-
- verbose = arguments contains "-verbose"
- printPrivates = arguments contains "-private"
- // construct a custom class path
- val cparg = List("-classpath", "-cp") map (arguments getArgument _) reduceLeft (_ orElse _)
- val path = cparg match {
- case Some(cp) => new JavaClassPath(DefaultJavaContext.classesInExpandedPath(cp), DefaultJavaContext)
- case _ => PathResolver.fromPathString(".") // include '.' in the default classpath SI-6669
+ def main(args: Array[String]): Unit =
+ // print usage information if there is no command-line argument
+ if (args.isEmpty) usage()
+ else {
+ val arguments = parseArguments(args)
+
+ if (arguments contains opts.version)
+ Console.println(versionMsg)
+ if (arguments contains opts.help)
+ usage()
+
+ verbose = arguments contains opts.verbose
+ printPrivates = arguments contains opts.showPrivateDefs
+ // construct a custom class path
+ val cpArg = List(opts.classpath, opts.cp) map arguments.getArgument reduceLeft (_ orElse _)
+
+ val settings = new Settings()
+
+ arguments getArgument opts.classPathImplType foreach settings.YclasspathImpl.tryToSetFromPropertyValue
+ settings.YdisableFlatCpCaching.value = arguments contains opts.disableFlatClassPathCaching
+ settings.Ylogcp.value = arguments contains opts.logClassPath
+
+ val path = createClassPath(cpArg, settings)
+
+ // print the classpath if output is verbose
+ if (verbose)
+ Console.println(Console.BOLD + "CLASSPATH" + Console.RESET + " = " + path.asClassPathString)
+
+ // process all given classes
+ arguments.getOthers foreach process(arguments, path)
}
- // print the classpath if output is verbose
- if (verbose)
- Console.println(Console.BOLD + "CLASSPATH" + Console.RESET + " = " + path)
- // process all given classes
- arguments.getOthers foreach process(arguments, path)
+ private def parseArguments(args: Array[String]) =
+ Arguments.Parser('-')
+ .withOption(opts.showPrivateDefs)
+ .withOption(opts.verbose)
+ .withOption(opts.version)
+ .withOption(opts.help)
+ .withOptionalArg(opts.classpath)
+ .withOptionalArg(opts.cp)
+ // TODO three temporary, hidden options to be able to test different classpath representations
+ .withOptionalArg(opts.classPathImplType)
+ .withOption(opts.disableFlatClassPathCaching)
+ .withOption(opts.logClassPath)
+ .parse(args)
+
+ private def createClassPath(cpArg: Option[String], settings: Settings) = cpArg match {
+ case Some(cp) => settings.YclasspathImpl.value match {
+ case ClassPathRepresentationType.Flat =>
+ AggregateFlatClassPath(new FlatClassPathFactory(settings).classesInExpandedPath(cp))
+ case ClassPathRepresentationType.Recursive =>
+ new JavaClassPath(DefaultJavaContext.classesInExpandedPath(cp), DefaultJavaContext)
+ }
+ case _ =>
+ settings.classpath.value = "." // include '.' in the default classpath SI-6669
+ PathResolverFactory.create(settings).result
}
}