@@ -20,6 +20,8 @@ import internal.Flags._
20
20
// import scala.tools.nsc.util.ScalaClassLoader._
21
21
import ReflectionUtils .{singletonInstance }
22
22
import language .existentials
23
+ import scala .runtime .{ScalaRunTime , BoxesRunTime }
24
+ import scala .reflect .internal .util .Collections ._
23
25
24
26
trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self : SymbolTable =>
25
27
@@ -124,15 +126,16 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
124
126
private def ErrorStaticClass (wannabe : Symbol ) = throw new ScalaReflectionException (s " $wannabe is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror " )
125
127
private def ErrorStaticModule (wannabe : Symbol ) = throw new ScalaReflectionException (s " $wannabe is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror " )
126
128
private def ErrorNotMember (wannabe : Symbol , owner : Symbol ) = throw new ScalaReflectionException (s " expected a member of $owner, you provided ${wannabe.kind} ${wannabe.fullName}" )
127
- private def ErrorNotField (wannabe : Symbol ) = throw new ScalaReflectionException (s " expected a field or an accessor method symbol, you provided $wannabe} " )
129
+ private def ErrorNotField (wannabe : Symbol ) = throw new ScalaReflectionException (s " expected a field or an accessor method symbol, you provided $wannabe" )
128
130
private def ErrorNonExistentField (wannabe : Symbol ) = throw new ScalaReflectionException (s """
129
131
|Scala field ${wannabe.name} isn't represented as a Java field, neither it has a Java accessor method
130
132
|note that private parameters of class constructors don't get mapped onto fields and/or accessors,
131
133
|unless they are used outside of their declaring constructors.
132
134
""" .trim.stripMargin)
133
135
private def ErrorSetImmutableField (wannabe : Symbol ) = throw new ScalaReflectionException (s " cannot set an immutable field ${wannabe.name}" )
136
+ private def ErrorNotConstructor (wannabe : Symbol , owner : Symbol ) = throw new ScalaReflectionException (s " expected a constructor of $owner, you provided $wannabe" )
134
137
135
- def reflect (obj : Any ): InstanceMirror = new JavaInstanceMirror (obj. asInstanceOf [ AnyRef ] )
138
+ def reflect [ T : ClassTag ] (obj : T ): InstanceMirror = new JavaInstanceMirror (obj)
136
139
137
140
def reflectClass (cls : ClassSymbol ): ClassMirror = {
138
141
if (! cls.isStatic) ErrorInnerClass (cls)
@@ -152,13 +155,25 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
152
155
153
156
def moduleSymbol (rtcls : RuntimeClass ): ModuleSymbol = classToScala(rtcls).companionModule.asModule
154
157
155
- private def checkMemberOf (wannabe : Symbol , owner : Symbol ) =
156
- if (! owner.info.member(wannabe.name).alternatives.contains(wannabe)) ErrorNotMember (wannabe, owner)
158
+ private def checkMemberOf (wannabe : Symbol , owner : ClassSymbol ) {
159
+ if (wannabe.owner == AnyClass || wannabe.owner == AnyRefClass || wannabe.owner == ObjectClass ) {
160
+ // do nothing
161
+ } else if (wannabe.owner == AnyValClass ) {
162
+ if (! owner.isPrimitiveValueClass && ! owner.isDerivedValueClass) ErrorNotMember (wannabe, owner)
163
+ } else {
164
+ if (! (owner.info.baseClasses contains wannabe.owner)) ErrorNotMember (wannabe, owner)
165
+ }
166
+ }
167
+
168
+ private def preciseClass [T : ClassTag ](instance : T ) = {
169
+ val staticClazz = classTag[T ].runtimeClass
170
+ val dynamicClazz = instance.getClass
171
+ if (staticClazz.isPrimitive) staticClazz else dynamicClazz
172
+ }
157
173
158
- private class JavaInstanceMirror ( obj : AnyRef )
174
+ private class JavaInstanceMirror [ T : ClassTag ]( val instance : T )
159
175
extends InstanceMirror {
160
- def instance = obj
161
- def symbol = wholemirror.classSymbol(obj.getClass)
176
+ def symbol = wholemirror.classSymbol(preciseClass(instance))
162
177
def reflectField (field : TermSymbol ): FieldMirror = {
163
178
checkMemberOf(field, symbol)
164
179
if ((field.isMethod && ! field.isAccessor) || field.isModule) ErrorNotField (field)
@@ -171,26 +186,26 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
171
186
catch {
172
187
case _ : NoSuchFieldException => ErrorNonExistentField (field1)
173
188
}
174
- new JavaFieldMirror (obj , field1)
189
+ new JavaFieldMirror (instance , field1)
175
190
}
176
191
def reflectMethod (method : MethodSymbol ): MethodMirror = {
177
192
checkMemberOf(method, symbol)
178
- new JavaMethodMirror (obj , method)
193
+ mkJavaMethodMirror(instance , method)
179
194
}
180
195
def reflectClass (cls : ClassSymbol ): ClassMirror = {
181
196
if (cls.isStatic) ErrorStaticClass (cls)
182
197
checkMemberOf(cls, symbol)
183
- new JavaClassMirror (instance, cls)
198
+ new JavaClassMirror (instance. asInstanceOf [ AnyRef ] , cls)
184
199
}
185
200
def reflectModule (mod : ModuleSymbol ): ModuleMirror = {
186
201
if (mod.isStatic) ErrorStaticModule (mod)
187
202
checkMemberOf(mod, symbol)
188
- new JavaModuleMirror (instance, mod)
203
+ new JavaModuleMirror (instance. asInstanceOf [ AnyRef ] , mod)
189
204
}
190
- override def toString = s " instance mirror for $obj "
205
+ override def toString = s " instance mirror for $instance "
191
206
}
192
207
193
- private class JavaFieldMirror (val receiver : AnyRef , val symbol : TermSymbol )
208
+ private class JavaFieldMirror (val receiver : Any , val symbol : TermSymbol )
194
209
extends FieldMirror {
195
210
lazy val jfield = {
196
211
val jfield = fieldToJava(symbol)
@@ -230,26 +245,115 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
230
245
sig
231
246
}
232
247
233
- private class JavaMethodMirror (val receiver : AnyRef , val symbol : MethodSymbol )
248
+ // the "symbol == Any_getClass || symbol == Object_getClass" test doesn't cut it
249
+ // because both AnyVal and its primitive descendants define their own getClass methods
250
+ private def isGetClass (meth : MethodSymbol ) = meth.name.toString == " getClass" && meth.params.flatten.isEmpty
251
+ private def isMagicPrimitiveMethod (meth : MethodSymbol ) = meth.owner.isPrimitiveValueClass
252
+ private def isStringConcat (meth : MethodSymbol ) = meth == String_+ || (isMagicPrimitiveMethod(meth) && meth.returnType =:= StringClass .toType)
253
+ lazy val magicMethodOwners = Set [Symbol ](AnyClass , AnyValClass , AnyRefClass , ObjectClass , ArrayClass ) ++ ScalaPrimitiveValueClasses
254
+ lazy val nonMagicObjectMethods = Set [Symbol ](Object_clone , Object_equals , Object_finalize , Object_hashCode , Object_toString ,
255
+ Object_notify , Object_notifyAll ) ++ ObjectClass .info.member(nme.wait_).asTerm.alternatives.map(_.asMethod)
256
+ private def isMagicMethod (meth : MethodSymbol ): Boolean = {
257
+ if (isGetClass(meth) || isStringConcat(meth) || isMagicPrimitiveMethod(meth) || meth == Predef_classOf || meth.isTermMacro) return true
258
+ magicMethodOwners(meth.owner) && ! nonMagicObjectMethods(meth)
259
+ }
260
+
261
+ // unlike other mirrors, method mirrors are created by a factory
262
+ // that's because we want to have decent performance
263
+ // therefore we move special cases into separate subclasses
264
+ // rather than have them on a hot path them in a unified implementation of the `apply` method
265
+ private def mkJavaMethodMirror [T : ClassTag ](receiver : T , symbol : MethodSymbol ): JavaMethodMirror = {
266
+ if (isMagicMethod(symbol)) new JavaMagicMethodMirror (receiver, symbol)
267
+ else if (symbol.params.flatten exists (p => isByNameParamType(p.info))) new JavaByNameMethodMirror (receiver, symbol)
268
+ else new JavaVanillaMethodMirror (receiver, symbol)
269
+ }
270
+
271
+ private abstract class JavaMethodMirror (val symbol : MethodSymbol )
234
272
extends MethodMirror {
235
273
lazy val jmeth = {
236
274
val jmeth = methodToJava(symbol)
237
275
if (! jmeth.isAccessible) jmeth.setAccessible(true )
238
276
jmeth
239
277
}
240
- def apply (args : Any * ): Any =
241
- if (symbol.owner == ArrayClass )
242
- symbol.name match {
243
- case nme.length => jArray.getLength(receiver)
244
- case nme.apply => jArray.get(receiver, args(0 ).asInstanceOf [Int ])
245
- case nme.update => jArray.set(receiver, args(0 ).asInstanceOf [Int ], args(1 ))
246
- case _ => assert(false , s " unexpected array method: $symbol" )
247
- }
248
- else
249
- jmeth.invoke(receiver, args.asInstanceOf [Seq [AnyRef ]]: _* )
278
+
279
+ def jinvoke (jmeth : jMethod, receiver : Any , args : Seq [Any ]): Any = {
280
+ val result = jmeth.invoke(receiver, args.asInstanceOf [Seq [AnyRef ]]: _* )
281
+ if (jmeth.getReturnType == java.lang.Void .TYPE ) ()
282
+ else result
283
+ }
284
+
250
285
override def toString = s " method mirror for ${showMethodSig(symbol)} (bound to $receiver) "
251
286
}
252
287
288
+ private class JavaVanillaMethodMirror (val receiver : Any , symbol : MethodSymbol )
289
+ extends JavaMethodMirror (symbol) {
290
+ def apply (args : Any * ): Any = jinvoke(jmeth, receiver, args)
291
+ }
292
+
293
+ private class JavaByNameMethodMirror (val receiver : Any , symbol : MethodSymbol )
294
+ extends JavaMethodMirror (symbol) {
295
+ def apply (args : Any * ): Any = {
296
+ val transformed = map2(args.toList, symbol.params.flatten)((arg, param) => if (isByNameParamType(param.info)) () => arg else arg)
297
+ jinvoke(jmeth, receiver, transformed)
298
+ }
299
+ }
300
+
301
+ private class JavaMagicMethodMirror [T : ClassTag ](val receiver : T , symbol : MethodSymbol )
302
+ extends JavaMethodMirror (symbol) {
303
+ def apply (args : Any * ): Any = {
304
+ // checking type conformance is too much of a hassle, so we don't do it here
305
+ // actually it's not even necessary, because we manually dispatch arguments to magic methods below
306
+ val params = symbol.paramss.flatten
307
+ val perfectMatch = args.length == params.length
308
+ // todo. this doesn't account for multiple vararg parameter lists
309
+ // however those aren't supported by the mirror API: https://issues.scala-lang.org/browse/SI-6182
310
+ // hence I leave this code as is, to be fixed when the corresponding bug is fixed
311
+ val varargMatch = args.length >= params.length - 1 && isVarArgsList(params)
312
+ if (! perfectMatch && ! varargMatch) {
313
+ val n_arguments = if (isVarArgsList(params)) s " ${params.length - 1 } or more " else s " ${params.length}"
314
+ var s_arguments = if (params.length == 1 && ! isVarArgsList(params)) " argument" else " arguments"
315
+ throw new ScalaReflectionException (s " ${showMethodSig(symbol)} takes $n_arguments $s_arguments" )
316
+ }
317
+
318
+ def objReceiver = receiver.asInstanceOf [AnyRef ]
319
+ def objArg0 = args(0 ).asInstanceOf [AnyRef ]
320
+ def objArgs = args.asInstanceOf [Seq [AnyRef ]]
321
+ def fail (msg : String ) = throw new ScalaReflectionException (msg + " , it cannot be invoked with mirrors" )
322
+
323
+ def invokeMagicPrimitiveMethod = {
324
+ val jmeths = classOf [BoxesRunTime ].getDeclaredMethods.filter(_.getName == nme.primitiveMethodName(symbol.name).toString)
325
+ assert(jmeths.length == 1 , jmeths.toList)
326
+ jinvoke(jmeths.head, null , objReceiver +: objArgs)
327
+ }
328
+
329
+ symbol match {
330
+ case Any_== | Object_== => ScalaRunTime .inlinedEquals(objReceiver, objArg0)
331
+ case Any_!= | Object_!= => ! ScalaRunTime .inlinedEquals(objReceiver, objArg0)
332
+ case Any_## | Object_## => ScalaRunTime .hash(objReceiver)
333
+ case Any_equals => receiver.equals(objArg0)
334
+ case Any_hashCode => receiver.hashCode
335
+ case Any_toString => receiver.toString
336
+ case Object_eq => objReceiver eq objArg0
337
+ case Object_ne => objReceiver ne objArg0
338
+ case Object_synchronized => objReceiver.synchronized (objArg0)
339
+ case sym if isGetClass(sym) => preciseClass(receiver)
340
+ case Any_asInstanceOf => fail(" Any.asInstanceOf requires a type argument" )
341
+ case Any_isInstanceOf => fail(" Any.isInstanceOf requires a type argument" )
342
+ case Object_asInstanceOf => fail(" AnyRef.$asInstanceOf is an internal method" )
343
+ case Object_isInstanceOf => fail(" AnyRef.$isInstanceOf is an internal method" )
344
+ case Array_length => ScalaRunTime .array_length(objReceiver)
345
+ case Array_apply => ScalaRunTime .array_apply(objReceiver, args(0 ).asInstanceOf [Int ])
346
+ case Array_update => ScalaRunTime .array_update(objReceiver, args(0 ).asInstanceOf [Int ], args(1 ))
347
+ case Array_clone => ScalaRunTime .array_clone(objReceiver)
348
+ case sym if isStringConcat(sym) => receiver.toString + objArg0
349
+ case sym if isMagicPrimitiveMethod(sym) => invokeMagicPrimitiveMethod
350
+ case sym if sym == Predef_classOf => fail(" Predef.classOf is a compile-time function" )
351
+ case sym if sym.isTermMacro => fail(s " ${symbol.fullName} is a macro, i.e. a compile-time function " )
352
+ case _ => assert(false , this )
353
+ }
354
+ }
355
+ }
356
+
253
357
private class JavaConstructorMirror (val outer : AnyRef , val symbol : MethodSymbol )
254
358
extends MethodMirror {
255
359
override val receiver = outer
@@ -259,6 +363,9 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
259
363
jconstr
260
364
}
261
365
def apply (args : Any * ): Any = {
366
+ if (symbol.owner == ArrayClass )
367
+ throw new ScalaReflectionException (" Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag(<class of element>).newArray(<length>)` instead" )
368
+
262
369
val effectiveArgs =
263
370
if (outer == null ) args.asInstanceOf [Seq [AnyRef ]]
264
371
else outer +: args.asInstanceOf [Seq [AnyRef ]]
@@ -279,7 +386,11 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
279
386
extends JavaTemplateMirror with ClassMirror {
280
387
def erasure = symbol
281
388
def isStatic = false
282
- def reflectConstructor (constructor : MethodSymbol ) = new JavaConstructorMirror (outer, constructor)
389
+ def reflectConstructor (constructor : MethodSymbol ) = {
390
+ if (! constructor.isClassConstructor) ErrorNotConstructor (constructor, symbol)
391
+ if (! symbol.info.decls.toList.contains(constructor)) ErrorNotConstructor (constructor, symbol)
392
+ new JavaConstructorMirror (outer, constructor)
393
+ }
283
394
def companion : Option [ModuleMirror ] = symbol.companionModule match {
284
395
case module : ModuleSymbol => Some (new JavaModuleMirror (outer, module))
285
396
case _ => None
@@ -1067,7 +1178,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
1067
1178
mirrors(rootToLoader getOrElseUpdate(root, findLoader)).get.get
1068
1179
}
1069
1180
1070
- private lazy val magicSymbols : Map [(String , Name ), Symbol ] = {
1181
+ private lazy val magicClasses : Map [(String , Name ), Symbol ] = {
1071
1182
def mapEntry (sym : Symbol ): ((String , Name ), Symbol ) = (sym.owner.fullName, sym.name) -> sym
1072
1183
Map () ++ (definitions.magicSymbols filter (_.isType) map mapEntry)
1073
1184
}
@@ -1088,7 +1199,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
1088
1199
if (name.isTermName && ! owner.isEmptyPackageClass)
1089
1200
return mirror.makeScalaPackage(
1090
1201
if (owner.isRootSymbol) name.toString else owner.fullName+ " ." + name)
1091
- magicSymbols get (owner.fullName, name) match {
1202
+ magicClasses get (owner.fullName, name) match {
1092
1203
case Some (tsym) =>
1093
1204
owner.info.decls enter tsym
1094
1205
return tsym
0 commit comments