diff options
Diffstat (limited to 'src/dotty/tools')
-rw-r--r-- | src/dotty/tools/dotc/config/JavaPlatform.scala | 75 | ||||
-rw-r--r-- | src/dotty/tools/dotc/config/PathResolver.scala | 271 | ||||
-rw-r--r-- | src/dotty/tools/dotc/config/Platform.scala | 38 | ||||
-rw-r--r-- | src/dotty/tools/dotc/config/Properties.scala | 165 | ||||
-rw-r--r-- | src/dotty/tools/dotc/config/Settings.scala | 26 | ||||
-rw-r--r-- | src/dotty/tools/dotc/config/WrappedProperties.scala | 34 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Contexts.scala | 14 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Flags.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/NameOps.scala | 63 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymbolLoaders.scala | 107 | ||||
-rw-r--r-- | src/dotty/tools/io/ClassPath.scala | 72 |
12 files changed, 733 insertions, 136 deletions
diff --git a/src/dotty/tools/dotc/config/JavaPlatform.scala b/src/dotty/tools/dotc/config/JavaPlatform.scala new file mode 100644 index 000000000..3c6ea3a8a --- /dev/null +++ b/src/dotty/tools/dotc/config/JavaPlatform.scala @@ -0,0 +1,75 @@ +package dotty.tools +package dotc +package config + +import io.{AbstractFile,ClassPath,JavaClassPath,MergedClassPath,DeltaClassPath} +import ClassPath.{ JavaContext, DefaultJavaContext } + +trait JavaPlatform /*extends Platform { + import global._ + import definitions._ + + type BinaryRepr = AbstractFile + + private var currentClassPath: Option[MergedClassPath[BinaryRepr]] = None + + def classPath: ClassPath[BinaryRepr] = { + if (currentClassPath.isEmpty) currentClassPath = Some(new PathResolver(settings).result) + currentClassPath.get + } + + /** Update classpath with a substituted subentry */ + def updateClassPath(subst: Map[ClassPath[BinaryRepr], ClassPath[BinaryRepr]]) = + currentClassPath = Some(new DeltaClassPath(currentClassPath.get, subst)) + + def rootLoader = new loaders.PackageLoader(classPath.asInstanceOf[ClassPath[platform.BinaryRepr]]) + // [Martin] Why do we need a cast here? + // The problem is that we cannot specify at this point that global.platform should be of type JavaPlatform. + // So we cannot infer that global.platform.BinaryRepr is AbstractFile. + // Ideally, we should be able to write at the top of the JavaPlatform trait: + // val global: Global { val platform: JavaPlatform } + // import global._ + // Right now, this does nothing because the concrete definition of platform in Global + // replaces the tighter abstract definition here. If we had DOT typing rules, the two + // types would be conjoined and everything would work out. Yet another reason to push for DOT. + + private def depAnalysisPhase = + if (settings.make.isDefault) Nil + else List(dependencyAnalysis) + + private def classEmitPhase = + if (settings.target.value == "jvm-1.5-fjbg") genJVM + else genASM + + def platformPhases = List( + flatten, // get rid of inner classes + classEmitPhase // generate .class files + ) ++ depAnalysisPhase + + lazy val externalEquals = getDecl(BoxesRunTimeClass, nme.equals_) + lazy val externalEqualsNumNum = getDecl(BoxesRunTimeClass, nme.equalsNumNum) + lazy val externalEqualsNumChar = getDecl(BoxesRunTimeClass, nme.equalsNumChar) + lazy val externalEqualsNumObject = getDecl(BoxesRunTimeClass, nme.equalsNumObject) + + /** We could get away with excluding BoxedBooleanClass for the + * purpose of equality testing since it need not compare equal + * to anything but other booleans, but it should be present in + * case this is put to other uses. + */ + def isMaybeBoxed(sym: Symbol) = { + (sym == ObjectClass) || + (sym == JavaSerializableClass) || + (sym == ComparableClass) || + (sym isNonBottomSubClass BoxedNumberClass) || + (sym isNonBottomSubClass BoxedCharacterClass) || + (sym isNonBottomSubClass BoxedBooleanClass) + } + + def newClassLoader(bin: AbstractFile): loaders.SymbolLoader = + new loaders.ClassfileLoader(bin) + + def doLoad(cls: ClassPath[BinaryRepr]#ClassRep): Boolean = true + + def needCompile(bin: AbstractFile, src: AbstractFile) = + src.lastModified >= bin.lastModified +}*/ diff --git a/src/dotty/tools/dotc/config/PathResolver.scala b/src/dotty/tools/dotc/config/PathResolver.scala new file mode 100644 index 000000000..6771e1465 --- /dev/null +++ b/src/dotty/tools/dotc/config/PathResolver.scala @@ -0,0 +1,271 @@ +package dotty.tools +package dotc +package config + +import java.net.{ URL, MalformedURLException } +import WrappedProperties.AccessControl +import io.{ ClassPath, JavaClassPath, File, Directory, Path, AbstractFile } +import ClassPath.{ JavaContext, DefaultJavaContext, join, split } +import PartialFunction.condOpt +import scala.language.postfixOps + +// Loosely based on the draft specification at: +// https://wiki.scala-lang.org/display/SW/Classpath + +object PathResolver { + + /* + // Imports property/environment functions which suppress + // security exceptions. + import AccessControl._ + + def firstNonEmpty(xs: String*) = xs find (_ != "") getOrElse "" + + /** Map all classpath elements to absolute paths and reconstruct the classpath. + */ + def makeAbsolute(cp: String) = ClassPath.map(cp, x => Path(x).toAbsolute.path) + + /** pretty print class path */ + def ppcp(s: String) = split(s) match { + case Nil => "" + case Seq(x) => x + case xs => xs map ("\n" + _) mkString + } + + /** Values found solely by inspecting environment or property variables. + */ + object Environment { + private def searchForBootClasspath = ( + systemProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse "" + ) + + /** Environment variables which java pays attention to so it + * seems we do as well. + */ + def classPathEnv = envOrElse("CLASSPATH", "") + def sourcePathEnv = envOrElse("SOURCEPATH", "") + + def javaBootClassPath = propOrElse("sun.boot.class.path", searchForBootClasspath) + def javaExtDirs = propOrEmpty("java.ext.dirs") + def scalaHome = propOrEmpty("scala.home") + def scalaExtDirs = propOrEmpty("scala.ext.dirs") + + /** The java classpath and whether to use it. */ + def javaUserClassPath = propOrElse("java.class.path", "") + def useJavaClassPath = propOrFalse("scala.usejavacp") + + override def toString = """ + |object Environment { + | scalaHome = %s (useJavaClassPath = %s) + | javaBootClassPath = <%d chars> + | javaExtDirs = %s + | javaUserClassPath = %s + | scalaExtDirs = %s + |}""".trim.stripMargin.format( + scalaHome, useJavaClassPath, + javaBootClassPath.length, + ppcp(javaExtDirs), + ppcp(javaUserClassPath), + ppcp(scalaExtDirs) + ) + } + + /** Default values based on those in Environment as interpreted according + * to the path resolution specification. + */ + object Defaults { + def scalaSourcePath = Environment.sourcePathEnv + def javaBootClassPath = Environment.javaBootClassPath + def javaUserClassPath = Environment.javaUserClassPath + def javaExtDirs = Environment.javaExtDirs + def useJavaClassPath = Environment.useJavaClassPath + + def scalaHome = Environment.scalaHome + def scalaHomeDir = Directory(scalaHome) + def scalaHomeExists = scalaHomeDir.isDirectory + def scalaLibDir = Directory(scalaHomeDir / "lib") + def scalaClassesDir = Directory(scalaHomeDir / "classes") + + def scalaLibAsJar = File(scalaLibDir / "scala-library.jar") + def scalaLibAsDir = Directory(scalaClassesDir / "library") + + def scalaLibDirFound: Option[Directory] = + if (scalaLibAsJar.isFile) Some(scalaLibDir) + else if (scalaLibAsDir.isDirectory) Some(scalaClassesDir) + else None + + def scalaLibFound = + if (scalaLibAsJar.isFile) scalaLibAsJar.path + else if (scalaLibAsDir.isDirectory) scalaLibAsDir.path + else "" + + // XXX It must be time for someone to figure out what all these things + // are intended to do. This is disabled here because it was causing all + // the scala jars to end up on the classpath twice: one on the boot + // classpath as set up by the runner (or regular classpath under -nobootcp) + // and then again here. + def scalaBootClassPath = "" + // scalaLibDirFound match { + // case Some(dir) if scalaHomeExists => + // val paths = ClassPath expandDir dir.path + // join(paths: _*) + // case _ => "" + // } + + def scalaExtDirs = Environment.scalaExtDirs + + def scalaPluginPath = (scalaHomeDir / "misc" / "scala-devel" / "plugins").path + + override def toString = """ + |object Defaults { + | scalaHome = %s + | javaBootClassPath = %s + | scalaLibDirFound = %s + | scalaLibFound = %s + | scalaBootClassPath = %s + | scalaPluginPath = %s + |}""".trim.stripMargin.format( + scalaHome, + ppcp(javaBootClassPath), + scalaLibDirFound, scalaLibFound, + ppcp(scalaBootClassPath), ppcp(scalaPluginPath) + ) + } + + def fromPathString(path: String, context: JavaContext = DefaultJavaContext): JavaClassPath = { + val s = new Settings() { + classpath = 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. + */ + def main(args: Array[String]): Unit = { + if (args.isEmpty) { + println(Environment) + println(Defaults) + } + else { + val settings = new Settings() + val rest = settings.processArguments(args.toList, false)._2 + val pr = new PathResolver(settings) + println(" COMMAND: 'scala %s'".format(args.mkString(" "))) + println("RESIDUAL: 'scala %s'\n".format(rest.mkString(" "))) + pr.result.show + } + } +} +import PathResolver.{ Defaults, Environment, firstNonEmpty, ppcp } + +class PathResolver(settings: Settings, context: JavaContext = DefaultJavaContext) { + + private def cmdLineOrElse(name: String, alt: String) = { + (commandLineFor(name) match { + case Some("") => None + case x => x + }) getOrElse alt + } + + private def commandLineFor(s: String): Option[String] = ??? + /*condOpt(s) { + case "javabootclasspath" => settings.javabootclasspath.value + case "javaextdirs" => settings.javaextdirs.value + case "bootclasspath" => settings.bootclasspath.value + case "extdirs" => settings.extdirs.value + case "classpath" | "cp" => settings.classpath.value + case "sourcepath" => settings.sourcepath.value + }*/ + + /** Calculated values based on any given command line options, falling back on + * those in Defaults. + */ + object Calculated { + def scalaHome = Defaults.scalaHome + def useJavaClassPath = settings.usejavacp.value || Defaults.useJavaClassPath + def javaBootClassPath = cmdLineOrElse("javabootclasspath", Defaults.javaBootClassPath) + def javaExtDirs = cmdLineOrElse("javaextdirs", Defaults.javaExtDirs) + def javaUserClassPath = if (useJavaClassPath) Defaults.javaUserClassPath else "" + def scalaBootClassPath = cmdLineOrElse("bootclasspath", Defaults.scalaBootClassPath) + def scalaExtDirs = cmdLineOrElse("extdirs", Defaults.scalaExtDirs) + /** Scaladoc doesn't need any bootstrapping, otherwise will create errors such as: + * [scaladoc] ../scala-trunk/src/reflect/scala/reflect/macros/Reifiers.scala:89: error: object api is not a member of package reflect + * [scaladoc] case class ReificationException(val pos: reflect.api.PositionApi, val msg: String) extends Throwable(msg) + * [scaladoc] ^ + * because the bootstrapping will look at the sourcepath and create package "reflect" in "<root>" + * and then when typing relative names, instead of picking <root>.scala.relect, typedIdentifier will pick up the + * <root>.reflect package created by the bootstrapping. Thus, no bootstrapping for scaladoc! + * TODO: we should refactor this as a separate -bootstrap option to have a clean implementation, no? */ + def sourcePath = if (!settings.isScaladoc) cmdLineOrElse("sourcepath", Defaults.scalaSourcePath) else "" + + /** Against my better judgment, giving in to martin here and allowing + * CLASSPATH to be used automatically. So for the user-specified part + * of the classpath: + * + * - If -classpath or -cp is given, it is that + * - Otherwise, if CLASSPATH is set, it is that + * - If neither of those, then "." is used. + */ + def userClassPath = ( + if (!settings.classpath.isDefault) + settings.classpath.value + else sys.env.getOrElse("CLASSPATH", ".") + ) + + import context._ + + // Assemble the elements! + 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. + classesInPath(scalaBootClassPath), // 4. The Scala boot class path. + contentsOfDirsInPath(scalaExtDirs), // 5. The Scala extension class path. + classesInExpandedPath(userClassPath), // 6. The Scala application class path. + sourcesInPath(sourcePath) // 7. The Scala source path. + ) + + lazy val containers = basis.flatten.distinct + + override def toString = """ + |object Calculated { + | scalaHome = %s + | javaBootClassPath = %s + | javaExtDirs = %s + | javaUserClassPath = %s + | useJavaClassPath = %s + | scalaBootClassPath = %s + | scalaExtDirs = %s + | userClassPath = %s + | sourcePath = %s + |}""".trim.stripMargin.format( + scalaHome, + ppcp(javaBootClassPath), ppcp(javaExtDirs), ppcp(javaUserClassPath), + useJavaClassPath, + ppcp(scalaBootClassPath), ppcp(scalaExtDirs), ppcp(userClassPath), + ppcp(sourcePath) + ) + } + + def containers = Calculated.containers + + lazy val result = { + val cp = new JavaClassPath(containers.toIndexedSeq, context) + if (settings.Ylogcp.value) { + Console.println("Classpath built from " + settings.toConciseString) + Console.println("Defaults: " + PathResolver.Defaults) + Console.println("Calculated: " + Calculated) + + val xs = (Calculated.basis drop 2).flatten.distinct + println("After java boot/extdirs classpath has %d entries:" format xs.size) + xs foreach (x => println(" " + x)) + } + cp + } + + def asURLs = result.asURLs + + */ +} diff --git a/src/dotty/tools/dotc/config/Platform.scala b/src/dotty/tools/dotc/config/Platform.scala new file mode 100644 index 000000000..73d32f193 --- /dev/null +++ b/src/dotty/tools/dotc/config/Platform.scala @@ -0,0 +1,38 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2012 LAMP/EPFL + * @author Paul Phillips + */ + +package dotty.tools +package dotc +package config + +import io.{ClassPath, AbstractFile} +import core.Contexts._ +import core.SymDenotations.ClassCompleter +import core.SymbolLoader + +/** The platform dependent pieces of Global. + */ +abstract class Platform(base: ContextBase) { + + /** The root symbol loader. */ + def rootLoader: ClassCompleter + + /** The compiler classpath. */ + def classPath: ClassPath + + /** Update classpath with a substitution that maps entries to entries */ + def updateClassPath(subst: Map[ClassPath, ClassPath]) + + /** Any platform-specific phases. */ + //def platformPhases: List[SubComponent] + + /** The various ways a boxed primitive might materialize at runtime. */ + def isMaybeBoxed(sym: Symbol): Boolean + + /** Create a new class loader to load class file `bin` */ + def newClassLoader(bin: AbstractFile): SymbolLoader + +} + diff --git a/src/dotty/tools/dotc/config/Properties.scala b/src/dotty/tools/dotc/config/Properties.scala new file mode 100644 index 000000000..4507568e4 --- /dev/null +++ b/src/dotty/tools/dotc/config/Properties.scala @@ -0,0 +1,165 @@ +package dotty.tools +package dotc +package config + +import java.io.{ IOException, PrintWriter } +import java.util.jar.Attributes.{ Name => AttributeName } + +/** Loads `library.properties` from the jar. */ +object Properties extends PropertiesTrait { + protected def propCategory = "library" + protected def pickJarBasedOn = classOf[Option[_]] + + /** Scala manifest attributes. + */ + val ScalaCompilerVersion = new AttributeName("Scala-Compiler-Version") +} + +trait PropertiesTrait { + protected def propCategory: String // specializes the remainder of the values + protected def pickJarBasedOn: Class[_] // props file comes from jar containing this + + /** The name of the properties file */ + protected val propFilename = "/" + propCategory + ".properties" + + /** The loaded properties */ + protected lazy val scalaProps: java.util.Properties = { + val props = new java.util.Properties + val stream = pickJarBasedOn getResourceAsStream propFilename + if (stream ne null) + quietlyDispose(props load stream, stream.close) + + props + } + + private def quietlyDispose(action: => Unit, disposal: => Unit) = + try { action } + finally { + try { disposal } + catch { case _: IOException => } + } + + def propIsSet(name: String) = System.getProperty(name) != null + def propIsSetTo(name: String, value: String) = propOrNull(name) == value + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def propOrEmpty(name: String) = propOrElse(name, "") + def propOrNull(name: String) = propOrElse(name, null) + def propOrNone(name: String) = Option(propOrNull(name)) + def propOrFalse(name: String) = propOrNone(name) exists (x => List("yes", "on", "true") contains x.toLowerCase) + def setProp(name: String, value: String) = System.setProperty(name, value) + def clearProp(name: String) = System.clearProperty(name) + + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def envOrNone(name: String) = Option(System getenv name) + + // for values based on propFilename + def scalaPropOrElse(name: String, alt: String): String = scalaProps.getProperty(name, alt) + def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") + def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)) + + /** The numeric portion of the runtime Scala version, if this is a final + * release. If for instance the versionString says "version 2.9.0.final", + * this would return Some("2.9.0"). + * + * @return Some(version) if this is a final release build, None if + * it is an RC, Beta, etc. or was built from source, or if the version + * cannot be read. + */ + val releaseVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if !(v endsWith "-SNAPSHOT") + } yield v + + /** The development Scala version, if this is not a final release. + * The precise contents are not guaranteed, but it aims to provide a + * unique repository identifier (currently the svn revision) in the + * fourth dotted segment if the running version was built from source. + * + * @return Some(version) if this is a non-final version, None if this + * is a final release or the version cannot be read. + */ + val developmentVersion = + for { + v <- scalaPropOrNone("maven.version.number") + if v endsWith "-SNAPSHOT" + ov <- scalaPropOrNone("version.number") + } yield ov + + /** Either the development or release version if known, otherwise + * the empty string. + */ + def versionNumberString = scalaPropOrEmpty("version.number") + + /** The version number of the jar this was loaded from plus "version " prefix, + * or "version (unknown)" if it cannot be determined. + */ + val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") + val copyrightString = scalaPropOrElse("copyright.string", "(c) 2002-2011 LAMP/EPFL") + + /** This is the encoding to use reading in source files, overridden with -encoding + * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. + */ + def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8") + def sourceReader = scalaPropOrElse("source.reader", "scala.tools.nsc.io.SourceReader") + + /** This is the default text encoding, overridden (unreliably) with + * `JAVA_OPTS="-Dfile.encoding=Foo"` + */ + def encodingString = propOrElse("file.encoding", "UTF-8") + + /** The default end of line character. + */ + def lineSeparator = propOrElse("line.separator", "\n") + + /** Various well-known properties. + */ + def javaClassPath = propOrEmpty("java.class.path") + def javaHome = propOrEmpty("java.home") + def javaVendor = propOrEmpty("java.vendor") + def javaVersion = propOrEmpty("java.version") + def javaVmInfo = propOrEmpty("java.vm.info") + def javaVmName = propOrEmpty("java.vm.name") + def javaVmVendor = propOrEmpty("java.vm.vendor") + def javaVmVersion = propOrEmpty("java.vm.version") + def osName = propOrEmpty("os.name") + def scalaHome = propOrEmpty("scala.home") + def tmpDir = propOrEmpty("java.io.tmpdir") + def userDir = propOrEmpty("user.dir") + def userHome = propOrEmpty("user.home") + def userName = propOrEmpty("user.name") + + /** Some derived values. + */ + def isWin = osName startsWith "Windows" + def isMac = javaVendor startsWith "Apple" + + // This is looking for javac, tools.jar, etc. + // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, + // and finally the system property based javaHome. + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + + def versionMsg = "Scala %s %s -- %s".format(propCategory, versionString, copyrightString) + def scalaCmd = if (isWin) "scala.bat" else "scala" + def scalacCmd = if (isWin) "scalac.bat" else "scalac" + + /** Can the java version be determined to be at least as high as the argument? + * Hard to properly future proof this but at the rate 1.7 is going we can leave + * the issue for our cyborg grandchildren to solve. + */ + def isJavaAtLeast(version: String) = { + val okVersions = version match { + case "1.5" => List("1.5", "1.6", "1.7") + case "1.6" => List("1.6", "1.7") + case "1.7" => List("1.7") + case _ => Nil + } + okVersions exists (javaVersion startsWith _) + } + + // provide a main method so version info can be obtained by running this + def main(args: Array[String]) { + val writer = new PrintWriter(Console.err, true) + writer println versionMsg + } +} diff --git a/src/dotty/tools/dotc/config/Settings.scala b/src/dotty/tools/dotc/config/Settings.scala new file mode 100644 index 000000000..83c7a747f --- /dev/null +++ b/src/dotty/tools/dotc/config/Settings.scala @@ -0,0 +1,26 @@ +package dotty.tools.dotc +package config + + +class Settings { + + protected def defaultClasspath = sys.env.getOrElse("CLASSPATH", ".") + + protected implicit def mkSetting[T](x: T): Setting[T] = new Setting(x) + + var default: Settings = this + + var classpath: Setting[String] = defaultClasspath + var debug: Setting[Boolean] = false + var verbose: Setting[Boolean] = false + + var XmaxClassfileName: Setting[Int] = 255 + + var YtermConflict: Setting[String] = "error" + + def processArguments(arguments: List[String], processAll: Boolean): (Boolean, List[String]) = ??? + + +} + +case class Setting[T](value: T)
\ No newline at end of file diff --git a/src/dotty/tools/dotc/config/WrappedProperties.scala b/src/dotty/tools/dotc/config/WrappedProperties.scala new file mode 100644 index 000000000..07972b99b --- /dev/null +++ b/src/dotty/tools/dotc/config/WrappedProperties.scala @@ -0,0 +1,34 @@ +package dotty.tools +package dotc +package config + +import java.security.AccessControlException + +/** For placing a wrapper function around property functions. + * Motivated by places like google app engine throwing exceptions + * on property lookups. + */ +trait WrappedProperties extends PropertiesTrait { + def wrap[T](body: => T): Option[T] + + protected def propCategory = "wrapped" + protected def pickJarBasedOn = this.getClass + + override def propIsSet(name: String) = wrap(super.propIsSet(name)) exists (x => x) + override def propOrElse(name: String, alt: String) = wrap(super.propOrElse(name, alt)) getOrElse alt + override def setProp(name: String, value: String) = wrap(super.setProp(name, value)).orNull + override def clearProp(name: String) = wrap(super.clearProp(name)).orNull + override def envOrElse(name: String, alt: String) = wrap(super.envOrElse(name, alt)) getOrElse alt + override def envOrNone(name: String) = wrap(super.envOrNone(name)).flatten + + def systemProperties: Iterator[(String, String)] = { + import scala.collection.JavaConverters._ + wrap(System.getProperties.asScala.iterator) getOrElse Iterator.empty + } +} + +object WrappedProperties { + object AccessControl extends WrappedProperties { + def wrap[T](body: => T) = try Some(body) catch { case _: AccessControlException => None } + } +} diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index e2fe9822f..d7c8609f1 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -10,6 +10,7 @@ import Symbols._ import TypeComparers._, Printers._, NameOps._, SymDenotations._ import collection.mutable import collection.immutable.BitSet +import config.{Settings, Platform} object Contexts { @@ -53,12 +54,16 @@ object Contexts { protected def owner_=(owner: Symbol) = _owner = owner def owner: Symbol = _owner + private[this] var _settings: Settings = _ + protected def settings_=(settings: Settings) = _settings = settings + def settings: Settings = _settings + def phase: Phase = ??? // phase(period.phaseId) def enclClass: Context = ??? def erasedTypes: Boolean = ??? def debug: Boolean = ??? -// def settings: Settings = ??? def warning(msg: String) = ??? + def inform(msg: String) = ??? def fresh: FreshContext = { val newctx = super.clone.asInstanceOf[FreshContext] @@ -90,8 +95,7 @@ object Contexts { } class ContextBase extends Transformers.TransformerBase - with Printers.PrinterBase - with NameOps.NameOpsBase { + with Printers.PrinterBase { val initialCtx: Context = new InitialContext(this) @@ -99,6 +103,10 @@ object Contexts { lazy val definitions = new Definitions()(initialCtx) + lazy val loaders = new SymbolLoaders + + lazy val platform: Platform = ??? + // Symbols state /** A map from a superclass id to the class that has it */ private[core] var classOfId = new Array[ClassSymbol](InitialSuperIdsSize) diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index cafc4495d..13392a1c9 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -331,7 +331,7 @@ object Flags { /** Flags representing access rights */ final val AccessFlags = Private | Protected | Local - final val ModuleFlags: FlagSet = ??? + final val RetainedModuleFlags: FlagSet = ??? final val UninstantiatableFlags = Abstract | Final diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 2bbfa1360..5aa7e2e25 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -10,25 +10,24 @@ import Decorators.StringDecorator object NameOps { - trait NameOpsBase { this: ContextBase => - - final object compactify extends (String => String) { - val md5 = MessageDigest.getInstance("MD5") - - /** COMPACTIFY - * - * The hashed name has the form (prefix + marker + md5 + marker + suffix), where - * - prefix/suffix.length = MaxNameLength / 4 - * - md5.length = 32 - * - * We obtain the formula: - * - * FileNameLength = 2*(MaxNameLength / 4) + 2.marker.length + 32 + 6 - * - * (+6 for ".class"). MaxNameLength can therefore be computed as follows: - */ + final object compactify { + lazy val md5 = MessageDigest.getInstance("MD5") + + /** COMPACTIFY + * + * The hashed name has the form (prefix + marker + md5 + marker + suffix), where + * - prefix/suffix.length = MaxNameLength / 4 + * - md5.length = 32 + * + * We obtain the formula: + * + * FileNameLength = 2*(MaxNameLength / 4) + 2.marker.length + 32 + 6 + * + * (+6 for ".class"). MaxNameLength can therefore be computed as follows: + */ + def apply(s: String)(implicit ctx: Context): String = { val marker = "$$$$" - val limit: Int = ??? // !!! settings.maxClassfileName.value + val limit: Int = ctx.settings.XmaxClassfileName.value val MaxNameLength = (limit - 6) min 2 * (limit - 6 - 2 * marker.length - 32) def toMD5(s: String, edge: Int): String = { @@ -42,21 +41,11 @@ object NameOps { prefix + marker + md5chars + marker + suffix } - def apply(s: String): String = - if (s.length <= MaxNameLength) s else toMD5(s, MaxNameLength / 4) + + if (s.length <= MaxNameLength) s else toMD5(s, MaxNameLength / 4) } } - private val Boxed = Map[TypeName, TypeName]( - tpnme.Boolean -> jtpnme.BoxedBoolean, - tpnme.Byte -> jtpnme.BoxedByte, - tpnme.Char -> jtpnme.BoxedCharacter, - tpnme.Short -> jtpnme.BoxedShort, - tpnme.Int -> jtpnme.BoxedInteger, - tpnme.Long -> jtpnme.BoxedLong, - tpnme.Float -> jtpnme.BoxedFloat, - tpnme.Double -> jtpnme.BoxedDouble) - implicit class NameDecorator(val name: Name) extends AnyVal { import nme._ @@ -147,9 +136,21 @@ object NameOps { } /** If name length exceeds allowable limit, replace part of it by hash */ - def compactified(implicit ctx: Context): TermName = termName(ctx.compactify(name.toString)) + def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString)) } + // needed??? + private val Boxed = Map[TypeName, TypeName]( + tpnme.Boolean -> jtpnme.BoxedBoolean, + tpnme.Byte -> jtpnme.BoxedByte, + tpnme.Char -> jtpnme.BoxedCharacter, + tpnme.Short -> jtpnme.BoxedShort, + tpnme.Int -> jtpnme.BoxedInteger, + tpnme.Long -> jtpnme.BoxedLong, + tpnme.Float -> jtpnme.BoxedFloat, + tpnme.Double -> jtpnme.BoxedDouble) + + // needed??? implicit class TypeNameDecorator(val name: TypeName) extends AnyVal { def isUnboxedName = Boxed contains name def boxedName: TypeName = Boxed(name) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 0b61906b1..e916d0e70 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -721,7 +721,7 @@ object SymDenotations { def classDenot(denot: LazySymDenotation) = denot.moduleClass.denot.asInstanceOf[LazyClassDenotation] def copyLoadedFields(denot: LazySymDenotation, from: LazyClassDenotation) = { - denot.setFlags(from.flags.toTermFlags & ModuleFlags) + denot.setFlags(from.flags.toTermFlags & RetainedModuleFlags) denot.privateWithin = from.privateWithin } def copyCompletedFields(denot: LazySymDenotation, from: LazyClassDenotation) = { diff --git a/src/dotty/tools/dotc/core/SymbolLoaders.scala b/src/dotty/tools/dotc/core/SymbolLoaders.scala index 972242fbd..41bd0fdeb 100644 --- a/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -10,108 +10,84 @@ package core import java.io.IOException import scala.compat.Platform.currentTime import dotty.tools.io.{ClassPath, AbstractFile} -import Contexts._, Symbols._, Flags._, SymDenotations._ +import Contexts._, Symbols._, Flags._, SymDenotations._, Types._ import Decorators.StringDecorator //import classfile.ClassfileParser +abstract class SymbolLoader extends ClassCompleter -abstract class SymbolLoaders(implicit ctx: Context) { - protected def enterIfNew(owner: Symbol, member: Symbol, completer: SymbolLoader): Symbol = { +class SymbolLoaders { + + protected def enterIfNew(owner: Symbol, member: Symbol, completer: SymbolLoader)(implicit ctx: Context): Symbol = { assert(owner.info.decls.lookup(member.name) == NoSymbol, owner.fullName + "." + member.name) owner.info.decls enter member member } - /** Enter class with given `name` into scope of `root` - * and give them `completer` as type. + /** Enter class with given `name` into scope of `owner`. */ - def enterClass(owner: Symbol, name: String, completer: SymbolLoader): Symbol = { + def enterClass(owner: Symbol, name: String, completer: SymbolLoader)(implicit ctx: Context): Symbol = { val cls = ctx.newLazyClassSymbol(owner, name.toTypeName, EmptyFlags, completer) enterIfNew(owner, cls, completer) } - /** Enter module with given `name` into scope of `root` - * and give them `completer` as type. + /** Enter module with given `name` into scope of `owner`. */ - def enterModule(owner: Symbol, name: String, completer: SymbolLoader): Symbol = { + def enterModule(owner: Symbol, name: String, completer: SymbolLoader)(implicit ctx: Context): Symbol = { val module = ctx.newLazyModuleSymbol(owner, name.toTermName, EmptyFlags, completer) enterIfNew(owner, module, completer) } - abstract class SymbolLoader extends ClassCompleter -} -/* - /** Enter module with given `name` into scope of `root` - * and give them `completer` as type. - */ - def enterModule(owner: Symbol, name: String, completer: SymbolLoader): Symbol = { - val module = owner.newModule(newTermName(name)) - module setInfo completer - module.moduleClass setInfo moduleClassLoader - enterIfNew(owner, module, completer) - } - - /** Enter package with given `name` into scope of `root` + /** Enter package with given `name` into scope of `owner` * and give them `completer` as type. */ - def enterPackage(root: Symbol, name: String, completer: SymbolLoader): Symbol = { - val pname = newTermName(name) - val preExisting = root.info.decls lookup pname + def enterPackage(owner: Symbol, name: String, completer: SymbolLoader)(implicit ctx: Context): Symbol = { + val pname = name.toTermName + val preExisting = owner.info.decls lookup pname if (preExisting != NoSymbol) { // Some jars (often, obfuscated ones) include a package and // object with the same name. Rather than render them unusable, // offer a setting to resolve the conflict one way or the other. // This was motivated by the desire to use YourKit probes, which // require yjp.jar at runtime. See SI-2089. - if (settings.termConflict.isDefault) + if (ctx.settings.YtermConflict == ctx.settings.default.YtermConflict) throw new TypeError( - root+" contains object and package with same name: "+ - name+"\none of them needs to be removed from classpath" - ) - else if (settings.termConflict.value == "package") { - global.warning( - "Resolving package/object name conflict in favor of package " + - preExisting.fullName + ". The object will be inaccessible." - ) - root.info.decls.unlink(preExisting) - } - else { - global.warning( - "Resolving package/object name conflict in favor of object " + - preExisting.fullName + ". The package will be inaccessible." - ) + s"""$owner contains object and package with same name: $name + |one of them needs to be removed from classpath""".stripMargin) + else if (ctx.settings.YtermConflict.value == "package") { + ctx.warning( + s"Resolving package/object name conflict in favor of package ${preExisting.fullName}. The object will be inaccessible.") + owner.info.decls.unlink(preExisting) + } else { + ctx.warning( + s"Resolving package/object name conflict in favor of object ${preExisting.fullName}. The package will be inaccessible.") return NoSymbol } } - // todo: find out initialization sequence for pkg/pkg.moduleClass is different from enterModule - val pkg = root.newPackage(pname) - pkg.moduleClass setInfo completer - pkg setInfo pkg.moduleClass.tpe - root.info.decls enter pkg - pkg + ctx.newLazyModuleSymbol(owner, pname, PackageCreationFlags, completer).entered } - /** Enter class and module with given `name` into scope of `root` + /** Enter class and module with given `name` into scope of `owner` * and give them `completer` as type. */ - def enterClassAndModule(root: Symbol, name: String, completer: SymbolLoader) { - val clazz = enterClass(root, name, completer) - val module = enterModule(root, name, completer) + def enterClassAndModule(owner: Symbol, name: String, completer: SymbolLoader)(implicit ctx: Context) { + val clazz = enterClass(owner, name, completer) + val module = enterModule(owner, name, completer) if (!clazz.isAnonymousClass) { assert(clazz.companionModule == module, module) assert(module.companionClass == clazz, clazz) } } - /** In batch mode: Enter class and module with given `name` into scope of `root` + /** In batch mode: Enter class and module with given `name` into scope of `owner` * and give them a source completer for given `src` as type. - * In IDE mode: Find all toplevel definitions in `src` and enter then into scope of `root` + * In IDE mode: Find all toplevel definitions in `src` and enter then into scope of `owner` * with source completer for given `src` as type. * (overridden in interactive.Global). */ - def enterToplevelsFromSource(root: Symbol, name: String, src: AbstractFile) { - enterClassAndModule(root, name, new SourcefileLoader(src)) + def enterToplevelsFromSource(owner: Symbol, name: String, src: AbstractFile)(implicit ctx: Context) { + ??? // !!! enterClassAndModule(owner, name, new SourcefileLoader(src)) } /** The package objects of scala and scala.reflect should always @@ -121,26 +97,29 @@ abstract class SymbolLoaders(implicit ctx: Context) { * Note: We do a name-base comparison here because the method is called before we even * have ReflectPackage defined. */ - def binaryOnly(owner: Symbol, name: String): Boolean = + def binaryOnly(owner: Symbol, name: String)(implicit ctx: Context): Boolean = name == "package" && (owner.fullName == "scala" || owner.fullName == "scala.reflect") /** Initialize toplevel class and module symbols in `owner` from class path representation `classRep` */ - def initializeFromClassPath(owner: Symbol, classRep: ClassPath[platform.BinaryRepr]#ClassRep) { + def initializeFromClassPath(owner: Symbol, classRep: ClassPath#ClassRep)(implicit ctx: Context) { ((classRep.binary, classRep.source) : @unchecked) match { case (Some(bin), Some(src)) - if platform.needCompile(bin, src) && !binaryOnly(owner, classRep.name) => - if (settings.verbose.value) inform("[symloader] picked up newer source file for " + src.path) - global.loaders.enterToplevelsFromSource(owner, classRep.name, src) + if needCompile(bin, src) && !binaryOnly(owner, classRep.name) => + if (ctx.settings.verbose.value) ctx.inform("[symloader] picked up newer source file for " + src.path) + enterToplevelsFromSource(owner, classRep.name, src) case (None, Some(src)) => - if (settings.verbose.value) inform("[symloader] no class, picked up source file for " + src.path) - global.loaders.enterToplevelsFromSource(owner, classRep.name, src) + if (ctx.settings.verbose.value) ctx.inform("[symloader] no class, picked up source file for " + src.path) + enterToplevelsFromSource(owner, classRep.name, src) case (Some(bin), _) => - global.loaders.enterClassAndModule(owner, classRep.name, platform.newClassLoader(bin)) + enterClassAndModule(owner, classRep.name, ctx.platform.newClassLoader(bin)) } } + def needCompile(bin: AbstractFile, src: AbstractFile) = + src.lastModified >= bin.lastModified +}/* /** * A lazy type that completes itself by calling parameter doComplete. * Any linked modules/classes or module classes are also initialized. diff --git a/src/dotty/tools/io/ClassPath.scala b/src/dotty/tools/io/ClassPath.scala index 5a2fdad41..83a7703f4 100644 --- a/src/dotty/tools/io/ClassPath.scala +++ b/src/dotty/tools/io/ClassPath.scala @@ -105,7 +105,7 @@ object ClassPath { /** A class modeling aspects of a ClassPath which should be * propagated to any classpaths it creates. */ - abstract class ClassPathContext[T] { + abstract class ClassPathContext { /** A filter which can be used to exclude entities from the classpath * based on their name. */ @@ -113,30 +113,30 @@ object ClassPath { /** From the representation to its identifier. */ - def toBinaryName(rep: T): String + def toBinaryName(rep: AbstractFile): String /** Create a new classpath based on the abstract file. */ - def newClassPath(file: AbstractFile): ClassPath[T] + def newClassPath(file: AbstractFile): ClassPath /** Creators for sub classpaths which preserve this context. */ - def sourcesInPath(path: String): List[ClassPath[T]] = + def sourcesInPath(path: String): List[ClassPath] = for (file <- expandPath(path, false) ; dir <- Option(AbstractFile getDirectory file)) yield - new SourcePath[T](dir, this) + new SourcePath(dir, this) - def contentsOfDirsInPath(path: String): List[ClassPath[T]] = + def contentsOfDirsInPath(path: String): List[ClassPath] = for (dir <- expandPath(path, false) ; name <- expandDir(dir) ; entry <- Option(AbstractFile getDirectory name)) yield newClassPath(entry) - def classesAtAllURLS(path: String): List[ClassPath[T]] = + def classesAtAllURLS(path: String): List[ClassPath] = (path split " ").toList flatMap classesAtURL def classesAtURL(spec: String) = for (url <- specToURL(spec).toList ; location <- Option(AbstractFile getURL url)) yield newClassPath(location) - def classesInExpandedPath(path: String): IndexedSeq[ClassPath[T]] = + def classesInExpandedPath(path: String): IndexedSeq[ClassPath] = classesInPathImpl(path, true).toIndexedSeq def classesInPath(path: String) = classesInPathImpl(path, false) @@ -147,7 +147,7 @@ object ClassPath { newClassPath(dir) } - class JavaContext extends ClassPathContext[AbstractFile] { + class JavaContext extends ClassPathContext { def toBinaryName(rep: AbstractFile) = { val name = rep.name assert(endsClass(name), name) @@ -178,8 +178,8 @@ object ClassPath { /** * Represents a package which contains classes and other packages */ -abstract class ClassPath[T] { - type AnyClassRep = ClassPath[T]#ClassRep +abstract class ClassPath { + type AnyClassRep = ClassPath#ClassRep /** * The short name of the package (without prefix) @@ -202,19 +202,19 @@ abstract class ClassPath[T] { /** Info which should be propagated to any sub-classpaths. */ - def context: ClassPathContext[T] + def context: ClassPathContext /** Lists of entities. */ def classes: IndexedSeq[AnyClassRep] - def packages: IndexedSeq[ClassPath[T]] + def packages: IndexedSeq[ClassPath] def sourcepaths: IndexedSeq[AbstractFile] /** * Represents classes which can be loaded with a ClassfileLoader/MsilFileLoader * and / or a SourcefileLoader. */ - case class ClassRep(binary: Option[T], source: Option[AbstractFile]) { + case class ClassRep(binary: Option[AbstractFile], source: Option[AbstractFile]) { def name: String = binary match { case Some(x) => context.toBinaryName(x) case _ => @@ -253,8 +253,8 @@ abstract class ClassPath[T] { def sortString = join(split(asClasspathString).sorted: _*) override def equals(that: Any) = that match { - case x: ClassPath[_] => this.sortString == x.sortString - case _ => false + case x: ClassPath => this.sortString == x.sortString + case _ => false } override def hashCode = sortString.hashCode() } @@ -262,7 +262,7 @@ abstract class ClassPath[T] { /** * A Classpath containing source files */ -class SourcePath[T](dir: AbstractFile, val context: ClassPathContext[T]) extends ClassPath[T] { +class SourcePath(dir: AbstractFile, val context: ClassPathContext) extends ClassPath { def name = dir.name override def origin = dir.underlyingSource map (_.path) def asURLs = if (dir.file == null) Nil else List(dir.toURL) @@ -271,12 +271,12 @@ class SourcePath[T](dir: AbstractFile, val context: ClassPathContext[T]) extends private def traverse() = { val classBuf = immutable.Vector.newBuilder[ClassRep] - val packageBuf = immutable.Vector.newBuilder[SourcePath[T]] + val packageBuf = immutable.Vector.newBuilder[SourcePath] 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 += new SourcePath(f, context) } (packageBuf.result, classBuf.result) } @@ -288,7 +288,7 @@ class SourcePath[T](dir: AbstractFile, val context: ClassPathContext[T]) extends /** * A directory (or a .jar file) containing classfiles and packages */ -class DirectoryClassPath(val dir: AbstractFile, val context: ClassPathContext[AbstractFile]) extends ClassPath[AbstractFile] { +class DirectoryClassPath(val dir: AbstractFile, val context: ClassPathContext) extends ClassPath { def name = dir.name override def origin = dir.underlyingSource map (_.path) def asURLs = if (dir.file == null) Nil else List(dir.toURL) @@ -312,8 +312,8 @@ class DirectoryClassPath(val dir: AbstractFile, val context: ClassPathContext[Ab override def toString() = "directory classpath: "+ origin.getOrElse("?") } -class DeltaClassPath[T](original: MergedClassPath[T], subst: Map[ClassPath[T], ClassPath[T]]) -extends MergedClassPath[T](original.entries map (e => subst getOrElse (e, e)), original.context) { +class DeltaClassPath(original: MergedClassPath, subst: Map[ClassPath, ClassPath]) +extends MergedClassPath(original.entries map (e => subst getOrElse (e, e)), original.context) { // not sure we should require that here. Commented out for now. // require(subst.keySet subsetOf original.entries.toSet) // We might add specialized operations for computing classes packages here. Not sure it's worth it. @@ -322,11 +322,11 @@ extends MergedClassPath[T](original.entries map (e => subst getOrElse (e, e)), o /** * A classpath unifying multiple class- and sourcepath entries. */ -class MergedClassPath[T]( - val entries: IndexedSeq[ClassPath[T]], - val context: ClassPathContext[T]) -extends ClassPath[T] { - def this(entries: TraversableOnce[ClassPath[T]], context: ClassPathContext[T]) = +class MergedClassPath( + val entries: IndexedSeq[ClassPath], + val context: ClassPathContext) +extends ClassPath { + def this(entries: TraversableOnce[ClassPath], context: ClassPathContext) = this(entries.toIndexedSeq, context) def name = entries.head.name @@ -361,10 +361,10 @@ extends ClassPath[T] { cls.toIndexedSeq } - lazy val packages: IndexedSeq[ClassPath[T]] = { + lazy val packages: IndexedSeq[ClassPath] = { var count = 0 val indices = mutable.HashMap[String, Int]() - val pkg = new mutable.ArrayBuffer[ClassPath[T]](256) + val pkg = new mutable.ArrayBuffer[ClassPath](256) for (e <- entries; p <- e.packages) { val name = p.name @@ -381,12 +381,12 @@ extends ClassPath[T] { 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) + private def addPackage(to: ClassPath, pkg: ClassPath) = { + val newEntries: IndexedSeq[ClassPath] = to match { + case cp: MergedClassPath => cp.entries :+ pkg + case _ => IndexedSeq(to, pkg) } - new MergedClassPath[T](newEntries, context) + new MergedClassPath(newEntries, context) } def show() { println("ClassPath %s has %d entries and results in:\n".format(name, entries.size)) @@ -400,9 +400,9 @@ extends ClassPath[T] { * as AbstractFile. nsc.io.ZipArchive is used to view zip/jar archives as directories. */ class JavaClassPath( - containers: IndexedSeq[ClassPath[AbstractFile]], + containers: IndexedSeq[ClassPath], context: JavaContext) -extends MergedClassPath[AbstractFile](containers, context) { } +extends MergedClassPath(containers, context) { } object JavaClassPath { def fromURLs(urls: Seq[URL], context: JavaContext): JavaClassPath = { |