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?<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<T>,int) ????/** ?????*?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类型. ? 说了个大概,具体的细节还有很多~~~ (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |