Skip to content

Commit 3aa221e

Browse files
committed
SI-6179 mirrors now work with value classes
mirrors now carry a class tag of the receiver, so that they can detect value classes being reflected upon and adjust accordingly (e.g. allow Int_+ for ints, but disallow it for Integers). Surprisingly enough derived value classes (SIP-15 guys that inherit from AnyVal) have been working all along, so no modification were required to fix them.
1 parent 432d7b8 commit 3aa221e

9 files changed

+1681
-34
lines changed

src/reflect/scala/reflect/api/Mirrors.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ trait Mirrors { self: Universe =>
9191
trait FieldMirror {
9292

9393
/** The object containing the field */
94-
def receiver: AnyRef
94+
def receiver: Any
9595

9696
/** The field symbol representing the field.
9797
*
@@ -125,7 +125,7 @@ trait Mirrors { self: Universe =>
125125
trait MethodMirror {
126126

127127
/** The receiver object of the method */
128-
def receiver: AnyRef
128+
def receiver: Any
129129

130130
/** The method symbol representing the method */
131131
def symbol: MethodSymbol
@@ -226,7 +226,7 @@ trait Mirrors { self: Universe =>
226226
* Such a mirror can be used to further reflect against the members of the object
227227
* to get/set fields, invoke methods and inspect inner classes and objects.
228228
*/
229-
def reflect(obj: Any): InstanceMirror
229+
def reflect[T: ClassTag](obj: T): InstanceMirror
230230

231231
/** Reflects against a static class symbol and returns a mirror
232232
* that can be used to create instances of the class, inspect its companion object or perform further reflections.

src/reflect/scala/reflect/internal/StdNames.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,12 @@ trait StdNames {
937937
case _ => NO_NAME
938938
}
939939

940+
def primitiveMethodName(name: Name): TermName =
941+
primitiveInfixMethodName(name) match {
942+
case NO_NAME => primitivePostfixMethodName(name)
943+
case name => name
944+
}
945+
940946
/** Translate a String into a list of simple TypeNames and TermNames.
941947
* In all segments before the last, type/term is determined by whether
942948
* the following separator char is '.' or '#'. In the last segment,

src/reflect/scala/reflect/runtime/JavaMirrors.scala

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import internal.Flags._
2020
//import scala.tools.nsc.util.ScalaClassLoader._
2121
import ReflectionUtils.{singletonInstance}
2222
import language.existentials
23-
import scala.runtime.ScalaRunTime
23+
import scala.runtime.{ScalaRunTime, BoxesRunTime}
2424

2525
trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: SymbolTable =>
2626

@@ -133,7 +133,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
133133
""".trim.stripMargin)
134134
private def ErrorSetImmutableField(wannabe: Symbol) = throw new ScalaReflectionException(s"cannot set an immutable field ${wannabe.name}")
135135

136-
def reflect(obj: Any): InstanceMirror = new JavaInstanceMirror(obj.asInstanceOf[AnyRef])
136+
def reflect[T: ClassTag](obj: T): InstanceMirror = new JavaInstanceMirror(obj)
137137

138138
def reflectClass(cls: ClassSymbol): ClassMirror = {
139139
if (!cls.isStatic) ErrorInnerClass(cls)
@@ -155,18 +155,23 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
155155

156156
private def checkMemberOf(wannabe: Symbol, owner: ClassSymbol) {
157157
if (wannabe.owner == AnyClass || wannabe.owner == AnyRefClass || wannabe.owner == ObjectClass) {
158-
// do nothing
158+
// do nothing
159159
} else if (wannabe.owner == AnyValClass) {
160160
if (!owner.isPrimitiveValueClass && !owner.isDerivedValueClass) ErrorNotMember(wannabe, owner)
161161
} else {
162162
if (!owner.info.member(wannabe.name).alternatives.contains(wannabe)) ErrorNotMember(wannabe, owner)
163163
}
164164
}
165165

166-
private class JavaInstanceMirror(obj: AnyRef)
166+
private def preciseClass[T: ClassTag](instance: T) = {
167+
val staticClazz = classTag[T].runtimeClass
168+
val dynamicClazz = instance.getClass
169+
if (staticClazz.isPrimitive) staticClazz else dynamicClazz
170+
}
171+
172+
private class JavaInstanceMirror[T: ClassTag](val instance: T)
167173
extends InstanceMirror {
168-
def instance = obj
169-
def symbol = wholemirror.classSymbol(obj.getClass)
174+
def symbol = wholemirror.classSymbol(preciseClass(instance))
170175
def reflectField(field: TermSymbol): FieldMirror = {
171176
checkMemberOf(field, symbol)
172177
if ((field.isMethod && !field.isAccessor) || field.isModule) ErrorNotField(field)
@@ -179,26 +184,26 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
179184
catch {
180185
case _: NoSuchFieldException => ErrorNonExistentField(field1)
181186
}
182-
new JavaFieldMirror(obj, field1)
187+
new JavaFieldMirror(instance, field1)
183188
}
184189
def reflectMethod(method: MethodSymbol): MethodMirror = {
185190
checkMemberOf(method, symbol)
186-
mkJavaMethodMirror(obj, method)
191+
mkJavaMethodMirror(instance, method)
187192
}
188193
def reflectClass(cls: ClassSymbol): ClassMirror = {
189194
if (cls.isStatic) ErrorStaticClass(cls)
190195
checkMemberOf(cls, symbol)
191-
new JavaClassMirror(instance, cls)
196+
new JavaClassMirror(instance.asInstanceOf[AnyRef], cls)
192197
}
193198
def reflectModule(mod: ModuleSymbol): ModuleMirror = {
194199
if (mod.isStatic) ErrorStaticModule(mod)
195200
checkMemberOf(mod, symbol)
196-
new JavaModuleMirror(instance, mod)
201+
new JavaModuleMirror(instance.asInstanceOf[AnyRef], mod)
197202
}
198-
override def toString = s"instance mirror for $obj"
203+
override def toString = s"instance mirror for $instance"
199204
}
200205

201-
private class JavaFieldMirror(val receiver: AnyRef, val symbol: TermSymbol)
206+
private class JavaFieldMirror(val receiver: Any, val symbol: TermSymbol)
202207
extends FieldMirror {
203208
lazy val jfield = {
204209
val jfield = fieldToJava(symbol)
@@ -255,12 +260,12 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
255260
// that's because we want to have decent performance
256261
// therefore we move special cases into separate subclasses
257262
// rather than have them on a hot path them in a unified implementation of the `apply` method
258-
private def mkJavaMethodMirror(receiver: AnyRef, symbol: MethodSymbol): JavaMethodMirror = {
263+
private def mkJavaMethodMirror[T: ClassTag](receiver: T, symbol: MethodSymbol): JavaMethodMirror = {
259264
if (isMagicMethod(symbol)) new JavaMagicMethodMirror(receiver, symbol)
260265
else new JavaVanillaMethodMirror(receiver, symbol)
261266
}
262267

263-
private abstract class JavaMethodMirror(val receiver: AnyRef, val symbol: MethodSymbol)
268+
private abstract class JavaMethodMirror(val symbol: MethodSymbol)
264269
extends MethodMirror {
265270
lazy val jmeth = {
266271
val jmeth = methodToJava(symbol)
@@ -271,13 +276,13 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
271276
override def toString = s"method mirror for ${showMethodSig(symbol)} (bound to $receiver)"
272277
}
273278

274-
private class JavaVanillaMethodMirror(receiver: AnyRef, symbol: MethodSymbol)
275-
extends JavaMethodMirror(receiver, symbol) {
279+
private class JavaVanillaMethodMirror(val receiver: Any, symbol: MethodSymbol)
280+
extends JavaMethodMirror(symbol) {
276281
def apply(args: Any*): Any = jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*)
277282
}
278283

279-
private class JavaMagicMethodMirror(receiver: AnyRef, symbol: MethodSymbol)
280-
extends JavaMethodMirror(receiver, symbol) {
284+
private class JavaMagicMethodMirror[T: ClassTag](val receiver: T, symbol: MethodSymbol)
285+
extends JavaMethodMirror(symbol) {
281286
def apply(args: Any*): Any = {
282287
// checking type conformance is too much of a hassle, so we don't do it here
283288
// actually it's not even necessary, because we manually dispatch arguments to magic methods below
@@ -293,37 +298,44 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
293298
throw new ScalaReflectionException(s"${showMethodSig(symbol)} takes $n_arguments $s_arguments")
294299
}
295300

301+
def objReceiver = receiver.asInstanceOf[AnyRef]
296302
def objArg0 = args(0).asInstanceOf[AnyRef]
297303
def objArgs = args.asInstanceOf[Seq[AnyRef]]
298304
def fail(msg: String) = throw new ScalaReflectionException(msg + ", it cannot be invoked with mirrors")
299305

306+
def invokeMagicPrimitiveMethod = {
307+
val jmeths = classOf[BoxesRunTime].getDeclaredMethods.filter(_.getName == nme.primitiveMethodName(symbol.name).toString)
308+
assert(jmeths.length == 1, jmeths.toList)
309+
jmeths.head.invoke(null, (objReceiver +: objArgs): _*)
310+
}
311+
300312
symbol match {
301-
case Any_== | Object_== => ScalaRunTime.inlinedEquals(receiver, objArg0)
302-
case Any_!= | Object_!= => !ScalaRunTime.inlinedEquals(receiver, objArg0)
303-
case Any_## | Object_## => ScalaRunTime.hash(receiver)
313+
case Any_== | Object_== => ScalaRunTime.inlinedEquals(objReceiver, objArg0)
314+
case Any_!= | Object_!= => !ScalaRunTime.inlinedEquals(objReceiver, objArg0)
315+
case Any_## | Object_## => ScalaRunTime.hash(objReceiver)
304316
case Any_equals => receiver.equals(objArg0)
305317
case Any_hashCode => receiver.hashCode
306318
case Any_toString => receiver.toString
307-
case Object_eq => receiver eq objArg0
308-
case Object_ne => receiver ne objArg0
309-
case Object_synchronized => receiver.synchronized(objArg0)
310-
case sym if isGetClass(sym) => receiver.getClass
319+
case Object_eq => objReceiver eq objArg0
320+
case Object_ne => objReceiver ne objArg0
321+
case Object_synchronized => objReceiver.synchronized(objArg0)
322+
case sym if isGetClass(sym) => preciseClass(receiver)
311323
case Any_asInstanceOf => fail("Any.asInstanceOf requires a type argument")
312324
case Any_isInstanceOf => fail("Any.isInstanceOf requires a type argument")
313325
case Object_asInstanceOf => fail("AnyRef.$asInstanceOf is an internal method")
314326
case Object_isInstanceOf => fail("AnyRef.$isInstanceOf is an internal method")
315-
case Array_length => ScalaRunTime.array_length(receiver)
316-
case Array_apply => ScalaRunTime.array_apply(receiver, args(0).asInstanceOf[Int])
317-
case Array_update => ScalaRunTime.array_update(receiver, args(0).asInstanceOf[Int], args(1))
318-
case Array_clone => ScalaRunTime.array_clone(receiver)
327+
case Array_length => ScalaRunTime.array_length(objReceiver)
328+
case Array_apply => ScalaRunTime.array_apply(objReceiver, args(0).asInstanceOf[Int])
329+
case Array_update => ScalaRunTime.array_update(objReceiver, args(0).asInstanceOf[Int], args(1))
330+
case Array_clone => ScalaRunTime.array_clone(objReceiver)
319331
case sym if isStringConcat(sym) => receiver.toString + objArg0
320-
case sym if isMagicPrimitiveMethod(sym) => fail("implementation restriction: ${symbol.fullName} is a magic primitive method")
332+
case sym if isMagicPrimitiveMethod(sym) => invokeMagicPrimitiveMethod
321333
case sym if sym == Predef_classOf => fail("Predef.classOf is a compile-time function")
322334
case sym if sym.isTermMacro => fail(s"${symbol.fullName} is a macro, i.e. a compile-time function")
323335
case _ => assert(false, this)
324336
}
325337
}
326-
}
338+
}
327339

328340
private class JavaConstructorMirror(val outer: AnyRef, val symbol: MethodSymbol)
329341
extends MethodMirror {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
4
2+
class C
3+
C@2
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import scala.reflect.runtime.universe._
2+
import scala.reflect.runtime.{currentMirror => cm}
3+
4+
class C(val x: Int) extends AnyVal {
5+
def foo(y: Int) = x + y
6+
}
7+
8+
object Test extends App {
9+
println(cm.reflect(new C(2)).reflectMethod(typeOf[C].member(newTermName("foo")).asMethod)(2))
10+
println(cm.reflect(new C(2)).reflectMethod(typeOf[C].member(newTermName("getClass")).asMethod)())
11+
println(cm.reflect(new C(2)).reflectMethod(typeOf[C].member(newTermName("toString")).asMethod)())
12+
}

0 commit comments

Comments
 (0)