summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2011-07-20 16:39:39 +0000
committerMartin Odersky <odersky@gmail.com>2011-07-20 16:39:39 +0000
commit628c0265aaeaab8ef84dfc623d45119972f83656 (patch)
tree5b707fbc500cc0455a0de7a9796d5f7eb5c1c616 /src
parent00da8a8f07b9ba21ee93a007dbf7c2961c355fd6 (diff)
downloadscala-628c0265aaeaab8ef84dfc623d45119972f83656.tar.gz
scala-628c0265aaeaab8ef84dfc623d45119972f83656.tar.bz2
scala-628c0265aaeaab8ef84dfc623d45119972f83656.zip
Fleshed out reflection that now also correctly ...
Fleshed out reflection that now also correctly interpretes Java classes. We are getting there! No review; let's wait until it is complete.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/reflect/runtime/ConversionUtil.scala29
-rw-r--r--src/compiler/scala/reflect/runtime/JavaToScala.scala202
-rw-r--r--src/compiler/scala/reflect/runtime/Loaders.scala16
-rw-r--r--src/compiler/scala/reflect/runtime/Universe.scala3
4 files changed, 213 insertions, 37 deletions
diff --git a/src/compiler/scala/reflect/runtime/ConversionUtil.scala b/src/compiler/scala/reflect/runtime/ConversionUtil.scala
index e406f99cdf..4cebcb8ed6 100644
--- a/src/compiler/scala/reflect/runtime/ConversionUtil.scala
+++ b/src/compiler/scala/reflect/runtime/ConversionUtil.scala
@@ -4,7 +4,7 @@ package runtime
import java.lang.{Class => jClass, Package => jPackage}
import java.lang.reflect.{
Method => jMethod, Constructor => jConstructor, Modifier => jModifier, Field => jField,
- Member => jMember, Type => jType, GenericDeclaration}
+ Member => jMember, Type => jType, TypeVariable => jTypeVariable, GenericDeclaration}
import collection.mutable.HashMap
trait ConversionUtil extends internal.transform.Transforms { self: Universe =>
@@ -13,17 +13,37 @@ trait ConversionUtil extends internal.transform.Transforms { self: Universe =>
* and Scala reflection type `S`.
*/
protected class TwoWayCache[J, S] {
+
private val toScalaMap = new HashMap[J, S]
private val toJavaMap = new HashMap[S, J]
- def toScala(key: J)(body: => S): S = toScalaMap.getOrElseUpdate(key, body)
+ def enter(j: J, s: S) = {
+ toScalaMap(j) = s
+ toJavaMap(s) = j
+ }
- def toJava(key: S)(body: => J): J = toJavaMap.getOrElseUpdate(key, body)
+ def toScala(key: J)(body: => S): S = toScalaMap get key match {
+ case Some(v) =>
+ v
+ case none =>
+ val result = body
+ enter(key, result)
+ result
+ }
+
+ def toJava(key: S)(body: => J): J = toJavaMap get key match {
+ case Some(v) =>
+ v
+ case none =>
+ val result = body
+ enter(result, key)
+ result
+ }
def toJavaOption(key: S)(body: => Option[J]): Option[J] = toJavaMap get key match {
case None =>
val result = body
- for (value <- result) toJavaMap(key) = value
+ for (value <- result) enter(value, key)
result
case some => some
}
@@ -34,6 +54,7 @@ trait ConversionUtil extends internal.transform.Transforms { self: Universe =>
protected val methodCache = new TwoWayCache[jMethod, Symbol]
protected val constructorCache = new TwoWayCache[jConstructor[_], Symbol]
protected val fieldCache = new TwoWayCache[jField, Symbol]
+ protected val tparamCache = new TwoWayCache[jTypeVariable[_], Symbol]
def typeToJavaClass(tpe: Type): jClass[_]
diff --git a/src/compiler/scala/reflect/runtime/JavaToScala.scala b/src/compiler/scala/reflect/runtime/JavaToScala.scala
index e67dbd3ce4..833b42529d 100644
--- a/src/compiler/scala/reflect/runtime/JavaToScala.scala
+++ b/src/compiler/scala/reflect/runtime/JavaToScala.scala
@@ -4,19 +4,22 @@ package runtime
import java.lang.{Class => jClass, Package => jPackage}
import java.lang.reflect.{
Method => jMethod, Constructor => jConstructor, Modifier => jModifier, Field => jField,
- Member => jMember, Type => jType, GenericDeclaration}
+ Member => jMember, Type => jType, TypeVariable => jTypeVariable,
+ GenericDeclaration, ParameterizedType, WildcardType, AnnotatedElement}
import internal.pickling.ByteCodecs
import internal.ClassfileConstants._
import internal.pickling.UnPickler
-import collection.mutable.HashMap
+import collection.mutable.{HashMap, ListBuffer}
+import internal.Flags._
trait JavaToScala extends ConversionUtil { self: Universe =>
+ import definitions._
+
private object unpickler extends UnPickler {
val global: JavaToScala.this.type = self
}
-
/** 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
@@ -46,23 +49,80 @@ trait JavaToScala extends ConversionUtil { self: Universe =>
unpickler.unpickle(bytes, 0, clazz, module, jclazz.getName)
} else { // class does not have a Scala signature; it's a Java class
println("no sig found for "+jclazz)
- copyMembers(clazz, module, jclazz)
+ initClassModule(clazz, module, new FromJavaClassCompleter(clazz, module, jclazz))
}
}
}
- /** Generate types for top-level Scala root class and root companion object
- * by copying corresponding types from a Java class. This method used
+ /** 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]): Symbol = {
+ val tparam = sOwner(jtvar).newTypeParameter(NoPosition, 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 complete(sym: Symbol) = {
+ sym setInfo TypeBounds(NothingClass.tpe, lub(jtvar.getBounds.toList map typeToScala))
+ }
+ }
+
+ /** 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 top-level Scala class for which info is copied
- * @param module The top-level Scala companion object for which info is copied
+ * @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
*/
- def copyMembers(clazz: Symbol, module: Symbol, jclazz: jClass[_]) {
- clazz setInfo new ClassInfoType(List(), newScope, clazz)
- module.moduleClass setInfo new ClassInfoType(List(), newScope, module.moduleClass)
- module setInfo module.moduleClass.tpe
+ private class FromJavaClassCompleter(clazz: Symbol, module: Symbol, jclazz: jClass[_]) extends LazyType {
+ override def complete(sym: Symbol) = {
+ println("completing from Java "+sym+"/"+clazz.fullName)
+ assert(sym == clazz || sym == module || sym == module.moduleClass, sym)
+ val flags = toScalaFlags(jclazz.getModifiers, isClass = true)
+ clazz setFlag (flags | JAVA)
+ module setFlag (flags & PRIVATE | JAVA)
+ module.moduleClass setFlag (flags & PRIVATE | JAVA)
+
+ copyAnnotations(clazz, jclazz)
+ // to do: annotations to set also for module?
+
+ val tparams = jclazz.getTypeParameters.toList map createTypeParameter
+
+ val jsuperclazz = jclazz.getGenericSuperclass
+ val superclazz = if (jsuperclazz == null) AnyClass.tpe else typeToScala(jsuperclazz)
+ val parents = superclazz :: (jclazz.getGenericInterfaces.toList map typeToScala)
+ clazz setInfo polyType(tparams, new ClassInfoType(parents, newScope, clazz))
+ module.moduleClass setInfo new ClassInfoType(List(), newScope, module.moduleClass)
+ module setInfo module.moduleClass.tpe
+
+ def enter(sym: Symbol) =
+ (if (sym.isStatic) module.moduleClass else clazz).info.decls enter sym
+
+ for (jfield <- jclazz.getDeclaredFields)
+ enter(jfieldAsScala(jfield))
+
+ for (jmeth <- jclazz.getDeclaredMethods)
+ enter(jmethodAsScala(jmeth))
+
+ for (jconstr <- jclazz.getConstructors)
+ enter(jconstrAsScala(jconstr))
+ }
}
/** If Java modifiers `mods` contain STATIC, return the module class
@@ -78,8 +138,10 @@ trait JavaToScala extends ConversionUtil { self: Universe =>
followStatic(classToScala(jclazz.getEnclosingClass), jclazz.getModifiers)
else if (jclazz.isLocalClass)
methodToScala(jclazz.getEnclosingMethod) orElse constrToScala(jclazz.getEnclosingConstructor)
- else
+ else {
+ assert(jclazz.getPackage != null, jclazz)
packageToScala(jclazz.getPackage)
+ }
}
/** The Scala owner of the Scala symbol corresponding to the Java member `jmember`
@@ -88,6 +150,11 @@ trait JavaToScala extends ConversionUtil { self: Universe =>
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)
+
/** Returns `true` if Scala name `name` equals Java name `jstr`, possibly after
* make-not-private expansion.
*/
@@ -145,7 +212,7 @@ trait JavaToScala extends ConversionUtil { self: Universe =>
*/
private def makeScalaPackage(fullname: String): Symbol = {
val split = fullname lastIndexOf '.'
- val owner = if (split > 0) packageNameToScala(fullname take split) else definitions.RootClass
+ val owner = if (split > 0) packageNameToScala(fullname take split) else RootClass
assert(owner.isModuleClass)
val name = fullname drop (split + 1)
val pkg = owner.info decl newTermName(name)
@@ -164,14 +231,77 @@ trait JavaToScala extends ConversionUtil { self: Universe =>
} else if (jclazz.isLocalClass) { // local classes not preserved by unpickling - treat as Java
jclassAsScala(jclazz)
} else { // jclazz is top-level - get signature
- val (clazz, module) = createClassModule(sOwner(jclazz), newTypeName(jclazz.getSimpleName))
+ val (clazz, module) = createClassModule(
+ sOwner(jclazz), newTypeName(jclazz.getSimpleName), new TopClassCompleter(_, _))
clazz
}
}
+ /** 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 tparamToScala(jparam: jTypeVariable[_ <: GenericDeclaration]): Symbol = tparamCache.toScala(jparam) {
+ val owner = genericDeclarationToScala(jparam.getGenericDeclaration)
+ owner.info match {
+ case PolyType(tparams, _) => tparams.find(_.name.toString == jparam.getName).get
+ }
+ }
+
+ /** The Scala symbol that corresponds to a given Java generic declaration (class, method, or constructor)
+ */
+ def genericDeclarationToScala(jdecl: GenericDeclaration) = jdecl match {
+ case jclazz: jClass[_] => classToScala(jclazz)
+ case jmeth: jMethod => methodToScala(jmeth)
+ case jconstr: jConstructor[_] => constrToScala(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[Symbol]) = {
+ val tparams = new ListBuffer[Symbol]
+ def targToScala(arg: jType): Type = arg match {
+ case jwild: WildcardType =>
+ val tparam = owner.newExistential(NoPosition, newTypeName("T$"+tparams.length))
+ .setInfo(TypeBounds(
+ lub(jwild.getLowerBounds.toList map typeToScala),
+ glb(jwild.getUpperBounds.toList map typeToScala)))
+ tparams += tparam
+ typeRef(NoPrefix, tparam, List())
+ case _ =>
+ typeToScala(arg)
+ }
+ (args map targToScala, tparams.toList)
+ }
+
/** The Scala type that corresponds to given Java type (to be done)
*/
- def jtypeToScala(tpe: jType): Type = NoType
+ def typeToScala(jtpe: jType): Type = jtpe match {
+ case java.lang.Void.TYPE => UnitClass.tpe
+ case java.lang.Byte.TYPE => ByteClass.tpe
+ case java.lang.Character.TYPE => CharClass.tpe
+ case java.lang.Short.TYPE => ShortClass.tpe
+ case java.lang.Integer.TYPE => IntClass.tpe
+ case java.lang.Long.TYPE => LongClass.tpe
+ case java.lang.Float.TYPE => FloatClass.tpe
+ case java.lang.Double.TYPE => DoubleClass.tpe
+ case java.lang.Boolean.TYPE => BooleanClass.tpe
+ case jclazz: jClass[_] =>
+ 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 jtvar: jTypeVariable[_] =>
+ val tparam = tparamToScala(jtvar)
+ typeRef(NoPrefix, tparam, List())
+ }
/** The Scala class that corresponds to given Java class without taking
* Scala pickling info into account.
@@ -179,9 +309,8 @@ trait JavaToScala extends ConversionUtil { self: Universe =>
* @return A Scala class symbol that wraps all reflection info of `jclazz`
*/
private def jclassAsScala(jclazz: jClass[_]): Symbol = {
- val (clazz, module) = createClassModule(sOwner(jclazz), newTypeName(jclazz.getSimpleName))
- // fill in clazz, module from jclazz
- copyMembers(clazz, module, jclazz)
+ val (clazz, module) = createClassModule(
+ sOwner(jclazz), newTypeName(jclazz.getSimpleName), new FromJavaClassCompleter(_, _, jclazz))
clazz
}
@@ -191,10 +320,11 @@ trait JavaToScala extends ConversionUtil { self: Universe =>
* @return A Scala value symbol that wraps all reflection info of `jfield`
*/
private def jfieldAsScala(jfield: jField): Symbol = fieldCache.toScala(jfield) {
- sOwner(jfield).newValue(NoPosition, newTermName(jfield.getName))
- .setFlag(toScalaFlags(jfield.getModifiers, isClass = false))
- .setInfo(jtypeToScala(jfield.getGenericType))
- // todo: copy annotations
+ val field = sOwner(jfield).newValue(NoPosition, newTermName(jfield.getName))
+ .setFlag(toScalaFlags(jfield.getModifiers, isClass = false) | JAVA)
+ .setInfo(typeToScala(jfield.getGenericType))
+ copyAnnotations(field, jfield)
+ field
}
/** The Scala method that corresponds to given Java method without taking
@@ -202,14 +332,32 @@ trait JavaToScala extends ConversionUtil { self: Universe =>
* @param jmeth The Java method
* @return A Scala method symbol that wraps all reflection info of `jmethod`
*/
- private def jmethodAsScala(jmeth: jMethod): Symbol = NoSymbol // to be done
+ private def jmethodAsScala(jmeth: jMethod): Symbol = methodCache.toScala(jmeth) {
+ val clazz = sOwner(jmeth)
+ val meth = clazz.newMethod(NoPosition, newTermName(jmeth.getName))
+ .setFlag(toScalaFlags(jmeth.getModifiers, isClass = false) | JAVA)
+ val tparams = jmeth.getTypeParameters.toList map createTypeParameter
+ val paramtpes = jmeth.getGenericParameterTypes.toList map typeToScala
+ val resulttpe = typeToScala(jmeth.getGenericReturnType)
+ meth setInfo polyType(tparams, MethodType(clazz.newSyntheticValueParams(paramtpes), resulttpe))
+ copyAnnotations(meth, jmeth)
+ 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[_]): Symbol = NoSymbol // to be done
-
-
+ private def jconstrAsScala(jconstr: jConstructor[_]): Symbol = {
+ // [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 meth = clazz.newMethod(NoPosition, nme.CONSTRUCTOR)
+ .setFlag(toScalaFlags(jconstr.getModifiers, isClass = false) | JAVA)
+ val tparams = jconstr.getTypeParameters.toList map createTypeParameter
+ val paramtpes = jconstr.getGenericParameterTypes.toList map typeToScala
+ meth setInfo polyType(tparams, MethodType(clazz.newSyntheticValueParams(paramtpes), clazz.tpe))
+ copyAnnotations(meth, jconstr)
+ meth
+ }
} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/runtime/Loaders.scala b/src/compiler/scala/reflect/runtime/Loaders.scala
index cd608cd471..bb2f628041 100644
--- a/src/compiler/scala/reflect/runtime/Loaders.scala
+++ b/src/compiler/scala/reflect/runtime/Loaders.scala
@@ -51,19 +51,23 @@ trait Loaders { self: Universe =>
* and initialize with a lazy type completer.
* @param owner The owner of the newly created class and object
* @param name The simple name of the newly created class
+ * @param completer The completer to be used to set the info of the class and the module
*/
- protected def createClassModule(owner: Symbol, name: TypeName) = {
+ protected def createClassModule(owner: Symbol, name: TypeName, completer: (Symbol, Symbol) => LazyType) = {
val clazz = owner.newClass(NoPosition, name)
val module = owner.newModule(NoPosition, name.toTermName)
owner.info.decls enter clazz
owner.info.decls enter module
- val classCompleter = new TopClassCompleter(clazz, module)
- clazz.setInfo(classCompleter)
- module.setInfo(classCompleter)
- module.moduleClass.setInfo(classCompleter)
+ initClassModule(clazz, module, completer(clazz, module))
(clazz, module)
}
+ protected def initClassModule(clazz: Symbol, module: Symbol, completer: LazyType) = {
+ clazz.setInfo(completer)
+ module.setInfo(completer)
+ module.moduleClass.setInfo(completer)
+ }
+
/** The type for packages.
* Since we cannot search the file system for classes in directories to populate
* a package, we do the following instead: For every member that is looked up in
@@ -77,7 +81,7 @@ trait Loaders { self: Universe =>
assert(this eq pkg.info, this+" "+pkg.info)
assert(decls eq pkg.info.decls)
println("creating "+name+" in "+pkg)
- val (clazz, module) = createClassModule(pkg, name.toTypeName)
+ val (clazz, module) = createClassModule(pkg, name.toTypeName, new TopClassCompleter(_, _))
if (name.isTypeName) clazz else module
}
override def member(name: Name): Symbol = decl(name)
diff --git a/src/compiler/scala/reflect/runtime/Universe.scala b/src/compiler/scala/reflect/runtime/Universe.scala
index bc295694be..edabd4eb84 100644
--- a/src/compiler/scala/reflect/runtime/Universe.scala
+++ b/src/compiler/scala/reflect/runtime/Universe.scala
@@ -36,6 +36,9 @@ class Universe extends internal.SymbolTable with JavaToScala with ScalaToJava wi
type Position = String // source file?
val NoPosition = ""
+
+ // establish root association to avoid cyclic dependency errors later
+ classToScala(classOf[java.lang.Object]).initialize
}
object Universe extends Universe