Can it reflect the original values โ€‹โ€‹used in the trait?

I play with reflection to get a deep analysis on the basis of. One of the things I would like to get is the initial value set for the member field. For example, in a sign:

trait A { val x: Int = 3 val y: String = "y" } 

it would be nice to know 3 and "y". I did not find anything related to this task in the API and due to the following output (generated by scalac -Xprint):

 abstract trait A extends Object { <accessor> def com$hablapps$A$_setter_$x_=(x$1: Int): Unit; <accessor> def com$hablapps$A$_setter_$y_=(x$1: String): Unit; <stable> <accessor> def x(): Int; <stable> <accessor> def y(): String }; abstract trait A$class extends { def /*A$class*/$init$($this: com.hablapps.A): Unit = { $this.com$hablapps$A$_setter_$x_=(3); $this.com$hablapps$A$_setter_$y_=("y"); () } } 

I'm afraid it will be quite difficult to access them, as they are stored in the $ init $ method test. Is there a (simple) way to get these values โ€‹โ€‹with reflection?

+4
source share
3 answers

You need to parse the bytecode:

 trait A { val x: Int = 3 } public abstract class A$class extends java.lang.Object{ public static void $init$(A); Code: 0: aload_0 1: iconst_3 2: invokeinterface #12, 2; //InterfaceMethod AA$_setter_$x_$eq:(I)V 7: return 

See line 1 - the only place that matters is in the bytecode for the init method!

You cannot do this because if you have

 trait A { val x: Int = 3 } trait B extends A { override val x = 7 } class C extends B {} 

You will find that C extends A$_setter_$x_$eq does nothing - make a call to A$class.$init$ no-op and make it invalid.

Evidence:

 public class C extends java.lang.Object implements B,scala.ScalaObject{ public void A$_setter_$x_$eq(int); Code: 0: return public void B$_setter_$x_$eq(int); Code: 0: aload_0 1: iload_1 2: putfield #11; //Field x:I 5: return 
+1
source

I doubt you can understand this. This is not type information, but code. If you have a tree for this trait, you can find it, but otherwise, I doubt it.

However, you can use class file analyzers for further study. I assume that they will appear as constants for the class that can be read. I'm not sure if you could associate them with a variable, but ...

I'm not very good at class parsers, but I think a tool called asm does this.

+1
source

You can use Java Proxy to create an instance of the attribute that collects values โ€‹โ€‹from all calls to the trait ... $ setter $ .... method. Here is a hacker example:

 object Reflection { def traitInits(clazz : Class[_]) : Map[String, Object] = { var cl = clazz.getClassLoader if (cl == null) { cl = ClassLoader.getSystemClassLoader } var init : Option[java.lang.reflect.Method] = None try { for (m <- cl.loadClass(clazz.getName + "$class").getMethods if init.isEmpty) if (m.getName == "$init$") init = Some(m) } catch { case e : Exception => } if (init.isEmpty) return Map() var encodedToDecodedSetterNameMap = Map[String, String]() for (m <- clazz.getDeclaredMethods()) { val setterPrefix = clazz.getName.replace('.', '$') + "$_setter_$" val encoded = m.getName if (encoded.startsWith(setterPrefix)) { val decoded = encoded.substring(setterPrefix.length, encoded.length - 4) encodedToDecodedSetterNameMap += (encoded -> decoded) } } var result = Map[String, Object]() import java.lang.reflect.InvocationHandler import java.lang.reflect.Proxy init.get.invoke(null, Proxy.newProxyInstance(cl, Array[Class[_]](clazz), new InvocationHandler { def invoke(proxy : Object, method : java.lang.reflect.Method, args : Array[Object]) = { encodedToDecodedSetterNameMap.get(method.getName) match { case Some(decodedName) => result += (decodedName -> args(0)) case _ => } null } })) result } //> traitInits: (clazz: Class[_])Map[String,Object] trait A { val x : Int = 3 val y : String = "y" } traitInits(classOf[A]) //> res0: Map[String,Object] = Map(x -> 3, y -> y) } 
+1
source

All Articles