summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@epfl.ch>2012-08-07 08:52:55 -0700
committerAdriaan Moors <adriaan.moors@epfl.ch>2012-08-07 08:52:55 -0700
commitbd71c84adcaafb21de8801d547a49aacfd4956cc (patch)
tree1f7aeacfafa3081b117e70d701bb733d11801cd1 /src
parente815dd88b7ab19374084b693fc8ffb855b547359 (diff)
parent73a9e71828f6c1b857840fa13ac5aff123f39f80 (diff)
downloadscala-bd71c84adcaafb21de8801d547a49aacfd4956cc.tar.gz
scala-bd71c84adcaafb21de8801d547a49aacfd4956cc.tar.bz2
scala-bd71c84adcaafb21de8801d547a49aacfd4956cc.zip
Merge pull request #1067 from scalamacros/topic/ultimate-reflection-pull-request
Ultimate reflection pull request #2
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala3
-rw-r--r--src/reflect/scala/reflect/api/Mirrors.scala24
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala6
-rw-r--r--src/reflect/scala/reflect/runtime/JavaMirrors.scala165
4 files changed, 167 insertions, 31 deletions
diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
index 9987931cf3..eeec973299 100644
--- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
+++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
@@ -256,7 +256,8 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
// }
val (singleton, jmeth) = compileExpr(expr)
val result = jmeth.invoke(singleton, thunks map (_.asInstanceOf[AnyRef]): _*)
- result
+ if (jmeth.getReturnType == java.lang.Void.TYPE) ()
+ else result
}
def parseExpr(code: String): Tree = {
diff --git a/src/reflect/scala/reflect/api/Mirrors.scala b/src/reflect/scala/reflect/api/Mirrors.scala
index a4d86cf1fd..f2f96645e3 100644
--- a/src/reflect/scala/reflect/api/Mirrors.scala
+++ b/src/reflect/scala/reflect/api/Mirrors.scala
@@ -33,6 +33,14 @@ trait Mirrors { self: Universe =>
/** Reflects against a field symbol and returns a mirror
* that can be used to get and, if appropriate, set the value of the field.
*
+ * FieldMirrors are the only way to get at private[this] vals and vars and
+ * might be useful to inspect the data of underlying Java fields.
+ * For all other uses, it's better to go through the fields accessor.
+ *
+ * In particular, there should be no need to ever access a field mirror
+ * when reflecting on just the public members of a class or trait.
+ * Note also that only accessor MethodMirrors, but not FieldMirrors will accurately reflect overriding behavior.
+ *
* To get a field symbol by the name of the field you would like to reflect,
* use `<this mirror>.symbol.typeSignature.member(newTermName(<name of the field>)).asTerm.accessed`.
* For further information about member lookup refer to `Symbol.typeSignature`.
@@ -91,7 +99,7 @@ trait Mirrors { self: Universe =>
trait FieldMirror {
/** The object containing the field */
- def receiver: AnyRef
+ def receiver: Any
/** The field symbol representing the field.
*
@@ -107,6 +115,10 @@ trait Mirrors { self: Universe =>
* Scala reflection uses reflection capabilities of the underlying platform,
* so `FieldMirror.get` might throw platform-specific exceptions associated
* with getting a field or invoking a getter method of the field.
+ *
+ * If `symbol` represents a field of a base class with respect to the class of the receiver,
+ * and this base field is overriden in the class of the receiver, then this method will retrieve
+ * the value of the base field. To achieve overriding behavior, use reflectMethod on an accessor.
*/
def get: Any
@@ -117,6 +129,10 @@ trait Mirrors { self: Universe =>
* Scala reflection uses reflection capabilities of the underlying platform,
* so `FieldMirror.get` might throw platform-specific exceptions associated
* with setting a field or invoking a setter method of the field.
+ *
+ * If `symbol` represents a field of a base class with respect to the class of the receiver,
+ * and this base field is overriden in the class of the receiver, then this method will set
+ * the value of the base field. To achieve overriding behavior, use reflectMethod on an accessor.
*/
def set(value: Any): Unit
}
@@ -125,7 +141,7 @@ trait Mirrors { self: Universe =>
trait MethodMirror {
/** The receiver object of the method */
- def receiver: AnyRef
+ def receiver: Any
/** The method symbol representing the method */
def symbol: MethodSymbol
@@ -226,7 +242,9 @@ trait Mirrors { self: Universe =>
* Such a mirror can be used to further reflect against the members of the object
* to get/set fields, invoke methods and inspect inner classes and objects.
*/
- def reflect(obj: Any): InstanceMirror
+ // we need a ClassTag here to preserve boxity of primitives
+ // the class tag lets us tell apart `mirror.reflect(2)` and `mirror.reflect(new Integer(2))`
+ def reflect[T: ClassTag](obj: T): InstanceMirror
/** Reflects against a static class symbol and returns a mirror
* that can be used to create instances of the class, inspect its companion object or perform further reflections.
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index ce9454e82d..c1e5f78d50 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -937,6 +937,12 @@ trait StdNames {
case _ => NO_NAME
}
+ def primitiveMethodName(name: Name): TermName =
+ primitiveInfixMethodName(name) match {
+ case NO_NAME => primitivePostfixMethodName(name)
+ case name => name
+ }
+
/** Translate a String into a list of simple TypeNames and TermNames.
* In all segments before the last, type/term is determined by whether
* the following separator char is '.' or '#'. In the last segment,
diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
index 5eb7770de6..2bf57d61cc 100644
--- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala
+++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
@@ -20,6 +20,8 @@ import internal.Flags._
//import scala.tools.nsc.util.ScalaClassLoader._
import ReflectionUtils.{singletonInstance}
import language.existentials
+import scala.runtime.{ScalaRunTime, BoxesRunTime}
+import scala.reflect.internal.util.Collections._
trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: SymbolTable =>
@@ -124,15 +126,16 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
private def ErrorStaticClass(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror")
private def ErrorStaticModule(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror")
private def ErrorNotMember(wannabe: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a member of $owner, you provided ${wannabe.kind} ${wannabe.fullName}")
- private def ErrorNotField(wannabe: Symbol) = throw new ScalaReflectionException(s"expected a field or an accessor method symbol, you provided $wannabe}")
+ private def ErrorNotField(wannabe: Symbol) = throw new ScalaReflectionException(s"expected a field or an accessor method symbol, you provided $wannabe")
private def ErrorNonExistentField(wannabe: Symbol) = throw new ScalaReflectionException(s"""
|Scala field ${wannabe.name} isn't represented as a Java field, neither it has a Java accessor method
|note that private parameters of class constructors don't get mapped onto fields and/or accessors,
|unless they are used outside of their declaring constructors.
""".trim.stripMargin)
private def ErrorSetImmutableField(wannabe: Symbol) = throw new ScalaReflectionException(s"cannot set an immutable field ${wannabe.name}")
+ private def ErrorNotConstructor(wannabe: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a constructor of $owner, you provided $wannabe")
- def reflect(obj: Any): InstanceMirror = new JavaInstanceMirror(obj.asInstanceOf[AnyRef])
+ def reflect[T: ClassTag](obj: T): InstanceMirror = new JavaInstanceMirror(obj)
def reflectClass(cls: ClassSymbol): ClassMirror = {
if (!cls.isStatic) ErrorInnerClass(cls)
@@ -152,13 +155,25 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
def moduleSymbol(rtcls: RuntimeClass): ModuleSymbol = classToScala(rtcls).companionModule.asModule
- private def checkMemberOf(wannabe: Symbol, owner: Symbol) =
- if (!owner.info.member(wannabe.name).alternatives.contains(wannabe)) ErrorNotMember(wannabe, owner)
+ private def checkMemberOf(wannabe: Symbol, owner: ClassSymbol) {
+ if (wannabe.owner == AnyClass || wannabe.owner == AnyRefClass || wannabe.owner == ObjectClass) {
+ // do nothing
+ } else if (wannabe.owner == AnyValClass) {
+ if (!owner.isPrimitiveValueClass && !owner.isDerivedValueClass) ErrorNotMember(wannabe, owner)
+ } else {
+ if (!(owner.info.baseClasses contains wannabe.owner)) ErrorNotMember(wannabe, owner)
+ }
+ }
+
+ private def preciseClass[T: ClassTag](instance: T) = {
+ val staticClazz = classTag[T].runtimeClass
+ val dynamicClazz = instance.getClass
+ if (staticClazz.isPrimitive) staticClazz else dynamicClazz
+ }
- private class JavaInstanceMirror(obj: AnyRef)
+ private class JavaInstanceMirror[T: ClassTag](val instance: T)
extends InstanceMirror {
- def instance = obj
- def symbol = wholemirror.classSymbol(obj.getClass)
+ def symbol = wholemirror.classSymbol(preciseClass(instance))
def reflectField(field: TermSymbol): FieldMirror = {
checkMemberOf(field, symbol)
if ((field.isMethod && !field.isAccessor) || field.isModule) ErrorNotField(field)
@@ -171,26 +186,26 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
catch {
case _: NoSuchFieldException => ErrorNonExistentField(field1)
}
- new JavaFieldMirror(obj, field1)
+ new JavaFieldMirror(instance, field1)
}
def reflectMethod(method: MethodSymbol): MethodMirror = {
checkMemberOf(method, symbol)
- new JavaMethodMirror(obj, method)
+ mkJavaMethodMirror(instance, method)
}
def reflectClass(cls: ClassSymbol): ClassMirror = {
if (cls.isStatic) ErrorStaticClass(cls)
checkMemberOf(cls, symbol)
- new JavaClassMirror(instance, cls)
+ new JavaClassMirror(instance.asInstanceOf[AnyRef], cls)
}
def reflectModule(mod: ModuleSymbol): ModuleMirror = {
if (mod.isStatic) ErrorStaticModule(mod)
checkMemberOf(mod, symbol)
- new JavaModuleMirror(instance, mod)
+ new JavaModuleMirror(instance.asInstanceOf[AnyRef], mod)
}
- override def toString = s"instance mirror for $obj"
+ override def toString = s"instance mirror for $instance"
}
- private class JavaFieldMirror(val receiver: AnyRef, val symbol: TermSymbol)
+ private class JavaFieldMirror(val receiver: Any, val symbol: TermSymbol)
extends FieldMirror {
lazy val jfield = {
val jfield = fieldToJava(symbol)
@@ -230,26 +245,115 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
sig
}
- private class JavaMethodMirror(val receiver: AnyRef, val symbol: MethodSymbol)
+ // the "symbol == Any_getClass || symbol == Object_getClass" test doesn't cut it
+ // because both AnyVal and its primitive descendants define their own getClass methods
+ private def isGetClass(meth: MethodSymbol) = meth.name.toString == "getClass" && meth.params.flatten.isEmpty
+ private def isMagicPrimitiveMethod(meth: MethodSymbol) = meth.owner.isPrimitiveValueClass
+ private def isStringConcat(meth: MethodSymbol) = meth == String_+ || (isMagicPrimitiveMethod(meth) && meth.returnType =:= StringClass.toType)
+ lazy val magicMethodOwners = Set[Symbol](AnyClass, AnyValClass, AnyRefClass, ObjectClass, ArrayClass) ++ ScalaPrimitiveValueClasses
+ lazy val nonMagicObjectMethods = Set[Symbol](Object_clone, Object_equals, Object_finalize, Object_hashCode, Object_toString,
+ Object_notify, Object_notifyAll) ++ ObjectClass.info.member(nme.wait_).asTerm.alternatives.map(_.asMethod)
+ private def isMagicMethod(meth: MethodSymbol): Boolean = {
+ if (isGetClass(meth) || isStringConcat(meth) || isMagicPrimitiveMethod(meth) || meth == Predef_classOf || meth.isTermMacro) return true
+ magicMethodOwners(meth.owner) && !nonMagicObjectMethods(meth)
+ }
+
+ // unlike other mirrors, method mirrors are created by a factory
+ // that's because we want to have decent performance
+ // therefore we move special cases into separate subclasses
+ // rather than have them on a hot path them in a unified implementation of the `apply` method
+ private def mkJavaMethodMirror[T: ClassTag](receiver: T, symbol: MethodSymbol): JavaMethodMirror = {
+ if (isMagicMethod(symbol)) new JavaMagicMethodMirror(receiver, symbol)
+ else if (symbol.params.flatten exists (p => isByNameParamType(p.info))) new JavaByNameMethodMirror(receiver, symbol)
+ else new JavaVanillaMethodMirror(receiver, symbol)
+ }
+
+ private abstract class JavaMethodMirror(val symbol: MethodSymbol)
extends MethodMirror {
lazy val jmeth = {
val jmeth = methodToJava(symbol)
if (!jmeth.isAccessible) jmeth.setAccessible(true)
jmeth
}
- def apply(args: Any*): Any =
- if (symbol.owner == ArrayClass)
- symbol.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 _ => assert(false, s"unexpected array method: $symbol")
- }
- else
- jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*)
+
+ def jinvoke(jmeth: jMethod, receiver: Any, args: Seq[Any]): Any = {
+ val result = jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*)
+ if (jmeth.getReturnType == java.lang.Void.TYPE) ()
+ else result
+ }
+
override def toString = s"method mirror for ${showMethodSig(symbol)} (bound to $receiver)"
}
+ private class JavaVanillaMethodMirror(val receiver: Any, symbol: MethodSymbol)
+ extends JavaMethodMirror(symbol) {
+ def apply(args: Any*): Any = jinvoke(jmeth, receiver, args)
+ }
+
+ private class JavaByNameMethodMirror(val receiver: Any, symbol: MethodSymbol)
+ extends JavaMethodMirror(symbol) {
+ def apply(args: Any*): Any = {
+ val transformed = map2(args.toList, symbol.params.flatten)((arg, param) => if (isByNameParamType(param.info)) () => arg else arg)
+ jinvoke(jmeth, receiver, transformed)
+ }
+ }
+
+ private class JavaMagicMethodMirror[T: ClassTag](val receiver: T, symbol: MethodSymbol)
+ extends JavaMethodMirror(symbol) {
+ def apply(args: Any*): Any = {
+ // checking type conformance is too much of a hassle, so we don't do it here
+ // actually it's not even necessary, because we manually dispatch arguments to magic methods below
+ val params = symbol.paramss.flatten
+ val perfectMatch = args.length == params.length
+ // todo. this doesn't account for multiple vararg parameter lists
+ // however those aren't supported by the mirror API: https://issues.scala-lang.org/browse/SI-6182
+ // hence I leave this code as is, to be fixed when the corresponding bug is fixed
+ val varargMatch = args.length >= params.length - 1 && isVarArgsList(params)
+ if (!perfectMatch && !varargMatch) {
+ val n_arguments = if (isVarArgsList(params)) s"${params.length - 1} or more" else s"${params.length}"
+ var s_arguments = if (params.length == 1 && !isVarArgsList(params)) "argument" else "arguments"
+ throw new ScalaReflectionException(s"${showMethodSig(symbol)} takes $n_arguments $s_arguments")
+ }
+
+ def objReceiver = receiver.asInstanceOf[AnyRef]
+ def objArg0 = args(0).asInstanceOf[AnyRef]
+ def objArgs = args.asInstanceOf[Seq[AnyRef]]
+ def fail(msg: String) = throw new ScalaReflectionException(msg + ", it cannot be invoked with mirrors")
+
+ def invokeMagicPrimitiveMethod = {
+ val jmeths = classOf[BoxesRunTime].getDeclaredMethods.filter(_.getName == nme.primitiveMethodName(symbol.name).toString)
+ assert(jmeths.length == 1, jmeths.toList)
+ jinvoke(jmeths.head, null, objReceiver +: objArgs)
+ }
+
+ symbol match {
+ case Any_== | Object_== => ScalaRunTime.inlinedEquals(objReceiver, objArg0)
+ case Any_!= | Object_!= => !ScalaRunTime.inlinedEquals(objReceiver, objArg0)
+ case Any_## | Object_## => ScalaRunTime.hash(objReceiver)
+ case Any_equals => receiver.equals(objArg0)
+ case Any_hashCode => receiver.hashCode
+ case Any_toString => receiver.toString
+ case Object_eq => objReceiver eq objArg0
+ case Object_ne => objReceiver ne objArg0
+ case Object_synchronized => objReceiver.synchronized(objArg0)
+ case sym if isGetClass(sym) => preciseClass(receiver)
+ case Any_asInstanceOf => fail("Any.asInstanceOf requires a type argument")
+ case Any_isInstanceOf => fail("Any.isInstanceOf requires a type argument")
+ case Object_asInstanceOf => fail("AnyRef.$asInstanceOf is an internal method")
+ case Object_isInstanceOf => fail("AnyRef.$isInstanceOf is an internal method")
+ case Array_length => ScalaRunTime.array_length(objReceiver)
+ case Array_apply => ScalaRunTime.array_apply(objReceiver, args(0).asInstanceOf[Int])
+ case Array_update => ScalaRunTime.array_update(objReceiver, args(0).asInstanceOf[Int], args(1))
+ case Array_clone => ScalaRunTime.array_clone(objReceiver)
+ case sym if isStringConcat(sym) => receiver.toString + objArg0
+ case sym if isMagicPrimitiveMethod(sym) => invokeMagicPrimitiveMethod
+ case sym if sym == Predef_classOf => fail("Predef.classOf is a compile-time function")
+ case sym if sym.isTermMacro => fail(s"${symbol.fullName} is a macro, i.e. a compile-time function")
+ case _ => assert(false, this)
+ }
+ }
+ }
+
private class JavaConstructorMirror(val outer: AnyRef, val symbol: MethodSymbol)
extends MethodMirror {
override val receiver = outer
@@ -259,6 +363,9 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
jconstr
}
def apply(args: Any*): Any = {
+ if (symbol.owner == ArrayClass)
+ throw new ScalaReflectionException("Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag(<class of element>).newArray(<length>)` instead")
+
val effectiveArgs =
if (outer == null) args.asInstanceOf[Seq[AnyRef]]
else outer +: args.asInstanceOf[Seq[AnyRef]]
@@ -279,7 +386,11 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
extends JavaTemplateMirror with ClassMirror {
def erasure = symbol
def isStatic = false
- def reflectConstructor(constructor: MethodSymbol) = new JavaConstructorMirror(outer, constructor)
+ def reflectConstructor(constructor: MethodSymbol) = {
+ if (!constructor.isClassConstructor) ErrorNotConstructor(constructor, symbol)
+ if (!symbol.info.decls.toList.contains(constructor)) ErrorNotConstructor(constructor, symbol)
+ new JavaConstructorMirror(outer, constructor)
+ }
def companion: Option[ModuleMirror] = symbol.companionModule match {
case module: ModuleSymbol => Some(new JavaModuleMirror(outer, module))
case _ => None
@@ -1067,7 +1178,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
mirrors(rootToLoader getOrElseUpdate(root, findLoader)).get.get
}
- private lazy val magicSymbols: Map[(String, Name), Symbol] = {
+ private lazy val magicClasses: Map[(String, Name), Symbol] = {
def mapEntry(sym: Symbol): ((String, Name), Symbol) = (sym.owner.fullName, sym.name) -> sym
Map() ++ (definitions.magicSymbols filter (_.isType) map mapEntry)
}
@@ -1088,7 +1199,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
if (name.isTermName && !owner.isEmptyPackageClass)
return mirror.makeScalaPackage(
if (owner.isRootSymbol) name.toString else owner.fullName+"."+name)
- magicSymbols get (owner.fullName, name) match {
+ magicClasses get (owner.fullName, name) match {
case Some(tsym) =>
owner.info.decls enter tsym
return tsym