20
20
package org .elasticsearch .painless ;
21
21
22
22
import org .elasticsearch .painless .lookup .PainlessClass ;
23
+ import org .elasticsearch .painless .lookup .PainlessConstructor ;
23
24
import org .elasticsearch .painless .lookup .PainlessLookup ;
24
25
import org .elasticsearch .painless .lookup .PainlessLookupUtility ;
25
26
import org .elasticsearch .painless .lookup .PainlessMethod ;
26
27
import org .objectweb .asm .Type ;
27
28
28
29
import java .lang .invoke .MethodType ;
30
+ import java .lang .reflect .Constructor ;
29
31
import java .lang .reflect .Modifier ;
32
+ import java .util .List ;
30
33
31
34
import static org .elasticsearch .painless .WriterConstants .CLASS_NAME ;
32
35
import static org .objectweb .asm .Opcodes .H_INVOKEINTERFACE ;
@@ -59,8 +62,10 @@ public class FunctionRef {
59
62
60
63
/** interface method */
61
64
public final PainlessMethod interfaceMethod ;
62
- /** delegate method */
63
- public final PainlessMethod delegateMethod ;
65
+ /** delegate method type parameters */
66
+ public final List <Class <?>> delegateTypeParameters ;
67
+ /** delegate method return type */
68
+ public final Class <?> delegateReturnType ;
64
69
65
70
/** factory method type descriptor */
66
71
public final String factoryDescriptor ;
@@ -80,9 +85,47 @@ public class FunctionRef {
80
85
* @param call the right hand side of a method reference expression
81
86
* @param numCaptures number of captured arguments
82
87
*/
83
- public FunctionRef (PainlessLookup painlessLookup , Class <?> expected , String type , String call , int numCaptures ) {
84
- this (expected , painlessLookup .getPainlessStructFromJavaClass (expected ).functionalMethod ,
85
- lookup (painlessLookup , expected , type , call , numCaptures > 0 ), numCaptures );
88
+ public static FunctionRef resolveFromLookup (
89
+ PainlessLookup painlessLookup , Class <?> expected , String type , String call , int numCaptures ) {
90
+
91
+ if ("new" .equals (call )) {
92
+ return new FunctionRef (expected , painlessLookup .getPainlessStructFromJavaClass (expected ).functionalMethod ,
93
+ lookup (painlessLookup , expected , type ), numCaptures );
94
+ } else {
95
+ return new FunctionRef (expected , painlessLookup .getPainlessStructFromJavaClass (expected ).functionalMethod ,
96
+ lookup (painlessLookup , expected , type , call , numCaptures > 0 ), numCaptures );
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Creates a new FunctionRef (already resolved)
102
+ * @param expected functional interface type to implement
103
+ * @param interfaceMethod functional interface method
104
+ * @param delegateConstructor implementation constructor
105
+ * @param numCaptures number of captured arguments
106
+ */
107
+ public FunctionRef (Class <?> expected , PainlessMethod interfaceMethod , PainlessConstructor delegateConstructor , int numCaptures ) {
108
+ Constructor <?> javaConstructor = delegateConstructor .javaConstructor ;
109
+ MethodType delegateMethodType = delegateConstructor .methodType ;
110
+
111
+ interfaceMethodName = interfaceMethod .name ;
112
+ factoryMethodType = MethodType .methodType (expected ,
113
+ delegateMethodType .dropParameterTypes (numCaptures , delegateMethodType .parameterCount ()));
114
+ interfaceMethodType = interfaceMethod .methodType .dropParameterTypes (0 , 1 );
115
+
116
+ delegateClassName = javaConstructor .getDeclaringClass ().getName ();
117
+ isDelegateInterface = false ;
118
+ delegateInvokeType = H_NEWINVOKESPECIAL ;
119
+ delegateMethodName = PainlessLookupUtility .CONSTRUCTOR_NAME ;
120
+ this .delegateMethodType = delegateMethodType .dropParameterTypes (0 , numCaptures );
121
+
122
+ this .interfaceMethod = interfaceMethod ;
123
+ delegateTypeParameters = delegateConstructor .typeParameters ;
124
+ delegateReturnType = void .class ;
125
+
126
+ factoryDescriptor = factoryMethodType .toMethodDescriptorString ();
127
+ interfaceType = Type .getMethodType (interfaceMethodType .toMethodDescriptorString ());
128
+ delegateType = Type .getMethodType (this .delegateMethodType .toMethodDescriptorString ());
86
129
}
87
130
88
131
/**
@@ -112,9 +155,7 @@ public FunctionRef(Class<?> expected, PainlessMethod interfaceMethod, PainlessMe
112
155
isDelegateInterface = delegateMethod .target .isInterface ();
113
156
}
114
157
115
- if ("<init>" .equals (delegateMethod .name )) {
116
- delegateInvokeType = H_NEWINVOKESPECIAL ;
117
- } else if (Modifier .isStatic (delegateMethod .modifiers )) {
158
+ if (Modifier .isStatic (delegateMethod .modifiers )) {
118
159
delegateInvokeType = H_INVOKESTATIC ;
119
160
} else if (delegateMethod .target .isInterface ()) {
120
161
delegateInvokeType = H_INVOKEINTERFACE ;
@@ -126,7 +167,8 @@ public FunctionRef(Class<?> expected, PainlessMethod interfaceMethod, PainlessMe
126
167
this .delegateMethodType = delegateMethodType .dropParameterTypes (0 , numCaptures );
127
168
128
169
this .interfaceMethod = interfaceMethod ;
129
- this .delegateMethod = delegateMethod ;
170
+ delegateTypeParameters = delegateMethod .arguments ;
171
+ delegateReturnType = delegateMethod .rtn ;
130
172
131
173
factoryDescriptor = factoryMethodType .toMethodDescriptorString ();
132
174
interfaceType = Type .getMethodType (interfaceMethodType .toMethodDescriptorString ());
@@ -151,13 +193,37 @@ public FunctionRef(Class<?> expected,
151
193
isDelegateInterface = false ;
152
194
153
195
this .interfaceMethod = null ;
154
- delegateMethod = null ;
196
+ delegateTypeParameters = null ;
197
+ delegateReturnType = null ;
155
198
156
199
factoryDescriptor = null ;
157
200
interfaceType = null ;
158
201
delegateType = null ;
159
202
}
160
203
204
+ /**
205
+ * Looks up {@code type} from the whitelist, and returns a matching constructor.
206
+ */
207
+ private static PainlessConstructor lookup (PainlessLookup painlessLookup , Class <?> expected , String type ) {
208
+ // check its really a functional interface
209
+ // for e.g. Comparable
210
+ PainlessMethod method = painlessLookup .getPainlessStructFromJavaClass (expected ).functionalMethod ;
211
+ if (method == null ) {
212
+ throw new IllegalArgumentException ("Cannot convert function reference [" + type + "::new] " +
213
+ "to [" + PainlessLookupUtility .typeToCanonicalTypeName (expected ) + "], not a functional interface" );
214
+ }
215
+
216
+ // lookup requested constructor
217
+ PainlessClass struct = painlessLookup .getPainlessStructFromJavaClass (painlessLookup .getJavaClassFromPainlessType (type ));
218
+ PainlessConstructor impl = struct .constructors .get (PainlessLookupUtility .buildPainlessConstructorKey (method .arguments .size ()));
219
+
220
+ if (impl == null ) {
221
+ throw new IllegalArgumentException ("Unknown reference [" + type + "::new] matching [" + expected + "]" );
222
+ }
223
+
224
+ return impl ;
225
+ }
226
+
161
227
/**
162
228
* Looks up {@code type::call} from the whitelist, and returns a matching method.
163
229
*/
@@ -174,27 +240,22 @@ private static PainlessMethod lookup(PainlessLookup painlessLookup, Class<?> exp
174
240
// lookup requested method
175
241
PainlessClass struct = painlessLookup .getPainlessStructFromJavaClass (painlessLookup .getJavaClassFromPainlessType (type ));
176
242
final PainlessMethod impl ;
177
- // ctor ref
178
- if ("new" .equals (call )) {
179
- impl = struct .constructors .get (PainlessLookupUtility .buildPainlessMethodKey ("<init>" , method .arguments .size ()));
180
- } else {
181
- // look for a static impl first
182
- PainlessMethod staticImpl =
183
- struct .staticMethods .get (PainlessLookupUtility .buildPainlessMethodKey (call , method .arguments .size ()));
184
- if (staticImpl == null ) {
185
- // otherwise a virtual impl
186
- final int arity ;
187
- if (receiverCaptured ) {
188
- // receiver captured
189
- arity = method .arguments .size ();
190
- } else {
191
- // receiver passed
192
- arity = method .arguments .size () - 1 ;
193
- }
194
- impl = struct .methods .get (PainlessLookupUtility .buildPainlessMethodKey (call , arity ));
243
+ // look for a static impl first
244
+ PainlessMethod staticImpl =
245
+ struct .staticMethods .get (PainlessLookupUtility .buildPainlessMethodKey (call , method .arguments .size ()));
246
+ if (staticImpl == null ) {
247
+ // otherwise a virtual impl
248
+ final int arity ;
249
+ if (receiverCaptured ) {
250
+ // receiver captured
251
+ arity = method .arguments .size ();
195
252
} else {
196
- impl = staticImpl ;
253
+ // receiver passed
254
+ arity = method .arguments .size () - 1 ;
197
255
}
256
+ impl = struct .methods .get (PainlessLookupUtility .buildPainlessMethodKey (call , arity ));
257
+ } else {
258
+ impl = staticImpl ;
198
259
}
199
260
if (impl == null ) {
200
261
throw new IllegalArgumentException ("Unknown reference [" + type + "::" + call + "] matching " +
0 commit comments