在Groovy中可以很方便的交换两个变量的值, 如:
1def (a, b) = [1, 2]; 2(a, b) = [b, a];
这样, a,b变量的值就交换了, 那么Groovy是怎样实现的呢?
来看看生成的字节码文件, 关键的代码如下:
1 // Method descriptor #39 ()Ljava/lang/Object; 2 // Stack: 4, Locals: 6 3 public java.lang.Object run(); 4 0 invokestatic Main.$getCallSiteArray() : org.codehaus.groovy.runtime.callsite.CallSite[] [17] 5 3 astore_1 6 4 iconst_2 7 5 anewarray java.lang.Object [41] 8 8 dup 9 9 iconst_0 10 10 iconst_1 11 11 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [47] 12 14 aastore 13 15 dup 14 16 iconst_1 15 17 iconst_2 16 18 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [47] 17 21 aastore 18 22 invokestatic org.codehaus.groovy.runtime.ScriptBytecodeAdapter.createList(java.lang.Object[]) : java.util.List [53] 19 25 astore_2 20 26 aload_1 21 27 ldc <Integer 1> [54] 22 29 aaload 23 30 aload_2 24 31 iconst_0 25 32 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [47] 26 35 invokeinterface org.codehaus.groovy.runtime.callsite.CallSite.call(java.lang.Object, java.lang.Object) : java.lang.Object [57] [nargs: 3] 27 40 astore_3 [a] 28 41 aload_1 29 42 ldc <Integer 2> [58] 30 44 aaload 31 45 aload_2 32 46 iconst_1 33 47 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [47] 34 50 invokeinterface org.codehaus.groovy.runtime.callsite.CallSite.call(java.lang.Object, java.lang.Object) : java.lang.Object [57] [nargs: 3] 35 55 astore 4 [b] 36 57 aload_2 37 58 pop 38 59 iconst_2 39 60 anewarray java.lang.Object [41] 40 63 dup 41 64 iconst_0 42 65 aload 4 [b] 43 67 aastore 44 68 dup 45 69 iconst_1 46 70 aload_3 [a] 47 71 aastore 48 72 invokestatic org.codehaus.groovy.runtime.ScriptBytecodeAdapter.createList(java.lang.Object[]) : java.util.List [53] 49 75 astore 5 50 77 aload_1 51 78 ldc <Integer 3> [59] 52 80 aaload 53 81 aload 5 54 83 iconst_0 55 84 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [47] 56 87 invokeinterface org.codehaus.groovy.runtime.callsite.CallSite.call(java.lang.Object, java.lang.Object) : java.lang.Object [57] [nargs: 3] 57 92 astore_3 [a] 58 93 aload_1 59 94 ldc <Integer 4> [60] 60 96 aaload 61 97 aload 5 62 99 iconst_1 63 100 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [47] 64 103 invokeinterface org.codehaus.groovy.runtime.callsite.CallSite.call(java.lang.Object, java.lang.Object) : java.lang.Object [57] [nargs: 3] 65 108 astore 4 [b] 66 110 aload 5 67 112 areturn 68 113 aconst_null 69 114 areturn
反编译过来, 类似于这样的代码:
1public Object main(){ 2 org.codehaus.groovy.runtime.callsite.CallSite[] callsite = Main.$getCallSiteArray(); 3 4 List alist = org.codehaus.groovy.runtime.ScriptBytecodeAdapter.createList(new Object[]{1,2}); 5 6 Object a = callsite[1].call(alist, 0);//等价于 alist.getAt(0) 等价于alist.get(0); 7 Object b = callsite[2].call(alist, 1);//等价于 alist.getAt(1) 等价于alist.get(1); 8 9 List blist = org.codehaus.groovy.runtime.ScriptBytecodeAdapter.createList(new Object[]{b,a}); 10 11 a = callsite[3].call(blist, 0);//等价于 blist.getAt(0) 等价于blist.get(0); 12 b = callsite[4].call(blist, 1);//等价于 blist.getAt(1) 等价于blist.get(1); 13} 14 15private static synthetic SoftReference<CallSiteArray> $callSiteArray; 16 17private static synthetic org.codehaus.groovy.runtime.callsite.CallSite[] $getCallSiteArray(){ 18 org.codehaus.groovy.runtime.callsite.CallSiteArray rtrun = null; 19 20 if(Main.$callSiteArray == null){ 21 rtrun = Main.$createCallSiteArray(); 22 Main.$callSiteArray = new SoftReference<CallSiteArray>(temp); 23 }else{ 24 rtrun = Main.$callSiteArray.get(); 25 } 26 return rturn.array; 27} 28 29private static synthetic org.codehaus.groovy.runtime.callsite.CallSiteArray $createCallSiteArray(){ 30 String[] sarry = new String[5]; 31 Main.$createCallSiteArray_1(sarry) 32 33 return new CallSiteArray(Main.class, sarry); 34} 35 36private static synthetic void $createCallSiteArray_1(java.lang.String[] sarry){ 37 sarry[0] = "runScript"; 38 sarry[1] = "getAt"; 39 sarry[2] = "getAt"; 40 sarry[3] = "getAt"; 41 sarry[4] = "getAt"; 42}
可以很清楚的看到Groovy编译器所做的事情.
很简单,但是可以看出,Groovy的执行方式, 编译器会根据方法的调用来创建对应的CallSiteArray对象,
Groovy将很多方法的调用都改为CallSite.call方式的调用,利用这种方式来支持很多的动态特性.
比如上面的例子, Groovy通过创建CallSite, 然后通过CallSite来调用 getAt 方法.
Groovy将调用委托到CallSite后, 开始时, CallSite的具体实现为CallSiteArray,
CallSiteArray通过将调用委托到org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSite, Object, Object[])方法
该方法负责创建具体的调用点对象, 并执行对应方法.
这里最终创建的调用点对象为 org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.PojoMetaMethodSiteNoUnwrapNoCoerce, 具体代码如下:
1 public static class PojoMetaMethodSiteNoUnwrapNoCoerce extends PojoMetaMethodSite { 2 3 public PojoMetaMethodSiteNoUnwrapNoCoerce(CallSite site, MetaClassImpl metaClass, MetaMethod metaMethod, Class params[]) { 4 super(site, metaClass, metaMethod, params); 5 } 6 7 public final Object invoke(Object receiver, Object[] args) throws Throwable { 8 try { 9 return metaMethod.invoke(receiver, args); 10 } catch (GroovyRuntimeException gre) { 11 throw ScriptBytecodeAdapter.unwrap(gre); 12 } 13 } 14 }
真实的调用会委托到这个类的invoke方法:
这里metaMethod具体的类型为:
org.codehaus.groovy.runtime.dgm$243@527e5409[name: getAt params: [int] returns: class java.lang.Object owner: interface java.util.List]
这个类没有源码, 只有class文件,反编译如下:
1import java.util.List; 2import org.codehaus.groovy.reflection.CachedClass; 3import org.codehaus.groovy.reflection.GeneratedMetaMethod; 4import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; 5 6public class dgm$243 extends GeneratedMetaMethod { 7 public dgm$243(String paramString, CachedClass paramCachedClass, Class paramClass, Class[] paramArrayOfClass) { 8 super(paramString, paramCachedClass, paramClass, paramArrayOfClass); 9 } 10 11 public Object invoke(Object paramObject, Object[] paramArrayOfObject) { 12 return DefaultGroovyMethods 13 .getAt((List) paramObject, DefaultTypeTransformation.intUnbox(paramArrayOfObject[0])); 14 } 15 16 public final Object doMethodInvoke(Object paramObject, Object[] paramArrayOfObject) { 17 paramArrayOfObject = coerceArgumentsToClasses(paramArrayOfObject); 18 return DefaultGroovyMethods 19 .getAt((List) paramObject, DefaultTypeTransformation.intUnbox(paramArrayOfObject[0])); 20 } 21}
可以看到, 具体的调用又是:org.codehaus.groovy.runtime.DefaultGroovyMethods.getAt(List<T>, int)
1 /** 2 * Support the subscript operator for a List. 3 * <pre class="groovyTestCase">def list = [2, "a", 5.3] 4 * assert list[1] == "a"</pre> 5 * 6 * @param self a List 7 * @param idx an index 8 * @return the value at the given index 9 * @since 1.0 10 */ 11 public static <T> T getAt(List<T> self, int idx) { 12 int size = self.size(); 13 int i = normaliseIndex(idx, size); 14 if (i < size) { 15 return self.get(i); 16 } else { 17 return null; 18 } 19 }
终于, 一个方法的调用完成了, 可以看到, 虽然提供了很高的灵活性, 但是也牺牲了一部分性能.
PS: Groovy会将上面创建的CallSite对象缓存, 且为SoftReference类型.
说了个大概,具体的细节还有很多~~~