diff options
Diffstat (limited to 'src')
42 files changed, 558 insertions, 1026 deletions
diff --git a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala index b5b0f93750..242e5d60b3 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala @@ -55,7 +55,7 @@ trait GenUtils { mirrorCall(TermName("" + prefix), args: _*) def scalaFactoryCall(name: TermName, args: Tree*): Tree = - call(s"scala.$name.apply", args: _*) + call(s"_root_.scala.$name.apply", args: _*) def scalaFactoryCall(name: String, args: Tree*): Tree = scalaFactoryCall(TermName(name), args: _*) diff --git a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala index 3b91d28360..a5c4c7e0a3 100644 --- a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala +++ b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala @@ -28,7 +28,7 @@ trait NodePrinters { var s = line substring 2 s = s.replace(nme.UNIVERSE_PREFIX.toString, "") s = s.replace(".apply", "") - s = "([^\"])scala\\.collection\\.immutable\\.".r.replaceAllIn(s, "$1") + s = "([^\"])(_root_\\.)?scala\\.collection\\.immutable\\.".r.replaceAllIn(s, "$1") s = "List\\[List\\[.*?\\].*?\\]".r.replaceAllIn(s, "List") s = "List\\[.*?\\]".r.replaceAllIn(s, "List") s = s.replace("immutable.this.Nil", "List()") diff --git a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala index e99cce9186..c82ed68da8 100644 --- a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala +++ b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala @@ -6,10 +6,10 @@ package scala.tools.nsc import java.net.URL -import scala.tools.util.PathResolverFactory +import scala.tools.util.PathResolver class GenericRunnerSettings(error: String => Unit) extends Settings(error) { - lazy val classpathURLs: Seq[URL] = PathResolverFactory.create(this).resultAsURLs + lazy val classpathURLs: Seq[URL] = new PathResolver(this).resultAsURLs val howtorun = ChoiceSetting( diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 94a6a0e4e2..7417d9c09d 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -13,7 +13,7 @@ import java.nio.charset.{Charset, CharsetDecoder, IllegalCharsetNameException, U import scala.collection.{immutable, mutable} import io.{AbstractFile, Path, SourceReader} import reporters.Reporter -import util.{ClassFileLookup, ClassPath, StatisticsInfo, returning} +import util.{ClassPath, StatisticsInfo, returning} import scala.reflect.ClassTag import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, ScalaClassLoader, ScriptSourceFile, SourceFile} import scala.reflect.internal.pickling.PickleBuffer @@ -30,7 +30,6 @@ import backend.jvm.GenBCode import scala.language.postfixOps import scala.tools.nsc.ast.{TreeGen => AstTreeGen} import scala.tools.nsc.classpath._ -import scala.tools.nsc.settings.ClassPathRepresentationType class Global(var currentSettings: Settings, var reporter: Reporter) extends SymbolTable @@ -54,12 +53,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) class GlobalMirror extends Roots(NoSymbol) { val universe: self.type = self - def rootLoader: LazyType = { - settings.YclasspathImpl.value match { - case ClassPathRepresentationType.Flat => new loaders.PackageLoaderUsingFlatClassPath(FlatClassPath.RootPackage, flatClassPath) - case ClassPathRepresentationType.Recursive => new loaders.PackageLoader(recursiveClassPath) - } - } + def rootLoader: LazyType = new loaders.PackageLoader(ClassPath.RootPackage, classPath) override def toString = "compiler mirror" } implicit val MirrorTag: ClassTag[Mirror] = ClassTag[Mirror](classOf[GlobalMirror]) @@ -102,14 +96,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) type ThisPlatform = JavaPlatform { val global: Global.this.type } lazy val platform: ThisPlatform = new GlobalPlatform - 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 + def classPath: ClassPath = platform.classPath // sub-components -------------------------------------------------- @@ -771,17 +758,10 @@ 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) { - val urlClasspaths = urls.map(u => FlatClassPathFactory.newClassPath(AbstractFile.getURL(u), settings)) - val newClassPath = AggregateFlatClassPath.createAggregate(platform.flatClassPath +: urlClasspaths : _*) - platform.currentFlatClassPath = Some(newClassPath) - invalidateClassPathEntries(urls.map(_.getPath): _*) - } else { - val newClassPath = platform.classPath.mergeUrlsIntoClassPath(urls: _*) - platform.currentClassPath = Some(newClassPath) - // Reload all specified jars into this compiler instance - invalidateClassPathEntries(urls.map(_.getPath): _*) - } + val urlClasspaths = urls.map(u => ClassPathFactory.newClassPath(AbstractFile.getURL(u), settings)) + val newClassPath = AggregateClassPath.createAggregate(platform.classPath +: urlClasspaths : _*) + platform.currentClassPath = Some(newClassPath) + invalidateClassPathEntries(urls.map(_.getPath): _*) } // ------------ Invalidations --------------------------------- @@ -813,28 +793,26 @@ class Global(var currentSettings: Settings, var reporter: Reporter) * entries on the classpath. */ def invalidateClassPathEntries(paths: String*): Unit = { - implicit object ClassPathOrdering extends Ordering[ClassFileLookup[AbstractFile]] { - def compare(a:ClassFileLookup[AbstractFile], b:ClassFileLookup[AbstractFile]) = a.asClassPathString compare b.asClassPathString + implicit object ClassPathOrdering extends Ordering[ClassPath] { + def compare(a: ClassPath, b: ClassPath): Int = a.asClassPathString compareTo b.asClassPathString } val invalidated, failed = new mutable.ListBuffer[ClassSymbol] - def assoc(path: String): Option[(ClassFileLookup[AbstractFile], ClassFileLookup[AbstractFile])] = { - def origin(lookup: ClassFileLookup[AbstractFile]): Option[String] = lookup match { - case cp: ClassPath[_] => cp.origin + def assoc(path: String): Option[(ClassPath, ClassPath)] = { + def origin(lookup: ClassPath): Option[String] = lookup match { case cp: JFileDirectoryLookup[_] => Some(cp.dir.getPath) case cp: ZipArchiveFileLookup[_] => Some(cp.zipFile.getPath) case _ => None } - def entries(lookup: ClassFileLookup[AbstractFile]): Seq[ClassFileLookup[AbstractFile]] = lookup match { - case cp: ClassPath[_] => cp.entries - case cp: AggregateFlatClassPath => cp.aggregates - case cp: FlatClassPath => Seq(cp) + def entries(lookup: ClassPath): Seq[ClassPath] = lookup match { + case cp: AggregateClassPath => cp.aggregates + case cp: ClassPath => Seq(cp) } val dir = AbstractFile.getDirectory(path) // if path is a `jar`, this is a FileZipArchive (isDirectory is true) val canonical = dir.canonicalPath // this is the canonical path of the .jar - def matchesCanonical(e: ClassFileLookup[AbstractFile]) = origin(e) match { + def matchesCanonical(e: ClassPath) = origin(e) match { case Some(opath) => AbstractFile.getDirectory(opath).canonicalPath == canonical case None => @@ -842,7 +820,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } entries(classPath) find matchesCanonical match { case Some(oldEntry) => - Some(oldEntry -> ClassFileLookup.createForFile(dir, classPath, settings)) + Some(oldEntry -> ClassPathFactory.newClassPath(dir, settings)) case None => error(s"Error adding entry to classpath. During invalidation, no entry named $path in classpath $classPath") None @@ -852,19 +830,15 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if (subst.nonEmpty) { platform updateClassPath subst informProgress(s"classpath updated on entries [${subst.keys mkString ","}]") - def mkClassPath(elems: Iterable[ClassFileLookup[AbstractFile]]): ClassFileLookup[AbstractFile] = + def mkClassPath(elems: Iterable[ClassPath]): ClassPath = if (elems.size == 1) elems.head - else ClassFileLookup.createAggregate(elems, classPath) + else AggregateClassPath.createAggregate(elems.toSeq: _*) val oldEntries = mkClassPath(subst.keys) val newEntries = mkClassPath(subst.values) classPath match { - case rcp: ClassPath[_] => mergeNewEntriesRecursive( - newEntries.asInstanceOf[ClassPath[AbstractFile]], RootClass, Some(rcp), Some(oldEntries.asInstanceOf[ClassPath[AbstractFile]]), - invalidated, failed) - - case fcp: FlatClassPath => mergeNewEntriesFlat( + case cp: ClassPath => mergeNewEntries( RootClass, "", - oldEntries.asInstanceOf[FlatClassPath], newEntries.asInstanceOf[FlatClassPath], fcp, + oldEntries, newEntries, cp, invalidated, failed) } } @@ -875,69 +849,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) show("could not invalidate system packages", failed) } - /** Merges new classpath entries into the symbol table - * - * @param newEntries The new classpath entries - * @param root The root symbol to be resynced (a package class) - * @param allEntries Optionally, the corresponding package in the complete current classpath - * @param oldEntries Optionally, the corresponding package in the old classpath entries - * @param invalidated A listbuffer collecting the invalidated package classes - * @param failed A listbuffer collecting system package classes which could not be invalidated - * - * The merging strategy is determined by the absence or presence of classes and packages. - * - * If either oldEntries or newEntries contains classes, root is invalidated provided that a corresponding package - * exists in allEntries. Otherwise it is removed. - * Otherwise, the action is determined by the following matrix, with columns: - * - * old sym action - * + + recurse into all child packages of newEntries - * - + invalidate root - * - - create and enter root - * - * Here, old means classpath, and sym means symboltable. + is presence of an entry in its column, - is absence. - */ - private def mergeNewEntriesRecursive(newEntries: ClassPath[AbstractFile], root: ClassSymbol, - allEntries: Option[ClassPath[AbstractFile]], oldEntries: Option[ClassPath[AbstractFile]], - invalidated: mutable.ListBuffer[ClassSymbol], failed: mutable.ListBuffer[ClassSymbol]) { - ifDebug(informProgress(s"syncing $root, $oldEntries -> $newEntries")) - - val getPackageName: ClassPath[AbstractFile] => String = _.name - def hasClasses(cp: Option[ClassPath[AbstractFile]]) = cp.isDefined && cp.get.classes.nonEmpty - def invalidateOrRemove(root: ClassSymbol) = { - allEntries match { - case Some(cp) => root setInfo new loaders.PackageLoader(cp) - case None => root.owner.info.decls unlink root.sourceModule - } - invalidated += root - } - def subPackage(cp: ClassPath[AbstractFile], name: String): Option[ClassPath[AbstractFile]] = - cp.packages find (cp1 => getPackageName(cp1) == name) - - val classesFound = hasClasses(oldEntries) || newEntries.classes.nonEmpty - if (classesFound && !isSystemPackageClass(root)) { - invalidateOrRemove(root) - } else { - if (classesFound) { - if (root.isRoot) invalidateOrRemove(EmptyPackageClass) - else failed += root - } - if (oldEntries.isEmpty) invalidateOrRemove(root) - else - for (pstr <- newEntries.packages.map(getPackageName)) { - val pname = newTermName(pstr) - val pkg = (root.info decl pname) orElse { - // package does not exist in symbol table, create symbol to track it - assert(subPackage(oldEntries.get, pstr).isEmpty) - loaders.enterPackage(root, pstr, new loaders.PackageLoader(allEntries.get)) - } - mergeNewEntriesRecursive(subPackage(newEntries, pstr).get, pkg.moduleClass.asClass, - subPackage(allEntries.get, pstr), subPackage(oldEntries.get, pstr), - invalidated, failed) - } - } - } - /** * Merges new classpath entries into the symbol table * @@ -956,20 +867,19 @@ class Global(var currentSettings: Settings, var reporter: Reporter) * Otherwise, sub-packages in newEntries are looked up in the symbol table (created if * non-existent) and the merge function is called recursively. */ - private def mergeNewEntriesFlat( - packageClass: ClassSymbol, fullPackageName: String, - oldEntries: FlatClassPath, newEntries: FlatClassPath, fullClasspath: FlatClassPath, - invalidated: mutable.ListBuffer[ClassSymbol], failed: mutable.ListBuffer[ClassSymbol]): Unit = { + private def mergeNewEntries(packageClass: ClassSymbol, fullPackageName: String, + oldEntries: ClassPath, newEntries: ClassPath, fullClasspath: ClassPath, + invalidated: mutable.ListBuffer[ClassSymbol], failed: mutable.ListBuffer[ClassSymbol]): Unit = { ifDebug(informProgress(s"syncing $packageClass, $oldEntries -> $newEntries")) - def packageExists(cp: FlatClassPath): Boolean = { + def packageExists(cp: ClassPath): Boolean = { val (parent, _) = PackageNameUtils.separatePkgAndClassNames(fullPackageName) cp.packages(parent).exists(_.name == fullPackageName) } def invalidateOrRemove(pkg: ClassSymbol) = { if (packageExists(fullClasspath)) - pkg setInfo new loaders.PackageLoaderUsingFlatClassPath(fullPackageName, fullClasspath) + pkg setInfo new loaders.PackageLoader(fullPackageName, fullClasspath) else pkg.owner.info.decls unlink pkg.sourceModule invalidated += pkg @@ -987,9 +897,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val (_, subPackageName) = PackageNameUtils.separatePkgAndClassNames(p.name) val subPackage = packageClass.info.decl(newTermName(subPackageName)) orElse { // package does not exist in symbol table, create a new symbol - loaders.enterPackage(packageClass, subPackageName, new loaders.PackageLoaderUsingFlatClassPath(p.name, fullClasspath)) + loaders.enterPackage(packageClass, subPackageName, new loaders.PackageLoader(p.name, fullClasspath)) } - mergeNewEntriesFlat( + mergeNewEntries( subPackage.moduleClass.asClass, p.name, oldEntries, newEntries, fullClasspath, invalidated, failed) diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index bf93ad30bc..1f66657d8d 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -8,10 +8,8 @@ package tools.nsc import io.{ AbstractFile, Directory, File, Path } import java.io.IOException -import scala.tools.nsc.classpath.DirectoryFlatClassPath +import scala.tools.nsc.classpath.DirectoryClassPath 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. @@ -115,10 +113,7 @@ class ScriptRunner extends HasCompileSocket { } def hasClassToRun(d: Directory): Boolean = { - val cp = settings.YclasspathImpl.value match { - case ClassPathRepresentationType.Recursive => DefaultJavaContext.newClassPath(AbstractFile.getDirectory(d)) - case ClassPathRepresentationType.Flat => DirectoryFlatClassPath(d.jfile) - } + val cp = DirectoryClassPath(d.jfile) cp.findClass(mainClass).isDefined } diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala index 0e2f059a36..dc63b335cc 100644 --- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala @@ -7,11 +7,9 @@ package scala.tools.nsc package backend import io.AbstractFile -import scala.tools.nsc.classpath.{AggregateFlatClassPath, FlatClassPath} -import scala.tools.nsc.settings.ClassPathRepresentationType -import scala.tools.nsc.util.{ClassFileLookup, ClassPath, MergedClassPath} -import scala.tools.util.FlatClassPathResolver +import scala.tools.nsc.classpath.AggregateClassPath import scala.tools.util.PathResolver +import scala.tools.nsc.util.ClassPath trait JavaPlatform extends Platform { val global: Global @@ -19,38 +17,20 @@ trait JavaPlatform extends Platform { import global._ import definitions._ - 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.") + private[nsc] var currentClassPath: Option[ClassPath] = None + private[nsc] def classPath: ClassPath = { if (currentClassPath.isEmpty) currentClassPath = Some(new PathResolver(settings).result) currentClassPath.get } - private[nsc] var currentFlatClassPath: Option[FlatClassPath] = None - - private[nsc] def flatClassPath: FlatClassPath = { - assert(settings.YclasspathImpl.value == ClassPathRepresentationType.Flat, - "To use flat classpath representation you must enable it with -YclasspathImpl:flat compiler option.") - - if (currentFlatClassPath.isEmpty) currentFlatClassPath = Some(new FlatClassPathResolver(settings).result) - currentFlatClassPath.get - } - /** Update classpath with a substituted subentry */ - def updateClassPath(subst: Map[ClassFileLookup[AbstractFile], ClassFileLookup[AbstractFile]]) = global.classPath match { - case cp: ClassPath[AbstractFile] => - val s = subst.asInstanceOf[Map[ClassPath[AbstractFile], ClassPath[AbstractFile]]] - currentClassPath = Some(new MergedClassPath(cp.entries map (e => s.getOrElse(e, e)), cp.context)) - - case AggregateFlatClassPath(entries) => - val s = subst.asInstanceOf[Map[FlatClassPath, FlatClassPath]] - currentFlatClassPath = Some(AggregateFlatClassPath(entries map (e => s.getOrElse(e, e)))) + def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit = global.classPath match { + case AggregateClassPath(entries) => + currentClassPath = Some(AggregateClassPath(entries map (e => subst.getOrElse(e, e)))) - case cp: FlatClassPath => - currentFlatClassPath = Some(subst.getOrElse(cp, cp).asInstanceOf[FlatClassPath]) + case cp: ClassPath => + currentClassPath = Some(subst.getOrElse(cp, cp)) } def platformPhases = List( diff --git a/src/compiler/scala/tools/nsc/backend/Platform.scala b/src/compiler/scala/tools/nsc/backend/Platform.scala index 369bcc44ed..e464768bb3 100644 --- a/src/compiler/scala/tools/nsc/backend/Platform.scala +++ b/src/compiler/scala/tools/nsc/backend/Platform.scala @@ -6,9 +6,8 @@ package scala.tools.nsc package backend -import util.{ClassFileLookup, ClassPath} import io.AbstractFile -import scala.tools.nsc.classpath.FlatClassPath +import scala.tools.nsc.util.ClassPath /** The platform dependent pieces of Global. */ @@ -16,14 +15,11 @@ trait Platform { val symbolTable: symtab.SymbolTable import symbolTable._ - /** The old, recursive implementation of compiler classpath. */ - def classPath: ClassPath[AbstractFile] - /** The new implementation of compiler classpath. */ - private[nsc] def flatClassPath: FlatClassPath + private[nsc] def classPath: ClassPath /** Update classpath with a substitution that maps entries to entries */ - def updateClassPath(subst: Map[ClassFileLookup[AbstractFile], ClassFileLookup[AbstractFile]]) + def updateClassPath(subst: Map[ClassPath, ClassPath]) /** Any platform-specific phases. */ def platformPhases: List[SubComponent] diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala index 01206aa6eb..4287c24dc8 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala @@ -42,15 +42,15 @@ object BackendReporting { def assertionError(message: String): Nothing = throw new AssertionError(message) implicit class RightBiasedEither[A, B](val v: Either[A, B]) extends AnyVal { - def map[U](f: B => U) = v.right.map(f) - def flatMap[BB](f: B => Either[A, BB]) = v.right.flatMap(f) + def map[C](f: B => C): Either[A, C] = v.right.map(f) + def flatMap[C](f: B => Either[A, C]): Either[A, C] = v.right.flatMap(f) def withFilter(f: B => Boolean)(implicit empty: A): Either[A, B] = v match { case Left(_) => v case Right(e) => if (f(e)) v else Left(empty) // scalaz.\/ requires an implicit Monoid m to get m.empty } - def foreach[U](f: B => U) = v.right.foreach(f) + def foreach[U](f: B => U): Unit = v.right.foreach(f) - def getOrElse[BB >: B](alt: => BB): BB = v.right.getOrElse(alt) + def getOrElse[C >: B](alt: => C): C = v.right.getOrElse(alt) /** * Get the value, fail with an assertion if this is an error. @@ -101,11 +101,14 @@ object BackendReporting { else "" } - case MethodNotFound(name, descriptor, ownerInternalName, missingClasses) => - val (javaDef, others) = missingClasses.partition(_.definedInJavaSource) - s"The method $name$descriptor could not be found in the class $ownerInternalName or any of its parents." + - (if (others.isEmpty) "" else others.map(_.internalName).mkString("\nNote that the following parent classes could not be found on the classpath: ", ", ", "")) + - (if (javaDef.isEmpty) "" else javaDef.map(_.internalName).mkString("\nNote that the following parent classes are defined in Java sources (mixed compilation), no bytecode is available: ", ",", "")) + case MethodNotFound(name, descriptor, ownerInternalName, missingClass) => + val missingClassWarning = missingClass match { + case None => "" + case Some(c) => + if (c.definedInJavaSource) s"\nNote that the parent class ${c.internalName} is defined in a Java source (mixed compilation), no bytecode is available." + else s"\nNote that the parent class ${c.internalName} could not be found on the classpath." + } + s"The method $name$descriptor could not be found in the class $ownerInternalName or any of its parents." + missingClassWarning case FieldNotFound(name, descriptor, ownerInternalName, missingClass) => s"The field node $name$descriptor could not be found because the classfile $ownerInternalName cannot be found on the classpath." + @@ -127,7 +130,7 @@ object BackendReporting { } case class ClassNotFound(internalName: InternalName, definedInJavaSource: Boolean) extends MissingBytecodeWarning - case class MethodNotFound(name: String, descriptor: String, ownerInternalNameOrArrayDescriptor: InternalName, missingClasses: List[ClassNotFound]) extends MissingBytecodeWarning { + case class MethodNotFound(name: String, descriptor: String, ownerInternalNameOrArrayDescriptor: InternalName, missingClass: Option[ClassNotFound]) extends MissingBytecodeWarning { def isArrayMethod = ownerInternalNameOrArrayDescriptor.charAt(0) == '[' } case class FieldNotFound(name: String, descriptor: String, ownerInternalName: InternalName, missingClass: Option[ClassNotFound]) extends MissingBytecodeWarning diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala index 0b53ea2fb1..1feca56923 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala @@ -107,6 +107,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { lazy val juHashMapRef : ClassBType = classBTypeFromSymbol(JavaUtilHashMap) // java/util/HashMap lazy val sbScalaBeanInfoRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.beans.ScalaBeanInfo]) lazy val jliSerializedLambdaRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.SerializedLambda]) + lazy val jliMethodHandleRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodHandle]) lazy val jliMethodHandlesRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodHandles]) lazy val jliMethodHandlesLookupRef : ClassBType = classBTypeFromSymbol(exitingPickler(getRequiredClass("java.lang.invoke.MethodHandles.Lookup"))) // didn't find a reliable non-stringly-typed way that works for inner classes in the backend lazy val jliMethodTypeRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType]) @@ -320,6 +321,7 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] { def jliCallSiteRef : ClassBType def jliMethodTypeRef : ClassBType def jliSerializedLambdaRef : ClassBType + def jliMethodHandleRef : ClassBType def jliMethodHandlesLookupRef : ClassBType def srBoxesRunTimeRef : ClassBType def srBoxedUnitRef : ClassBType @@ -383,6 +385,7 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: def juHashMapRef : ClassBType = _coreBTypes.juHashMapRef def sbScalaBeanInfoRef : ClassBType = _coreBTypes.sbScalaBeanInfoRef def jliSerializedLambdaRef : ClassBType = _coreBTypes.jliSerializedLambdaRef + def jliMethodHandleRef : ClassBType = _coreBTypes.jliMethodHandleRef def jliMethodHandlesRef : ClassBType = _coreBTypes.jliMethodHandlesRef def jliMethodHandlesLookupRef : ClassBType = _coreBTypes.jliMethodHandlesLookupRef def jliMethodTypeRef : ClassBType = _coreBTypes.jliMethodTypeRef diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala index eaf82f5c65..16590ec75c 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala @@ -10,11 +10,10 @@ package opt import scala.tools.asm import asm.tree._ import scala.collection.JavaConverters._ -import scala.collection.concurrent +import scala.collection.{concurrent, mutable} import scala.tools.asm.Attribute import scala.tools.nsc.backend.jvm.BackendReporting._ -import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.util.ClassFileLookup +import scala.tools.nsc.util.ClassPath import BytecodeUtils._ import ByteCodeRepository._ import BTypes.InternalName @@ -26,7 +25,7 @@ import java.util.concurrent.atomic.AtomicLong * * @param classPath The compiler classpath where classfiles are searched and read from. */ -class ByteCodeRepository[BT <: BTypes](val classPath: ClassFileLookup[AbstractFile], val btypes: BT) { +class ByteCodeRepository[BT <: BTypes](val classPath: ClassPath, val btypes: BT) { import btypes._ /** @@ -132,38 +131,135 @@ class ByteCodeRepository[BT <: BTypes](val classPath: ClassFileLookup[AbstractFi * The method node for a method matching `name` and `descriptor`, accessed in class `ownerInternalNameOrArrayDescriptor`. * The declaration of the method may be in one of the parents. * - * TODO: make sure we always return the right method, the one being invoked. write tests. - * - if there's an abstract and a concrete one. could possibly somehow the abstract be returned? - * - with traits and default methods, if there is more than one default method inherited and - * no override: what should be returned? We should not just inline one of the two. + * Note that the JVM spec performs method lookup in two steps: resolution and selection. + * + * Method resolution, defined in jvms-5.4.3.3 and jvms-5.4.3.4, is the first step and is identical + * for all invocation styles (virtual, interface, special, static). If C is the receiver class + * in the invocation instruction: + * 1 find a matching method (name and descriptor) in C + * 2 then in C's superclasses + * 3 then find the maximally-specific matching superinterface methods, succeed if there's a + * single non-abstract one. static and private methods in superinterfaces are not considered. + * 4 then pick a random non-static, non-private superinterface method. + * 5 then fail. + * + * Note that for an `invokestatic` instruction, a method reference `B.m` may resolve to `A.m`, if + * class `B` doesn't specify a matching method `m`, but the parent `A` does. + * + * Selection depends on the invocation style and is defined in jvms-6.5. + * - invokestatic: invokes the resolved method + * - invokevirtual / invokeinterface: searches for an override of the resolved method starting + * at the dynamic receiver type. the search procedure is basically the same as in resolution, + * but it fails at 4 instead of picking a superinterface method at random. + * - invokespecial: if C is the receiver in the invocation instruction, searches for an override + * of the resolved method starting at + * - the superclass of the current class, if C is a superclass of the current class + * - C otherwise + * again, the search procedure is the same. + * + * In the method here we implement method *resolution*. Whether or not the returned method is + * actually invoked at runtime depends on the invocation instruction and the class hierarchy, so + * the users (e.g. the inliner) have to be aware of method selection. + * + * Note that the returned method may be abstract (ACC_ABSTRACT), native (ACC_NATIVE) or signature + * polymorphic (methods `invoke` and `invokeExact` in class `MehtodHandles`). * * @return The [[MethodNode]] of the requested method and the [[InternalName]] of its declaring - * class, or an error message if the method could not be found. + * class, or an error message if the method could not be found. An error message is also + * returned if method resolution results in multiple default methods. */ def methodNode(ownerInternalNameOrArrayDescriptor: String, name: String, descriptor: String): Either[MethodNotFound, (MethodNode, InternalName)] = { - // on failure, returns a list of class names that could not be found on the classpath - def methodNodeImpl(ownerInternalName: InternalName): Either[List[ClassNotFound], (MethodNode, InternalName)] = { - classNode(ownerInternalName) match { - case Left(e) => Left(List(e)) - case Right(c) => - c.methods.asScala.find(m => m.name == name && m.desc == descriptor) match { - case Some(m) => Right((m, ownerInternalName)) - case None => findInParents(Option(c.superName) ++: c.interfaces.asScala.toList, Nil) - } + def findMethod(c: ClassNode): Option[MethodNode] = c.methods.asScala.find(m => m.name == name && m.desc == descriptor) + + // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9: "In Java SE 8, the only + // signature polymorphic methods are the invoke and invokeExact methods of the class MethodHandle. + def isSignaturePolymorphic(owner: InternalName) = owner == coreBTypes.jliMethodHandleRef.internalName && (name == "invoke" || name == "invokeExact") + + // Note: if `owner` is an interface, in the first iteration we search for a matching member in the interface itself. + // If that fails, the recursive invocation checks in the superclass (which is Object) with `publicInstanceOnly == true`. + // This is specified in jvms-5.4.3.4: interface method resolution only returns public, non-static methods of Object. + def findInSuperClasses(owner: ClassNode, publicInstanceOnly: Boolean = false): Either[ClassNotFound, Option[(MethodNode, InternalName)]] = { + findMethod(owner) match { + case Some(m) if !publicInstanceOnly || (isPublicMethod(m) && !isStaticMethod(m)) => Right(Some((m, owner.name))) + case None => + if (isSignaturePolymorphic(owner.name)) Right(Some((owner.methods.asScala.find(_.name == name).get, owner.name))) + else if (owner.superName == null) Right(None) + else classNode(owner.superName).flatMap(findInSuperClasses(_, isInterface(owner))) } } - // find the MethodNode in one of the parent classes - def findInParents(parents: List[InternalName], failedClasses: List[ClassNotFound]): Either[List[ClassNotFound], (MethodNode, InternalName)] = parents match { - case x :: xs => methodNodeImpl(x).left.flatMap(failed => findInParents(xs, failed ::: failedClasses)) - case Nil => Left(failedClasses) + def findInInterfaces(initialOwner: ClassNode): Either[ClassNotFound, Option[(MethodNode, InternalName)]] = { + val visited = mutable.Set.empty[InternalName] + val found = mutable.ListBuffer.empty[(MethodNode, ClassNode)] + + def findIn(owner: ClassNode): Option[ClassNotFound] = { + for (i <- owner.interfaces.asScala if !visited(i)) classNode(i) match { + case Left(e) => return Some(e) + case Right(c) => + visited += i + // abstract and static methods are excluded, see jvms-5.4.3.3 + for (m <- findMethod(c) if !isPrivateMethod(m) && !isStaticMethod(m)) found += ((m, c)) + val recusionResult = findIn(c) + if (recusionResult.isDefined) return recusionResult + } + None + } + + findIn(initialOwner) + + val result = + if (found.size <= 1) found.headOption + else { + val maxSpecific = found.filterNot({ + case (method, owner) => + isAbstractMethod(method) || { + val ownerTp = classBTypeFromClassNode(owner) + found exists { + case (other, otherOwner) => + (other ne method) && { + val otherTp = classBTypeFromClassNode(otherOwner) + otherTp.isSubtypeOf(ownerTp).get + } + } + } + }) + // (*) note that if there's no single, non-abstract, maximally-specific method, the jvm + // method resolution (jvms-5.4.3.3) returns any of the non-private, non-static parent + // methods at random (abstract or concrete). + // we chose not to do this here, to prevent the inliner from potentially inlining the + // wrong method. in other words, we guarantee that a concrete method is only returned if + // it resolves deterministically. + // however, there may be multiple abstract methods inherited. in this case we *do* want + // to return a result to allow performing accessibility checks in the inliner. note that + // for accessibility it does not matter which of these methods is return, as they are all + // non-private (i.e., public, protected is not possible, jvms-4.1). + // the remaining case (when there's no max-specific method, but some non-abstract one) + // does not occur in bytecode generated by scalac or javac. we return no result in this + // case. this may at worst prevent some optimizations from happening. + if (maxSpecific.size == 1) maxSpecific.headOption + else if (found.forall(p => isAbstractMethod(p._1))) found.headOption // (*) + else None + } + Right(result.map(p => (p._1, p._2.name))) } // In a MethodInsnNode, the `owner` field may be an array descriptor, for example when invoking `clone`. We don't have a method node to return in this case. - if (ownerInternalNameOrArrayDescriptor.charAt(0) == '[') - Left(MethodNotFound(name, descriptor, ownerInternalNameOrArrayDescriptor, Nil)) - else - methodNodeImpl(ownerInternalNameOrArrayDescriptor).left.map(MethodNotFound(name, descriptor, ownerInternalNameOrArrayDescriptor, _)) + if (ownerInternalNameOrArrayDescriptor.charAt(0) == '[') { + Left(MethodNotFound(name, descriptor, ownerInternalNameOrArrayDescriptor, None)) + } else { + def notFound(cnf: Option[ClassNotFound]) = Left(MethodNotFound(name, descriptor, ownerInternalNameOrArrayDescriptor, cnf)) + val res: Either[ClassNotFound, Option[(MethodNode, InternalName)]] = classNode(ownerInternalNameOrArrayDescriptor).flatMap(c => + findInSuperClasses(c) flatMap { + case None => findInInterfaces(c) + case res => Right(res) + } + ) + res match { + case Left(e) => notFound(Some(e)) + case Right(None) => notFound(None) + case Right(Some(res)) => Right(res) + } + } } private def parseClass(internalName: InternalName): Either[ClassNotFound, ClassNode] = { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala index f8c16e34bd..63906d80e5 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala @@ -99,6 +99,10 @@ object BytecodeUtils { methodNode.name == INSTANCE_CONSTRUCTOR_NAME || methodNode.name == CLASS_CONSTRUCTOR_NAME } + def isPublicMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_PUBLIC) != 0 + + def isPrivateMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_PRIVATE) != 0 + def isStaticMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_STATIC) != 0 def isAbstractMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_ABSTRACT) != 0 @@ -107,10 +111,12 @@ object BytecodeUtils { def isNativeMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_NATIVE) != 0 - def hasCallerSensitiveAnnotation(methodNode: MethodNode) = methodNode.visibleAnnotations != null && methodNode.visibleAnnotations.asScala.exists(_.desc == "Lsun/reflect/CallerSensitive;") + def hasCallerSensitiveAnnotation(methodNode: MethodNode): Boolean = methodNode.visibleAnnotations != null && methodNode.visibleAnnotations.asScala.exists(_.desc == "Lsun/reflect/CallerSensitive;") def isFinalClass(classNode: ClassNode): Boolean = (classNode.access & ACC_FINAL) != 0 + def isInterface(classNode: ClassNode): Boolean = (classNode.access & ACC_INTERFACE) != 0 + def isFinalMethod(methodNode: MethodNode): Boolean = (methodNode.access & (ACC_FINAL | ACC_PRIVATE | ACC_STATIC)) != 0 def isStrictfpMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_STRICT) != 0 diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala index 156c80d5a1..d241acf7b1 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala @@ -131,19 +131,19 @@ class CallGraph[BT <: BTypes](val btypes: BT) { (method, declarationClass) <- byteCodeRepository.methodNode(call.owner, call.name, call.desc): Either[OptimizerWarning, (MethodNode, InternalName)] (declarationClassNode, source) <- byteCodeRepository.classNodeAndSource(declarationClass): Either[OptimizerWarning, (ClassNode, Source)] } yield { - val declarationClassBType = classBTypeFromClassNode(declarationClassNode) - val info = analyzeCallsite(method, declarationClassBType, call, source) - import info._ - Callee( - callee = method, - calleeDeclarationClass = declarationClassBType, - safeToInline = safeToInline, - canInlineFromSource = canInlineFromSource, - annotatedInline = annotatedInline, - annotatedNoInline = annotatedNoInline, - samParamTypes = info.samParamTypes, - calleeInfoWarning = warning) - } + val declarationClassBType = classBTypeFromClassNode(declarationClassNode) + val info = analyzeCallsite(method, declarationClassBType, call, source) + import info._ + Callee( + callee = method, + calleeDeclarationClass = declarationClassBType, + safeToInline = safeToInline, + canInlineFromSource = canInlineFromSource, + annotatedInline = annotatedInline, + annotatedNoInline = annotatedNoInline, + samParamTypes = info.samParamTypes, + calleeInfoWarning = warning) + } val argInfos = computeArgInfos(callee, call, prodCons) @@ -388,12 +388,11 @@ class CallGraph[BT <: BTypes](val btypes: BT) { * @param calleeInfoWarning An inliner warning if some information was not available while * gathering the information about this callee. */ - final case class Callee( - callee: MethodNode, calleeDeclarationClass: btypes.ClassBType, - safeToInline: Boolean, canInlineFromSource: Boolean, - annotatedInline: Boolean, annotatedNoInline: Boolean, - samParamTypes: IntMap[btypes.ClassBType], - calleeInfoWarning: Option[CalleeInfoWarning]) { + final case class Callee(callee: MethodNode, calleeDeclarationClass: btypes.ClassBType, + safeToInline: Boolean, canInlineFromSource: Boolean, + annotatedInline: Boolean, annotatedNoInline: Boolean, + samParamTypes: IntMap[btypes.ClassBType], + calleeInfoWarning: Option[CalleeInfoWarning]) { override def toString = s"Callee($calleeDeclarationClass.${callee.name})" } diff --git a/src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/AggregateClassPath.scala index f97d97548e..6b435542a3 100644 --- a/src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/AggregateClassPath.scala @@ -12,15 +12,16 @@ import scala.tools.nsc.util.ClassRepresentation /** * A classpath unifying multiple class- and sourcepath entries. - * Flat classpath can obtain entries for classes and sources independently + * The 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 { +case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath { override def findClassFile(className: String): Option[AbstractFile] = { @tailrec - def find(aggregates: Seq[FlatClassPath]): Option[AbstractFile] = + def find(aggregates: Seq[ClassPath]): Option[AbstractFile] = if (aggregates.nonEmpty) { val classFile = aggregates.head.findClassFile(className) if (classFile.isDefined) classFile @@ -30,22 +31,24 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl find(aggregates) } - override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = { - val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className) - + override def findClass(className: String): Option[ClassRepresentation] = { @tailrec - def findEntry[T <: ClassRepClassPathEntry](aggregates: Seq[FlatClassPath], getEntries: FlatClassPath => Seq[T]): Option[T] = + def findEntry(aggregates: Seq[ClassPath], isSource: Boolean): Option[ClassRepresentation] = if (aggregates.nonEmpty) { - val entry = getEntries(aggregates.head).find(_.name == simpleClassName) + val entry = aggregates.head.findClass(className) match { + case s @ Some(_: SourceFileEntry) if isSource => s + case s @ Some(_: ClassFileEntry) if !isSource => s + case _ => None + } if (entry.isDefined) entry - else findEntry(aggregates.tail, getEntries) + else findEntry(aggregates.tail, isSource) } else None - val classEntry = findEntry(aggregates, classesGetter(pkg)) - val sourceEntry = findEntry(aggregates, sourcesGetter(pkg)) + val classEntry = findEntry(aggregates, isSource = false) + val sourceEntry = findEntry(aggregates, isSource = true) (classEntry, sourceEntry) match { - case (Some(c), Some(s)) => Some(ClassAndSourceFilesEntry(c.file, s.file)) + case (Some(c: ClassFileEntry), Some(s: SourceFileEntry)) => Some(ClassAndSourceFilesEntry(c.file, s.file)) case (c @ Some(_), _) => c case (_, s) => s } @@ -63,16 +66,16 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl } override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = - getDistinctEntries(classesGetter(inPackage)) + getDistinctEntries(_.classes(inPackage)) override private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = - getDistinctEntries(sourcesGetter(inPackage)) + getDistinctEntries(_.sources(inPackage)) - override private[nsc] def list(inPackage: String): FlatClassPathEntries = { + override private[nsc] def list(inPackage: String): ClassPathEntries = { val (packages, classesAndSources) = aggregates.map(_.list(inPackage)).unzip val distinctPackages = packages.flatten.distinct val distinctClassesAndSources = mergeClassesAndSources(classesAndSources: _*) - FlatClassPathEntries(distinctPackages, distinctClassesAndSources) + ClassPathEntries(distinctPackages, distinctClassesAndSources) } /** @@ -80,11 +83,11 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl * 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] = { + private def mergeClassesAndSources(entries: Seq[ClassRepresentation]*): Seq[ClassRepresentation] = { // based on the implementation from MergedClassPath var count = 0 val indices = collection.mutable.HashMap[String, Int]() - val mergedEntries = new ArrayBuffer[ClassRepClassPathEntry](1024) + val mergedEntries = new ArrayBuffer[ClassRepresentation](1024) for { partOfEntries <- entries @@ -109,7 +112,7 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl mergedEntries.toIndexedSeq } - private def getDistinctEntries[EntryType <: ClassRepClassPathEntry](getEntries: FlatClassPath => Seq[EntryType]): Seq[EntryType] = { + private def getDistinctEntries[EntryType <: ClassRepresentation](getEntries: ClassPath => Seq[EntryType]): Seq[EntryType] = { val seenNames = collection.mutable.HashSet[String]() val entriesBuffer = new ArrayBuffer[EntryType](1024) for { @@ -121,19 +124,16 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl } entriesBuffer.toIndexedSeq } - - private def classesGetter(pkg: String) = (cp: FlatClassPath) => cp.classes(pkg) - private def sourcesGetter(pkg: String) = (cp: FlatClassPath) => cp.sources(pkg) } -object AggregateFlatClassPath { - def createAggregate(parts: FlatClassPath*): FlatClassPath = { - val elems = new ArrayBuffer[FlatClassPath]() +object AggregateClassPath { + def createAggregate(parts: ClassPath*): ClassPath = { + val elems = new ArrayBuffer[ClassPath]() parts foreach { - case AggregateFlatClassPath(ps) => elems ++= ps + case AggregateClassPath(ps) => elems ++= ps case p => elems += p } if (elems.size == 1) elems.head - else AggregateFlatClassPath(elems.toIndexedSeq) + else AggregateClassPath(elems.toIndexedSeq) } } diff --git a/src/compiler/scala/tools/nsc/classpath/ClassPath.scala b/src/compiler/scala/tools/nsc/classpath/ClassPath.scala new file mode 100644 index 0000000000..08bd98b1d8 --- /dev/null +++ b/src/compiler/scala/tools/nsc/classpath/ClassPath.scala @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014 Contributor. All rights reserved. + */ +package scala.tools.nsc.classpath + +import scala.reflect.io.AbstractFile +import scala.tools.nsc.util.ClassRepresentation + +case class ClassPathEntries(packages: Seq[PackageEntry], classesAndSources: Seq[ClassRepresentation]) + +object ClassPathEntries { + import scala.language.implicitConversions + // to have working unzip method + implicit def entry2Tuple(entry: ClassPathEntries): (Seq[PackageEntry], Seq[ClassRepresentation]) = (entry.packages, entry.classesAndSources) +} + +trait ClassFileEntry extends ClassRepresentation { + def file: AbstractFile +} + +trait SourceFileEntry extends ClassRepresentation { + 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 ClassRepresentation { + 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/ClassPathFactory.scala b/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala index 9bf4e3f779..3a29f1ba11 100644 --- a/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala +++ b/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala @@ -3,47 +3,49 @@ */ package scala.tools.nsc.classpath -import scala.reflect.io.AbstractFile +import scala.reflect.io.{AbstractFile, VirtualDirectory} +import scala.tools.nsc.Settings +import FileUtils.AbstractFileOps 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. + * Provides factory methods for 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. */ -trait ClassPathFactory[T] { - +class ClassPathFactory(settings: Settings) { /** - * Create a new classpath based on the abstract file. - */ - def newClassPath(file: AbstractFile): T + * Create a new classpath based on the abstract file. + */ + def newClassPath(file: AbstractFile): ClassPath = ClassPathFactory.newClassPath(file, settings) /** - * Creators for sub classpaths which preserve this context. - */ - def sourcesInPath(path: String): List[T] + * Creators for sub classpaths which preserve this context. + */ + def sourcesInPath(path: String): List[ClassPath] = + for { + file <- expandPath(path, expandStar = false) + dir <- Option(AbstractFile getDirectory file) + } yield createSourcePath(dir) + - def expandPath(path: String, expandStar: Boolean = true): List[String] = ClassPath.expandPath(path, expandStar) + def expandPath(path: String, expandStar: Boolean = true): List[String] = scala.tools.nsc.util.ClassPath.expandPath(path, expandStar) - def expandDir(extdir: String): List[String] = ClassPath.expandDir(extdir) + def expandDir(extdir: String): List[String] = scala.tools.nsc.util.ClassPath.expandDir(extdir) - def contentsOfDirsInPath(path: String): List[T] = + def contentsOfDirsInPath(path: String): List[ClassPath] = for { dir <- expandPath(path, expandStar = false) name <- expandDir(dir) entry <- Option(AbstractFile.getDirectory(name)) } yield newClassPath(entry) - def classesInExpandedPath(path: String): IndexedSeq[T] = + def classesInExpandedPath(path: String): IndexedSeq[ClassPath] = 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)) + if (useManifestClassPath) scala.tools.nsc.util.ClassPath.manifests.map(url => newClassPath(AbstractFile getResources url)) else Nil // Internal @@ -52,4 +54,25 @@ trait ClassPathFactory[T] { file <- expandPath(path, expand) dir <- Option(AbstractFile.getDirectory(file)) } yield newClassPath(dir) + + private def createSourcePath(file: AbstractFile): ClassPath = + if (file.isJarOrZip) + ZipAndJarSourcePathFactory.create(file, settings) + else if (file.isDirectory) + new DirectorySourcePath(file.file) + else + sys.error(s"Unsupported sourcepath element: $file") +} + +object ClassPathFactory { + def newClassPath(file: AbstractFile, settings: Settings): ClassPath = file match { + case vd: VirtualDirectory => VirtualDirectoryClassPath(vd) + case _ => + if (file.isJarOrZip) + ZipAndJarClassPathFactory.create(file, settings) + else if (file.isDirectory) + new DirectoryClassPath(file.file) + else + sys.error(s"Unsupported classpath element: $file") + } } diff --git a/src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala index e3964dfa78..aba941e043 100644 --- a/src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala @@ -5,9 +5,8 @@ package scala.tools.nsc.classpath import java.io.File import java.net.URL -import scala.reflect.io.AbstractFile -import scala.reflect.io.PlainFile -import scala.tools.nsc.util.ClassRepresentation +import scala.reflect.io.{AbstractFile, PlainFile} +import scala.tools.nsc.util.{ClassPath, ClassRepresentation} import FileUtils._ /** @@ -17,7 +16,7 @@ import FileUtils._ * when we have a name of a package. * It abstracts over the file representation to work with both JFile and AbstractFile. */ -trait DirectoryLookup[FileEntryType <: ClassRepClassPathEntry] extends FlatClassPath { +trait DirectoryLookup[FileEntryType <: ClassRepresentation] extends ClassPath { type F val dir: F @@ -33,7 +32,7 @@ trait DirectoryLookup[FileEntryType <: ClassRepClassPathEntry] extends FlatClass protected def isMatchingFile(f: F): Boolean private def getDirectory(forPackage: String): Option[F] = { - if (forPackage == FlatClassPath.RootPackage) { + if (forPackage == ClassPath.RootPackage) { Some(dir) } else { val packageDirName = FileUtils.dirPath(forPackage) @@ -60,7 +59,7 @@ trait DirectoryLookup[FileEntryType <: ClassRepClassPathEntry] extends FlatClass files.map(f => createFileEntry(toAbstractFile(f))) } - private[nsc] def list(inPackage: String): FlatClassPathEntries = { + private[nsc] def list(inPackage: String): ClassPathEntries = { val dirForPackage = getDirectory(inPackage) val files: Array[F] = dirForPackage match { case None => emptyFiles @@ -75,11 +74,11 @@ trait DirectoryLookup[FileEntryType <: ClassRepClassPathEntry] extends FlatClass else if (isMatchingFile(file)) fileBuf += createFileEntry(toAbstractFile(file)) } - FlatClassPathEntries(packageBuf, fileBuf) + ClassPathEntries(packageBuf, fileBuf) } } -trait JFileDirectoryLookup[FileEntryType <: ClassRepClassPathEntry] extends DirectoryLookup[FileEntryType] { +trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends DirectoryLookup[FileEntryType] { type F = File protected def emptyFiles: Array[File] = Array.empty @@ -102,8 +101,8 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepClassPathEntry] extends Dire def asClassPathStrings: Seq[String] = Seq(dir.getPath) } -case class DirectoryFlatClassPath(dir: File) extends JFileDirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { - override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = findClassFile(className) map ClassFileEntryImpl +case class DirectoryClassPath(dir: File) extends JFileDirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { + override def findClass(className: String): Option[ClassRepresentation] = findClassFile(className) map ClassFileEntryImpl def findClassFile(className: String): Option[AbstractFile] = { val relativePath = FileUtils.dirPath(className) @@ -121,13 +120,13 @@ case class DirectoryFlatClassPath(dir: File) extends JFileDirectoryLookup[ClassF private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage) } -case class DirectoryFlatSourcePath(dir: File) extends JFileDirectoryLookup[SourceFileEntryImpl] with NoClassPaths { +case class DirectorySourcePath(dir: File) extends JFileDirectoryLookup[SourceFileEntryImpl] with NoClassPaths { def asSourcePathString: String = asClassPathString protected def createFileEntry(file: AbstractFile): SourceFileEntryImpl = SourceFileEntryImpl(file) protected def isMatchingFile(f: File): Boolean = endsScalaOrJava(f.getName) - override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = findSourceFile(className) map SourceFileEntryImpl + override def findClass(className: String): Option[ClassRepresentation] = findSourceFile(className) map SourceFileEntryImpl private def findSourceFile(className: String): Option[AbstractFile] = { val relativePath = FileUtils.dirPath(className) diff --git a/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala deleted file mode 100644 index e95ffe02e3..0000000000 --- a/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 overridden, if we can create the more efficient - // solution for a 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): (Seq[PackageEntry], Seq[ClassRepClassPathEntry]) = (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 deleted file mode 100644 index 463301696e..0000000000 --- a/src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2014 Contributor. All rights reserved. - */ -package scala.tools.nsc.classpath - -import scala.reflect.io.VirtualDirectory -import scala.tools.nsc.Settings -import scala.tools.nsc.io.AbstractFile -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] { - def newClassPath(file: AbstractFile): FlatClassPath = FlatClassPathFactory.newClassPath(file, settings) - - 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") -} - -object FlatClassPathFactory { - def newClassPath(file: AbstractFile, settings: Settings): FlatClassPath = file match { - case vd: VirtualDirectory => VirtualDirectoryFlatClassPath(vd) - case _ => - if (file.isJarOrZip) - ZipAndJarFlatClassPathFactory.create(file, settings) - else if (file.isDirectory) - new DirectoryFlatClassPath(file.file) - else - sys.error(s"Unsupported classpath element: $file") - } -} diff --git a/src/compiler/scala/tools/nsc/classpath/PackageNameUtils.scala b/src/compiler/scala/tools/nsc/classpath/PackageNameUtils.scala index c907d565d2..39b0d78135 100644 --- a/src/compiler/scala/tools/nsc/classpath/PackageNameUtils.scala +++ b/src/compiler/scala/tools/nsc/classpath/PackageNameUtils.scala @@ -3,7 +3,7 @@ */ package scala.tools.nsc.classpath -import scala.tools.nsc.classpath.FlatClassPath.RootPackage +import scala.tools.nsc.util.ClassPath.RootPackage /** * Common methods related to package names represented as String diff --git a/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryFlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala index 06cdab583c..8df0c3743d 100644 --- a/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryFlatClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala @@ -4,8 +4,9 @@ import scala.tools.nsc.util.ClassRepresentation import scala.reflect.io.{Path, PlainFile, VirtualDirectory, AbstractFile} import FileUtils._ import java.net.URL +import scala.tools.nsc.util.ClassPath -case class VirtualDirectoryFlatClassPath(dir: VirtualDirectory) extends FlatClassPath with DirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { +case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath with DirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { type F = AbstractFile protected def emptyFiles: Array[AbstractFile] = Array.empty @@ -23,7 +24,7 @@ case class VirtualDirectoryFlatClassPath(dir: VirtualDirectory) extends FlatClas def asURLs: Seq[URL] = Seq(new URL(dir.name)) def asClassPathStrings: Seq[String] = Seq(dir.path) - override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = findClassFile(className) map ClassFileEntryImpl + override def findClass(className: String): Option[ClassRepresentation] = findClassFile(className) map ClassFileEntryImpl def findClassFile(className: String): Option[AbstractFile] = { val relativePath = FileUtils.dirPath(className) diff --git a/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala b/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala index 6ec3805d8b..fe74e5f874 100644 --- a/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala +++ b/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala @@ -6,7 +6,8 @@ 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.reflect.io.{AbstractFile, FileZipArchive, ManifestResources} +import scala.tools.nsc.util.ClassPath import scala.tools.nsc.Settings import FileUtils._ @@ -19,16 +20,16 @@ import FileUtils._ * 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] + private val cache = collection.mutable.Map.empty[AbstractFile, ClassPath] - def create(zipFile: AbstractFile, settings: Settings): FlatClassPath = { + def create(zipFile: AbstractFile, settings: Settings): ClassPath = { if (settings.YdisableFlatCpCaching) createForZipFile(zipFile) else createUsingCache(zipFile, settings) } - protected def createForZipFile(zipFile: AbstractFile): FlatClassPath + protected def createForZipFile(zipFile: AbstractFile): ClassPath - private def createUsingCache(zipFile: AbstractFile, settings: Settings): FlatClassPath = cache.synchronized { + private def createUsingCache(zipFile: AbstractFile, settings: Settings): ClassPath = cache.synchronized { def newClassPathInstance = { if (settings.verbose || settings.Ylogcp) println(s"$zipFile is not yet in the classpath cache") @@ -39,11 +40,11 @@ sealed trait ZipAndJarFileLookupFactory { } /** - * Manages creation of flat classpath for class files placed in zip and jar files. + * Manages creation of 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) +object ZipAndJarClassPathFactory extends ZipAndJarFileLookupFactory { + private case class ZipArchiveClassPath(zipFile: File) extends ZipArchiveFileLookup[ClassFileEntryImpl] with NoSourcePaths { @@ -65,7 +66,7 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { * 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 { + private case class ManifestResourcesClassPath(file: ManifestResources) extends ClassPath with NoSourcePaths { override def findClassFile(className: String): Option[AbstractFile] = { val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className) classes(pkg).find(_.name == simpleClassName).map(_.file) @@ -75,8 +76,8 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { override def asURLs: Seq[URL] = file.toURLs() - import ManifestResourcesFlatClassPath.PackageFileInfo - import ManifestResourcesFlatClassPath.PackageInfo + import ManifestResourcesClassPath.PackageFileInfo + import ManifestResourcesClassPath.PackageInfo /** * A cache mapping package name to abstract file for package directory and subpackages of given package. @@ -114,8 +115,8 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { } val subpackages = getSubpackages(file) - packages.put(FlatClassPath.RootPackage, PackageFileInfo(file, subpackages)) - traverse(FlatClassPath.RootPackage, subpackages, collection.mutable.Queue()) + packages.put(ClassPath.RootPackage, PackageFileInfo(file, subpackages)) + traverse(ClassPath.RootPackage, subpackages, collection.mutable.Queue()) packages } @@ -132,21 +133,21 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { (for (file <- pkg if file.isClass) yield ClassFileEntryImpl(file))(collection.breakOut) } - override private[nsc] def list(inPackage: String): FlatClassPathEntries = FlatClassPathEntries(packages(inPackage), classes(inPackage)) + override private[nsc] def list(inPackage: String): ClassPathEntries = ClassPathEntries(packages(inPackage), classes(inPackage)) } - private object ManifestResourcesFlatClassPath { + private object ManifestResourcesClassPath { case class PackageFileInfo(packageFile: AbstractFile, subpackages: Seq[AbstractFile]) case class PackageInfo(packageName: String, subpackages: List[AbstractFile]) } - override protected def createForZipFile(zipFile: AbstractFile): FlatClassPath = + override protected def createForZipFile(zipFile: AbstractFile): ClassPath = if (zipFile.file == null) createWithoutUnderlyingFile(zipFile) - else ZipArchiveFlatClassPath(zipFile.file) + else ZipArchiveClassPath(zipFile.file) private def createWithoutUnderlyingFile(zipFile: AbstractFile) = zipFile match { case manifestRes: ManifestResources => - ManifestResourcesFlatClassPath(manifestRes) + ManifestResourcesClassPath(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) @@ -154,11 +155,11 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { } /** - * Manages creation of flat classpath for source files placed in zip and jar files. + * Manages creation of 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) +object ZipAndJarSourcePathFactory extends ZipAndJarFileLookupFactory { + private case class ZipArchiveSourcePath(zipFile: File) extends ZipArchiveFileLookup[SourceFileEntryImpl] with NoClassPaths { @@ -170,5 +171,5 @@ object ZipAndJarFlatSourcePathFactory extends ZipAndJarFileLookupFactory { override protected def isRequiredFileType(file: AbstractFile): Boolean = file.isScalaOrJavaSource } - override protected def createForZipFile(zipFile: AbstractFile): FlatClassPath = ZipArchiveFlatSourcePath(zipFile.file) + override protected def createForZipFile(zipFile: AbstractFile): ClassPath = ZipArchiveSourcePath(zipFile.file) } diff --git a/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala b/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala index a24d989306..9c147cf8cc 100644 --- a/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala +++ b/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala @@ -9,13 +9,14 @@ import scala.collection.Seq import scala.reflect.io.AbstractFile import scala.reflect.io.FileZipArchive import FileUtils.AbstractFileOps +import scala.tools.nsc.util.{ClassPath, ClassRepresentation} /** * 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 { +trait ZipArchiveFileLookup[FileEntryType <: ClassRepresentation] extends ClassPath { val zipFile: File assert(zipFile != null, "Zip file in ZipArchiveFileLookup cannot be null") @@ -39,7 +40,7 @@ trait ZipArchiveFileLookup[FileEntryType <: ClassRepClassPathEntry] extends Flat entry <- dirEntry.iterator if isRequiredFileType(entry) } yield createFileEntry(entry) - override private[nsc] def list(inPackage: String): FlatClassPathEntries = { + override private[nsc] def list(inPackage: String): ClassPathEntries = { val foundDirEntry = findDirEntry(inPackage) foundDirEntry map { dirEntry => @@ -53,8 +54,8 @@ trait ZipArchiveFileLookup[FileEntryType <: ClassRepClassPathEntry] extends Flat else if (isRequiredFileType(entry)) fileBuf += createFileEntry(entry) } - FlatClassPathEntries(pkgBuf, fileBuf) - } getOrElse FlatClassPathEntries(Seq.empty, Seq.empty) + ClassPathEntries(pkgBuf, fileBuf) + } getOrElse ClassPathEntries(Seq.empty, Seq.empty) } private def findDirEntry(pkg: String): Option[archive.DirEntry] = { diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 7b98011759..9a0d86a94d 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -200,7 +200,6 @@ trait ScalaSettings extends AbsScalaSettings val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overridden 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.Flat) val YdisableFlatCpCaching = BooleanSetting ("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.") val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() @@ -389,8 +388,3 @@ trait ScalaSettings extends AbsScalaSettings None } } - -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 4f5589fd7c..b36d5d4ef1 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -10,10 +10,8 @@ import classfile.ClassfileParser import java.io.IOException 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 } +import scala.reflect.io.{AbstractFile, NoAbstractFile} +import scala.tools.nsc.util.{ClassPath, ClassRepresentation} /** This class ... * @@ -154,7 +152,7 @@ abstract class SymbolLoaders { /** Initialize toplevel class and module symbols in `owner` from class path representation `classRep` */ - def initializeFromClassPath(owner: Symbol, classRep: ClassRepresentation[AbstractFile]) { + def initializeFromClassPath(owner: Symbol, classRep: ClassRepresentation) { ((classRep.binary, classRep.source) : @unchecked) match { case (Some(bin), Some(src)) if platform.needCompile(bin, src) && !binaryOnly(owner, classRep.name) => @@ -247,41 +245,11 @@ abstract class SymbolLoaders { } /** - * Load contents of a package - */ - class PackageLoader(classpath: ClassPath[AbstractFile]) extends SymbolLoader with FlagAgnosticCompleter { - protected def description = s"package loader ${classpath.name}" - - protected def doComplete(root: Symbol) { - assert(root.isPackageClass, root) - // Time travel to a phase before refchecks avoids an initialization issue. `openPackageModule` - // creates a module symbol and invokes invokes `companionModule` while the `infos` field is - // still null. This calls `isModuleNotMethod`, which forces the `info` if run after refchecks. - enteringPhase(phaseBeforeRefchecks) { - root.setInfo(new PackageClassInfoType(newScope, root)) - - if (!root.isRoot) { - for (classRep <- classpath.classes) { - initializeFromClassPath(root, classRep) - } - } - if (!root.isEmptyPackageClass) { - for (pkg <- classpath.packages) { - enterPackage(root, pkg.name, new PackageLoader(pkg)) - } - - openPackageModule(root) - } - } - } - } - - /** * Loads contents of a package */ - class PackageLoaderUsingFlatClassPath(packageName: String, classPath: FlatClassPath) extends SymbolLoader with FlagAgnosticCompleter { + class PackageLoader(packageName: String, classPath: ClassPath) extends SymbolLoader with FlagAgnosticCompleter { protected def description = { - val shownPackageName = if (packageName == FlatClassPath.RootPackage) "<root package>" else packageName + val shownPackageName = if (packageName == ClassPath.RootPackage) "<root package>" else packageName s"package loader $shownPackageName" } @@ -298,9 +266,9 @@ abstract class SymbolLoaders { val fullName = pkg.name val name = - if (packageName == FlatClassPath.RootPackage) fullName + if (packageName == ClassPath.RootPackage) fullName else fullName.substring(packageName.length + 1) - val packageLoader = new PackageLoaderUsingFlatClassPath(fullName, classPath) + val packageLoader = new PackageLoader(fullName, classPath) enterPackage(root, name, packageLoader) } @@ -329,10 +297,7 @@ abstract class SymbolLoaders { val loaders = SymbolLoaders.this.asInstanceOf[SymbolLoadersRefined] - override def classFileLookup: util.ClassFileLookup[AbstractFile] = settings.YclasspathImpl.value match { - case ClassPathRepresentationType.Recursive => platform.classPath - case ClassPathRepresentationType.Flat => platform.flatClassPath - } + override def classPath: ClassPath = platform.classPath } 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 fffd48d145..0533d420cd 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -8,16 +8,16 @@ package tools.nsc package symtab package classfile -import java.io.{ File, IOException } +import java.io.{File, IOException} import java.lang.Integer.toHexString -import scala.collection.{ mutable, immutable } -import scala.collection.mutable.{ ListBuffer, ArrayBuffer } +import scala.collection.{immutable, mutable} +import scala.collection.mutable.{ArrayBuffer, ListBuffer} import scala.annotation.switch -import scala.reflect.internal.{ JavaAccFlags } -import scala.reflect.internal.pickling.{PickleBuffer, ByteCodecs} +import scala.reflect.internal.JavaAccFlags +import scala.reflect.internal.pickling.{ByteCodecs, PickleBuffer} import scala.reflect.io.NoAbstractFile +import scala.tools.nsc.util.ClassPath import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.util.ClassFileLookup /** This abstract class implements a class file parser. * @@ -43,8 +43,8 @@ abstract class ClassfileParser { */ protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol - /** The way of the class file lookup used by the compiler. */ - def classFileLookup: ClassFileLookup[AbstractFile] + /** The compiler classpath. */ + def classPath: ClassPath import definitions._ import scala.reflect.internal.ClassfileConstants._ @@ -357,7 +357,7 @@ abstract class ClassfileParser { } private def loadClassSymbol(name: Name): Symbol = { - val file = classFileLookup findClassFile name.toString getOrElse { + val file = classPath 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) @@ -1079,7 +1079,7 @@ abstract class ClassfileParser { for (entry <- innerClasses.entries) { // create a new class member for immediate inner classes if (entry.outerName == currentClass) { - val file = classFileLookup.findClassFile(entry.externalName.toString) + val file = classPath.findClassFile(entry.externalName.toString) enterClassAndModule(entry, file.getOrElse(NoAbstractFile)) } } diff --git a/src/compiler/scala/tools/nsc/util/ClassFileLookup.scala b/src/compiler/scala/tools/nsc/util/ClassFileLookup.scala deleted file mode 100644 index 5d8831a607..0000000000 --- a/src/compiler/scala/tools/nsc/util/ClassFileLookup.scala +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2014 Contributor. All rights reserved. - */ -package scala.tools.nsc.util - -import scala.tools.nsc.Settings -import scala.tools.nsc.classpath.{AggregateFlatClassPath, FlatClassPath, FlatClassPathFactory} -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 -} - -object ClassFileLookup { - def createForFile(f: AbstractFile, current: ClassFileLookup[AbstractFile], settings: Settings): ClassFileLookup[AbstractFile] = current match { - case cp: ClassPath[_] => cp.context.newClassPath(f) - case _: FlatClassPath => FlatClassPathFactory.newClassPath(f, settings) - } - - def createAggregate(elems: Iterable[ClassFileLookup[AbstractFile]], current: ClassFileLookup[AbstractFile]): ClassFileLookup[AbstractFile] = { - assert(elems.nonEmpty) - if (elems.size == 1) elems.head - else current match { - case cp: ClassPath[_] => - new MergedClassPath(elems.asInstanceOf[Iterable[ClassPath[AbstractFile]]], cp.context) - - case _: FlatClassPath => - AggregateFlatClassPath.createAggregate(elems.asInstanceOf[Iterable[FlatClassPath]].toSeq : _*) - } - } -} - -/** - * 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 e5af1edadb..cef2fc4bbf 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -7,28 +7,61 @@ package scala.tools.nsc package util -import io.{ AbstractFile, Directory, File, Jar } +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 scala.reflect.internal.util.StringOps.splitWhere -import scala.tools.nsc.classpath.FileUtils import File.pathSeparator -import FileUtils.endsClass -import FileUtils.endsScalaOrJava import Jar.isJarOrZip -/** <p> - * This module provides star expansion of '-classpath' option arguments, behaves the same as - * java, see [[http://docs.oracle.com/javase/6/docs/technotes/tools/windows/classpath.html]] - * </p> - * - * @author Stepan Koltsov - */ +/** + * A representation of the compiler's class- or sourcepath. + */ +trait ClassPath { + import scala.tools.nsc.classpath._ + def asURLs: Seq[URL] + + /** 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): ClassPathEntries + + /** + * 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(className: String): Option[ClassRepresentation] = { + // A default implementation which should be overridden, if we can create the more efficient + // solution for a given type of ClassPath + val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className) + + val foundClassFromClassFiles = classes(pkg).find(_.name == simpleClassName) + def findClassInSources = sources(pkg).find(_.name == simpleClassName) + + foundClassFromClassFiles orElse findClassInSources + } + def findClassFile(className: String): Option[AbstractFile] + + def asClassPathStrings: Seq[String] + + /** The whole classpath in the form of one String. + */ + def asClassPathString: String = ClassPath.join(asClassPathStrings: _*) + // 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 +} + object ClassPath { - import scala.language.postfixOps + val RootPackage = "" /** Expand single path entry */ private def expandS(pattern: String): List[String] = { @@ -36,14 +69,14 @@ object ClassPath { /* Get all subdirectories, jars, zips out of a directory. */ def lsDir(dir: Directory, filt: String => Boolean = _ => true) = - dir.list filter (x => filt(x.name) && (x.isDirectory || isJarOrZip(x))) map (_.path) toList + dir.list.filter(x => filt(x.name) && (x.isDirectory || isJarOrZip(x))).map(_.path).toList if (pattern == "*") lsDir(Directory(".")) else if (pattern endsWith wildSuffix) lsDir(Directory(pattern dropRight 2)) else if (pattern contains '*') { try { val regexp = ("^" + pattern.replaceAllLiterally("""\*""", """.*""") + "$").r - lsDir(Directory(pattern).parent, regexp findFirstIn _ isDefined) + lsDir(Directory(pattern).parent, regexp.findFirstIn(_).isDefined) } catch { case _: PatternSyntaxException => List(pattern) } } @@ -51,7 +84,7 @@ object ClassPath { } /** Split classpath using platform-dependent path separator */ - def split(path: String): List[String] = (path split pathSeparator).toList filterNot (_ == "") distinct + def split(path: String): List[String] = (path split pathSeparator).toList.filterNot(_ == "").distinct /** Join classpath using platform-dependent path separator */ def join(paths: String*): String = paths filterNot (_ == "") mkString pathSeparator @@ -68,9 +101,10 @@ object ClassPath { def expandDir(extdir: String): List[String] = { AbstractFile getDirectory extdir match { case null => Nil - case dir => dir filter (_.isClassContainer) map (x => new java.io.File(dir.file, x.name) getPath) toList + case dir => dir.filter(_.isClassContainer).map(x => new java.io.File(dir.file, x.name).getPath).toList } } + /** Expand manifest jar classpath entries: these are either urls, or paths * relative to the location of the jar. */ @@ -88,301 +122,30 @@ object ClassPath { try Some(new URL(spec)) catch { case _: MalformedURLException => None } - /** A class modeling aspects of a ClassPath which should be - * propagated to any classpaths it creates. - */ - 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. - */ - def isValidName(name: String): Boolean = true - - /** Filters for assessing validity of various entities. - */ - def validClassFile(name: String) = endsClass(name) && isValidName(name) - def validPackage(name: String) = (name != "META-INF") && (name != "") && (name.charAt(0) != '.') - def validSourceFile(name: String) = endsScalaOrJava(name) - - /** From the representation to its identifier. - */ - def toBinaryName(rep: T): String - - 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 manifests: List[java.net.URL] = { import scala.collection.JavaConverters._ val resources = Thread.currentThread().getContextClassLoader().getResources("META-INF/MANIFEST.MF") resources.asScala.filter(_.getProtocol == "jar").toList } - class JavaContext extends ClassPathContext[AbstractFile] { - def toBinaryName(rep: AbstractFile) = { - val name = rep.name - assert(endsClass(name), name) - FileUtils.stripClassExtension(name) - } + @deprecated("Shim for sbt's compiler interface", since = "2.12") + sealed abstract class ClassPathContext - def newClassPath(dir: AbstractFile) = new DirectoryClassPath(dir, this) - } - - object DefaultJavaContext extends JavaContext - - /** From the source file to its identifier. - */ - def toSourceName(f: AbstractFile): String = FileUtils.stripSourceExtension(f.name) + @deprecated("Shim for sbt's compiler interface", since = "2.12") + sealed abstract class JavaContext } -import ClassPath._ - -/** - * Represents a package which contains classes and other packages - */ -abstract class ClassPath[T] extends ClassFileLookup[T] { - /** - * The short name of the package (without prefix) - */ +trait ClassRepresentation { def name: String - - /** - * A String representing the origin of this classpath element, if known. - * For example, the path of the directory or jar. - */ - def origin: Option[String] = None - - /** Info which should be propagated to any sub-classpaths. - */ - def context: ClassPathContext[T] - - /** Lists of entities. - */ - def classes: IndexedSeq[ClassRepresentation[T]] - def packages: IndexedSeq[ClassPath[T]] - def sourcepaths: IndexedSeq[AbstractFile] - - /** The entries this classpath is composed of. In class `ClassPath` it's just the singleton list containing `this`. - * Subclasses such as `MergedClassPath` typically return lists with more elements. - */ - def entries: IndexedSeq[ClassPath[T]] = IndexedSeq(this) - - /** Merge classpath of `platform` and `urls` into merged classpath */ - def mergeUrlsIntoClassPath(urls: URL*): MergedClassPath[T] = { - // Collect our new jars/directories and add them to the existing set of classpaths - val allEntries = - (entries ++ - urls.map(url => context.newClassPath(io.AbstractFile.getURL(url))) - ).distinct - - // Combine all of our classpaths (old and new) into one merged classpath - new MergedClassPath(allEntries, context) - } - - /** - * Represents classes which can be loaded with a ClassfileLoader and/or SourcefileLoader. - */ - case class ClassRep(binary: Option[T], source: Option[AbstractFile]) extends ClassRepresentation[T] { - def name: String = binary match { - case Some(x) => context.toBinaryName(x) - case _ => - assert(source.isDefined) - toSourceName(source.get) - } - } - - /** Filters for assessing validity of various entities. - */ - def validClassFile(name: String) = context.validClassFile(name) - def validPackage(name: String) = context.validPackage(name) - def validSourceFile(name: String) = context.validSourceFile(name) - - /** - * Find a ClassRep given a class name of the form "package.subpackage.ClassName". - * Does not support nested classes on .NET - */ - 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: ClassRepresentation[T] => x - case x => throw new FatalError("Unexpected ClassRep '%s' found searching for name '%s'".format(x, name)) - } - case _ => - classes find (_.name == name) - } - - override def findClassFile(name: String): Option[AbstractFile] = - findClass(name) match { - case Some(ClassRepresentation(Some(x: AbstractFile), _)) => Some(x) - case _ => None - } - - 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 - } - override def hashCode = sortString.hashCode() -} - -/** - * 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 = dir.toURLs() - def asClassPathString = dir.path - val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq(dir) - - private def traverse() = { - val classBuf = immutable.Vector.newBuilder[ClassRep] - val packageBuf = immutable.Vector.newBuilder[SourcePath[T]] - dir foreach { f => - if (!f.isDirectory && validSourceFile(f.name)) - classBuf += ClassRep(None, Some(f)) - else if (f.isDirectory && validPackage(f.name)) - packageBuf += new SourcePath[T](f, context) - } - (packageBuf.result(), classBuf.result()) - } - - lazy val (packages, classes) = traverse() - override def toString() = "sourcepath: "+ dir.toString() + def binary: Option[AbstractFile] + def source: Option[AbstractFile] } -/** - * 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 = dir.toURLs(default = Seq(new URL(name))) - def asClassPathString = dir.path - val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq() - - // calculates (packages, classes) in one traversal. - private def traverse() = { - val classBuf = immutable.Vector.newBuilder[ClassRep] - val packageBuf = immutable.Vector.newBuilder[DirectoryClassPath] - dir foreach { - f => - // Optimization: We assume the file was not changed since `dir` called - // `Path.apply` and categorized existent files as `Directory` - // or `File` (avoids IO operation JFile.isDirectory()). - val isDirectory = f match { - case pf: io.PlainFile => pf.givenPath match { - case _: io.Directory => true - case _: io.File => false - case _ => f.isDirectory - } - case _ => - f.isDirectory - } - if (!isDirectory && validClassFile(f.name)) - classBuf += ClassRep(Some(f), None) - else if (isDirectory && validPackage(f.name)) - packageBuf += new DirectoryClassPath(f, context) - } - (packageBuf.result(), classBuf.result()) - } +@deprecated("Shim for sbt's compiler interface", since = "2.12") +sealed abstract class DirectoryClassPath - lazy val (packages, classes) = traverse() - override def toString() = "directory classpath: "+ origin.getOrElse("?") -} +@deprecated("Shim for sbt's compiler interface", since = "2.12") +sealed abstract class MergedClassPath -/** - * A classpath unifying multiple class- and sourcepath entries. - */ -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) - - def name = entries.head.name - def asURLs = (entries flatMap (_.asURLs)).toList - 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) : _*) - - lazy val classes: IndexedSeq[ClassRepresentation[T]] = { - var count = 0 - val indices = mutable.HashMap[String, Int]() - val cls = new mutable.ArrayBuffer[ClassRepresentation[T]](1024) - - for (e <- entries; c <- e.classes) { - val name = c.name - if (indices contains name) { - val idx = indices(name) - val existing = cls(idx) - - if (existing.binary.isEmpty && c.binary.isDefined) - cls(idx) = ClassRep(binary = c.binary, source = existing.source) - if (existing.source.isEmpty && c.source.isDefined) - cls(idx) = ClassRep(binary = existing.binary, source = c.source) - } - else { - indices(name) = count - cls += c - count += 1 - } - } - cls.toIndexedSeq - } - - lazy val packages: IndexedSeq[ClassPath[T]] = { - var count = 0 - val indices = mutable.HashMap[String, Int]() - val pkg = new mutable.ArrayBuffer[ClassPath[T]](256) - - for (e <- entries; p <- e.packages) { - val name = p.name - if (indices contains name) { - val idx = indices(name) - pkg(idx) = addPackage(pkg(idx), p) - } - else { - indices(name) = count - pkg += p - count += 1 - } - } - pkg.toIndexedSeq - } - - private def addPackage(to: ClassPath[T], pkg: ClassPath[T]) = { - val newEntries: IndexedSeq[ClassPath[T]] = to match { - case cp: MergedClassPath[_] => cp.entries :+ pkg - case _ => IndexedSeq(to, pkg) - } - 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)) - } - - override def toString() = "merged classpath "+ entries.mkString("(", "\n", ")") -} - -/** - * The classpath when compiling with target:jvm. Binary files (classfiles) are represented - * as AbstractFile. nsc.io.ZipArchive is used to view zip/jar archives as directories. - */ -class JavaClassPath( - containers: IndexedSeq[ClassPath[AbstractFile]], - context: JavaContext) -extends MergedClassPath[AbstractFile](containers, context) { } +@deprecated("Shim for sbt's compiler interface", since = "2.12") +sealed abstract class JavaClassPath diff --git a/src/compiler/scala/tools/reflect/ReflectMain.scala b/src/compiler/scala/tools/reflect/ReflectMain.scala index 8d8418945a..7d82910699 100644 --- a/src/compiler/scala/tools/reflect/ReflectMain.scala +++ b/src/compiler/scala/tools/reflect/ReflectMain.scala @@ -5,12 +5,12 @@ import scala.reflect.internal.util.ScalaClassLoader import scala.tools.nsc.Driver import scala.tools.nsc.Global import scala.tools.nsc.Settings -import scala.tools.util.PathResolverFactory +import scala.tools.util.PathResolver object ReflectMain extends Driver { private def classloaderFromSettings(settings: Settings) = { - val classPathURLs = PathResolverFactory.create(settings).resultAsURLs + val classPathURLs = new PathResolver(settings).resultAsURLs ScalaClassLoader.fromURLs(classPathURLs, getClass.getClassLoader) } diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index 9decc99c8d..c351b6ace1 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -10,12 +10,10 @@ package util import java.net.URL import scala.tools.reflect.WrappedProperties.AccessControl import scala.tools.nsc.Settings -import scala.tools.nsc.util.{ ClassFileLookup, ClassPath, JavaClassPath } -import scala.reflect.io.{ File, Directory, Path, AbstractFile } -import ClassPath.{ JavaContext, DefaultJavaContext, split } +import scala.tools.nsc.util.ClassPath +import scala.reflect.io.{Directory, File, Path} import PartialFunction.condOpt -import scala.tools.nsc.classpath.{ AggregateFlatClassPath, ClassPathFactory, FlatClassPath, FlatClassPathFactory } -import scala.tools.nsc.settings.ClassPathRepresentationType +import scala.tools.nsc.classpath._ // Loosely based on the draft specification at: // https://wiki.scala-lang.org/display/SIW/Classpath @@ -40,7 +38,7 @@ object PathResolver { } /** pretty print class path */ - def ppcp(s: String) = split(s) match { + def ppcp(s: String) = ClassPath.split(s) match { case Nil => "" case Seq(x) => x case xs => xs.mkString(EOL, EOL, "") @@ -164,13 +162,6 @@ object PathResolver { |}""".asLines } - @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 - } - /** 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. @@ -182,28 +173,19 @@ object PathResolver { } else { val settings = new Settings() val rest = settings.processArguments(args.toList, processAll = false)._2 - val pr = PathResolverFactory.create(settings) + val pr = new PathResolver(settings) println("COMMAND: 'scala %s'".format(args.mkString(" "))) println("RESIDUAL: 'scala %s'\n".format(rest.mkString(" "))) pr.result match { - case cp: JavaClassPath => - cp.show() - case cp: AggregateFlatClassPath => + case cp: AggregateClassPath => println(s"ClassPath has ${cp.aggregates.size} entries and results in:\n${cp.asClassPathStrings}") } } } -trait PathResolverResult { - def result: ClassFileLookup[AbstractFile] - - def resultAsURLs: Seq[URL] = result.asURLs -} - -abstract class PathResolverBase[BaseClassPathType <: ClassFileLookup[AbstractFile], ResultClassPathType <: BaseClassPathType] -(settings: Settings, classPathFactory: ClassPathFactory[BaseClassPathType]) - extends PathResolverResult { +final class PathResolver(settings: Settings) { + private val classPathFactory = new ClassPathFactory(settings) import PathResolver.{ AsLines, Defaults, ppcp } @@ -251,7 +233,7 @@ abstract class PathResolverBase[BaseClassPathType <: ClassFileLookup[AbstractFil import classPathFactory._ // Assemble the elements! - def basis = List[Traversable[BaseClassPathType]]( + def basis = List[Traversable[ClassPath]]( classesInPath(javaBootClassPath), // 1. The Java bootstrap class path. contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path. classesInExpandedPath(javaUserClassPath), // 3. The Java application class path. @@ -282,7 +264,7 @@ abstract class PathResolverBase[BaseClassPathType <: ClassFileLookup[AbstractFil import PathResolver.MkLines - def result: ResultClassPathType = { + def result: ClassPath = { val cp = computeResult() if (settings.Ylogcp) { Console print f"Classpath built from ${settings.toConciseString} %n" @@ -295,34 +277,11 @@ abstract class PathResolverBase[BaseClassPathType <: ClassFileLookup[AbstractFil cp } + def resultAsURLs: Seq[URL] = result.asURLs + @deprecated("Use resultAsURLs instead of this one", "2.11.5") def asURLs: List[URL] = resultAsURLs.toList - protected def computeResult(): ResultClassPathType + private def computeResult(): ClassPath = AggregateClassPath(containers.toIndexedSeq) } -class PathResolver(settings: Settings, context: JavaContext) - extends PathResolverBase[ClassPath[AbstractFile], JavaClassPath](settings, context) { - - def this(settings: Settings) = this(settings, 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/eclipse/partest/.classpath b/src/eclipse/partest/.classpath index ab9ede5a19..22afd65d43 100644 --- a/src/eclipse/partest/.classpath +++ b/src/eclipse/partest/.classpath @@ -9,6 +9,6 @@ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/partest/scala-partest_2.12.0-M4-1.0.13.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/partest/scala-partest_2.12.0-M4-1.0.14.jar"/> <classpathentry kind="output" path="build-quick-partest-extras"/> </classpath> diff --git a/src/eclipse/scaladoc/.classpath b/src/eclipse/scaladoc/.classpath index 40387983b0..b4450df4ef 100644 --- a/src/eclipse/scaladoc/.classpath +++ b/src/eclipse/scaladoc/.classpath @@ -8,6 +8,6 @@ <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/scaladoc/scala-xml_2.12.0-M4-1.0.5.jar"/> <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/scaladoc/scala-parser-combinators_2.12.0-M4-1.0.4.jar"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/partest/scala-partest_2.12.0-M4-1.0.13.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/partest/scala-partest_2.12.0-M4-1.0.14.jar"/> <classpathentry kind="output" path="build-quick-scaladoc"/> </classpath> diff --git a/src/library/scala/Product.scala b/src/library/scala/Product.scala index 9cd38ed148..f3a96fb333 100644 --- a/src/library/scala/Product.scala +++ b/src/library/scala/Product.scala @@ -19,7 +19,7 @@ package scala */ trait Product extends Any with Equals { /** The n^th^ element of this product, 0-based. In other words, for a - * product `A(x,,1,,, ..., x,,k,,)`, returns `x,,(n+1),,` where `0 < n < k`. + * product `A(x,,1,,, ..., x,,k,,)`, returns `x,,(n+1),,` where `0 <= n < k`. * * @param n the index of the element to return * @throws IndexOutOfBoundsException diff --git a/src/library/scala/collection/immutable/BitSet.scala b/src/library/scala/collection/immutable/BitSet.scala index 6bb1f116fe..ecf3326c7f 100644 --- a/src/library/scala/collection/immutable/BitSet.scala +++ b/src/library/scala/collection/immutable/BitSet.scala @@ -68,6 +68,8 @@ object BitSet extends BitSetFactory[BitSet] { /** The empty bitset */ val empty: BitSet = new BitSet1(0L) + private def createSmall(a: Long, b: Long): BitSet = if (b == 0L) new BitSet1(a) else new BitSet2(a, b) + /** A builder that takes advantage of mutable BitSets. */ def newBuilder: Builder[Int, BitSet] = new Builder[Int, BitSet] { private[this] val b = new mutable.BitSet @@ -84,7 +86,7 @@ object BitSet extends BitSetFactory[BitSet] { val len = elems.length if (len == 0) empty else if (len == 1) new BitSet1(elems(0)) - else if (len == 2) new BitSet2(elems(0), elems(1)) + else if (len == 2) createSmall(elems(0), elems(1)) else { val a = new Array[Long](len) Array.copy(elems, 0, a, 0, len) @@ -99,7 +101,7 @@ object BitSet extends BitSetFactory[BitSet] { val len = elems.length if (len == 0) empty else if (len == 1) new BitSet1(elems(0)) - else if (len == 2) new BitSet2(elems(0), elems(1)) + else if (len == 2) createSmall(elems(0), elems(1)) else new BitSetN(elems) } @@ -109,7 +111,7 @@ object BitSet extends BitSetFactory[BitSet] { protected def word(idx: Int) = if (idx == 0) elems else 0L protected def updateWord(idx: Int, w: Long): BitSet = if (idx == 0) new BitSet1(w) - else if (idx == 1) new BitSet2(elems, w) + else if (idx == 1) createSmall(elems, w) else fromBitMaskNoCopy(updateArray(Array(elems), idx, w)) override def head: Int = if (elems == 0L) throw new NoSuchElementException("Empty BitSet") @@ -124,7 +126,7 @@ object BitSet extends BitSetFactory[BitSet] { protected def word(idx: Int) = if (idx == 0) elems0 else if (idx == 1) elems1 else 0L protected def updateWord(idx: Int, w: Long): BitSet = if (idx == 0) new BitSet2(w, elems1) - else if (idx == 1) new BitSet2(elems0, w) + else if (idx == 1) createSmall(elems0, w) else fromBitMaskNoCopy(updateArray(Array(elems0, elems1), idx, w)) override def head: Int = if (elems0 == 0L) { @@ -135,7 +137,7 @@ object BitSet extends BitSetFactory[BitSet] { override def tail: BitSet = if (elems0 == 0L) { if (elems1 == 0L) throw new NoSuchElementException("Empty BitSet") - new BitSet2(elems0, elems1 - java.lang.Long.lowestOneBit(elems1)) + createSmall(elems0, elems1 - java.lang.Long.lowestOneBit(elems1)) } else new BitSet2(elems0 - java.lang.Long.lowestOneBit(elems0), elems1) } diff --git a/src/library/scala/collection/mutable/PriorityQueue.scala b/src/library/scala/collection/mutable/PriorityQueue.scala index b4112c03dd..d5b7673c37 100644 --- a/src/library/scala/collection/mutable/PriorityQueue.scala +++ b/src/library/scala/collection/mutable/PriorityQueue.scala @@ -200,9 +200,7 @@ sealed class PriorityQueue[A](implicit val ord: Ordering[A]) * @return A reversed priority queue. */ def reverse = { - val revq = new PriorityQueue[A]()(new scala.math.Ordering[A] { - def compare(x: A, y: A) = ord.compare(y, x) - }) + val revq = new PriorityQueue[A]()(ord.reverse) for (i <- 1 until resarr.length) revq += resarr(i) revq } diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index b10aad0ecc..d9d3d572e8 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -29,8 +29,8 @@ import scala.reflect.ClassTag * val f: Future[String] = Future { * s + " future!" * } - * f onSuccess { - * case msg => println(msg) + * f foreach { + * msg => println(msg) * } * }}} * diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 6d3d015b1a..bd55fb5d04 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -182,6 +182,9 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * val namedYears = for (m <- namedDate findAllMatchIn dates) yield m group "year" * }}} * + * Group names supplied to the constructor are preferred to inline group names + * when retrieving matched groups by name. Not all platforms support inline names. + * * This constructor does not support options as flags, which must be * supplied as inline flags in the pattern string: `(?idmsux-idmsux)`. * @@ -578,6 +581,9 @@ object Regex { */ trait MatchData { + /** Basically, wraps a platform Matcher. */ + protected def matcher: Matcher + /** The source from which the match originated */ val source: CharSequence @@ -650,16 +656,25 @@ object Regex { private lazy val nameToIndex: Map[String, Int] = Map[String, Int]() ++ ("" :: groupNames.toList).zipWithIndex - /** Returns the group with given name. + /** Returns the group with the given name. + * + * Uses explicit group names when supplied; otherwise, + * queries the underlying implementation for inline named groups. + * Not all platforms support inline group names. * * @param id The group name * @return The requested group - * @throws NoSuchElementException if the requested group name is not defined + * @throws IllegalArgumentException if the requested group name is not defined */ - def group(id: String): String = nameToIndex.get(id) match { - case None => throw new NoSuchElementException("group name "+id+" not defined") - case Some(index) => group(index) - } + def group(id: String): String = ( + if (groupNames.isEmpty) + matcher group id + else + nameToIndex.get(id) match { + case Some(index) => group(index) + case None => matcher group id + } + ) /** The matched string; equivalent to `matched.toString`. */ override def toString = matched @@ -667,7 +682,7 @@ object Regex { /** Provides information about a successful match. */ class Match(val source: CharSequence, - private[matching] val matcher: Matcher, + protected[matching] val matcher: Matcher, val groupNames: Seq[String]) extends MatchData { /** The index of the first matched character. */ diff --git a/src/partest-extras/scala/tools/partest/BytecodeTest.scala b/src/partest-extras/scala/tools/partest/BytecodeTest.scala index 290b7b434e..532dfd2a73 100644 --- a/src/partest-extras/scala/tools/partest/BytecodeTest.scala +++ b/src/partest-extras/scala/tools/partest/BytecodeTest.scala @@ -1,10 +1,10 @@ package scala.tools.partest -import scala.tools.nsc.util.JavaClassPath import scala.collection.JavaConverters._ -import scala.tools.asm.{ClassWriter, ClassReader} +import scala.tools.asm.{ClassReader, ClassWriter} import scala.tools.asm.tree._ -import java.io.{File => JFile, InputStream} +import java.io.{InputStream, File => JFile} + import AsmNode._ /** @@ -125,12 +125,16 @@ abstract class BytecodeTest { cn } - protected lazy val classpath: JavaClassPath = { - import scala.tools.nsc.util.ClassPath.DefaultJavaContext + protected lazy val classpath: scala.tools.nsc.util.ClassPath = { + import scala.tools.nsc.classpath.AggregateClassPath + import scala.tools.nsc.classpath.ClassPathFactory import scala.tools.util.PathResolver.Defaults + import scala.tools.nsc.Settings // logic inspired by scala.tools.util.PathResolver implementation - val containers = DefaultJavaContext.classesInExpandedPath(Defaults.javaUserClassPath) - new JavaClassPath(containers, DefaultJavaContext) + // `Settings` is used to check YdisableFlatCpCaching in ZipArchiveFlatClassPath + val factory = new ClassPathFactory(new Settings()) + val containers = factory.classesInExpandedPath(Defaults.javaUserClassPath) + new AggregateClassPath(containers) } } diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 37b07ce775..9b0d66f41c 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -613,7 +613,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive loadBytes[String]("scala.reflect.ScalaSignature") match { case Some(ssig) => info(s"unpickling Scala $clazz and $module, owner = ${clazz.owner}") - val bytes = ssig.getBytes + val bytes = ssig.getBytes(java.nio.charset.StandardCharsets.UTF_8) val len = ByteCodecs.decode(bytes) assignAssociatedFile(clazz, module, jclazz) unpickler.unpickle(bytes take len, 0, clazz, module, jclazz.getName) @@ -622,7 +622,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive loadBytes[Array[String]]("scala.reflect.ScalaLongSignature") match { case Some(slsig) => info(s"unpickling Scala $clazz and $module with long Scala signature") - val encoded = slsig flatMap (_.getBytes) + val encoded = slsig flatMap (_.getBytes(java.nio.charset.StandardCharsets.UTF_8)) val len = ByteCodecs.decode(encoded) val decoded = encoded.take(len) assignAssociatedFile(clazz, module, jclazz) diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index 893bde42ab..8c91242b36 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -11,18 +11,18 @@ import PartialFunction.cond import scala.language.implicitConversions import scala.beans.BeanProperty import scala.collection.mutable -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.PathResolverFactory +import scala.concurrent.{ExecutionContext, Future} +import scala.reflect.runtime.{universe => ru} +import scala.reflect.{ClassTag, classTag} +import scala.reflect.internal.util.{BatchSourceFile, SourceFile} import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.typechecker.{ TypeStrings, StructuredTypeStrings } +import scala.tools.nsc.typechecker.{StructuredTypeStrings, TypeStrings} import scala.tools.nsc.util._ import ScalaClassLoader.URLClassLoader import scala.tools.nsc.util.Exceptional.unwrap -import javax.script.{AbstractScriptEngine, Bindings, ScriptContext, ScriptEngine, ScriptEngineFactory, ScriptException, CompiledScript, Compilable} +import javax.script.{AbstractScriptEngine, Bindings, Compilable, CompiledScript, ScriptContext, ScriptEngine, ScriptEngineFactory, ScriptException} import java.net.URL +import scala.tools.util.PathResolver /** An interpreter for Scala code. * @@ -91,7 +91,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set def compilerClasspath: Seq[java.net.URL] = ( if (isInitializeComplete) global.classPath.asURLs - else PathResolverFactory.create(settings).resultAsURLs // the compiler's classpath + else new PathResolver(settings).resultAsURLs // the compiler's classpath ) def settings = initialSettings // Run the code body with the given boolean settings flipped to true. diff --git a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala index e3dc72b717..b9a4054ffc 100644 --- a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala +++ b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala @@ -7,9 +7,7 @@ package scala.tools.nsc.interpreter import scala.reflect.internal.util.RangePosition import scala.reflect.io.AbstractFile import scala.tools.nsc.backend.JavaPlatform -import scala.tools.nsc.settings.ClassPathRepresentationType -import scala.tools.nsc.util.ClassPath.DefaultJavaContext -import scala.tools.nsc.util.{ClassPath, MergedClassPath, DirectoryClassPath} +import scala.tools.nsc.util.ClassPath import scala.tools.nsc.{interactive, Settings} import scala.tools.nsc.reporters.StoreReporter import scala.tools.nsc.classpath._ @@ -58,12 +56,8 @@ trait PresentationCompilation { */ def newPresentationCompiler(): interactive.Global = { def mergedFlatClasspath = { - val replOutClasspath = FlatClassPathFactory.newClassPath(replOutput.dir, settings) - AggregateFlatClassPath(replOutClasspath :: global.platform.flatClassPath :: Nil) - } - def mergedRecursiveClasspath = { - val replOutClasspath: DirectoryClassPath = new DirectoryClassPath(replOutput.dir, DefaultJavaContext) - new MergedClassPath[AbstractFile](replOutClasspath :: global.platform.classPath :: Nil, DefaultJavaContext) + val replOutClasspath = ClassPathFactory.newClassPath(replOutput.dir, settings) + AggregateClassPath(replOutClasspath :: global.platform.classPath :: Nil) } def copySettings: Settings = { val s = new Settings(_ => () /* ignores "bad option -nc" errors, etc */) @@ -74,16 +68,9 @@ trait PresentationCompilation { val storeReporter: StoreReporter = new StoreReporter val interactiveGlobal = new interactive.Global(copySettings, storeReporter) { self => override lazy val platform: ThisPlatform = { - if (settings.YclasspathImpl.value == ClassPathRepresentationType.Flat) { - new JavaPlatform { - val global: self.type = self - override private[nsc] lazy val flatClassPath: FlatClassPath = mergedFlatClasspath - } - } else { - new JavaPlatform { - val global: self.type = self - override def classPath: ClassPath[AbstractFile] = mergedRecursiveClasspath - } + new JavaPlatform { + val global: self.type = self + override private[nsc] lazy val classPath: ClassPath = mergedFlatClasspath } } } diff --git a/src/repl/scala/tools/nsc/interpreter/package.scala b/src/repl/scala/tools/nsc/interpreter/package.scala index 56f1e65376..7de1dda15f 100644 --- a/src/repl/scala/tools/nsc/interpreter/package.scala +++ b/src/repl/scala/tools/nsc/interpreter/package.scala @@ -88,9 +88,6 @@ package object interpreter extends ReplConfig with ReplStrings { } } - if (filtered.isEmpty) - return "No implicits have been imported other than those in Predef." - filtered foreach { case (source, syms) => p("/* " + syms.size + " implicit members imported from " + source.fullName + " */") @@ -126,7 +123,14 @@ package object interpreter extends ReplConfig with ReplStrings { } p("") } - "" + + if (filtered.nonEmpty) + "" // side-effects above + else if (global.settings.nopredef || global.settings.noimports) + "No implicits have been imported." + else + "No implicits have been imported other than those in Predef." + } def kindCommandInternal(expr: String, verbose: Boolean): Unit = { diff --git a/src/scalap/scala/tools/scalap/Main.scala b/src/scalap/scala/tools/scalap/Main.scala index 3d2bfd7251..6a37bbc270 100644 --- a/src/scalap/scala/tools/scalap/Main.scala +++ b/src/scalap/scala/tools/scalap/Main.scala @@ -8,17 +8,12 @@ package scala package tools.scalap -import java.io.{ PrintStream, OutputStreamWriter, ByteArrayOutputStream } +import java.io.{ByteArrayOutputStream, OutputStreamWriter, PrintStream} import scala.reflect.NameTransformer 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 scala.tools.nsc.classpath.{AggregateClassPath, ClassPathFactory} +import scala.tools.nsc.util.ClassPath +import scala.tools.util.PathResolver import scalax.rules.scalasig._ /**The main object used to execute scalap on the command-line. @@ -101,7 +96,7 @@ class Main { /** Executes scalap with the given arguments and classpath for the * class denoted by `classname`. */ - def process(args: Arguments, path: ClassFileLookup[AbstractFile])(classname: String): Unit = { + def process(args: Arguments, path: ClassPath)(classname: String): Unit = { // find the classfile val encName = classname match { case "scala.AnyRef" => "java.lang.Object" @@ -145,7 +140,6 @@ object Main extends Main { val verbose = "-verbose" val version = "-version" - val classPathImplType = "-YclasspathImpl" val disableFlatClassPathCaching = "-YdisableFlatCpCaching" val logClassPath = "-Ylog-classpath" } @@ -183,7 +177,6 @@ object Main extends Main { 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 @@ -205,21 +198,16 @@ object Main extends Main { .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) + // TODO two temporary, hidden options to be able to test different classpath representations .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 Some(cp) => + AggregateClassPath(new ClassPathFactory(settings).classesInExpandedPath(cp)) case _ => settings.classpath.value = "." // include '.' in the default classpath SI-6669 - PathResolverFactory.create(settings).result + new PathResolver(settings).result } } |