From b5f721fb52767d0b839a0ef4614d1bcb039adba1 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 20 Jul 2012 12:46:05 +0200 Subject: SI-5999 a real fix to the packageless problem After a discussion on a reflection meeting on Jul 17 we concluded that we should split staticModule into staticModule and staticPackage to remove the ambiguity between packageless objects and packageless packages (more in the comments in the body of the commit). The motivation is verbosely outlined in the comments, but the bottom line is that Scala allows packages and packageless objects to have the same name within the same program. Therefore at times we need to disambiguate, hence the introduction of the staticPackage method. As of such staticModule no longer works for packages. In the same fashion staticPackage doesn't work for modules. This is done to ensure robustness of reification. I would like to do the same for getModule in Definitions, but we have to maintain backward compatibility. That's why I retained the old behavior, but replaced getModule invocations with getPackage where appropriate to be in line with staticModule and staticPackage. Another important thing that follows from the discussion is that both staticClass and staticModule prefer parent packages over parent objects in cases of ambiguity. Say, if we have the following snippet of code: object B { class C } next to package B { class C } then staticClass("B.C") will never even consider a C inside the object B. This is how scalac operates, so we decided to be consistent here. Finally reification logic got changed to distinguish between staticModule and staticPackage, and to allow for the fact that staticClass and staticModule prefer parent packages to parent objects. --- src/reflect/scala/reflect/internal/Mirrors.scala | 99 +++++++++++++--------- src/reflect/scala/reflect/internal/StdNames.scala | 1 + .../scala/reflect/runtime/JavaMirrors.scala | 12 +++ 3 files changed, 71 insertions(+), 41 deletions(-) (limited to 'src/reflect') diff --git a/src/reflect/scala/reflect/internal/Mirrors.scala b/src/reflect/scala/reflect/internal/Mirrors.scala index 100168a69d..f7561ae274 100644 --- a/src/reflect/scala/reflect/internal/Mirrors.scala +++ b/src/reflect/scala/reflect/internal/Mirrors.scala @@ -29,24 +29,14 @@ trait Mirrors extends api.Mirrors { else definitions.findNamedMember(segs.tail, RootClass.info member segs.head) } - /** If you're looking for a class, pass a type name. - * If a module, a term name. - * - * `tryPackageless` tells this function to search for requested a requested symbol in EmptyPackageClass as well. - * Compiler might ignore them, but they should be loadable with macros if the programmer wishes. - * More info here: http://groups.google.com/group/scala-internals/browse_thread/thread/5146021fd7c0cec - */ - private def getModuleOrClass(path: Name, tryPackageless: Boolean): Symbol = getModuleOrClass(path, path.length, tryPackageless) - /** Todo: organize similar to mkStatic in reflect.Base */ - private def getModuleOrClass(path: Name, len: Int, tryPackageless: Boolean): Symbol = { + private def getModuleOrClass(path: Name, len: Int): Symbol = { val point = path lastPos('.', len - 1) val owner = - if (point > 0) getModuleOrClass(path.toTermName, point, tryPackageless) + if (point > 0) getModuleOrClass(path.toTermName, point) else RootClass val name = path subName (point + 1, len) var sym = owner.info member name - if (sym == NoSymbol && owner == RootClass && tryPackageless) sym = EmptyPackageClass.info decl name val result = if (path.isTermName) sym.suchThat(_ hasFlag MODULE) else sym if (result != NoSymbol) result else { @@ -57,6 +47,27 @@ trait Mirrors extends api.Mirrors { } } + /** If you're looking for a class, pass a type name. + * If a module, a term name. + * + * Unlike `getModuleOrClass`, this function + * loads unqualified names from the root package. + */ + private def getModuleOrClass(path: Name): Symbol = + getModuleOrClass(path, path.length) + + /** If you're looking for a class, pass a type name. + * If a module, a term name. + * + * Unlike `getModuleOrClass`, this function + * loads unqualified names from the empty package. + */ + private def staticModuleOrClass(path: Name): Symbol = { + val isPackageless = path.pos('.') == path.length + if (isPackageless) EmptyPackageClass.info decl path + else getModuleOrClass(path) + } + protected def mirrorMissingHook(owner: Symbol, name: Name): Symbol = NoSymbol protected def universeMissingHook(owner: Symbol, name: Name): Symbol = self.missingHook(owner, name) @@ -67,8 +78,8 @@ trait Mirrors extends api.Mirrors { /************************ loaders of class symbols ************************/ - private def getClassImpl(fullname: TypeName, tryPackageless: Boolean): ClassSymbol = { - var result = getModuleOrClass(fullname.toTypeName, tryPackageless) + private def ensureClassSymbol(fullname: String, sym: Symbol): ClassSymbol = { + var result = sym while (result.isAliasType) result = result.info.typeSymbol result match { case x: ClassSymbol => x @@ -81,7 +92,7 @@ trait Mirrors extends api.Mirrors { getClassByName(fullname) def getClassByName(fullname: Name): ClassSymbol = - getClassImpl(fullname.toTypeName, tryPackageless = false) + ensureClassSymbol(fullname.toString, getModuleOrClass(fullname.toTypeName)) def getRequiredClass(fullname: String): ClassSymbol = getClassByName(newTypeNameCached(fullname)) @@ -90,33 +101,33 @@ trait Mirrors extends api.Mirrors { getRequiredClass(erasureName[T]) def getClassIfDefined(fullname: String): Symbol = - getClassIfDefined(newTypeName(fullname)) + getClassIfDefined(newTypeNameCached(fullname)) def getClassIfDefined(fullname: Name): Symbol = wrapMissing(getClassByName(fullname.toTypeName)) - /** Unlike getClassByName/getRequiredClass this function can also load packageless symbols. + /** @inheritdoc + * + * Unlike getClassByName/getRequiredClass this function can also load packageless symbols. * Compiler might ignore them, but they should be loadable with macros. */ override def staticClass(fullname: String): ClassSymbol = - getClassImpl(newTypeNameCached(fullname), tryPackageless = true) + ensureClassSymbol(fullname, staticModuleOrClass(newTypeNameCached(fullname))) /************************ loaders of module symbols ************************/ - private def getModuleImpl(fullname: TermName, tryPackageless: Boolean): ModuleSymbol = { - var result = getModuleOrClass(fullname, tryPackageless) - result match { - case x: ModuleSymbol => x - case _ => MissingRequirementError.notFound("object " + fullname) + private def ensureModuleSymbol(fullname: String, sym: Symbol, allowPackages: Boolean): ModuleSymbol = + sym match { + case x: ModuleSymbol if allowPackages || !x.isPackage => x + case _ => MissingRequirementError.notFound("object " + fullname) } - } @deprecated("Use getModuleByName", "2.10.0") def getModule(fullname: Name): ModuleSymbol = getModuleByName(fullname) def getModuleByName(fullname: Name): ModuleSymbol = - getModuleImpl(fullname.toTermName, tryPackageless = false) + ensureModuleSymbol(fullname.toString, getModuleOrClass(fullname.toTermName), allowPackages = true) def getRequiredModule(fullname: String): ModuleSymbol = getModule(newTermNameCached(fullname)) @@ -132,41 +143,47 @@ trait Mirrors extends api.Mirrors { getRequiredModule(erasureName[T] stripSuffix "$") def getModuleIfDefined(fullname: String): Symbol = - getModuleIfDefined(newTermName(fullname)) + getModuleIfDefined(newTermNameCached(fullname)) def getModuleIfDefined(fullname: Name): Symbol = wrapMissing(getModule(fullname.toTermName)) - /** Unlike getModule/getRequiredModule this function can also load packageless symbols. + /** @inheritdoc + * + * Unlike getModule/getRequiredModule this function can also load packageless symbols. * Compiler might ignore them, but they should be loadable with macros. */ override def staticModule(fullname: String): ModuleSymbol = - getModuleImpl(newTermNameCached(fullname), tryPackageless = true) + ensureModuleSymbol(fullname, staticModuleOrClass(newTermNameCached(fullname)), allowPackages = false) /************************ loaders of package symbols ************************/ - def getPackage(fullname: Name): ModuleSymbol = getModule(fullname) + private def ensurePackageSymbol(fullname: String, sym: Symbol, allowModules: Boolean): ModuleSymbol = + sym match { + case x: ModuleSymbol if allowModules || x.isPackage => x + case _ => MissingRequirementError.notFound("package " + fullname) + } + + def getPackage(fullname: Name): ModuleSymbol = + ensurePackageSymbol(fullname.toString, getModuleOrClass(fullname), allowModules = true) def getRequiredPackage(fullname: String): ModuleSymbol = getPackage(newTermNameCached(fullname)) def getPackageObject(fullname: String): ModuleSymbol = - (getModule(newTermName(fullname)).info member nme.PACKAGE) match { + (getPackage(newTermName(fullname)).info member nme.PACKAGE) match { case x: ModuleSymbol => x case _ => MissingRequirementError.notFound("package object " + fullname) } - def getPackageObjectIfDefined(fullname: String): Symbol = { - val module = getModuleIfDefined(newTermName(fullname)) - if (module == NoSymbol) NoSymbol - else { - val packageObject = module.info member nme.PACKAGE - packageObject match { - case x: ModuleSymbol => x - case _ => NoSymbol - } - } - } + def getPackageObjectIfDefined(fullname: String): Symbol = + getPackageObjectIfDefined(newTermNameCached(fullname)) + + def getPackageObjectIfDefined(fullname: Name): Symbol = + wrapMissing(getPackageObject(fullname.toTermName)) + + override def staticPackage(fullname: String): ModuleSymbol = + ensurePackageSymbol(fullname.toString, getModuleOrClass(newTermNameCached(fullname)), allowModules = false) /************************ helpers ************************/ diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index c8a2424118..e5d0e96d9c 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -754,6 +754,7 @@ trait StdNames { val splice: NameType = "splice" val staticClass : NameType = "staticClass" val staticModule : NameType = "staticModule" + val staticPackage : NameType = "staticPackage" val synchronized_ : NameType = "synchronized" val tail: NameType = "tail" val `then` : NameType = "then" diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 9f9f79058d..0878801715 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -72,6 +72,18 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym override def complete(sym: Symbol) = sym setInfo new LazyPackageType } + // reflective mirrors can't know the exhaustive list of available packages + // (that's because compiler mirrors are based on directories and reflective mirrors are based on classloaders, + // and unlike directories classloaders might make up stuff on the fly) + // hence we need to be optimistic and create packages out of thin air + // the same thing is done by the `missingHook` below + override def staticPackage(fullname: String): ModuleSymbol = + try super.staticPackage(fullname) + catch { + case _: MissingRequirementError => + makeScalaPackage(fullname) + } + // ----------- Caching ------------------------------------------------------------------ // [Eugene++ to Martin] not weak? why? -- cgit v1.2.3