Skip to content

Commit 9b00f09

Browse files
authored
Painless: Move More Logic to PainlessLookup (#32689)
This moves some run-time lookups for methods and fields to the PainlessLookup.
1 parent 99c3d8a commit 9b00f09

24 files changed

+316
-330
lines changed

modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java

Lines changed: 42 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package org.elasticsearch.painless;
2121

2222
import org.elasticsearch.painless.Locals.LocalMethod;
23-
import org.elasticsearch.painless.lookup.PainlessClass;
2423
import org.elasticsearch.painless.lookup.PainlessLookup;
2524
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
2625
import org.elasticsearch.painless.lookup.PainlessMethod;
@@ -38,6 +37,8 @@
3837
import java.util.stream.Collectors;
3938
import java.util.stream.Stream;
4039

40+
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName;
41+
4142
/**
4243
* Support for dynamic type (def).
4344
* <p>
@@ -167,52 +168,6 @@ static MethodHandle arrayLengthGetter(Class<?> arrayType) {
167168
}
168169
}
169170

170-
/**
171-
* Looks up method entry for a dynamic method call.
172-
* <p>
173-
* A dynamic method call for variable {@code x} of type {@code def} looks like:
174-
* {@code x.method(args...)}
175-
* <p>
176-
* This method traverses {@code recieverClass}'s class hierarchy (including interfaces)
177-
* until it finds a matching whitelisted method. If one is not found, it throws an exception.
178-
* Otherwise it returns the matching method.
179-
* <p>
180-
* @params painlessLookup the whitelist
181-
* @param receiverClass Class of the object to invoke the method on.
182-
* @param name Name of the method.
183-
* @param arity arity of method
184-
* @return matching method to invoke. never returns null.
185-
* @throws IllegalArgumentException if no matching whitelisted method was found.
186-
*/
187-
static PainlessMethod lookupMethodInternal(PainlessLookup painlessLookup, Class<?> receiverClass, String name, int arity) {
188-
String key = PainlessLookupUtility.buildPainlessMethodKey(name, arity);
189-
// check whitelist for matching method
190-
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
191-
PainlessClass struct = painlessLookup.lookupPainlessClass(clazz);
192-
193-
if (struct != null) {
194-
PainlessMethod method = struct.methods.get(key);
195-
if (method != null) {
196-
return method;
197-
}
198-
}
199-
200-
for (Class<?> iface : clazz.getInterfaces()) {
201-
struct = painlessLookup.lookupPainlessClass(iface);
202-
203-
if (struct != null) {
204-
PainlessMethod method = struct.methods.get(key);
205-
if (method != null) {
206-
return method;
207-
}
208-
}
209-
}
210-
}
211-
212-
throw new IllegalArgumentException("Unable to find dynamic method [" + name + "] with [" + arity + "] arguments " +
213-
"for class [" + receiverClass.getCanonicalName() + "].");
214-
}
215-
216171
/**
217172
* Looks up handle for a dynamic method call, with lambda replacement
218173
* <p>
@@ -241,7 +196,14 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, Map<String, Loca
241196
int numArguments = callSiteType.parameterCount();
242197
// simple case: no lambdas
243198
if (recipeString.isEmpty()) {
244-
return lookupMethodInternal(painlessLookup, receiverClass, name, numArguments - 1).methodHandle;
199+
PainlessMethod painlessMethod = painlessLookup.lookupRuntimePainlessMethod(receiverClass, name, numArguments - 1);
200+
201+
if (painlessMethod == null) {
202+
throw new IllegalArgumentException("dynamic method " +
203+
"[" + typeToCanonicalTypeName(receiverClass) + ", " + name + "/" + (numArguments - 1) + "] not found");
204+
}
205+
206+
return painlessMethod.methodHandle;
245207
}
246208

247209
// convert recipe string to a bitset for convenience (the code below should be refactored...)
@@ -264,7 +226,13 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, Map<String, Loca
264226

265227
// lookup the method with the proper arity, then we know everything (e.g. interface types of parameters).
266228
// based on these we can finally link any remaining lambdas that were deferred.
267-
PainlessMethod method = lookupMethodInternal(painlessLookup, receiverClass, name, arity);
229+
PainlessMethod method = painlessLookup.lookupRuntimePainlessMethod(receiverClass, name, arity);
230+
231+
if (method == null) {
232+
throw new IllegalArgumentException(
233+
"dynamic method [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "/" + arity + "] not found");
234+
}
235+
268236
MethodHandle handle = method.methodHandle;
269237

270238
int replaced = 0;
@@ -332,15 +300,23 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, Map<String, Loca
332300
static MethodHandle lookupReference(PainlessLookup painlessLookup, Map<String, LocalMethod> localMethods,
333301
MethodHandles.Lookup methodHandlesLookup, String interfaceClass, Class<?> receiverClass, String name) throws Throwable {
334302
Class<?> interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass);
303+
if (interfaceType == null) {
304+
throw new IllegalArgumentException("type [" + interfaceClass + "] not found");
305+
}
335306
PainlessMethod interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(interfaceType);
336307
if (interfaceMethod == null) {
337308
throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
338309
}
339310
int arity = interfaceMethod.typeParameters.size();
340-
PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity);
311+
PainlessMethod implMethod = painlessLookup.lookupRuntimePainlessMethod(receiverClass, name, arity);
312+
if (implMethod == null) {
313+
throw new IllegalArgumentException(
314+
"dynamic method [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "/" + arity + "] not found");
315+
}
316+
341317
return lookupReferenceInternal(painlessLookup, localMethods, methodHandlesLookup,
342-
interfaceType, PainlessLookupUtility.typeToCanonicalTypeName(implMethod.targetClass),
343-
implMethod.javaMethod.getName(), 1);
318+
interfaceType, PainlessLookupUtility.typeToCanonicalTypeName(implMethod.targetClass),
319+
implMethod.javaMethod.getName(), 1);
344320
}
345321

346322
/** Returns a method handle to an implementation of clazz, given method reference signature. */
@@ -389,27 +365,12 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku
389365
*/
390366
static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class<?> receiverClass, String name) {
391367
// first try whitelist
392-
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
393-
PainlessClass struct = painlessLookup.lookupPainlessClass(clazz);
394-
395-
if (struct != null) {
396-
MethodHandle handle = struct.getterMethodHandles.get(name);
397-
if (handle != null) {
398-
return handle;
399-
}
400-
}
401-
402-
for (final Class<?> iface : clazz.getInterfaces()) {
403-
struct = painlessLookup.lookupPainlessClass(iface);
368+
MethodHandle getter = painlessLookup.lookupRuntimeGetterMethodHandle(receiverClass, name);
404369

405-
if (struct != null) {
406-
MethodHandle handle = struct.getterMethodHandles.get(name);
407-
if (handle != null) {
408-
return handle;
409-
}
410-
}
411-
}
370+
if (getter != null) {
371+
return getter;
412372
}
373+
413374
// special case: arrays, maps, and lists
414375
if (receiverClass.isArray() && "length".equals(name)) {
415376
// arrays expose .length as a read-only getter
@@ -426,12 +387,12 @@ static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class<?> receive
426387
int index = Integer.parseInt(name);
427388
return MethodHandles.insertArguments(LIST_GET, 1, index);
428389
} catch (NumberFormatException exception) {
429-
throw new IllegalArgumentException( "Illegal list shortcut value [" + name + "].");
390+
throw new IllegalArgumentException("Illegal list shortcut value [" + name + "].");
430391
}
431392
}
432393

433-
throw new IllegalArgumentException("Unable to find dynamic field [" + name + "] " +
434-
"for class [" + receiverClass.getCanonicalName() + "].");
394+
throw new IllegalArgumentException(
395+
"dynamic getter [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "] not found");
435396
}
436397

437398
/**
@@ -460,27 +421,12 @@ static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class<?> receive
460421
*/
461422
static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class<?> receiverClass, String name) {
462423
// first try whitelist
463-
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
464-
PainlessClass struct = painlessLookup.lookupPainlessClass(clazz);
465-
466-
if (struct != null) {
467-
MethodHandle handle = struct.setterMethodHandles.get(name);
468-
if (handle != null) {
469-
return handle;
470-
}
471-
}
472-
473-
for (final Class<?> iface : clazz.getInterfaces()) {
474-
struct = painlessLookup.lookupPainlessClass(iface);
424+
MethodHandle setter = painlessLookup.lookupRuntimeSetterMethodHandle(receiverClass, name);
475425

476-
if (struct != null) {
477-
MethodHandle handle = struct.setterMethodHandles.get(name);
478-
if (handle != null) {
479-
return handle;
480-
}
481-
}
482-
}
426+
if (setter != null) {
427+
return setter;
483428
}
429+
484430
// special case: maps, and lists
485431
if (Map.class.isAssignableFrom(receiverClass)) {
486432
// maps allow access like mymap.key
@@ -494,12 +440,12 @@ static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class<?> receive
494440
int index = Integer.parseInt(name);
495441
return MethodHandles.insertArguments(LIST_SET, 1, index);
496442
} catch (final NumberFormatException exception) {
497-
throw new IllegalArgumentException( "Illegal list shortcut value [" + name + "].");
443+
throw new IllegalArgumentException("Illegal list shortcut value [" + name + "].");
498444
}
499445
}
500446

501-
throw new IllegalArgumentException("Unable to find dynamic field [" + name + "] " +
502-
"for class [" + receiverClass.getCanonicalName() + "].");
447+
throw new IllegalArgumentException(
448+
"dynamic getter [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "] not found");
503449
}
504450

505451
/**

modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@ public static FunctionRef create(PainlessLookup painlessLookup, Map<String, Loca
6767
PainlessMethod interfaceMethod;
6868

6969
try {
70-
try {
71-
interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(targetClass);
72-
} catch (IllegalArgumentException iae) {
70+
interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(targetClass);
71+
72+
if (interfaceMethod == null) {
7373
throw new IllegalArgumentException("cannot convert function reference [" + typeName + "::" + methodName + "] " +
74-
"to a non-functional interface [" + targetClassName + "]", iae);
74+
"to a non-functional interface [" + targetClassName + "]");
7575
}
7676

7777
String interfaceMethodName = interfaceMethod.javaMethod.getName();
@@ -116,14 +116,12 @@ public static FunctionRef create(PainlessLookup painlessLookup, Map<String, Loca
116116
throw new IllegalStateException("internal error");
117117
}
118118

119-
PainlessConstructor painlessConstructor;
119+
PainlessConstructor painlessConstructor = painlessLookup.lookupPainlessConstructor(typeName, interfaceTypeParametersSize);
120120

121-
try {
122-
painlessConstructor = painlessLookup.lookupPainlessConstructor(typeName, interfaceTypeParametersSize);
123-
} catch (IllegalArgumentException iae) {
121+
if (painlessConstructor == null) {
124122
throw new IllegalArgumentException("function reference [" + typeName + "::new/" + interfaceTypeParametersSize + "] " +
125123
"matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " +
126-
"not found", iae);
124+
"not found");
127125
}
128126

129127
delegateClassName = painlessConstructor.javaConstructor.getDeclaringClass().getName();
@@ -140,24 +138,21 @@ public static FunctionRef create(PainlessLookup painlessLookup, Map<String, Loca
140138
}
141139

142140
boolean captured = numberOfCaptures == 1;
143-
PainlessMethod painlessMethod;
141+
PainlessMethod painlessMethod =
142+
painlessLookup.lookupPainlessMethod(typeName, true, methodName, interfaceTypeParametersSize);
144143

145-
try {
146-
painlessMethod = painlessLookup.lookupPainlessMethod(typeName, true, methodName, interfaceTypeParametersSize);
144+
if (painlessMethod == null) {
145+
painlessMethod = painlessLookup.lookupPainlessMethod(typeName, false, methodName,
146+
captured ? interfaceTypeParametersSize : interfaceTypeParametersSize - 1);
147147

148-
if (captured) {
149-
throw new IllegalStateException("internal error");
150-
}
151-
} catch (IllegalArgumentException staticIAE) {
152-
try {
153-
painlessMethod = painlessLookup.lookupPainlessMethod(typeName, false, methodName,
154-
captured ? interfaceTypeParametersSize : interfaceTypeParametersSize - 1);
155-
} catch (IllegalArgumentException iae) {
148+
if (painlessMethod == null) {
156149
throw new IllegalArgumentException(
157150
"function reference " + "[" + typeName + "::" + methodName + "/" + interfaceTypeParametersSize + "] " +
158151
"matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " +
159-
"not found", iae);
152+
"not found");
160153
}
154+
} else if (captured) {
155+
throw new IllegalStateException("internal error");
161156
}
162157

163158
delegateClassName = painlessMethod.javaMethod.getDeclaringClass().getName();

0 commit comments

Comments
 (0)