diff options
author | Martin Odersky <odersky@gmail.com> | 2011-11-08 18:18:19 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2011-11-08 18:18:19 +0000 |
commit | 27c8266eb62bcf4a3a3c4dae54ae13cb3d017b31 (patch) | |
tree | 5bbd8532138a34c35be83f91094375ad98606a95 /src | |
parent | 5cc3dad991cde9c55c49c451a93a496a16b3afe1 (diff) | |
download | scala-27c8266eb62bcf4a3a3c4dae54ae13cb3d017b31.tar.gz scala-27c8266eb62bcf4a3a3c4dae54ae13cb3d017b31.tar.bz2 scala-27c8266eb62bcf4a3a3c4dae54ae13cb3d017b31.zip |
Distentangled platform specific code from loade...
Distentangled platform specific code from loaders so that it becomes
possible to reset class symbols to their initial loader wihtout going
through an all-or-nothing recursive buildups of package scopes,.
Diffstat (limited to 'src')
6 files changed, 105 insertions, 84 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 602ff6bf0e..2bbf2f9ac7 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -62,13 +62,13 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb // platform specific elements - type ThisPlatform = Platform[_] { val global: Global.this.type } + type ThisPlatform = Platform { val global: Global.this.type } lazy val platform: ThisPlatform = if (forMSIL) new { val global: Global.this.type = Global.this } with MSILPlatform else new { val global: Global.this.type = Global.this } with JavaPlatform - def classPath: ClassPath[_] = platform.classPath + def classPath: ClassPath[platform.BinaryRepr] = platform.classPath def rootLoader: LazyType = platform.rootLoader // sub-components -------------------------------------------------- diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala index 9e83f2fbd7..d80a1c4f34 100644 --- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala @@ -7,16 +7,27 @@ package scala.tools.nsc package backend import io.AbstractFile -import util.JavaClassPath +import util.{ClassPath,JavaClassPath} import util.ClassPath.{ JavaContext, DefaultJavaContext } import scala.tools.util.PathResolver -trait JavaPlatform extends Platform[AbstractFile] { +trait JavaPlatform extends Platform { import global._ import definitions._ + type BinaryRepr = AbstractFile + lazy val classPath = new PathResolver(settings).result - def rootLoader = new loaders.JavaPackageLoader(classPath) + 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 @@ -45,4 +56,12 @@ trait JavaPlatform extends Platform[AbstractFile] { (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/compiler/scala/tools/nsc/backend/MSILPlatform.scala b/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala index 9781111004..b1daee1f10 100644 --- a/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala @@ -6,14 +6,17 @@ package scala.tools.nsc package backend -import ch.epfl.lamp.compiler.msil.{ Type => MSILType } -import util.MsilClassPath +import ch.epfl.lamp.compiler.msil.{ Type => MSILType, Attribute => MSILAttribute } +import util.{ClassPath, MsilClassPath} import msil.GenMSIL +import io.AbstractFile -trait MSILPlatform extends Platform[MSILType] { +trait MSILPlatform extends Platform { import global._ import definitions.{ ComparatorClass, BoxedNumberClass, getMember } + type BinaryRepr = MSILType + if (settings.verbose.value) inform("[AssemRefs = " + settings.assemrefs.value + "]") @@ -25,7 +28,8 @@ trait MSILPlatform extends Platform[MSILType] { } with GenMSIL lazy val classPath = MsilClassPath.fromSettings(settings) - def rootLoader = new loaders.NamespaceLoader(classPath) + def rootLoader = new loaders.PackageLoader(classPath.asInstanceOf[ClassPath[platform.BinaryRepr]]) + // See discussion in JavaPlatForm for why we need a cast here. def platformPhases = List( genMSIL // generate .msil files @@ -33,4 +37,30 @@ trait MSILPlatform extends Platform[MSILType] { lazy val externalEquals = getMember(ComparatorClass.companionModule, nme.equals_) def isMaybeBoxed(sym: Symbol) = sym isNonBottomSubClass BoxedNumberClass + + def newClassLoader(bin: MSILType): loaders.SymbolLoader = + new loaders.MSILTypeLoader(bin) + + /** + * Tells whether a class should be loaded and entered into the package + * scope. On .NET, this method returns `false` for all synthetic classes + * (anonymous classes, implementation classes, module classes), their + * symtab is encoded in the pickle of another class. + */ + def doLoad(cls: ClassPath[BinaryRepr]#ClassRep): Boolean = { + if (cls.binary.isDefined) { + val typ = cls.binary.get + if (typ.IsDefined(loaders.clrTypes.SCALA_SYMTAB_ATTR, false)) { + val attrs = typ.GetCustomAttributes(loaders.clrTypes.SCALA_SYMTAB_ATTR, false) + assert(attrs.length == 1, attrs.length) + val a = attrs(0).asInstanceOf[MSILAttribute] + // symtab_constr takes a byte array argument (the pickle), i.e. typ has a pickle. + // otherwise, symtab_default_constr was used, which marks typ as scala-synthetic. + a.getConstructor() == loaders.clrTypes.SYMTAB_CONSTR + } else true // always load non-scala types + } else true // always load source + } + + def needCompile(bin: MSILType, src: AbstractFile) = + false // always use compiled file on .net } diff --git a/src/compiler/scala/tools/nsc/backend/Platform.scala b/src/compiler/scala/tools/nsc/backend/Platform.scala index de271856b7..23592eeb61 100644 --- a/src/compiler/scala/tools/nsc/backend/Platform.scala +++ b/src/compiler/scala/tools/nsc/backend/Platform.scala @@ -7,15 +7,19 @@ package scala.tools.nsc package backend import util.ClassPath +import io.AbstractFile /** The platform dependent pieces of Global. */ -trait Platform[T] { +trait Platform { val global: Global import global._ + /** The binary classfile representation type */ + type BinaryRepr + /** The compiler classpath. */ - def classPath: ClassPath[T] + def classPath: ClassPath[BinaryRepr] /** The root symbol loader. */ def rootLoader: LazyType @@ -28,5 +32,24 @@ trait Platform[T] { /** 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: BinaryRepr): loaders.SymbolLoader + + /** + * Tells whether a class should be loaded and entered into the package + * scope. On .NET, this method returns `false` for all synthetic classes + * (anonymous classes, implementation classes, module classes), their + * symtab is encoded in the pickle of another class. + */ + def doLoad(cls: ClassPath[BinaryRepr]#ClassRep): Boolean + + /** + * Tells whether a class with both a binary and a source representation + * (found in classpath and in sourcepath) should be re-compiled. Behaves + * on the JVM similar to javac, i.e. if the source file is newer than the classfile, + * a re-compile is triggered. On .NET by contrast classfiles always take precedence. + */ + def needCompile(bin: BinaryRepr, src: AbstractFile): Boolean } diff --git a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala index 24e0dba271..14cbdf2f3b 100644 --- a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala +++ b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala @@ -33,8 +33,9 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana super.computeInternalPhases phasesSet += dependencyAnalysis } - lazy val _classpath: ClassPath[_] = new NoSourcePathPathResolver(settings).result - override def classPath: ClassPath[_] = _classpath + lazy val _classpath = new NoSourcePathPathResolver(settings).result + override def classPath = _classpath.asInstanceOf[ClassPath[platform.BinaryRepr]] + // See discussion in JavaPlatForm for why we need a cast here. def newRun() = new Run() } diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 58d8003f06..7909b5e7a8 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -79,6 +79,21 @@ abstract class SymbolLoaders { enterClassAndModule(root, name, new SourcefileLoader(src)) } + /** Initialize symbol `root` from class path representation `classRep` + */ + def initializeFromClassPath(root: Symbol, classRep: ClassPath[platform.BinaryRepr]#ClassRep) { + ((classRep.binary, classRep.source) : @unchecked) match { + case (Some(bin), Some(src)) if platform.needCompile(bin, src) => + if (settings.verbose.value) inform("[symloader] picked up newer source file for " + src.path) + global.loaders.enterToplevelsFromSource(root, 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(root, classRep.name, src) + case (Some(bin), _) => + global.loaders.enterClassAndModule(root, classRep.name, platform.newClassLoader(bin)) + } + } + /** * A lazy type that completes itself by calling parameter doComplete. * Any linked modules/classes or module classes are also initialized. @@ -155,7 +170,7 @@ abstract class SymbolLoaders { /** * Load contents of a package */ - abstract class PackageLoader[T](classpath: ClassPath[T]) extends SymbolLoader { + class PackageLoader(classpath: ClassPath[platform.BinaryRepr]) extends SymbolLoader { protected def description = "package loader "+ classpath.name def enterPackage(root: Symbol, name: String, completer: SymbolLoader) { @@ -192,90 +207,23 @@ abstract class SymbolLoaders { root.info.decls.enter(pkg) } - /** - * Tells whether a class with both a binary and a source representation - * (found in classpath and in sourcepath) should be re-compiled. Behaves - * similar to javac, i.e. if the source file is newer than the classfile, - * a re-compile is triggered. - */ - protected def needCompile(bin: T, src: AbstractFile): Boolean - - /** - * Tells whether a class should be loaded and entered into the package - * scope. On .NET, this method returns `false` for all synthetic classes - * (anonymous classes, implementation classes, module classes), their - * symtab is encoded in the pickle of another class. - */ - protected def doLoad(cls: classpath.AnyClassRep): Boolean - - protected def newClassLoader(bin: T): SymbolLoader - - protected def newPackageLoader(pkg: ClassPath[T]): SymbolLoader - protected def doComplete(root: Symbol) { assert(root.isPackageClass, root) root.setInfo(new PackageClassInfoType(new Scope(), root)) val sourcepaths = classpath.sourcepaths - for (classRep <- classpath.classes if doLoad(classRep)) { - ((classRep.binary, classRep.source) : @unchecked) match { - case (Some(bin), Some(src)) if needCompile(bin, src) => - if (settings.verbose.value) inform("[symloader] picked up newer source file for " + src.path) - enterToplevelsFromSource(root, classRep.name, src) - case (None, Some(src)) => - if (settings.verbose.value) inform("[symloader] no class, picked up source file for " + src.path) - enterToplevelsFromSource(root, classRep.name, src) - case (Some(bin), _) => - enterClassAndModule(root, classRep.name, newClassLoader(bin)) - } + for (classRep <- classpath.classes if platform.doLoad(classRep)) { + initializeFromClassPath(root, classRep) } for (pkg <- classpath.packages) { - enterPackage(root, pkg.name, newPackageLoader(pkg)) + enterPackage(root, pkg.name, new PackageLoader(pkg)) } openPackageModule(root) } } - class JavaPackageLoader(classpath: ClassPath[AbstractFile]) extends PackageLoader(classpath) { - protected def needCompile(bin: AbstractFile, src: AbstractFile) = - (src.lastModified >= bin.lastModified) - - protected def doLoad(cls: classpath.AnyClassRep) = true - - protected def newClassLoader(bin: AbstractFile) = - new ClassfileLoader(bin) - - protected def newPackageLoader(pkg: ClassPath[AbstractFile]) = - new JavaPackageLoader(pkg) - } - - class NamespaceLoader(classpath: ClassPath[MSILType]) extends PackageLoader(classpath) { - protected def needCompile(bin: MSILType, src: AbstractFile) = - false // always use compiled file on .net - - protected def doLoad(cls: classpath.AnyClassRep) = { - if (cls.binary.isDefined) { - val typ = cls.binary.get - if (typ.IsDefined(clrTypes.SCALA_SYMTAB_ATTR, false)) { - val attrs = typ.GetCustomAttributes(clrTypes.SCALA_SYMTAB_ATTR, false) - assert (attrs.length == 1, attrs.length) - val a = attrs(0).asInstanceOf[MSILAttribute] - // symtab_constr takes a byte array argument (the pickle), i.e. typ has a pickle. - // otherwise, symtab_default_constr was used, which marks typ as scala-synthetic. - a.getConstructor() == clrTypes.SYMTAB_CONSTR - } else true // always load non-scala types - } else true // always load source - } - - protected def newClassLoader(bin: MSILType) = - new MSILTypeLoader(bin) - - protected def newPackageLoader(pkg: ClassPath[MSILType]) = - new NamespaceLoader(pkg) - } - class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader { private object classfileParser extends ClassfileParser { val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global |