diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2014-01-27 17:25:16 +0300 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2014-02-15 09:23:58 +0100 |
commit | 1dda1760113f782ffbcf10f0ca7b9e4f8817a62a (patch) | |
tree | be926837dc9b6afd0c02ca3d536b1051e3e810d2 | |
parent | d7b6662ddd346d0a4dc12ea62a3392ac176bf271 (diff) | |
download | scala-1dda1760113f782ffbcf10f0ca7b9e4f8817a62a.tar.gz scala-1dda1760113f782ffbcf10f0ca7b9e4f8817a62a.tar.bz2 scala-1dda1760113f782ffbcf10f0ca7b9e4f8817a62a.zip |
SI-7044 deprecates Symbol.associatedFile
Before this commit, Symbol.associatedFile used to be broken in two ways.
Firstly, it was never autoloaded (just like we used to have flags,
privateWithin and annotations). Secondly, it was never filled in by runtime
reflection.
My first attempt at fixing those problems was, well, just fixing them.
However, its runtime implementation was based on a hacky function that
we were not very much excited about supported (see comments),
whereas its compile-time usefulness was somewhat questionable.
Therefore the second attempt at fixing this bug is deprecating the API
altogether, replacing it with `Symbol.pos.source`.
Since `Symbol.pos` isn't retained for runtime consumption,
`Symbol.pos.source` is still going to return `NoAbstractFile` as before
this commit, but that's left for future work, and suggested approach
is documented in SI-8259.
-rw-r--r-- | src/reflect/scala/reflect/api/Symbols.scala | 1 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Symbols.scala | 6 | ||||
-rw-r--r-- | src/reflect/scala/reflect/io/NoAbstractFile.scala | 1 | ||||
-rw-r--r-- | src/reflect/scala/reflect/runtime/JavaMirrors.scala | 9 | ||||
-rw-r--r-- | src/reflect/scala/reflect/runtime/ReflectionUtils.scala | 71 | ||||
-rw-r--r-- | test/files/run/t7044.check | 14 | ||||
-rw-r--r-- | test/files/run/t7044/Macros_1.scala | 26 | ||||
-rw-r--r-- | test/files/run/t7044/Test_2.scala | 19 |
8 files changed, 145 insertions, 2 deletions
diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index 22039d22b6..ff7ac3f574 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -280,6 +280,7 @@ trait Symbols { self: Universe => * * @group Basics */ + @deprecated("Use `pos.source.file` instead", "2.11.0") def associatedFile: scala.reflect.io.AbstractFile /** A list of annotations attached to this Symbol. diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 35c7a59683..5e81badfad 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -3166,8 +3166,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def associatedFile = ( if (!isTopLevel) super.associatedFile - else if (_associatedFile eq null) NoAbstractFile // guarantee not null, but save cost of initializing the var - else _associatedFile + else { + if (_associatedFile eq null) NoAbstractFile // guarantee not null, but save cost of initializing the var + else _associatedFile + } ) override def associatedFile_=(f: AbstractFile) { _associatedFile = f } diff --git a/src/reflect/scala/reflect/io/NoAbstractFile.scala b/src/reflect/scala/reflect/io/NoAbstractFile.scala index a4e869ed41..18eca7698d 100644 --- a/src/reflect/scala/reflect/io/NoAbstractFile.scala +++ b/src/reflect/scala/reflect/io/NoAbstractFile.scala @@ -31,4 +31,5 @@ object NoAbstractFile extends AbstractFile { def output: java.io.OutputStream = null def path: String = "" override def toByteArray = Array[Byte]() + override def toString = "<no file>" } diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 1c942e8858..de0ad7161b 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -614,6 +614,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive info(s"unpickling Scala $clazz and $module, owner = ${clazz.owner}") val bytes = ssig.getBytes val len = ByteCodecs.decode(bytes) + assignAssociatedFile(clazz, module, jclazz) unpickler.unpickle(bytes take len, 0, clazz, module, jclazz.getName) markAllCompleted(clazz, module) case None => @@ -623,6 +624,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive val encoded = slsig flatMap (_.getBytes) val len = ByteCodecs.decode(encoded) val decoded = encoded.take(len) + assignAssociatedFile(clazz, module, jclazz) unpickler.unpickle(decoded, 0, clazz, module, jclazz.getName) markAllCompleted(clazz, module) case None => @@ -664,6 +666,12 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive } } + private def assignAssociatedFile(clazz: Symbol, module: Symbol, jclazz: jClass[_]): Unit = { + val associatedFile = ReflectionUtils.associatedFile(jclazz) + clazz.associatedFile = associatedFile + if (module != NoSymbol) module.associatedFile = associatedFile + } + /** * Copy all annotations of Java annotated element `jann` over to Scala symbol `sym`. * Also creates `@throws` annotations if necessary. @@ -719,6 +727,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive debugInfo("completing from Java " + sym + "/" + clazz.fullName)//debug assert(sym == clazz || (module != NoSymbol && (sym == module || sym == module.moduleClass)), sym) + assignAssociatedFile(clazz, module, jclazz) propagatePackageBoundary(jclazz, relatedSymbols: _*) copyAnnotations(clazz, jclazz) // to do: annotations to set also for module? diff --git a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala index d642b25127..a4bd698068 100644 --- a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala +++ b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala @@ -9,6 +9,8 @@ package reflect.runtime import java.lang.{Class => jClass} import java.lang.reflect.{ Method, InvocationTargetException, UndeclaredThrowableException } import scala.reflect.internal.util.AbstractFileClassLoader +import scala.reflect.io._ +import java.io.{File => JFile} /** A few java-reflection oriented utility functions useful during reflection bootstrapping. */ @@ -97,5 +99,74 @@ object ReflectionUtils { object EnclosedInConstructor extends EnclosedIn(_.getEnclosingConstructor) object EnclosedInClass extends EnclosedIn(_.getEnclosingClass) object EnclosedInPackage extends EnclosedIn(_.getPackage) + + def associatedFile(clazz: Class[_]): AbstractFile = { + // TODO: I agree with Jason - this implementation isn't something that we'd like to support + // therefore I'm having it commented out and this function will now return NoAbstractFile + // I think we can keep the source code though, because it can be useful to the others + // + // def inferAssociatedFile(clazz: Class[_]): AbstractFile = { + // // http://stackoverflow.com/questions/227486/find-where-java-class-is-loaded-from + // try { + // var cl = clazz.getClassLoader() + // if (cl == null) { + // cl = ClassLoader.getSystemClassLoader() + // while (cl != null && cl.getParent != null) cl = cl.getParent + // } + // var result: AbstractFile = null + // if (cl != null) { + // val name = clazz.getCanonicalName() + // val resource = cl.getResource(name.replace(".", "/") + ".class") + // if (resource != null) { + // def fromFile(file: String) = AbstractFile.getFile(file) + // def fromJarEntry(jarfile: String, entrypath: String) = { + // val jar = fromFile(jarfile) + // new VirtualFile(clazz.getName, entrypath) { + // lazy val impl: AbstractFile = { + // def loop(root: AbstractFile, path: List[String]): AbstractFile = { + // def find(name: String) = root.iterator.find(_.name == name).getOrElse(NoAbstractFile) + // path match { + // case step :: Nil => find(step) + // case step :: rest => loop(find(step), rest) + // case Nil => NoAbstractFile + // } + // } + // loop(ZipArchive.fromFile(new JFile(jarfile)), entrypath.split("/").toList) + // } + // override def container = impl.container + // override def lastModified = impl.lastModified + // override def input = impl.input + // override def sizeOption = impl.sizeOption + // override def underlyingSource = Some(jar) + // override def toString = jarfile + "(" + entrypath + ")" + // } + // } + // def fallback() = new VirtualFile(clazz.getName, resource.toString) + // result = resource.getProtocol match { + // case "file" => + // fromFile(resource.getFile) + // case "jar" => + // val intrajarUrl = new java.net.URL(resource.getFile) + // intrajarUrl.getProtocol match { + // case "file" => + // val file = intrajarUrl.getFile() + // val expectedSuffix = "!/" + name.replace(".", "/") + ".class" + // if (file.endsWith(expectedSuffix)) fromJarEntry(file.stripSuffix(expectedSuffix), expectedSuffix.substring(2)) + // else fallback() + // case _ => fallback() + // } + // case _ => + // fallback() + // } + // } + // } + // if (result != null) result else NoAbstractFile + // } catch { + // case _: Exception => NoAbstractFile + // } + // } + // inferAssociatedFile(clazz) + NoAbstractFile + } } diff --git a/test/files/run/t7044.check b/test/files/run/t7044.check new file mode 100644 index 0000000000..ab523873bf --- /dev/null +++ b/test/files/run/t7044.check @@ -0,0 +1,14 @@ +compile-time +uninitialized File: <no file> +initialized File: <no file> +uninitialized BitSet: <no file> +initialized BitSet: <no file> +uninitialized C: Test_2.scala +initialized C: Test_2.scala +runtime +autoinitialized File: <no file> true +autoinitialized File: <no file> true +autoinitialized BitSet: <no file> true +autoinitialized BitSet: <no file> true +autoinitialized C: <no file> true +autoinitialized C: <no file> true diff --git a/test/files/run/t7044/Macros_1.scala b/test/files/run/t7044/Macros_1.scala new file mode 100644 index 0000000000..3b3f8c3385 --- /dev/null +++ b/test/files/run/t7044/Macros_1.scala @@ -0,0 +1,26 @@ +import scala.reflect.macros.whitebox._ +import scala.language.experimental.macros + +object Macros { + def impl(c: Context) = { + var messages = List[String]() + def println(msg: String) = messages :+= msg + + import c.universe._ + def test(tpe: Type): Unit = { + val sym = tpe.typeSymbol + println(s"uninitialized ${sym.name}: ${sym.pos.source.file.name}") + internal.initialize(sym) + println(s"initialized ${sym.name}: ${sym.pos.source.file.name}") + } + + println("compile-time") + test(typeOf[java.io.File]) + test(typeOf[scala.collection.BitSet]) + test(c.mirror.staticClass("C").toType) + + q"..${messages.map(msg => q"println($msg)")}" + } + + def foo: Any = macro impl +}
\ No newline at end of file diff --git a/test/files/run/t7044/Test_2.scala b/test/files/run/t7044/Test_2.scala new file mode 100644 index 0000000000..8dfb349086 --- /dev/null +++ b/test/files/run/t7044/Test_2.scala @@ -0,0 +1,19 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} + +class C + +object Test extends App { + def test(tpe: Type): Unit = { + val sym = tpe.typeSymbol + println(s"autoinitialized ${sym.name}: ${sym.pos.source.file.name} ${sym.pos.source.file.sizeOption.nonEmpty}") + internal.initialize(sym) + println(s"autoinitialized ${sym.name}: ${sym.pos.source.file.name} ${sym.pos.source.file.sizeOption.nonEmpty}") + } + + Macros.foo + println("runtime") + test(typeOf[java.io.File]) + test(typeOf[scala.collection.BitSet]) + test(typeOf[C]) +} |