diff options
25 files changed, 434 insertions, 223 deletions
@@ -364,7 +364,7 @@ TODO: </then></if> <!-- Allow this to be overridden simply --> - <property name="sbt.latest.version" value="0.12.2"/> + <property name="sbt.latest.version" value="0.12.4"/> <property name="sbt.src.dir" value="${build-sbt.dir}/${sbt.latest.version}/src"/> <property name="sbt.lib.dir" value="${build-sbt.dir}/${sbt.latest.version}/lib"/> @@ -1538,7 +1538,7 @@ TODO: <target name="test.junit" depends="test.junit.comp"> <stopwatch name="test.junit.timer"/> <mkdir dir="${test.junit.classes}"/> - <junit fork="yes" haltonfailure="yes" showoutput="yes" printsummary="on"> + <junit fork="yes" haltonfailure="yes" printsummary="on"> <classpath refid="test.junit.compiler.build.path"/> <batchtest fork="yes" todir="${build-junit.dir}"> <fileset dir="${test.junit.classes}"> diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 20cb1dab5b..a6c69091c5 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -51,7 +51,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) class GlobalMirror extends Roots(NoSymbol) { val universe: self.type = self - def rootLoader: LazyType = platform.rootLoader + def rootLoader: LazyType = new loaders.PackageLoader(classPath) override def toString = "compiler mirror" } @@ -83,12 +83,15 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // platform specific elements - type ThisPlatform = Platform { val global: Global.this.type } + protected class GlobalPlatform extends { + val global: Global.this.type = Global.this + val settings: Settings = Global.this.settings + } with JavaPlatform - lazy val platform: ThisPlatform = - new { val global: Global.this.type = Global.this } with JavaPlatform + type ThisPlatform = JavaPlatform { val global: Global.this.type } + lazy val platform: ThisPlatform = new GlobalPlatform - type PlatformClassPath = ClassPath[platform.BinaryRepr] + type PlatformClassPath = ClassPath[AbstractFile] type OptClassPath = Option[PlatformClassPath] def classPath: PlatformClassPath = platform.classPath @@ -268,12 +271,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) log("!!! " + msg) // such warnings always at least logged } - private def elapsedMessage(msg: String, start: Long) = - msg + " in " + (currentTime - start) + "ms" - def informComplete(msg: String): Unit = reporter.withoutTruncating(inform(msg)) - def informProgress(msg: String) = if (settings.verbose) inform("[" + msg + "]") - def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start)) def logError(msg: String, t: Throwable): Unit = () @@ -357,9 +355,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) getSourceFile(f) } - lazy val loaders = new SymbolLoaders { + lazy val loaders = new { val global: Global.this.type = Global.this - } + val platform: Global.this.platform.type = Global.this.platform + } with GlobalSymbolLoaders /** Returns the mirror that loaded given symbol */ def mirrorThatLoaded(sym: Symbol): Mirror = rootMirror @@ -914,7 +913,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) invalidated: mutable.ListBuffer[ClassSymbol], failed: mutable.ListBuffer[ClassSymbol]) { ifDebug(informProgress(s"syncing $root, $oldEntries -> $newEntries")) - val getName: ClassPath[platform.BinaryRepr] => String = (_.name) + val getName: ClassPath[AbstractFile] => String = (_.name) def hasClasses(cp: OptClassPath) = cp.isDefined && cp.get.classes.nonEmpty def invalidateOrRemove(root: ClassSymbol) = { allEntries match { diff --git a/src/compiler/scala/tools/nsc/GlobalSymbolLoaders.scala b/src/compiler/scala/tools/nsc/GlobalSymbolLoaders.scala new file mode 100644 index 0000000000..6921548230 --- /dev/null +++ b/src/compiler/scala/tools/nsc/GlobalSymbolLoaders.scala @@ -0,0 +1,30 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Martin Odersky + */ + +package scala +package tools +package nsc + +/** + * Symbol loaders implementation that wires dependencies using Global. + */ +abstract class GlobalSymbolLoaders extends symtab.SymbolLoaders { + val global: Global + val symbolTable: global.type = global + val platform: symbolTable.platform.type + import global._ + def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol = { + def lookup = sym.info.member(name) + // if loading during initialization of `definitions` typerPhase is not yet set. + // in that case we simply load the member at the current phase + if (currentRun.typerPhase eq null) + lookup + else + enteringTyper { lookup } + } + + protected def compileLate(srcfile: io.AbstractFile): Unit = + currentRun.compileLate(srcfile) +} diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala index c5fc12e3ec..32b5a98b98 100644 --- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala @@ -11,33 +11,22 @@ import util.{ClassPath,MergedClassPath,DeltaClassPath} import scala.tools.util.PathResolver trait JavaPlatform extends Platform { + val global: Global + override val symbolTable: global.type = global import global._ import definitions._ - type BinaryRepr = AbstractFile + private var currentClassPath: Option[MergedClassPath[AbstractFile]] = None - private var currentClassPath: Option[MergedClassPath[BinaryRepr]] = None - - def classPath: ClassPath[BinaryRepr] = { + def classPath: ClassPath[AbstractFile] = { 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]]) = + def updateClassPath(subst: Map[ClassPath[AbstractFile], ClassPath[AbstractFile]]) = 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 classEmitPhase = if (settings.isBCodeActive) genBCode else genASM @@ -66,10 +55,7 @@ trait JavaPlatform extends Platform { (sym isNonBottomSubClass BoxedBooleanClass) } - def newClassLoader(bin: AbstractFile): loaders.SymbolLoader = - new loaders.ClassfileLoader(bin) - - def doLoad(cls: ClassPath[BinaryRepr]#ClassRep): Boolean = true + def doLoad(cls: ClassPath[AbstractFile]#ClassRep): Boolean = true def needCompile(bin: AbstractFile, src: AbstractFile) = src.lastModified >= bin.lastModified diff --git a/src/compiler/scala/tools/nsc/backend/Platform.scala b/src/compiler/scala/tools/nsc/backend/Platform.scala index e2b22c06d7..3bca16635b 100644 --- a/src/compiler/scala/tools/nsc/backend/Platform.scala +++ b/src/compiler/scala/tools/nsc/backend/Platform.scala @@ -12,20 +12,18 @@ import io.AbstractFile /** The platform dependent pieces of Global. */ trait Platform { - val global: Global - import global._ + val symbolTable: symtab.SymbolTable + import symbolTable._ /** The binary classfile representation type */ - type BinaryRepr + @deprecated("BinaryRepr is not an abstract type anymore. It's an alias that points at AbstractFile. It'll be removed before Scala 2.11 is released.", "2.11.0-M5") + type BinaryRepr = AbstractFile /** The compiler classpath. */ - def classPath: ClassPath[BinaryRepr] - - /** The root symbol loader. */ - def rootLoader: LazyType + def classPath: ClassPath[AbstractFile] /** Update classpath with a substitution that maps entries to entries */ - def updateClassPath(subst: Map[ClassPath[BinaryRepr], ClassPath[BinaryRepr]]) + def updateClassPath(subst: Map[ClassPath[AbstractFile], ClassPath[AbstractFile]]) /** Any platform-specific phases. */ def platformPhases: List[SubComponent] @@ -36,16 +34,13 @@ trait Platform { /** 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 + def doLoad(cls: ClassPath[AbstractFile]#ClassRep): Boolean /** * Tells whether a class with both a binary and a source representation @@ -53,6 +48,6 @@ trait Platform { * 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 + def needCompile(bin: AbstractFile, src: AbstractFile): Boolean } diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index e6f21fc1e3..410d451316 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -1479,26 +1479,18 @@ abstract class GenICode extends SubComponent { if (mustUseAnyComparator) { // when -optimise is on we call the @inline-version of equals, found in ScalaRunTime - val equalsMethod = + val equalsMethod: Symbol = { if (!settings.optimise) { - def default = platform.externalEquals - platform match { - case x: JavaPlatform => - import x._ - if (l.tpe <:< BoxedNumberClass.tpe) { - if (r.tpe <:< BoxedNumberClass.tpe) externalEqualsNumNum - else if (r.tpe <:< BoxedCharacterClass.tpe) externalEqualsNumChar - else externalEqualsNumObject - } - else default - - case _ => default - } - } - else { + if (l.tpe <:< BoxedNumberClass.tpe) { + if (r.tpe <:< BoxedNumberClass.tpe) platform.externalEqualsNumNum + else if (r.tpe <:< BoxedCharacterClass.tpe) platform.externalEqualsNumChar + else platform.externalEqualsNumObject + } else platform.externalEquals + } else { ctx.bb.emit(LOAD_MODULE(ScalaRunTimeModule)) getMember(ScalaRunTimeModule, nme.inlinedEquals) } + } val ctx1 = genLoad(l, ctx, ObjectReference) val ctx2 = genLoad(r, ctx1, ObjectReference) diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala index 3f2141782a..b9eb8f8aac 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala @@ -10,6 +10,7 @@ package icode import java.io.PrintWriter import analysis.{ Liveness, ReachingDefinitions } import scala.tools.nsc.symtab.classfile.ICodeReader +import scala.reflect.io.AbstractFile /** Glue together ICode parts. * @@ -108,6 +109,12 @@ abstract class ICodes extends AnyRef object icodeReader extends ICodeReader { lazy val global: ICodes.this.global.type = ICodes.this.global + import global._ + def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol = + global.loaders.lookupMemberAtTyperPhaseIfPossible(sym, name) + lazy val symbolTable: global.type = global + lazy val loaders: global.loaders.type = global.loaders + def classPath: util.ClassPath[AbstractFile] = ICodes.this.global.platform.classPath } /** A phase which works on icode. */ diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index a7f43eefed..683f35e41f 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -1187,22 +1187,12 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { } if (mustUseAnyComparator) { - val equalsMethod = { - - def default = platform.externalEquals - - platform match { - case x: JavaPlatform => - import x._ - if (l.tpe <:< BoxedNumberClass.tpe) { - if (r.tpe <:< BoxedNumberClass.tpe) externalEqualsNumNum - else if (r.tpe <:< BoxedCharacterClass.tpe) externalEqualsNumChar - else externalEqualsNumObject - } - else default - - case _ => default - } + val equalsMethod: Symbol = { + if (l.tpe <:< BoxedNumberClass.tpe) { + if (r.tpe <:< BoxedNumberClass.tpe) platform.externalEqualsNumNum + else if (r.tpe <:< BoxedCharacterClass.tpe) platform.externalEqualsNumChar + else platform.externalEqualsNumObject + } else platform.externalEquals } genLoad(l, ObjectReference) genLoad(r, ObjectReference) diff --git a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala index 4e4efef607..4b9e056df3 100644 --- a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala @@ -12,9 +12,10 @@ import scala.tools.nsc.io.AbstractFile * This class should be used whenever file dependencies and recompile sets * are managed automatically. */ -abstract class BrowsingLoaders extends SymbolLoaders { - import global._ +abstract class BrowsingLoaders extends GlobalSymbolLoaders { + val global: Global + import global._ import syntaxAnalyzer.{OutlineParser, MalformedInput} /** In browse mode, it can happen that an encountered symbol is already diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index fd85bbb169..6f27eb8660 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -20,8 +20,23 @@ import scala.reflect.io.{ AbstractFile, NoAbstractFile } * @version 1.0 */ abstract class SymbolLoaders { - val global: Global - import global._ + val symbolTable: symtab.SymbolTable { + def settings: Settings + } + val platform: backend.Platform { + val symbolTable: SymbolLoaders.this.symbolTable.type + } + import symbolTable._ + /** + * Required by ClassfileParser. Check documentation in that class for details. + */ + def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol + /** + * Should forward to `Run.compileLate`. The more principled fix would be to + * determine why this functionality is needed and extract it into a separate + * interface. + */ + protected def compileLate(srcfile: AbstractFile): Unit import SymbolLoadersStats._ protected def enterIfNew(owner: Symbol, member: Symbol, completer: SymbolLoader): Symbol = { @@ -75,14 +90,14 @@ abstract class SymbolLoaders { name+"\none of them needs to be removed from classpath" ) else if (settings.termConflict.value == "package") { - global.warning( + 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( + warning( "Resolving package/object name conflict in favor of object " + preExisting.fullName + ". The package will be inaccessible." ) @@ -139,17 +154,17 @@ abstract class SymbolLoaders { /** 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[AbstractFile]#ClassRep) { ((classRep.binary, classRep.source) : @unchecked) match { case (Some(bin), Some(src)) if platform.needCompile(bin, src) && !binaryOnly(owner, classRep.name) => if (settings.verbose) inform("[symloader] picked up newer source file for " + src.path) - global.loaders.enterToplevelsFromSource(owner, classRep.name, src) + enterToplevelsFromSource(owner, classRep.name, src) case (None, Some(src)) => if (settings.verbose) inform("[symloader] no class, picked up source file for " + src.path) - global.loaders.enterToplevelsFromSource(owner, classRep.name, src) + enterToplevelsFromSource(owner, classRep.name, src) case (Some(bin), _) => - global.loaders.enterClassAndModule(owner, classRep.name, platform.newClassLoader(bin)) + enterClassAndModule(owner, classRep.name, new ClassfileLoader(bin)) } } @@ -221,7 +236,7 @@ abstract class SymbolLoaders { /** * Load contents of a package */ - class PackageLoader(classpath: ClassPath[platform.BinaryRepr]) extends SymbolLoader with FlagAgnosticCompleter { + class PackageLoader(classpath: ClassPath[AbstractFile]) extends SymbolLoader with FlagAgnosticCompleter { protected def description = "package loader "+ classpath.name protected def doComplete(root: Symbol) { @@ -245,8 +260,24 @@ abstract class SymbolLoaders { class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader with FlagAssigningCompleter { private object classfileParser extends { - val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global - } with ClassfileParser + val symbolTable: SymbolLoaders.this.symbolTable.type = SymbolLoaders.this.symbolTable + } with ClassfileParser { + override protected type ThisConstantPool = ConstantPool + override protected def newConstantPool: ThisConstantPool = new ConstantPool + override protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol = + SymbolLoaders.this.lookupMemberAtTyperPhaseIfPossible(sym, name) + /* + * The type alias and the cast (where the alias is used) is needed due to problem described + * in SI-7585. In this particular case, the problem is that we need to make sure that symbol + * table used by symbol loaders is exactly the same as they one used by classfileParser. + * If you look at the path-dependent types we have here everything should work out ok but + * due to issue described in SI-7585 type-checker cannot tie the knot here. + * + */ + private type SymbolLoadersRefined = SymbolLoaders { val symbolTable: classfileParser.symbolTable.type } + val loaders = SymbolLoaders.this.asInstanceOf[SymbolLoadersRefined] + val classPath = platform.classPath + } protected def description = "class file "+ classfile.toString @@ -272,7 +303,7 @@ abstract class SymbolLoaders { protected def description = "source file "+ srcfile.toString override def fromSource = true override def sourcefile = Some(srcfile) - protected def doComplete(root: Symbol): Unit = global.currentRun.compileLate(srcfile) + protected def doComplete(root: Symbol): Unit = compileLate(srcfile) } object moduleClassLoader extends SymbolLoader with FlagAssigningCompleter { diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index e4e3862bcd..c6ea6b23e5 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -17,6 +17,7 @@ import scala.reflect.internal.{ JavaAccFlags } import scala.reflect.internal.pickling.{PickleBuffer, ByteCodecs} import scala.tools.nsc.io.AbstractFile +import util.ClassPath /** This abstract class implements a class file parser. * @@ -24,18 +25,40 @@ import scala.tools.nsc.io.AbstractFile * @version 1.0 */ abstract class ClassfileParser { - val global: Global - import global._ + val symbolTable: SymbolTable { + def settings: Settings + } + val loaders: SymbolLoaders { + val symbolTable: ClassfileParser.this.symbolTable.type + } + + import symbolTable._ + /** + * If typer phase is defined then perform member lookup of a symbol + * `sym` at typer phase. This method results from refactoring. The + * original author of the logic that uses typer phase didn't explain + * why we need to force infos at that phase specifically. It only mentioned + * that ClassfileParse can be called late (e.g. at flatten phase) and + * we make to make sure we handle such situation properly. + */ + protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol + + /** The compiler classpath. */ + def classPath: ClassPath[AbstractFile] + import definitions._ import scala.reflect.internal.ClassfileConstants._ import Flags._ + protected type ThisConstantPool <: ConstantPool + protected def newConstantPool: ThisConstantPool + protected var in: AbstractFileReader = _ // the class file reader protected var clazz: Symbol = _ // the class symbol containing dynamic members protected var staticModule: Symbol = _ // the module symbol containing static members protected var instanceScope: Scope = _ // the scope of all instance definitions protected var staticScope: Scope = _ // the scope of all static definitions - protected var pool: ConstantPool = _ // the classfile's constant pool + protected var pool: ThisConstantPool = _ // the classfile's constant pool protected var isScala: Boolean = _ // does class file describe a scala class? protected var isScalaAnnot: Boolean = _ // does class file describe a scala class with its pickled info in an annotation? protected var isScalaRaw: Boolean = _ // this class file is a scala class with no pickled info @@ -50,7 +73,7 @@ abstract class ClassfileParser { def srcfile = srcfile0 - private def optimized = global.settings.optimise.value + private def optimized = settings.optimise.value private def currentIsTopLevel = !(currentClass.decodedName containsChar '$') // u1, u2, and u4 are what these data types are called in the JVM spec. @@ -70,7 +93,7 @@ abstract class ClassfileParser { private def readType() = pool getType u2 private object unpickler extends scala.reflect.internal.pickling.UnPickler { - val global: ClassfileParser.this.global.type = ClassfileParser.this.global + val symbolTable: ClassfileParser.this.symbolTable.type = ClassfileParser.this.symbolTable } private def handleMissing(e: MissingRequirementError) = { @@ -119,7 +142,7 @@ abstract class ClassfileParser { this.isScala = false parseHeader() - this.pool = new ConstantPool + this.pool = newConstantPool parseClass() } } @@ -134,11 +157,14 @@ abstract class ClassfileParser { abort(s"class file ${in.file} has unknown version $major.$minor, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION") } - class ConstantPool { - private val len = u2 - private val starts = new Array[Int](len) - private val values = new Array[AnyRef](len) - private val internalized = new Array[Name](len) + /** + * Constructor of this class should not be called directly, use `newConstantPool` instead. + */ + protected class ConstantPool { + protected val len = u2 + protected val starts = new Array[Int](len) + protected val values = new Array[AnyRef](len) + protected val internalized = new Array[Name](len) { var i = 1 while (i < starts.length) { @@ -212,76 +238,13 @@ abstract class ClassfileParser { getExternalName((in getChar start).toInt) } - /** Return the symbol of the class member at `index`. - * The following special cases exist: - * - If the member refers to special `MODULE$` static field, return - * the symbol of the corresponding module. - * - If the member is a field, and is not found with the given name, - * another try is made by appending `nme.LOCAL_SUFFIX_STRING` - * - If no symbol is found in the right tpe, a new try is made in the - * companion class, in case the owner is an implementation class. - */ - def getMemberSymbol(index: Int, static: Boolean): Symbol = { - if (index <= 0 || len <= index) errorBadIndex(index) - var f = values(index).asInstanceOf[Symbol] - if (f eq null) { - val start = starts(index) - val first = in.buf(start).toInt - if (first != CONSTANT_FIELDREF && - first != CONSTANT_METHODREF && - first != CONSTANT_INTFMETHODREF) errorBadTag(start) - val ownerTpe = getClassOrArrayType(in.getChar(start + 1).toInt) - debuglog("getMemberSymbol(static: " + static + "): owner type: " + ownerTpe + " " + ownerTpe.typeSymbol.originalName) - val (name0, tpe0) = getNameAndType(in.getChar(start + 3).toInt, ownerTpe) - debuglog("getMemberSymbol: name and tpe: " + name0 + ": " + tpe0) - - forceMangledName(tpe0.typeSymbol.name, module = false) - val (name, tpe) = getNameAndType(in.getChar(start + 3).toInt, ownerTpe) - if (name == nme.MODULE_INSTANCE_FIELD) { - val index = in.getChar(start + 1).toInt - val name = getExternalName(in.getChar(starts(index).toInt + 1).toInt) - //assert(name.endsWith("$"), "Not a module class: " + name) - f = forceMangledName(name dropRight 1, module = true) - if (f == NoSymbol) - f = rootMirror.getModuleByName(name dropRight 1) - } else { - val origName = nme.unexpandedName(name) - val owner = if (static) ownerTpe.typeSymbol.linkedClassOfClass else ownerTpe.typeSymbol - f = owner.info.findMember(origName, 0, 0, stableOnly = false).suchThat(_.tpe.widen =:= tpe) - if (f == NoSymbol) - f = owner.info.findMember(newTermName(origName + nme.LOCAL_SUFFIX_STRING), 0, 0, stableOnly = false).suchThat(_.tpe =:= tpe) - if (f == NoSymbol) { - // if it's an impl class, try to find it's static member inside the class - if (ownerTpe.typeSymbol.isImplClass) { - f = ownerTpe.findMember(origName, 0, 0, stableOnly = false).suchThat(_.tpe =:= tpe) - } else { - log("Couldn't find " + name + ": " + tpe + " inside: \n" + ownerTpe) - f = tpe match { - case MethodType(_, _) => owner.newMethod(name.toTermName, owner.pos) - case _ => owner.newVariable(name.toTermName, owner.pos) - } - f setInfo tpe - log("created fake member " + f.fullName) - } - } - } - assert(f != NoSymbol, - s"could not find $name: $tpe in $ownerTpe" + ( - if (settings.debug.value) ownerTpe.members.mkString(", members are:\n ", "\n ", "") else "" - ) - ) - values(index) = f - } - f - } - /** Return a name and a type at the given index. If the type is a method * type, a dummy symbol is created in `ownerTpe`, which is used as the * owner of its value parameters. This might lead to inconsistencies, * if a symbol of the given name already exists, and has a different * type. */ - private def getNameAndType(index: Int, ownerTpe: Type): (Name, Type) = { + protected def getNameAndType(index: Int, ownerTpe: Type): (Name, Type) = { if (index <= 0 || len <= index) errorBadIndex(index) values(index) match { case p: ((Name @unchecked, Type @unchecked)) => p @@ -381,37 +344,16 @@ abstract class ClassfileParser { } /** Throws an exception signaling a bad constant index. */ - private def errorBadIndex(index: Int) = + protected def errorBadIndex(index: Int) = abort(s"bad constant pool index: $index at pos: ${in.bp}") /** Throws an exception signaling a bad tag at given address. */ - private def errorBadTag(start: Int) = + protected def errorBadTag(start: Int) = abort("bad constant pool tag ${in.buf(start)} at byte $start") } - /** Try to force the chain of enclosing classes for the given name. Otherwise - * flatten would not lift classes that were not referenced in the source code. - */ - def forceMangledName(name: Name, module: Boolean): Symbol = { - val parts = name.decode.toString.split(Array('.', '$')) - var sym: Symbol = rootMirror.RootClass - - // was "at flatten.prev" - enteringFlatten { - for (part0 <- parts; if !(part0 == ""); part = newTermName(part0)) { - val sym1 = enteringIcode { - sym.linkedClassOfClass.info - sym.info.decl(part.encode) - }//.suchThat(module == _.isModule) - - sym = sym1 orElse sym.info.decl(part.encode.toTypeName) - } - } - sym - } - private def loadClassSymbol(name: Name): Symbol = { - val file = global.classPath findSourceFile ("" +name) getOrElse { + val file = classPath findSourceFile ("" +name) 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) @@ -419,7 +361,7 @@ abstract class ClassfileParser { warning(s"Class $name not found - continuing with a stub.") return NoSymbol.newClass(name.toTypeName) } - val completer = new global.loaders.ClassfileLoader(file) + val completer = new loaders.ClassfileLoader(file) var owner: Symbol = rootMirror.RootClass var sym: Symbol = NoSymbol var ss: Name = null @@ -1065,7 +1007,7 @@ abstract class ClassfileParser { def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile) { def jflags = entry.jflags - val completer = new global.loaders.ClassfileLoader(file) + val completer = new loaders.ClassfileLoader(file) val name = entry.originalName val sflags = jflags.toScalaFlags val owner = ownerForFlags(jflags) @@ -1073,7 +1015,7 @@ abstract class ClassfileParser { val innerClass = owner.newClass(name.toTypeName, NoPosition, sflags) setInfo completer val innerModule = owner.newModule(name.toTermName, NoPosition, sflags) setInfo completer - innerModule.moduleClass setInfo global.loaders.moduleClassLoader + innerModule.moduleClass setInfo loaders.moduleClassLoader List(innerClass, innerModule.moduleClass) foreach (_.associatedFile = file) scope enter innerClass @@ -1094,7 +1036,7 @@ abstract class ClassfileParser { for (entry <- innerClasses.entries) { // create a new class member for immediate inner classes if (entry.outerName == currentClass) { - val file = global.classPath.findSourceFile(entry.externalName.toString) getOrElse { + val file = classPath.findSourceFile(entry.externalName.toString) getOrElse { throw new AssertionError(entry.externalName) } enterClassAndModule(entry, file) @@ -1179,19 +1121,15 @@ abstract class ClassfileParser { case Some(entry) => innerSymbol(entry) case _ => NoSymbol } - // if loading during initialization of `definitions` typerPhase is not yet set. - // in that case we simply load the member at the current phase - @inline private def enteringTyperIfPossible(body: => Symbol): Symbol = - if (currentRun.typerPhase eq null) body else enteringTyper(body) private def innerSymbol(entry: InnerClassEntry): Symbol = { val name = entry.originalName.toTypeName val enclosing = entry.enclosing def getMember = ( if (enclosing == clazz) entry.scope lookup name - else enclosing.info member name + else lookupMemberAtTyperPhaseIfPossible(enclosing, name) ) - enteringTyperIfPossible(getMember) + getMember /* There used to be an assertion that this result is not NoSymbol; changing it to an error * revealed it had been going off all the time, but has been swallowed by a catch t: Throwable * in Repository.scala. Since it has been accomplishing nothing except misleading anyone who diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 01a117895f..f704d8ac89 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -20,6 +20,8 @@ import scala.reflect.internal.JavaAccFlags */ abstract class ICodeReader extends ClassfileParser { val global: Global + val symbolTable: global.type + val loaders: global.loaders.type import global._ import icodes._ @@ -28,6 +30,95 @@ abstract class ICodeReader extends ClassfileParser { var method: IMethod = NoIMethod // the current IMethod var isScalaModule = false + override protected type ThisConstantPool = ICodeConstantPool + override protected def newConstantPool = new ICodeConstantPool + + /** Try to force the chain of enclosing classes for the given name. Otherwise + * flatten would not lift classes that were not referenced in the source code. + */ + def forceMangledName(name: Name, module: Boolean): Symbol = { + val parts = name.decode.toString.split(Array('.', '$')) + var sym: Symbol = rootMirror.RootClass + + // was "at flatten.prev" + enteringFlatten { + for (part0 <- parts; if !(part0 == ""); part = newTermName(part0)) { + val sym1 = enteringIcode { + sym.linkedClassOfClass.info + sym.info.decl(part.encode) + }//.suchThat(module == _.isModule) + + sym = sym1 orElse sym.info.decl(part.encode.toTypeName) + } + } + sym + } + + protected class ICodeConstantPool extends ConstantPool { + /** Return the symbol of the class member at `index`. + * The following special cases exist: + * - If the member refers to special `MODULE$` static field, return + * the symbol of the corresponding module. + * - If the member is a field, and is not found with the given name, + * another try is made by appending `nme.LOCAL_SUFFIX_STRING` + * - If no symbol is found in the right tpe, a new try is made in the + * companion class, in case the owner is an implementation class. + */ + def getMemberSymbol(index: Int, static: Boolean): Symbol = { + if (index <= 0 || len <= index) errorBadIndex(index) + var f = values(index).asInstanceOf[Symbol] + if (f eq null) { + val start = starts(index) + val first = in.buf(start).toInt + if (first != CONSTANT_FIELDREF && + first != CONSTANT_METHODREF && + first != CONSTANT_INTFMETHODREF) errorBadTag(start) + val ownerTpe = getClassOrArrayType(in.getChar(start + 1).toInt) + debuglog("getMemberSymbol(static: " + static + "): owner type: " + ownerTpe + " " + ownerTpe.typeSymbol.originalName) + val (name0, tpe0) = getNameAndType(in.getChar(start + 3).toInt, ownerTpe) + debuglog("getMemberSymbol: name and tpe: " + name0 + ": " + tpe0) + + forceMangledName(tpe0.typeSymbol.name, module = false) + val (name, tpe) = getNameAndType(in.getChar(start + 3).toInt, ownerTpe) + if (name == nme.MODULE_INSTANCE_FIELD) { + val index = in.getChar(start + 1).toInt + val name = getExternalName(in.getChar(starts(index).toInt + 1).toInt) + //assert(name.endsWith("$"), "Not a module class: " + name) + f = forceMangledName(name dropRight 1, module = true) + if (f == NoSymbol) + f = rootMirror.getModuleByName(name dropRight 1) + } else { + val origName = nme.unexpandedName(name) + val owner = if (static) ownerTpe.typeSymbol.linkedClassOfClass else ownerTpe.typeSymbol + f = owner.info.findMember(origName, 0, 0, stableOnly = false).suchThat(_.tpe.widen =:= tpe) + if (f == NoSymbol) + f = owner.info.findMember(newTermName(origName + nme.LOCAL_SUFFIX_STRING), 0, 0, stableOnly = false).suchThat(_.tpe =:= tpe) + if (f == NoSymbol) { + // if it's an impl class, try to find it's static member inside the class + if (ownerTpe.typeSymbol.isImplClass) { + f = ownerTpe.findMember(origName, 0, 0, stableOnly = false).suchThat(_.tpe =:= tpe) + } else { + log("Couldn't find " + name + ": " + tpe + " inside: \n" + ownerTpe) + f = tpe match { + case MethodType(_, _) => owner.newMethod(name.toTermName, owner.pos) + case _ => owner.newVariable(name.toTermName, owner.pos) + } + f setInfo tpe + log("created fake member " + f.fullName) + } + } + } + assert(f != NoSymbol, + s"could not find $name: $tpe in $ownerTpe" + ( + if (settings.debug.value) ownerTpe.members.mkString(", members are:\n ", "\n ", "") else "" + ) + ) + values(index) = f + } + f + } + } + /** Read back bytecode for the given class symbol. It returns * two IClass objects, one for static members and one * for non-static members. diff --git a/src/eclipse/interactive/.classpath b/src/eclipse/interactive/.classpath index 73a67e45ed..9e773a39d2 100644 --- a/src/eclipse/interactive/.classpath +++ b/src/eclipse/interactive/.classpath @@ -3,7 +3,7 @@ <classpathentry kind="src" path="interactive"/> <classpathentry combineaccessrules="false" kind="src" path="/scaladoc"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_COMPILER_CONTAINER"/> - <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/> + <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> + <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> <classpathentry kind="output" path="build-quick-interactive"/> </classpath> diff --git a/src/eclipse/scala-compiler/.classpath b/src/eclipse/scala-compiler/.classpath index b6ef5f35bb..c185bc5391 100644 --- a/src/eclipse/scala-compiler/.classpath +++ b/src/eclipse/scala-compiler/.classpath @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="compiler"/> - <classpathentry combineaccessrules="false" kind="src" path="/asm"/> - <classpathentry combineaccessrules="false" kind="src" path="/reflect"/> - <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> - <classpathentry combineaccessrules="false" kind="src" path="/continuations-library"/> + <classpathentry combineaccessrules="false" exported="true" kind="src" path="/asm"/> + <classpathentry combineaccessrules="false" exported="true" kind="src" path="/reflect"/> + <classpathentry combineaccessrules="false" exported="true" kind="src" path="/scala-library"/> + <classpathentry combineaccessrules="false" exported="true" kind="src" path="/continuations-library"/> <classpathentry kind="var" path="SCALA_BASEDIR/lib/ant/ant.jar"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="output" path="build-quick-compiler"/> diff --git a/src/eclipse/scaladoc/.classpath b/src/eclipse/scaladoc/.classpath index caafcf33b0..8e03c97657 100644 --- a/src/eclipse/scaladoc/.classpath +++ b/src/eclipse/scaladoc/.classpath @@ -3,8 +3,10 @@ <classpathentry kind="src" path="scaladoc"/> <classpathentry combineaccessrules="false" kind="src" path="/partest"/> <classpathentry kind="var" path="SCALA_BASEDIR/lib/ant/ant.jar"/> - <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_COMPILER_CONTAINER"/> - <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/> <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 combineaccessrules="false" kind="src" path="/scala-xml"/> + <classpathentry combineaccessrules="false" kind="src" path="/scala-parser-combinators"/> <classpathentry kind="output" path="build-quick-scaladoc"/> </classpath> diff --git a/src/eclipse/test-junit/.classpath b/src/eclipse/test-junit/.classpath index 8e4f88e0f0..fe3c3e4f18 100644 --- a/src/eclipse/test-junit/.classpath +++ b/src/eclipse/test-junit/.classpath @@ -7,5 +7,6 @@ <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> <classpathentry combineaccessrules="false" kind="src" path="/continuations-library"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> <classpathentry kind="output" path="build-test-junit"/> </classpath> diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index 28b84d67ba..492f0f4fb4 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -365,13 +365,18 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") */ override def registerTopLevelSym(sym: Symbol) { currentTopLevelSyms += sym } + protected type SymbolLoadersInInteractive = GlobalSymbolLoaders { + val global: Global.this.type + val platform: Global.this.platform.type + } /** Symbol loaders in the IDE parse all source files loaded from a package for * top-level idents. Therefore, we can detect top-level symbols that have a name * different from their source file */ - override lazy val loaders: SymbolLoaders { val global: Global.this.type } = new BrowsingLoaders { + override lazy val loaders: SymbolLoadersInInteractive = new { val global: Global.this.type = Global.this - } + val platform: Global.this.platform.type = Global.this.platform + } with BrowsingLoaders // ----------------- Polling --------------------------------------- diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 2ae9f81a09..c340670635 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -52,6 +52,12 @@ abstract class SymbolTable extends macros.Universe def globalError(msg: String): Unit = abort(msg) def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg)) + protected def elapsedMessage(msg: String, start: Long) = + msg + " in " + (System.currentTimeMillis() - start) + "ms" + + def informProgress(msg: String) = if (settings.verbose) inform("[" + msg + "]") + def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start)) + def shouldLogAtThisPhase = false def isPastTyper = false diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index 6cffdbc193..f42dbf56e1 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -22,8 +22,8 @@ import scala.annotation.switch * @version 1.0 */ abstract class UnPickler { - val global: SymbolTable - import global._ + val symbolTable: SymbolTable + import symbolTable._ /** Unpickle symbol table information descending from a class and/or module root * from an array of bytes. diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 16405a88b4..93861b0899 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -529,7 +529,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } private object unpickler extends UnPickler { - val global: thisUniverse.type = thisUniverse + val symbolTable: thisUniverse.type = thisUniverse } /** how connected???? diff --git a/src/repl/scala/tools/nsc/interpreter/ReplVals.scala b/src/repl/scala/tools/nsc/interpreter/ReplVals.scala index ea100b25f2..9346b0553f 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplVals.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplVals.scala @@ -39,7 +39,7 @@ class StdReplVals(final val r: ILoop) extends ReplVals { def lastRequest = intp.lastRequest class ReplImplicits extends power.Implicits2 { - import intp.global._ + import intp.global.Symbol private val tagFn = ReplVals.mkCompilerTypeFromTag[intp.global.type](global) implicit def mkCompilerTypeFromTag(sym: Symbol) = tagFn(sym) diff --git a/src/scaladoc/scala/tools/nsc/doc/ScaladocGlobal.scala b/src/scaladoc/scala/tools/nsc/doc/ScaladocGlobal.scala index 20f24dc753..723f8b1dc8 100644 --- a/src/scaladoc/scala/tools/nsc/doc/ScaladocGlobal.scala +++ b/src/scaladoc/scala/tools/nsc/doc/ScaladocGlobal.scala @@ -23,9 +23,11 @@ trait ScaladocGlobalTrait extends Global { val runsAfter = List[String]() val runsRightAfter = None } - override lazy val loaders = new SymbolLoaders { - val global: outer.type = outer + override lazy val loaders = new { + val global: outer.type = outer + val platform: outer.platform.type = outer.platform + } with GlobalSymbolLoaders { // 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) diff --git a/test/files/presentation/doc/doc.scala b/test/files/presentation/doc/doc.scala index c884b6425b..f2233f1828 100755 --- a/test/files/presentation/doc/doc.scala +++ b/test/files/presentation/doc/doc.scala @@ -51,10 +51,6 @@ object Test extends InteractiveTest { new Typer(context) with InteractiveTyper with ScaladocTyper } - override lazy val loaders = new scala.tools.nsc.symtab.SymbolLoaders { - val global: outer.type = outer - } - def chooseLink(links: List[LinkTo]): LinkTo = links.head def internalLink(sym: Symbol, site: Symbol) = None def toString(link: LinkTo) = link.toString diff --git a/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala b/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala new file mode 100644 index 0000000000..285e87e3b2 --- /dev/null +++ b/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala @@ -0,0 +1,89 @@ +package scala.tools.nsc +package symtab + +import scala.reflect.internal.{Phase, NoPhase, SomePhase} +import scala.tools.util.PathResolver +import util.ClassPath +import io.AbstractFile + +/** + * A complete SymbolTable implementation designed to be used in JUnit tests. + * + * It enables `usejavacp` setting so classpath of JUnit runner is being used + * for symbol table's classpath. + * + * This class contains enough of logic implemented to make it possible to + * initialize definitions and inspect symbols. + */ +class SymbolTableForUnitTesting extends SymbolTable { + // Members declared in scala.reflect.api.Trees + override def newStrictTreeCopier: TreeCopier = new StrictTreeCopier + override def newLazyTreeCopier: TreeCopier = new LazyTreeCopier + trait TreeCopier extends InternalTreeCopierOps + // these should be mocks + class StrictTreeCopier extends super.StrictTreeCopier with TreeCopier + class LazyTreeCopier extends super.LazyTreeCopier with TreeCopier + + override def isCompilerUniverse: Boolean = true + def classPath = new PathResolver(settings).result + + object platform extends backend.Platform { + val symbolTable: SymbolTableForUnitTesting.this.type = SymbolTableForUnitTesting.this + lazy val loaders: SymbolTableForUnitTesting.this.loaders.type = SymbolTableForUnitTesting.this.loaders + def platformPhases: List[SubComponent] = Nil + val classPath: ClassPath[AbstractFile] = new PathResolver(settings).result + def doLoad(cls: ClassPath[AbstractFile]#ClassRep): Boolean = true + def isMaybeBoxed(sym: Symbol): Boolean = ??? + def needCompile(bin: AbstractFile, src: AbstractFile): Boolean = ??? + def externalEquals: Symbol = ??? + def updateClassPath(subst: Map[ClassPath[AbstractFile], ClassPath[AbstractFile]]): Unit = ??? + } + + object loaders extends symtab.SymbolLoaders { + val symbolTable: SymbolTableForUnitTesting.this.type = SymbolTableForUnitTesting.this + lazy val platform: symbolTable.platform.type = symbolTable.platform + def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol = + sym.info.member(name) + protected override def compileLate(srcfile: AbstractFile): Unit = + sys.error(s"We do not expect compileLate to be called in SymbolTableTest. The srcfile passed in is $srcfile") + } + + class GlobalMirror extends Roots(NoSymbol) { + val universe: SymbolTableForUnitTesting.this.type = SymbolTableForUnitTesting.this + def rootLoader: LazyType = new loaders.PackageLoader(classPath) + override def toString = "compiler mirror" + } + + lazy val rootMirror: Mirror = { + val rm = new GlobalMirror + rm.init() + rm.asInstanceOf[Mirror] + } + + def settings: Settings = { + val s = new Settings + // initialize classpath using java classpath + s.usejavacp.value = true + s + } + + // Members declared in scala.reflect.internal.Required + def picklerPhase: scala.reflect.internal.Phase = SomePhase + + // Members declared in scala.reflect.internal.SymbolTable + def currentRunId: Int = 1 + def log(msg: => AnyRef): Unit = println(msg) + def mirrorThatLoaded(sym: Symbol): Mirror = rootMirror + val phases: Seq[Phase] = List(NoPhase, SomePhase) + val phaseWithId: Array[Phase] = { + val maxId = phases.map(_.id).max + val phasesArray = Array.ofDim[Phase](maxId+1) + phases foreach { phase => + phasesArray(phase.id) = phase + } + phasesArray + } + lazy val treeInfo: scala.reflect.internal.TreeInfo{val global: SymbolTableForUnitTesting.this.type} = ??? + + phase = SomePhase +} diff --git a/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala b/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala new file mode 100644 index 0000000000..537cb93ef3 --- /dev/null +++ b/test/junit/scala/tools/nsc/symtab/SymbolTableTest.scala @@ -0,0 +1,50 @@ +package scala.tools.nsc +package symtab + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.runners.JUnit4 + +@RunWith(classOf[JUnit4]) +class SymbolTableTest { + private def createSymbolTable: SymbolTable = new SymbolTableForUnitTesting + + @Test + def initDefinitions = { + val symbolTable = createSymbolTable + symbolTable.definitions.init() + } + + @Test + def basicSubTypeCheck = { + val symbolTable = createSymbolTable + symbolTable.definitions.init() + val listClassTpe = symbolTable.definitions.ListClass.tpe + val seqClassTpe = symbolTable.definitions.SeqClass.tpe + assertTrue("List should be subclass of Seq", listClassTpe <:< seqClassTpe) + } + + /** + * Demonstrates how one can create symbols and type completely + * from scratch and perform sub type check. + */ + @Test + def customClassesSubTypeCheck: Unit = { + val symbolTable = createSymbolTable + import symbolTable._ + symbolTable.definitions.init() + val rootClass = symbolTable.rootMirror.RootClass + val fooSymbol = rootClass.newClassSymbol("Foo": TypeName, NoPosition, 0) + val fooType = new ClassInfoType(Nil, EmptyScope, fooSymbol) + fooSymbol.info = fooType + val barSymbol = rootClass.newClassSymbol("Bar": TypeName, NoPosition, 0) + val fooTypeRef = TypeRef(fooSymbol.owner.tpe, fooSymbol, Nil) + val barType = new ClassInfoType(List(fooTypeRef), EmptyScope, barSymbol) + barSymbol.info = barType + assertTrue("Bar should be subclass of Foo", barSymbol.tpe <:< fooSymbol.tpe) + assertFalse("Foo should be a superclass of Foo", fooSymbol.tpe <:< barSymbol.tpe) + } + +} |