2020package org .elasticsearch .painless ;
2121
2222import org .elasticsearch .painless .lookup .PainlessClass ;
23+ import org .elasticsearch .painless .lookup .PainlessConstructor ;
2324import org .elasticsearch .painless .lookup .PainlessLookup ;
2425import org .elasticsearch .painless .lookup .PainlessLookupUtility ;
2526import org .elasticsearch .painless .lookup .PainlessMethod ;
2627import org .objectweb .asm .Type ;
2728
2829import java .lang .invoke .MethodType ;
30+ import java .lang .reflect .Constructor ;
2931import java .lang .reflect .Modifier ;
32+ import java .util .List ;
3033
3134import static org .elasticsearch .painless .WriterConstants .CLASS_NAME ;
3235import static org .objectweb .asm .Opcodes .H_INVOKEINTERFACE ;
@@ -59,8 +62,10 @@ public class FunctionRef {
5962
6063 /** interface method */
6164 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 ;
6469
6570 /** factory method type descriptor */
6671 public final String factoryDescriptor ;
@@ -80,9 +85,47 @@ public class FunctionRef {
8085 * @param call the right hand side of a method reference expression
8186 * @param numCaptures number of captured arguments
8287 */
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 ());
86129 }
87130
88131 /**
@@ -112,9 +155,7 @@ public FunctionRef(Class<?> expected, PainlessMethod interfaceMethod, PainlessMe
112155 isDelegateInterface = delegateMethod .target .isInterface ();
113156 }
114157
115- if ("<init>" .equals (delegateMethod .name )) {
116- delegateInvokeType = H_NEWINVOKESPECIAL ;
117- } else if (Modifier .isStatic (delegateMethod .modifiers )) {
158+ if (Modifier .isStatic (delegateMethod .modifiers )) {
118159 delegateInvokeType = H_INVOKESTATIC ;
119160 } else if (delegateMethod .target .isInterface ()) {
120161 delegateInvokeType = H_INVOKEINTERFACE ;
@@ -126,7 +167,8 @@ public FunctionRef(Class<?> expected, PainlessMethod interfaceMethod, PainlessMe
126167 this .delegateMethodType = delegateMethodType .dropParameterTypes (0 , numCaptures );
127168
128169 this .interfaceMethod = interfaceMethod ;
129- this .delegateMethod = delegateMethod ;
170+ delegateTypeParameters = delegateMethod .arguments ;
171+ delegateReturnType = delegateMethod .rtn ;
130172
131173 factoryDescriptor = factoryMethodType .toMethodDescriptorString ();
132174 interfaceType = Type .getMethodType (interfaceMethodType .toMethodDescriptorString ());
@@ -151,13 +193,37 @@ public FunctionRef(Class<?> expected,
151193 isDelegateInterface = false ;
152194
153195 this .interfaceMethod = null ;
154- delegateMethod = null ;
196+ delegateTypeParameters = null ;
197+ delegateReturnType = null ;
155198
156199 factoryDescriptor = null ;
157200 interfaceType = null ;
158201 delegateType = null ;
159202 }
160203
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+
161227 /**
162228 * Looks up {@code type::call} from the whitelist, and returns a matching method.
163229 */
@@ -174,27 +240,22 @@ private static PainlessMethod lookup(PainlessLookup painlessLookup, Class<?> exp
174240 // lookup requested method
175241 PainlessClass struct = painlessLookup .getPainlessStructFromJavaClass (painlessLookup .getJavaClassFromPainlessType (type ));
176242 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 ();
195252 } else {
196- impl = staticImpl ;
253+ // receiver passed
254+ arity = method .arguments .size () - 1 ;
197255 }
256+ impl = struct .methods .get (PainlessLookupUtility .buildPainlessMethodKey (call , arity ));
257+ } else {
258+ impl = staticImpl ;
198259 }
199260 if (impl == null ) {
200261 throw new IllegalArgumentException ("Unknown reference [" + type + "::" + call + "] matching " +
0 commit comments