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