Groovy中方法的调用实现方式浅析(CallSite)

  在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= callsite[3].call(blist, 0);//等价于 blist.getAt(0) 等价于blist.get(0); 12= 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 (< size) { 15            return self.get(i); 16        } else { 17            return null; 18        } 19    }

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

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

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

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

swap空间的增减方法

(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Groovy中方法的调用实现方式浅析(CallSite) - HelloWorld