groovy call java_Groovy中方法的调用实现方式浅析(CallSite)

在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  [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  [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  [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  [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;

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(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, int)

/**

* Support the subscript operator for a List.

def list = [2, "a", 5.3]

* assert list[1] == "a"

*

* @param self a List

* @param idx  an index

* @return the value at the given index

* @since 1.0

*/

public static  T getAt(List self, int idx) {

int size = self.size();

int i = normaliseIndex(idx, size);

if (i 

return self.get(i);

} else {

return null;

}

}

终于, 一个方法的调用完成了, 可以看到, 虽然提供了很高的灵活性, 但是也牺牲了一部分性能.

PS: Groovy会将上面创建的CallSite对象缓存, 且为SoftReference类型.

说了个大概,具体的细节还有很多~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值