0%

Groovy 3之Native Lambda及Method/Constructor Reference

  虽然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源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// class version 52.0 (52)
// access flags 0x21
public class NativeDemo implements groovy/lang/GroovyObject {

// compiled from: script1617499378185.groovy
// access flags 0x11
public final INNERCLASS NativeDemo$_nativeLambda_lambda1 null _nativeLambda_lambda1

// access flags 0x100A
private static synthetic Lorg/codehaus/groovy/reflection/ClassInfo; $staticClassInfo

// access flags 0x1089
public static transient synthetic Z __$stMC

// access flags 0x1082
private transient synthetic Lgroovy/lang/MetaClass; metaClass

// access flags 0x1
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

// access flags 0x1
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; [
// handle kind 0x6 : INVOKESTATIC
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;
// arguments:
(Ljava/lang/Object;)Ljava/lang/Object;,
// handle kind 0x5 : INVOKEVIRTUAL
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

// access flags 0x1
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; [
// handle kind 0x6 : INVOKESTATIC
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;
// arguments:
(Ljava/lang/Object;)Ljava/lang/Object;,
// handle kind 0x6 : INVOKESTATIC
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

// access flags 0x1
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; [
// handle kind 0x6 : INVOKESTATIC
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;
// arguments:
(I)Ljava/lang/Object;,
// handle kind 0x6 : INVOKESTATIC
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

// access flags 0x1004
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

// access flags 0x1
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

// access flags 0x1
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

// access flags 0x101A
private final static synthetic ctorRef$nativeConstructorReference$0(I)[Ljava/lang/Integer;
@Lgroovy/transform/Generated;() // invisible
@Lgroovy/transform/CompileStatic;() // invisible
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而生成)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// class version 52.0 (52)
// access flags 0x31
public final class NativeDemo$_nativeLambda_lambda1 extends groovy/lang/Closure implements org/codehaus/groovy/runtime/GeneratedLambda {

// compiled from: script1617499378185.groovy
OUTERCLASS NativeDemo nativeLambda ()Ljava/lang/Object;
// access flags 0x11
public final INNERCLASS NativeDemo$_nativeLambda_lambda1 null _nativeLambda_lambda1

// access flags 0x100A
private static synthetic Lorg/codehaus/groovy/reflection/ClassInfo; $staticClassInfo

// access flags 0x1089
public static transient synthetic Z __$stMC

// access flags 0x1
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

// access flags 0x1
// signature (Ljava/lang/Integer;)TR;
// declaration: R doCall(java.lang.Integer)
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

// access flags 0x1004
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
}