summaryrefslogtreecommitdiff
path: root/src/compiler/scala/reflect/runtime/JavaMirrors.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/reflect/runtime/JavaMirrors.scala')
-rw-r--r--src/compiler/scala/reflect/runtime/JavaMirrors.scala981
1 files changed, 981 insertions, 0 deletions
diff --git a/src/compiler/scala/reflect/runtime/JavaMirrors.scala b/src/compiler/scala/reflect/runtime/JavaMirrors.scala
new file mode 100644
index 0000000000..a8120d220a
--- /dev/null
+++ b/src/compiler/scala/reflect/runtime/JavaMirrors.scala
@@ -0,0 +1,981 @@
+package scala.reflect
+package runtime
+
+import scala.ref.WeakReference
+import scala.collection.mutable.WeakHashMap
+
+import java.lang.{Class => jClass, Package => jPackage}
+import java.lang.reflect.{
+ Method => jMethod, Constructor => jConstructor, Modifier => jModifier, Field => jField,
+ Member => jMember, Type => jType, TypeVariable => jTypeVariable, Array => jArray,
+ GenericDeclaration, GenericArrayType, ParameterizedType, WildcardType, AnnotatedElement }
+import java.io.IOException
+import internal.MissingRequirementError
+import internal.pickling.ByteCodecs
+import internal.ClassfileConstants._
+import internal.pickling.UnPickler
+import collection.mutable.{ HashMap, ListBuffer }
+import internal.Flags._
+//import scala.tools.nsc.util.ScalaClassLoader
+//import scala.tools.nsc.util.ScalaClassLoader._
+import ReflectionUtils.{singletonInstance}
+import language.existentials
+
+trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: SymbolTable =>
+
+ private lazy val mirrors = new WeakHashMap[ClassLoader, WeakReference[JavaMirror]]()
+
+ private def createMirror(owner: Symbol, cl: ClassLoader): Mirror = {
+ val jm = new JavaMirror(owner, cl)
+ mirrors(cl) = new WeakReference(jm)
+ jm.init()
+ jm
+ }
+
+ override type Mirror = JavaMirror
+
+ override lazy val rootMirror: Mirror = createMirror(NoSymbol, rootClassLoader)
+
+ // overriden by ReflectGlobal
+ def rootClassLoader: ClassLoader = this.getClass.getClassLoader
+
+ def init() = {
+ definitions.AnyValClass // force it.
+
+ // establish root association to avoid cyclic dependency errors later
+ rootMirror.classToScala(classOf[java.lang.Object]).initialize
+
+ // println("initializing definitions")
+ definitions.init()
+ }
+
+ def runtimeMirror(cl: ClassLoader): Mirror = mirrors get cl match {
+ case Some(WeakReference(m)) => m
+ case _ => createMirror(rootMirror.RootClass, cl)
+ }
+
+ /** The API of a mirror for a reflective universe */
+ class JavaMirror(owner: Symbol,
+ /** Class loader that is a mastermind behind the reflexive mirror */
+ val classLoader: ClassLoader
+ ) extends Roots(owner) with super.JavaMirror { wholemirror =>
+
+ val universe: self.type = self
+
+ import definitions._
+
+ /** The lazy type for root.
+ */
+ override lazy val rootLoader = new LazyType {
+ override def complete(sym: Symbol) = sym setInfo new LazyPackageType
+ }
+
+// ----------- Caching ------------------------------------------------------------------
+
+ // [Eugene++ to Martin] not weak? why?
+ private val classCache = new TwoWayCache[jClass[_], ClassSymbol]
+ private val packageCache = new TwoWayCache[Package, ModuleSymbol]
+ private val methodCache = new TwoWayCache[jMethod, MethodSymbol]
+ private val constructorCache = new TwoWayCache[jConstructor[_], MethodSymbol]
+ private val fieldCache = new TwoWayCache[jField, TermSymbol]
+ private val tparamCache = new TwoWayCache[jTypeVariable[_ <: GenericDeclaration], TypeSymbol]
+
+ def toScala[J: HasJavaClass, S](cache: TwoWayCache[J, S], key: J)(body: (JavaMirror, J) => S): S =
+ cache.toScala(key){
+ val jclazz = implicitly[HasJavaClass[J]] getClazz key
+ body(mirrorDefining(jclazz), key)
+ }
+
+ private implicit val classHasJavaClass: HasJavaClass[jClass[_]] =
+ new HasJavaClass(identity)
+ private implicit val methHasJavaClass: HasJavaClass[jMethod]
+ = new HasJavaClass(_.getDeclaringClass)
+ private implicit val fieldHasJavaClass: HasJavaClass[jField] =
+ new HasJavaClass(_.getDeclaringClass)
+ private implicit val constrHasJavaClass: HasJavaClass[jConstructor[_]] =
+ new HasJavaClass(_.getDeclaringClass)
+ private implicit val tparamHasJavaClass: HasJavaClass[jTypeVariable[_ <: GenericDeclaration]] =
+ new HasJavaClass ( (tparam: jTypeVariable[_ <: GenericDeclaration]) => {
+ tparam.getGenericDeclaration match {
+ case jclazz: jClass[_] => jclazz
+ case jmeth: jMethod => jmeth.getDeclaringClass
+ case jconstr: jConstructor[_] => jconstr.getDeclaringClass
+ }
+ })
+
+// ----------- Implementations of mirror operations and classes -------------------
+
+ def reflect(obj: Any): InstanceMirror =
+ new JavaInstanceMirror(obj.asInstanceOf[AnyRef])
+
+ def reflectClass(runtimeClass: RuntimeClass): ClassMirror =
+ new JavaClassMirror(classToScala(runtimeClass))
+
+ def reflectClass(fullName: String): ClassMirror =
+ reflectClass(java.lang.Class.forName(fullName))
+
+ def reflectModule(runtimeClass: RuntimeClass): ModuleMirror =
+ new JavaModuleMirror(classToScala(runtimeClass).companionModule.asModuleSymbol)
+
+ def reflectModule(fullName: String): ModuleMirror =
+ reflectModule(java.lang.Class.forName(fullName))
+
+ def runtimeClass(tpe: Type): RuntimeClass = typeToJavaClass(tpe)
+
+ def runtimeClass(cls: ClassSymbol): RuntimeClass = classToJava(cls)
+
+ private class JavaInstanceMirror(obj: AnyRef)
+ extends InstanceMirror {
+ def instance = obj
+ def reflectClass = wholemirror.reflectClass(obj.getClass)
+ def reflectField(field: TermSymbol): FieldMirror = new JavaFieldMirror(obj, field)
+ def reflectMethod(method: MethodSymbol): MethodMirror = new JavaMethodMirror(obj, method)
+ }
+
+ private class JavaFieldMirror(val receiver: AnyRef, val field: TermSymbol)
+ extends FieldMirror {
+ lazy val jfield = fieldToJava(field)
+ def get = jfield.get(receiver)
+ def set(value: Any) = jfield.set(receiver, value)
+ }
+
+ private class JavaMethodMirror(val receiver: AnyRef, val method: MethodSymbol)
+ extends MethodMirror {
+ lazy val jmeth = methodToJava(method)
+ def apply(args: Any*): Any =
+ if (method.owner == ArrayClass)
+ method.name match {
+ case nme.length => jArray.getLength(receiver)
+ case nme.apply => jArray.get(receiver, args(0).asInstanceOf[Int])
+ case nme.update => jArray.set(receiver, args(0).asInstanceOf[Int], args(1))
+ case _ => throw new Error(s"unexpected array method $method")
+ }
+ else
+ jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*)
+ }
+
+ private class JavaConstructorMirror(val method: MethodSymbol)
+ extends MethodMirror {
+ override val receiver = null
+ lazy val jconstr = constructorToJava(method)
+ def apply(args: Any*): Any = jconstr.newInstance(args.asInstanceOf[Seq[AnyRef]]: _*)
+ }
+
+
+ private abstract class JavaTemplateMirror
+ extends TemplateMirror {
+ def erasure: ClassSymbol
+ lazy val runtimeClass = classToJava(erasure)
+ lazy val signature = typeToScala(runtimeClass)
+ }
+
+ private class JavaClassMirror(val symbol: ClassSymbol)
+ extends JavaTemplateMirror with ClassMirror {
+ def erasure = symbol
+ def isStatic = false
+ def reflectConstructor(constructor: MethodSymbol) = new JavaConstructorMirror(constructor)
+ def companion: Option[ModuleMirror] = symbol.companionModule match {
+ case module: ModuleSymbol => Some(new JavaModuleMirror(module))
+ case _ => None
+ }
+ }
+
+ private class JavaModuleMirror(val symbol: ModuleSymbol)
+ extends JavaTemplateMirror with ModuleMirror {
+ def erasure = symbol.moduleClass.asClassSymbol
+ def isStatic = true
+ def instance = singletonInstance(classLoader, symbol.fullName)
+ def companion: Option[ClassMirror] = symbol.companionClass match {
+ case cls: ClassSymbol => Some(new JavaClassMirror(cls))
+ case _ => None
+ }
+ }
+
+// -------------------- Java to Scala -----------------------------------
+
+ /** Does method `meth` erase to Java method `jmeth`?
+ * This is true if the Java method type is the same as the Scala method type after performing
+ * all Scala-specific transformations in InfoTransformers. (to be done)
+ */
+ private def erasesTo(meth: Symbol, jmeth: jMethod): Boolean = {
+ val mtpe = transformedType(meth)
+ (mtpe.paramTypes map runtimeClass) == jmeth.getParameterTypes.toList &&
+ runtimeClass(mtpe.resultType) == jmeth.getReturnType
+ }
+
+ private def erasesTo(meth: Symbol, jconstr: jConstructor[_]): Boolean = {
+ val mtpe = transformedType(meth)
+ (mtpe.paramTypes map runtimeClass) == jconstr.getParameterTypes.toList &&
+ runtimeClass(mtpe.resultType) == jconstr.getDeclaringClass
+ }
+
+ def javaClass(path: String): jClass[_] =
+ Class.forName(path, true, classLoader)
+
+ /** Does `path` correspond to a Java class with that fully qualified name in the current class loader? */
+ def tryJavaClass(path: String): Option[jClass[_]] =
+ try {
+ Some(javaClass(path))
+ } catch {
+ case (_: ClassNotFoundException) | (_: NoClassDefFoundError) | (_: IncompatibleClassChangeError) =>
+ None
+ }
+
+ /** The mirror that corresponds to the classloader that original defined the given Java class */
+ def mirrorDefining(jclazz: jClass[_]): JavaMirror = {
+ val cl = jclazz.getClassLoader
+ if (cl == this.classLoader) this else runtimeMirror(cl)
+ }
+
+ private object unpickler extends UnPickler {
+ val global: self.type = self
+ }
+
+ /** how connected????
+ * Generate types for top-level Scala root class and root companion object
+ * from the pickled information stored in a corresponding Java class
+ * @param clazz The top-level Scala class for which info is unpickled
+ * @param module The top-level Scala companion object for which info is unpickled
+ * @param jclazz The Java class which contains the unpickled information in a
+ * ScalaSignature or ScalaLongSignature annotation.
+ */
+ def unpickleClass(clazz: Symbol, module: Symbol, jclazz: jClass[_]): Unit = {
+ def markAbsent(tpe: Type) = setAllInfos(clazz, module, tpe)
+ def handleError(ex: Exception) = {
+ markAbsent(ErrorType)
+ if (settings.debug.value) ex.printStackTrace()
+ val msg = ex.getMessage()
+ MissingRequirementError.signal(
+ (if (msg eq null) "reflection error while loading " + clazz.name
+ else "error while loading " + clazz.name) + ", " + msg)
+ }
+ // don't use classOf[scala.reflect.ScalaSignature] here, because it will use getClass.getClassLoader, not mirror's classLoader
+ // don't use asInstanceOf either because of the same reason (lol, I cannot believe I fell for it)
+ // don't use structural types to simplify reflective invocations because of the same reason
+ def loadAnnotation(name: String): Option[java.lang.annotation.Annotation] =
+ tryJavaClass(name) flatMap { annotClass =>
+ val anns = jclazz.getAnnotations
+ val result = anns find (_.annotationType == annotClass)
+ if (result.isEmpty && (anns exists (_.annotationType.getName == name)))
+ throw new ClassNotFoundException(
+ s"""Mirror classloader mismatch: $jclazz (loaded by ${ReflectionUtils.show(jclazz.getClassLoader)})
+ |is unrelated to the mirror's classloader: (${ReflectionUtils.show(classLoader)})""".stripMargin)
+ result
+ }
+ def loadBytes[T: ClassTag](name: String): Option[T] =
+ loadAnnotation(name) map { ssig =>
+ val bytesMethod = ssig.annotationType.getMethod("bytes")
+ bytesMethod.invoke(ssig).asInstanceOf[T]
+ }
+
+ try {
+ markAbsent(NoType)
+ loadBytes[String]("scala.reflect.ScalaSignature") match {
+ case Some(ssig) =>
+ info(s"unpickling Scala $clazz and $module, owner = ${clazz.owner}")
+ val bytes = ssig.getBytes
+ val len = ByteCodecs.decode(bytes)
+ unpickler.unpickle(bytes take len, 0, clazz, module, jclazz.getName)
+ case None =>
+ loadBytes[Array[String]]("scala.reflect.ScalaLongSignature") match {
+ case Some(slsig) =>
+ info(s"unpickling Scala $clazz and $module with long Scala signature")
+ val byteSegments = slsig map (_.getBytes)
+ val lens = byteSegments map ByteCodecs.decode
+ val bytes = Array.ofDim[Byte](lens.sum)
+ var len = 0
+ for ((bs, l) <- byteSegments zip lens) {
+ bs.copyToArray(bytes, len, l)
+ len += l
+ }
+ unpickler.unpickle(bytes, 0, clazz, module, jclazz.getName)
+ case None =>
+ // class does not have a Scala signature; it's a Java class
+ info("translating reflection info for Java " + jclazz) //debug
+ initClassModule(clazz, module, new FromJavaClassCompleter(clazz, module, jclazz))
+ }
+ }
+ } catch {
+ case ex: MissingRequirementError =>
+ handleError(ex)
+ case ex: IOException =>
+ handleError(ex)
+ }
+ }
+
+ /**
+ * A fresh Scala type parameter that corresponds to a Java type variable.
+ * The association between Scala type parameter and Java type variable is entered in the cache.
+ * @param jtvar The Java type variable
+ */
+ private def createTypeParameter(jtvar: jTypeVariable[_ <: GenericDeclaration]): TypeSymbol = {
+ val tparam = sOwner(jtvar).newTypeParameter(newTypeName(jtvar.getName))
+ .setInfo(new TypeParamCompleter(jtvar))
+ tparamCache enter (jtvar, tparam)
+ tparam
+ }
+
+ /**
+ * A completer that fills in the type of a Scala type parameter from the bounds of a Java type variable.
+ * @param jtvar The Java type variable
+ */
+ private class TypeParamCompleter(jtvar: jTypeVariable[_ <: GenericDeclaration]) extends LazyType {
+ override def load(sym: Symbol) = complete(sym)
+ override def complete(sym: Symbol) = {
+ sym setInfo TypeBounds.upper(glb(jtvar.getBounds.toList map typeToScala map objToAny))
+ }
+ }
+
+ /**
+ * Copy all annotations of Java annotated element `jann` over to Scala symbol `sym`.
+ * Pre: `sym` is already initialized with a concrete type.
+ * Note: If `sym` is a method or constructor, its parameter annotations are copied as well.
+ */
+ private def copyAnnotations(sym: Symbol, jann: AnnotatedElement) {
+ // to do: implement
+ }
+
+ /**
+ * A completer that fills in the types of a Scala class and its companion object
+ * by copying corresponding type info from a Java class. This completer is used
+ * to reflect classes in Scala that do not have a Scala pickle info, be it
+ * because they are local classes or have been compiled from Java sources.
+ * @param clazz The Scala class for which info is copied
+ * @param module The Scala companion object for which info is copied
+ * @param jclazz The Java class
+ */
+ private class FromJavaClassCompleter(clazz: Symbol, module: Symbol, jclazz: jClass[_]) extends LazyType {
+
+ /** used to avoid cycles while initializing classes */
+ private var parentsLevel = 0
+ private var pendingLoadActions: List[() => Unit] = Nil
+
+ override def load(sym: Symbol): Unit = {
+ debugInfo("completing from Java " + sym + "/" + clazz.fullName)//debug
+ assert(sym == clazz || (module != NoSymbol && (sym == module || sym == module.moduleClass)), sym)
+ val flags = toScalaClassFlags(jclazz.getModifiers)
+ clazz setFlag (flags | JAVA)
+ if (module != NoSymbol) {
+ module setFlag (flags & PRIVATE | JAVA)
+ module.moduleClass setFlag (flags & PRIVATE | JAVA)
+ }
+
+ copyAnnotations(clazz, jclazz)
+ // to do: annotations to set also for module?
+
+ clazz setInfo new LazyPolyType(jclazz.getTypeParameters.toList map createTypeParameter)
+ if (module != NoSymbol) {
+ module setInfo module.moduleClass.tpe
+ module.moduleClass setInfo new LazyPolyType(List())
+ }
+ }
+
+ override def complete(sym: Symbol): Unit = {
+ load(sym)
+ completeRest()
+ }
+
+ def completeRest(): Unit = self.synchronized {
+ val tparams = clazz.rawInfo.typeParams
+
+ val parents = try {
+ parentsLevel += 1
+ val jsuperclazz = jclazz.getGenericSuperclass
+ val superclazz = if (jsuperclazz == null) AnyClass.tpe else typeToScala(jsuperclazz)
+ superclazz :: (jclazz.getGenericInterfaces.toList map typeToScala)
+ } finally {
+ parentsLevel -= 1
+ }
+ clazz setInfo GenPolyType(tparams, new ClassInfoType(parents, newScope, clazz))
+ if (module != NoSymbol) {
+ module.moduleClass setInfo new ClassInfoType(List(), newScope, module.moduleClass)
+ }
+
+ def enter(sym: Symbol, mods: Int) =
+ (if (jModifier.isStatic(mods)) module.moduleClass else clazz).info.decls enter sym
+
+ for (jinner <- jclazz.getDeclaredClasses) {
+ enter(jclassAsScala(jinner, clazz), jinner.getModifiers)
+ }
+
+ pendingLoadActions = { () =>
+
+ for (jfield <- jclazz.getDeclaredFields)
+ enter(jfieldAsScala(jfield), jfield.getModifiers)
+
+ for (jmeth <- jclazz.getDeclaredMethods)
+ enter(jmethodAsScala(jmeth), jmeth.getModifiers)
+
+ for (jconstr <- jclazz.getConstructors)
+ enter(jconstrAsScala(jconstr), jconstr.getModifiers)
+
+ } :: pendingLoadActions
+
+ if (parentsLevel == 0) {
+ while (!pendingLoadActions.isEmpty) {
+ val item = pendingLoadActions.head
+ pendingLoadActions = pendingLoadActions.tail
+ item()
+ }
+ }
+ }
+
+ class LazyPolyType(override val typeParams: List[Symbol]) extends LazyType {
+ override def complete(sym: Symbol) {
+ completeRest()
+ }
+ }
+ }
+
+ /**
+ * If Java modifiers `mods` contain STATIC, return the module class
+ * of the companion module of `clazz`, otherwise the class `clazz` itself.
+ */
+ private def followStatic(clazz: Symbol, mods: Int) =
+ if (jModifier.isStatic(mods)) clazz.companionModule.moduleClass else clazz
+
+ implicit class RichClass(jclazz: jClass[_]) {
+ // [Eugene++] `jclazz.isLocalClass` doesn't work because of problems with `getSimpleName`
+ // java.lang.Error: sOwner(class Test$A$1) has failed
+ // Caused by: java.lang.InternalError: Malformed class name
+ // at java.lang.Class.getSimpleName(Class.java:1133)
+ // at java.lang.Class.isAnonymousClass(Class.java:1188)
+ // at java.lang.Class.isLocalClass(Class.java:1199)
+ // (see t5256c.scala for more details)
+ // hence we have to approximate by removing the `isAnonymousClass` check
+// def isLocalClass0: Boolean = jclazz.isLocalClass
+ def isLocalClass0: Boolean = jclazz.getEnclosingMethod != null || jclazz.getEnclosingConstructor != null
+ }
+
+ // [Eugene++] overflow from Paul's changes made concurrently with reflection refactoring
+ // https://github.com/scala/scala/commit/90d2bee45b25844f809f8c5300aefcb1bfe9e336
+ //
+ // /** Methods which need to be wrapped because they either are getSimpleName
+ // * or call getSimpleName:
+ // *
+ // * public String getSimpleName()
+ // * public boolean isAnonymousClass()
+ // * public boolean isLocalClass()
+ // * public boolean isMemberClass()
+ // * public String getCanonicalName()
+ // *
+ // * TODO - find all such calls and wrap them.
+ // * TODO - create mechanism to avoid the recurrence of unwrapped calls.
+ // */
+ // private def wrapClassCheck[T](alt: T)(body: => T): T =
+ // try body catch { case x: InternalError if x.getMessage == "Malformed class name" => alt }
+
+ // private def wrapIsLocalClass(clazz: jClass[_]): Boolean =
+ // wrapClassCheck(false)(clazz.isLocalClass)
+
+ // private def wrapGetSimpleName(clazz: jClass[_]): String =
+ // wrapClassCheck("")(clazz.getSimpleName)
+
+ /**
+ * The Scala owner of the Scala class corresponding to the Java class `jclazz`
+ */
+ private def sOwner(jclazz: jClass[_]): Symbol =
+ if (jclazz.isMemberClass) {
+ val jEnclosingClass = jclazz.getEnclosingClass
+ val sEnclosingClass = classToScala(jEnclosingClass)
+ followStatic(sEnclosingClass, jclazz.getModifiers)
+ } else if (jclazz.isLocalClass0) {
+ val jEnclosingMethod = jclazz.getEnclosingMethod
+ if (jEnclosingMethod != null) {
+ methodToScala(jEnclosingMethod)
+ } else {
+ val jEnclosingConstructor = jclazz.getEnclosingConstructor
+ constructorToScala(jEnclosingConstructor)
+ }
+ } else if (jclazz.isPrimitive || jclazz.isArray) {
+ ScalaPackageClass
+ } else if (jclazz.getPackage != null) {
+ val jPackage = jclazz.getPackage
+ packageToScala(jPackage).moduleClass
+ } else {
+ // @eb: a weird classloader might return a null package for something with a non-empty package name
+ // for example, http://groups.google.com/group/scala-internals/browse_thread/thread/7be09ff8f67a1e5c
+ // in that case we could invoke packageNameToScala(jPackageName) and, probably, be okay
+ // however, I think, it's better to blow up, since weirdness of the class loader might bite us elsewhere
+ // [martin] I think it's better to be forgiving here. Restoring packageNameToScala.
+ val jPackageName = jclazz.getName take jclazz.getName.lastIndexOf('.')
+ packageNameToScala(jPackageName).moduleClass
+ }
+
+ /**
+ * The Scala owner of the Scala symbol corresponding to the Java member `jmember`
+ */
+ private def sOwner(jmember: jMember): Symbol = {
+ followStatic(classToScala(jmember.getDeclaringClass), jmember.getModifiers)
+ }
+
+ /**
+ * The Scala owner of the Scala type parameter corresponding to the Java type variable `jtvar`
+ */
+ private def sOwner(jtvar: jTypeVariable[_ <: GenericDeclaration]): Symbol =
+ genericDeclarationToScala(jtvar.getGenericDeclaration)
+
+ /**
+ * Find declarations or definition in class `clazz` that maps to a Java
+ * entity with name `jname`. Because of name-mangling, this is more difficult
+ * than a simple name-based lookup via `decl`. If `decl` fails, members
+ * that start with the given name are searched instead.
+ */
+ private def lookup(clazz: Symbol, jname: String): Symbol = {
+ def approximateMatch(sym: Symbol, jstr: String): Boolean =
+ (sym.name.toString == jstr) ||
+ sym.isPrivate && nme.expandedName(sym.name.toTermName, sym.owner).toString == jstr
+
+ clazz.info.decl(newTermName(jname)) orElse {
+ (clazz.info.decls.iterator filter (approximateMatch(_, jname))).toList match {
+ case List() => NoSymbol
+ case List(sym) => sym
+ case alts => clazz.newOverloaded(alts.head.tpe.prefix, alts)
+ }
+ }
+ }
+
+ /**
+ * The Scala method corresponding to given Java method.
+ * @param jmeth The Java method
+ * @return A Scala method object that corresponds to `jmeth`.
+ */
+ def methodToScala(jmeth: jMethod): MethodSymbol =
+ toScala(methodCache, jmeth)(_ methodToScala1 _)
+
+ private def methodToScala1(jmeth: jMethod): MethodSymbol = {
+ val jOwner = jmeth.getDeclaringClass
+ val preOwner = classToScala(jOwner)
+ val owner = followStatic(preOwner, jmeth.getModifiers)
+ (lookup(owner, jmeth.getName) suchThat (erasesTo(_, jmeth)) orElse jmethodAsScala(jmeth))
+ .asMethodSymbol
+ }
+
+ /**
+ * The Scala constructor corresponding to given Java constructor.
+ * @param jconstr The Java constructor
+ * @return A Scala method object that corresponds to `jconstr`.
+ */
+ def constructorToScala(jconstr: jConstructor[_]): MethodSymbol =
+ toScala(constructorCache, jconstr)(_ constructorToScala1 _)
+
+ private def constructorToScala1(jconstr: jConstructor[_]): MethodSymbol = {
+ val owner = followStatic(classToScala(jconstr.getDeclaringClass), jconstr.getModifiers)
+ (lookup(owner, jconstr.getName) suchThat (erasesTo(_, jconstr)) orElse jconstrAsScala(jconstr))
+ .asMethodSymbol
+ }
+
+ /**
+ * The Scala field corresponding to given Java field.
+ * @param jfield The Java field
+ * @return A Scala field object that corresponds to `jfield`.
+ * // ??? should we return the getter instead?
+ */
+ def fieldToScala(jfield: jField): TermSymbol =
+ toScala(fieldCache, jfield)(_ fieldToScala1 _)
+
+ private def fieldToScala1(jfield: jField): TermSymbol = {
+ val owner = followStatic(classToScala(jfield.getDeclaringClass), jfield.getModifiers)
+ (lookup(owner, jfield.getName) suchThat (!_.isMethod) orElse jfieldAsScala(jfield))
+ .asTermSymbol
+ }
+
+ /**
+ * The Scala package corresponding to given Java package
+ */
+ def packageToScala(jpkg: jPackage): ModuleSymbol = packageCache.toScala(jpkg) {
+ makeScalaPackage(jpkg.getName)
+ }
+
+ /**
+ * The Scala package with given fully qualified name.
+ */
+ def packageNameToScala(fullname: String): ModuleSymbol = {
+ if (fullname == "") EmptyPackage
+ else {
+ val jpkg = jPackage.getPackage(fullname)
+ if (jpkg != null) packageToScala(jpkg) else makeScalaPackage(fullname)
+ }
+ }
+
+ /**
+ * The Scala package with given fully qualified name. Unlike `packageNameToScala`,
+ * this one bypasses the cache.
+ */
+ private[JavaMirrors] def makeScalaPackage(fullname: String): ModuleSymbol = {
+ val split = fullname lastIndexOf '.'
+ val ownerModule: ModuleSymbol =
+ if (split > 0) packageNameToScala(fullname take split) else this.RootPackage
+ val owner = ownerModule.moduleClass
+ val name = newTermName(fullname drop (split + 1))
+ val opkg = owner.info decl name
+ if (opkg.isPackage)
+ opkg.asModuleSymbol
+ else if (opkg == NoSymbol) {
+ val pkg = owner.newPackage(name)
+ pkg.moduleClass setInfo new LazyPackageType
+ pkg setInfoAndEnter pkg.moduleClass.tpe
+ info("made Scala "+pkg)
+ pkg
+ } else
+ throw new ReflectError(opkg+" is not a package")
+ }
+
+ private def scalaSimpleName(jclazz: jClass[_]): TypeName = {
+ val owner = sOwner(jclazz)
+ val enclosingClass = jclazz.getEnclosingClass
+ var prefix = if (enclosingClass != null) enclosingClass.getName else ""
+ val isObject = owner.isModuleClass && !owner.isPackageClass
+ if (isObject && !prefix.endsWith(nme.MODULE_SUFFIX_STRING)) prefix += nme.MODULE_SUFFIX_STRING
+ assert(jclazz.getName.startsWith(prefix))
+ var name = jclazz.getName.substring(prefix.length)
+ name = name.substring(name.lastIndexOf(".") + 1)
+ newTypeName(name)
+ }
+
+ /**
+ * The Scala class that corresponds to a given Java class.
+ * @param jclazz The Java class
+ * @return A Scala class symbol that reflects all elements of the Java class,
+ * in the form they appear in the Scala pickling info, or, if that is
+ * not available, wrapped from the Java reflection info.
+ */
+ def classToScala(jclazz: jClass[_]): ClassSymbol =
+ toScala(classCache, jclazz)(_ classToScala1 _)
+
+ private def classToScala1(jclazz: jClass[_]): ClassSymbol = {
+ val jname = newTypeName(jclazz.getName)
+ if (jname == fulltpnme.RuntimeNothing) NothingClass
+ else if (jname == fulltpnme.RuntimeNull) NullClass
+ else {
+ val owner = sOwner(jclazz)
+ val simpleName = scalaSimpleName(jclazz)
+
+ def lookupClass = {
+ def coreLookup(name: Name): Symbol =
+ owner.info.decl(name) orElse {
+ if (name.startsWith(nme.NAME_JOIN_STRING)) coreLookup(name drop 1) else NoSymbol
+ }
+ if (nme.isModuleName(simpleName))
+ coreLookup(nme.stripModuleSuffix(simpleName).toTermName) map (_.moduleClass)
+ else
+ coreLookup(simpleName)
+ }
+
+ val cls =
+ if (jclazz.isMemberClass && !nme.isImplClassName(jname))
+ lookupClass
+ else if (jclazz.isLocalClass0 || isInvalidClassName(jname))
+ // local classes and implementation classes not preserved by unpickling - treat as Java
+ jclassAsScala(jclazz)
+ else if (jclazz.isArray)
+ ArrayClass
+ else
+ javaTypeToValueClass(jclazz) orElse lookupClass
+
+ assert (cls.isType,
+ s"""${if (cls == NoSymbol) "not a type: symbol" else "no symbol could be"}
+ | loaded from $jclazz in $owner with name $simpleName and classloader $classLoader""".stripMargin)
+
+ cls.asClassSymbol
+ }
+ }
+
+ /**
+ * The Scala type parameter that corresponds to a given Java type parameter.
+ * @param jparam The Java type parameter
+ * @return A Scala type parameter symbol that has the same owner and name as the Java type parameter
+ */
+ def typeParamToScala(jparam: jTypeVariable[_ <: GenericDeclaration]): TypeSymbol =
+ toScala(tparamCache, jparam)(_ typeParamToScala1 _)
+
+ private def typeParamToScala1(jparam: jTypeVariable[_ <: GenericDeclaration]): TypeSymbol = {
+ val owner = genericDeclarationToScala(jparam.getGenericDeclaration)
+ owner.info match {
+ case PolyType(tparams, _) => tparams.find(_.name.toString == jparam.getName).get.asTypeSymbol
+ }
+ }
+
+ /**
+ * The Scala symbol that corresponds to a given Java generic declaration (class, method, or constructor)
+ */
+ def genericDeclarationToScala(jdecl: GenericDeclaration): Symbol = jdecl match {
+ case jclazz: jClass[_] => classToScala(jclazz)
+ case jmeth: jMethod => methodToScala(jmeth)
+ case jconstr: jConstructor[_] => constructorToScala(jconstr)
+ }
+
+ /**
+ * Given some Java type arguments, a corresponding list of Scala types, plus potentially
+ * some existentially bound type variables that represent wildcard arguments.
+ */
+ private def targsToScala(owner: Symbol, args: List[jType]): (List[Type], List[TypeSymbol]) = {
+ val tparams = new ListBuffer[TypeSymbol]
+ def targToScala(arg: jType): Type = arg match {
+ case jwild: WildcardType =>
+ val tparam = owner.newExistential(newTypeName("T$" + tparams.length))
+ .setInfo(TypeBounds(
+ lub(jwild.getLowerBounds.toList map typeToScala),
+ glb(jwild.getUpperBounds.toList map typeToScala map objToAny)))
+ tparams += tparam
+ typeRef(NoPrefix, tparam, List())
+ case _ =>
+ typeToScala(arg)
+ }
+ (args map targToScala, tparams.toList)
+ }
+
+ /**
+ * The Scala type that corresponds to given Java type
+ */
+ def typeToScala(jtpe: jType): Type = jtpe match {
+ case jclazz: jClass[_] =>
+ if (jclazz.isArray)
+ arrayType(typeToScala(jclazz.getComponentType))
+ else {
+ val clazz = classToScala(jclazz)
+ rawToExistential(typeRef(clazz.owner.thisType, clazz, List()))
+ }
+ case japplied: ParameterizedType =>
+ val (pre, sym) = typeToScala(japplied.getRawType) match {
+ case ExistentialType(tparams, TypeRef(pre, sym, _)) => (pre, sym)
+ case TypeRef(pre, sym, _) => (pre, sym)
+ }
+ val args0 = japplied.getActualTypeArguments
+ val (args, bounds) = targsToScala(pre.typeSymbol, args0.toList)
+ ExistentialType(bounds, typeRef(pre, sym, args))
+ case jarr: GenericArrayType =>
+ arrayType(typeToScala(jarr.getGenericComponentType))
+ case jtvar: jTypeVariable[_] =>
+ val tparam = typeParamToScala(jtvar)
+ typeRef(NoPrefix, tparam, List())
+ }
+
+ /**
+ * The Scala class that corresponds to given Java class without taking
+ * Scala pickling info into account.
+ * @param jclazz The Java class
+ * @return A Scala class symbol that wraps all reflection info of `jclazz`
+ */
+ private def jclassAsScala(jclazz: jClass[_]): Symbol = jclassAsScala(jclazz, sOwner(jclazz))
+
+ private def jclassAsScala(jclazz: jClass[_], owner: Symbol): ClassSymbol = {
+ val name = scalaSimpleName(jclazz)
+ val completer = (clazz: Symbol, module: Symbol) => new FromJavaClassCompleter(clazz, module, jclazz)
+ val (clazz, module) = createClassModule(owner, name, completer)
+ classCache enter (jclazz, clazz)
+ clazz
+ }
+
+ /**
+ * The Scala field that corresponds to given Java field without taking
+ * Scala pickling info into account.
+ * @param jfield The Java field
+ * @return A Scala value symbol that wraps all reflection info of `jfield`
+ */
+ private def jfieldAsScala(jfield: jField): TermSymbol =
+ toScala(fieldCache, jfield)(_ jfieldAsScala1 _)
+
+ private def jfieldAsScala1(jfield: jField): TermSymbol = {
+ val field = sOwner(jfield)
+ .newValue(newTermName(jfield.getName), NoPosition, toScalaFieldFlags(jfield.getModifiers))
+ .setInfo(typeToScala(jfield.getGenericType))
+ fieldCache enter (jfield, field)
+ copyAnnotations(field, jfield)
+ field
+ }
+
+ private def setMethType(meth: Symbol, tparams: List[Symbol], paramtpes: List[Type], restpe: Type) = {
+ meth setInfo GenPolyType(tparams, MethodType(meth.owner.newSyntheticValueParams(paramtpes map objToAny), restpe))
+ }
+
+ /**
+ * The Scala method that corresponds to given Java method without taking
+ * Scala pickling info into account.
+ * @param jmeth The Java method
+ * @return A Scala method symbol that wraps all reflection info of `jmethod`
+ */
+ private def jmethodAsScala(jmeth: jMethod): MethodSymbol =
+ toScala(methodCache, jmeth)(_ jmethodAsScala1 _)
+
+ private def jmethodAsScala1(jmeth: jMethod): MethodSymbol = {
+ val clazz = sOwner(jmeth)
+ val meth = clazz.newMethod(newTermName(jmeth.getName), NoPosition, toScalaMethodFlags(jmeth.getModifiers))
+ methodCache enter (jmeth, meth)
+ val tparams = jmeth.getTypeParameters.toList map createTypeParameter
+ val paramtpes = jmeth.getGenericParameterTypes.toList map typeToScala
+ val resulttpe = typeToScala(jmeth.getGenericReturnType)
+ setMethType(meth, tparams, paramtpes, resulttpe)
+ copyAnnotations(meth, jmeth)
+ if ((jmeth.getModifiers & JAVA_ACC_VARARGS) != 0) meth.setInfo(arrayToRepeated(meth.info))
+ meth
+ }
+
+ /**
+ * The Scala constructor that corresponds to given Java constructor without taking
+ * Scala pickling info into account.
+ * @param jconstr The Java constructor
+ * @return A Scala constructor symbol that wraps all reflection info of `jconstr`
+ */
+ private def jconstrAsScala(jconstr: jConstructor[_]): MethodSymbol =
+ toScala(constructorCache, jconstr)(_ jconstrAsScala1 _)
+
+ private def jconstrAsScala1(jconstr: jConstructor[_]): MethodSymbol = {
+ // [Martin] Note: I know there's a lot of duplication wrt jmethodAsScala, but don't think it's worth it to factor this out.
+ val clazz = sOwner(jconstr)
+ val constr = clazz.newConstructor(NoPosition, toScalaMethodFlags(jconstr.getModifiers))
+ constructorCache enter (jconstr, constr)
+ val tparams = jconstr.getTypeParameters.toList map createTypeParameter
+ val paramtpes = jconstr.getGenericParameterTypes.toList map typeToScala
+ setMethType(constr, tparams, paramtpes, clazz.tpe)
+ constr setInfo GenPolyType(tparams, MethodType(clazz.newSyntheticValueParams(paramtpes), clazz.tpe))
+ copyAnnotations(constr, jconstr)
+ constr
+ }
+
+// -------------------- Scala to Java -----------------------------------
+
+ /** Optionally, the Java package corresponding to a given Scala package, or None if no such Java package exists.
+ * @param pkg The Scala package
+ */
+ def packageToJavaOption(pkg: ModuleSymbol): Option[jPackage] = packageCache.toJavaOption(pkg) {
+ Option(jPackage.getPackage(pkg.fullName.toString))
+ }
+
+ /** The Java class corresponding to given Scala class.
+ * Note: This only works for
+ * - top-level classes
+ * - Scala classes that were generated via jclassToScala
+ * - classes that have a class owner that has a corresponding Java class
+ * @throws A `ClassNotFoundException` for all Scala classes not in one of these categories.
+ */
+ @throws(classOf[ClassNotFoundException])
+ def classToJava(clazz: ClassSymbol): jClass[_] = classCache.toJava(clazz) {
+ def noClass = throw new ClassNotFoundException("no Java class corresponding to "+clazz+" found")
+ //println("classToJava "+clazz+" "+clazz.owner+" "+clazz.owner.isPackageClass)//debug
+ if (clazz.isPrimitiveValueClass)
+ valueClassToJavaType(clazz)
+ else if (clazz == ArrayClass)
+ noClass
+ else if (clazz.owner.isPackageClass)
+ javaClass(clazz.javaClassName)
+ else if (clazz.owner.isClass)
+ classToJava(clazz.owner.asClassSymbol)
+ .getDeclaredClasses
+ .find(_.getSimpleName == clazz.name.toString)
+ .getOrElse(noClass)
+ else
+ noClass
+ }
+
+ private def expandedName(sym: Symbol): String =
+ if (sym.isPrivate) nme.expandedName(sym.name.toTermName, sym.owner).toString
+ else sym.name.toString
+
+ /** The Java field corresponding to a given Scala field.
+ * @param meth The Scala field.
+ */
+ def fieldToJava(fld: TermSymbol): jField = fieldCache.toJava(fld) {
+ val jclazz = classToJava(fld.owner.asClassSymbol)
+ try jclazz getDeclaredField fld.name.toString
+ catch {
+ case ex: NoSuchFieldException => jclazz getDeclaredField expandedName(fld)
+ }
+ }
+
+ /** The Java method corresponding to a given Scala method.
+ * @param meth The Scala method
+ */
+ def methodToJava(meth: MethodSymbol): jMethod = methodCache.toJava(meth) {
+ val jclazz = classToJava(meth.owner.asClassSymbol)
+ val paramClasses = transformedType(meth).paramTypes map typeToJavaClass
+ try jclazz getDeclaredMethod (meth.name.toString, paramClasses: _*)
+ catch {
+ case ex: NoSuchMethodException =>
+ jclazz getDeclaredMethod (expandedName(meth), paramClasses: _*)
+ }
+ }
+
+ /** The Java constructor corresponding to a given Scala constructor.
+ * @param constr The Scala constructor
+ */
+ def constructorToJava(constr: MethodSymbol): jConstructor[_] = constructorCache.toJava(constr) {
+ val jclazz = classToJava(constr.owner.asClassSymbol)
+ val paramClasses = transformedType(constr).paramTypes map typeToJavaClass
+ jclazz getConstructor (paramClasses: _*)
+ }
+
+ private def jArrayClass(elemClazz: jClass[_]): jClass[_] = {
+ jArray.newInstance(elemClazz, 0).getClass
+ }
+
+ /** The Java class that corresponds to given Scala type.
+ * Pre: Scala type is already transformed to Java level.
+ */
+ def typeToJavaClass(tpe: Type): jClass[_] = tpe match {
+ case ExistentialType(_, rtpe) => typeToJavaClass(rtpe)
+ case TypeRef(_, ArrayClass, List(elemtpe)) => jArrayClass(typeToJavaClass(elemtpe))
+ case TypeRef(_, sym: ClassSymbol, _) => classToJava(sym.asClassSymbol)
+ case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found")
+ }
+ }
+
+ /** Assert that packages have package scopes */
+ override def validateClassInfo(tp: ClassInfoType) {
+ assert(!tp.typeSymbol.isPackageClass || tp.decls.isInstanceOf[PackageScope])
+ }
+
+ override def newPackageScope(pkgClass: Symbol) = new PackageScope(pkgClass)
+
+ override def scopeTransform(owner: Symbol)(op: => Scope): Scope =
+ if (owner.isPackageClass) owner.info.decls else op
+
+ private lazy val rootToLoader = new WeakHashMap[Symbol, ClassLoader]
+
+ override def mirrorThatLoaded(sym: Symbol): Mirror = {
+ val root = sym.enclosingRootClass
+ def findLoader = {
+ val loaders = (mirrors collect { case (cl, ref) if ref.get.get.RootClass == root => cl })
+ assert(loaders.nonEmpty, sym)
+ loaders.head
+ }
+ mirrors(rootToLoader getOrElseUpdate(root, findLoader)).get.get
+ }
+
+ private def byName(sym: Symbol): (Name, Symbol) = sym.name -> sym
+
+ private lazy val phantomTypes: Map[Name, Symbol] =
+ Map(byName(definitions.AnyRefClass)) ++ (definitions.isPhantomClass map byName)
+
+ /** 1. If `owner` is a package class (but not the empty package) and `name` is a term name, make a new package
+ * <owner>.<name>, otherwise return NoSymbol.
+ * Exception: If owner is root and a java class with given name exists, create symbol in empty package instead
+ * 2. If `owner` is the scala package and `name` designates a phantom class, return
+ * the corresponding class symbol and enter it into this mirror's ScalaPackage.
+ */
+ override def missingHook(owner: Symbol, name: Name): Symbol = {
+ if (owner.hasPackageFlag) {
+ val mirror = mirrorThatLoaded(owner)
+ // [Eugene++] this makes toolbox tests pass, but it's a mere workaround for SI-5865
+// assert((owner.info decl name) == NoSymbol, s"already exists: $owner . $name")
+ if (owner.isRootSymbol && mirror.tryJavaClass(name.toString).isDefined)
+ return mirror.EmptyPackageClass.info decl name
+ if (name.isTermName && !owner.isEmptyPackageClass)
+ return mirror.makeScalaPackage(
+ if (owner.isRootSymbol) name.toString else owner.fullName+"."+name)
+ if (owner.name.toTermName == nme.scala_ && owner.owner.isRoot)
+ phantomTypes get name match {
+ case Some(tsym) =>
+ owner.info.decls enter tsym
+ return tsym
+ case None =>
+ }
+ }
+ info("*** missing: "+name+"/"+name.isTermName+"/"+owner+"/"+owner.hasPackageFlag+"/"+owner.info.decls.getClass)
+ super.missingHook(owner, name)
+ }
+}
+
+class ReflectError(msg: String) extends java.lang.Error(msg)
+
+class HasJavaClass[J](val getClazz: J => java.lang.Class[_])