虽然Groovy 3 Alpha版本就已经支持Java 8的Lambda、Method Reference以及Constructor Reference全部语法,但从bytecode层面来看,其本质上依然是Closure及Method Pointer,换言之,只是语法上的兼容,但性能上并没有提升。而Groovy的性能常为人诟病,所以Groovy 2引入了静态编译,其编译出来的bytecode跟Java的相近,所以其性能逼近Java。为了延续静态编译的性能优势,Groovy核心团队决定在Groovy 3 Beta版本中实现Lambda、Method Reference以及Constructor Reference的Native版本,其语法不变,但生成的bytecode与Java的相似,这便是”Native”的由来。
Groovy 3编译器会借助静态编译的类型推断以及Java 7引入的invokedynamic指令生成与Java相似的bytecode,但如果类型推断失败,那么生成的bytecode则与Closure及Method Pointer相同。需要注意一点的是,除了性能方面的考量,在设计和实现Native Lambda时还考虑了尽可能保留Closure既有特性,为此每个Native Lambda都会对应一个内部类实例,性能方面稍有影响。另外,从DSL角度来看,Native Lambda无法引用自身,也无法引用外层Native Lambda实例,所以Native Lambda相对Closure而言不太适合应用于DSL的实现。
示例
下面提供一个简单例子来展示Native Lambda、Native Method Reference以及Native Constructor Reference的bytecode
NativeDemo的Groovy源码
import groovy.transform.CompileStatic
@CompileStatic class NativeDemo { def nativeLambda() { [1, -2, 3].stream().map(e -> Math.abs(e)).toList() } def nativeMethodReference() { [1, -2, 3].stream().map(Math::abs).toList() } def nativeConstructorReference() { [1, -2, 3].stream().toArray(Integer[]::new) } }
|
NativeDemo的bytecode
public class NativeDemo implements groovy/lang/GroovyObject {
public final INNERCLASS NativeDemo$_nativeLambda_lambda1 null _nativeLambda_lambda1
private static synthetic Lorg/codehaus/groovy/reflection/ClassInfo; $staticClassInfo
public static transient synthetic Z __$stMC
private transient synthetic Lgroovy/lang/MetaClass; metaClass
public <init>()V @Lgroovy/transform/Generated;() L0 ALOAD 0 INVOKESPECIAL java/lang/Object.<init> ()V ALOAD 0 INVOKEVIRTUAL NativeDemo.$getStaticMetaClass ()Lgroovy/lang/MetaClass; ASTORE 1 ALOAD 1 ALOAD 0 SWAP PUTFIELD NativeDemo.metaClass : Lgroovy/lang/MetaClass; ALOAD 1 POP L1 RETURN LOCALVARIABLE this LNativeDemo; L0 L1 0 MAXSTACK = 2 MAXLOCALS = 2
public nativeLambda()Ljava/lang/Object; L0 LINENUMBER 6 L0 ICONST_3 ANEWARRAY java/lang/Object DUP ICONST_0 ICONST_1 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; AASTORE DUP ICONST_1 BIPUSH -2 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; AASTORE DUP ICONST_2 ICONST_3 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; AASTORE INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List; INVOKEINTERFACE java/util/Collection.stream ()Ljava/util/stream/Stream; (itf) NEW NativeDemo$_nativeLambda_lambda1 DUP LDC LNativeDemo;.class DUP INVOKESPECIAL NativeDemo$_nativeLambda_lambda1.<init> (Ljava/lang/Object;Ljava/lang/Object;)V INVOKEDYNAMIC apply(LNativeDemo$_nativeLambda_lambda1;)Ljava/util/function/Function; [ java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; (Ljava/lang/Object;)Ljava/lang/Object;, NativeDemo$_nativeLambda_lambda1.doCall(Ljava/lang/Integer;)Ljava/lang/Object;, (Ljava/lang/Integer;)Ljava/lang/Object; ] INVOKEINTERFACE java/util/stream/Stream.map (Ljava/util/function/Function;)Ljava/util/stream/Stream; (itf) INVOKESTATIC org/codehaus/groovy/vmplugin/v8/PluginDefaultGroovyMethods.toList (Ljava/util/stream/Stream;)Ljava/util/List; ARETURN L1 LOCALVARIABLE this LNativeDemo; L0 L1 0 MAXSTACK = 5 MAXLOCALS = 1
public nativeMethodReference()Ljava/lang/Object; L0 LINENUMBER 9 L0 ICONST_3 ANEWARRAY java/lang/Object DUP ICONST_0 ICONST_1 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; AASTORE DUP ICONST_1 BIPUSH -2 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; AASTORE DUP ICONST_2 ICONST_3 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; AASTORE INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List; INVOKEINTERFACE java/util/Collection.stream ()Ljava/util/stream/Stream; (itf) INVOKEDYNAMIC apply()Ljava/util/function/Function; [ java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; (Ljava/lang/Object;)Ljava/lang/Object;, java/lang/Math.abs(I)I, (Ljava/lang/Integer;)I ] INVOKEINTERFACE java/util/stream/Stream.map (Ljava/util/function/Function;)Ljava/util/stream/Stream; (itf) INVOKESTATIC org/codehaus/groovy/vmplugin/v8/PluginDefaultGroovyMethods.toList (Ljava/util/stream/Stream;)Ljava/util/List; ARETURN L1 LOCALVARIABLE this LNativeDemo; L0 L1 0 MAXSTACK = 4 MAXLOCALS = 1
public nativeConstructorReference()Ljava/lang/Object; L0 LINENUMBER 12 L0 ICONST_3 ANEWARRAY java/lang/Object DUP ICONST_0 ICONST_1 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; AASTORE DUP ICONST_1 BIPUSH -2 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; AASTORE DUP ICONST_2 ICONST_3 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; AASTORE INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List; INVOKEINTERFACE java/util/Collection.stream ()Ljava/util/stream/Stream; (itf) INVOKEDYNAMIC apply()Ljava/util/function/IntFunction; [ java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; (I)Ljava/lang/Object;, NativeDemo.ctorRef$nativeConstructorReference$0(I)[Ljava/lang/Integer;, (I)[Ljava/lang/Integer; ] INVOKEINTERFACE java/util/stream/Stream.toArray (Ljava/util/function/IntFunction;)[Ljava/lang/Object; (itf) ARETURN L1 LOCALVARIABLE this LNativeDemo; L0 L1 0 MAXSTACK = 4 MAXLOCALS = 1
protected synthetic $getStaticMetaClass()Lgroovy/lang/MetaClass; ALOAD 0 INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; LDC LNativeDemo;.class IF_ACMPEQ L0 ALOAD 0 INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass (Ljava/lang/Object;)Lgroovy/lang/MetaClass; ARETURN L0 FRAME SAME GETSTATIC NativeDemo.$staticClassInfo : Lorg/codehaus/groovy/reflection/ClassInfo; ASTORE 1 ALOAD 1 IFNONNULL L1 ALOAD 0 INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; INVOKESTATIC org/codehaus/groovy/reflection/ClassInfo.getClassInfo (Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo; DUP ASTORE 1 PUTSTATIC NativeDemo.$staticClassInfo : Lorg/codehaus/groovy/reflection/ClassInfo; L1 FRAME APPEND [org/codehaus/groovy/reflection/ClassInfo] ALOAD 1 INVOKEVIRTUAL org/codehaus/groovy/reflection/ClassInfo.getMetaClass ()Lgroovy/lang/MetaClass; ARETURN MAXSTACK = 2 MAXLOCALS = 2
public getMetaClass()Lgroovy/lang/MetaClass; @Lgroovy/transform/Generated;() @Lgroovy/transform/Internal;() @Ljava/beans/Transient;() ALOAD 0 GETFIELD NativeDemo.metaClass : Lgroovy/lang/MetaClass; DUP IFNULL L0 ARETURN L0 FRAME SAME1 groovy/lang/MetaClass POP ALOAD 0 DUP INVOKEVIRTUAL NativeDemo.$getStaticMetaClass ()Lgroovy/lang/MetaClass; PUTFIELD NativeDemo.metaClass : Lgroovy/lang/MetaClass; ALOAD 0 GETFIELD NativeDemo.metaClass : Lgroovy/lang/MetaClass; ARETURN MAXSTACK = 2 MAXLOCALS = 1
public setMetaClass(Lgroovy/lang/MetaClass;)V @Lgroovy/transform/Generated;() @Lgroovy/transform/Internal;() ALOAD 0 ALOAD 1 PUTFIELD NativeDemo.metaClass : Lgroovy/lang/MetaClass; RETURN MAXSTACK = 2 MAXLOCALS = 2
private final static synthetic ctorRef$nativeConstructorReference$0(I)[Ljava/lang/Integer; @Lgroovy/transform/Generated;() @Lgroovy/transform/CompileStatic;() L0 ILOAD 0 MULTIANEWARRAY [Ljava/lang/Integer; 1 ARETURN L1 LOCALVARIABLE param0 I L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 }
|
NativeDemo$_nativeLambda_lambda1的bytecode(为Native Lambda而生成)
public final class NativeDemo$_nativeLambda_lambda1 extends groovy/lang/Closure implements org/codehaus/groovy/runtime/GeneratedLambda {
OUTERCLASS NativeDemo nativeLambda ()Ljava/lang/Object; public final INNERCLASS NativeDemo$_nativeLambda_lambda1 null _nativeLambda_lambda1
private static synthetic Lorg/codehaus/groovy/reflection/ClassInfo; $staticClassInfo
public static transient synthetic Z __$stMC
public <init>(Ljava/lang/Object;Ljava/lang/Object;)V L0 ALOAD 0 ALOAD 1 ALOAD 2 INVOKESPECIAL groovy/lang/Closure.<init> (Ljava/lang/Object;Ljava/lang/Object;)V L1 RETURN LOCALVARIABLE this LNativeDemo$_nativeLambda_lambda1; L0 L1 0 LOCALVARIABLE _outerInstance Ljava/lang/Object; L0 L1 1 LOCALVARIABLE _thisObject Ljava/lang/Object; L0 L1 2 MAXSTACK = 3 MAXLOCALS = 3
public doCall(Ljava/lang/Integer;)Ljava/lang/Object; L0 LINENUMBER 6 L0 ALOAD 1 INVOKEVIRTUAL java/lang/Integer.intValue ()I INVOKESTATIC java/lang/Math.abs (I)I INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; ARETURN L1 LINENUMBER 6 L1 FRAME FULL [] [java/lang/Throwable] NOP ATHROW L2 LOCALVARIABLE this LNativeDemo$_nativeLambda_lambda1; L0 L2 0 LOCALVARIABLE e Ljava/lang/Integer; L0 L2 1 MAXSTACK = 1 MAXLOCALS = 2
protected synthetic $getStaticMetaClass()Lgroovy/lang/MetaClass; ALOAD 0 INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; LDC LNativeDemo$_nativeLambda_lambda1;.class IF_ACMPEQ L0 ALOAD 0 INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass (Ljava/lang/Object;)Lgroovy/lang/MetaClass; ARETURN L0 FRAME SAME GETSTATIC NativeDemo$_nativeLambda_lambda1.$staticClassInfo : Lorg/codehaus/groovy/reflection/ClassInfo; ASTORE 1 ALOAD 1 IFNONNULL L1 ALOAD 0 INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; INVOKESTATIC org/codehaus/groovy/reflection/ClassInfo.getClassInfo (Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo; DUP ASTORE 1 PUTSTATIC NativeDemo$_nativeLambda_lambda1.$staticClassInfo : Lorg/codehaus/groovy/reflection/ClassInfo; L1 FRAME APPEND [org/codehaus/groovy/reflection/ClassInfo] ALOAD 1 INVOKEVIRTUAL org/codehaus/groovy/reflection/ClassInfo.getMetaClass ()Lgroovy/lang/MetaClass; ARETURN MAXSTACK = 2 MAXLOCALS = 2 }
|