Skip to content

Commit b973e8a

Browse files
committed
Painless: Move Some Lookup Logic to PainlessLookup (#32565)
Renames existing methods in PainlessLookup. Adds lookupPainlessClass, lookupPainlessMethod, and lookupPainlessField to PainlessLookup. This consolidates the logic necessary to look these things up into a single place and begins the clean up of some of the nodes that were looking each of these things up individually. This also has the added benefit of improved consistency in error messaging.
1 parent af18cf1 commit b973e8a

29 files changed

+225
-160
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public Class<?> findClass(String name) throws ClassNotFoundException {
9696
if (statefulFactoryClass != null && statefulFactoryClass.getName().equals(name)) {
9797
return statefulFactoryClass;
9898
}
99-
Class<?> found = painlessLookup.getClassFromBinaryName(name);
99+
Class<?> found = painlessLookup.canonicalTypeNameToType(name.replace('$', '.'));
100100

101101
return found != null ? found : super.findClass(name);
102102
}

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ static PainlessMethod lookupMethodInternal(PainlessLookup painlessLookup, Class<
187187
String key = PainlessLookupUtility.buildPainlessMethodKey(name, arity);
188188
// check whitelist for matching method
189189
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
190-
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz);
190+
PainlessClass struct = painlessLookup.lookupPainlessClass(clazz);
191191

192192
if (struct != null) {
193193
PainlessMethod method = struct.methods.get(key);
@@ -197,7 +197,7 @@ static PainlessMethod lookupMethodInternal(PainlessLookup painlessLookup, Class<
197197
}
198198

199199
for (Class<?> iface : clazz.getInterfaces()) {
200-
struct = painlessLookup.getPainlessStructFromJavaClass(iface);
200+
struct = painlessLookup.lookupPainlessClass(iface);
201201

202202
if (struct != null) {
203203
PainlessMethod method = struct.methods.get(key);
@@ -326,8 +326,8 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lo
326326
*/
327327
static MethodHandle lookupReference(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, String interfaceClass,
328328
Class<?> receiverClass, String name) throws Throwable {
329-
Class<?> interfaceType = painlessLookup.getJavaClassFromPainlessType(interfaceClass);
330-
PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(interfaceType).functionalMethod;
329+
Class<?> interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass);
330+
PainlessMethod interfaceMethod = painlessLookup.lookupPainlessClass(interfaceType).functionalMethod;
331331
if (interfaceMethod == null) {
332332
throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
333333
}
@@ -345,7 +345,7 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku
345345
final FunctionRef ref;
346346
if ("this".equals(type)) {
347347
// user written method
348-
PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(clazz).functionalMethod;
348+
PainlessMethod interfaceMethod = painlessLookup.lookupPainlessClass(clazz).functionalMethod;
349349
if (interfaceMethod == null) {
350350
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
351351
"to [" + PainlessLookupUtility.typeToCanonicalTypeName(clazz) + "], not a functional interface");
@@ -419,7 +419,7 @@ public static String getUserFunctionHandleFieldName(String name, int arity) {
419419
static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class<?> receiverClass, String name) {
420420
// first try whitelist
421421
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
422-
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz);
422+
PainlessClass struct = painlessLookup.lookupPainlessClass(clazz);
423423

424424
if (struct != null) {
425425
MethodHandle handle = struct.getterMethodHandles.get(name);
@@ -429,7 +429,7 @@ static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class<?> receive
429429
}
430430

431431
for (final Class<?> iface : clazz.getInterfaces()) {
432-
struct = painlessLookup.getPainlessStructFromJavaClass(iface);
432+
struct = painlessLookup.lookupPainlessClass(iface);
433433

434434
if (struct != null) {
435435
MethodHandle handle = struct.getterMethodHandles.get(name);
@@ -490,7 +490,7 @@ static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class<?> receive
490490
static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class<?> receiverClass, String name) {
491491
// first try whitelist
492492
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
493-
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz);
493+
PainlessClass struct = painlessLookup.lookupPainlessClass(clazz);
494494

495495
if (struct != null) {
496496
MethodHandle handle = struct.setterMethodHandles.get(name);
@@ -500,7 +500,7 @@ static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class<?> receive
500500
}
501501

502502
for (final Class<?> iface : clazz.getInterfaces()) {
503-
struct = painlessLookup.getPainlessStructFromJavaClass(iface);
503+
struct = painlessLookup.lookupPainlessClass(iface);
504504

505505
if (struct != null) {
506506
MethodHandle handle = struct.setterMethodHandles.get(name);

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,10 @@ public static FunctionRef resolveFromLookup(
9090
PainlessLookup painlessLookup, Class<?> expected, String type, String call, int numCaptures) {
9191

9292
if ("new".equals(call)) {
93-
return new FunctionRef(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod,
93+
return new FunctionRef(expected, painlessLookup.lookupPainlessClass(expected).functionalMethod,
9494
lookup(painlessLookup, expected, type), numCaptures);
9595
} else {
96-
return new FunctionRef(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod,
96+
return new FunctionRef(expected, painlessLookup.lookupPainlessClass(expected).functionalMethod,
9797
lookup(painlessLookup, expected, type, call, numCaptures > 0), numCaptures);
9898
}
9999
}
@@ -230,14 +230,14 @@ public FunctionRef(Class<?> expected,
230230
private static PainlessConstructor lookup(PainlessLookup painlessLookup, Class<?> expected, String type) {
231231
// check its really a functional interface
232232
// for e.g. Comparable
233-
PainlessMethod method = painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod;
233+
PainlessMethod method = painlessLookup.lookupPainlessClass(expected).functionalMethod;
234234
if (method == null) {
235235
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::new] " +
236236
"to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface");
237237
}
238238

239239
// lookup requested constructor
240-
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.getJavaClassFromPainlessType(type));
240+
PainlessClass struct = painlessLookup.lookupPainlessClass(painlessLookup.canonicalTypeNameToType(type));
241241
PainlessConstructor impl = struct.constructors.get(PainlessLookupUtility.buildPainlessConstructorKey(method.typeParameters.size()));
242242

243243
if (impl == null) {
@@ -254,14 +254,14 @@ private static PainlessMethod lookup(PainlessLookup painlessLookup, Class<?> exp
254254
String type, String call, boolean receiverCaptured) {
255255
// check its really a functional interface
256256
// for e.g. Comparable
257-
PainlessMethod method = painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod;
257+
PainlessMethod method = painlessLookup.lookupPainlessClass(expected).functionalMethod;
258258
if (method == null) {
259259
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
260260
"to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface");
261261
}
262262

263263
// lookup requested method
264-
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.getJavaClassFromPainlessType(type));
264+
PainlessClass struct = painlessLookup.lookupPainlessClass(painlessLookup.canonicalTypeNameToType(type));
265265
final PainlessMethod impl;
266266
// look for a static impl first
267267
PainlessMethod staticImpl =

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public Map<String, List<String>> getHeaders(PainlessLookup painlessLookup) {
5757
if (objectToExplain != null) {
5858
toString = objectToExplain.toString();
5959
javaClassName = objectToExplain.getClass().getName();
60-
PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(objectToExplain.getClass());
60+
PainlessClass struct = painlessLookup.lookupPainlessClass(objectToExplain.getClass());
6161
if (struct != null) {
6262
painlessClassName = PainlessLookupUtility.typeToCanonicalTypeName(objectToExplain.getClass());
6363
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ private static Class<?> definitionTypeForClass(PainlessLookup painlessLookup, Cl
190190
componentType = componentType.getComponentType();
191191
}
192192

193-
if (painlessLookup.getPainlessStructFromJavaClass(componentType) == null) {
193+
if (painlessLookup.lookupPainlessClass(componentType) == null) {
194194
throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType));
195195
}
196196

modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public void recover(final LexerNoViableAltException lnvae) {
7575

7676
@Override
7777
protected boolean isType(String name) {
78-
return painlessLookup.isSimplePainlessType(name);
78+
return painlessLookup.isValidCanonicalClassName(name);
7979
}
8080

8181
@Override

modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java

Lines changed: 95 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,41 +19,119 @@
1919

2020
package org.elasticsearch.painless.lookup;
2121

22-
import java.util.Collection;
2322
import java.util.Collections;
2423
import java.util.Map;
24+
import java.util.Objects;
25+
import java.util.Set;
2526

26-
/**
27-
* The entire API for Painless. Also used as a whitelist for checking for legal
28-
* methods and fields during at both compile-time and runtime.
29-
*/
30-
public final class PainlessLookup {
27+
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessConstructorKey;
28+
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessFieldKey;
29+
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessMethodKey;
30+
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName;
3131

32-
public Collection<Class<?>> getStructs() {
33-
return classesToPainlessClasses.keySet();
34-
}
32+
public final class PainlessLookup {
3533

3634
private final Map<String, Class<?>> canonicalClassNamesToClasses;
3735
private final Map<Class<?>, PainlessClass> classesToPainlessClasses;
3836

3937
PainlessLookup(Map<String, Class<?>> canonicalClassNamesToClasses, Map<Class<?>, PainlessClass> classesToPainlessClasses) {
38+
Objects.requireNonNull(canonicalClassNamesToClasses);
39+
Objects.requireNonNull(classesToPainlessClasses);
40+
4041
this.canonicalClassNamesToClasses = Collections.unmodifiableMap(canonicalClassNamesToClasses);
4142
this.classesToPainlessClasses = Collections.unmodifiableMap(classesToPainlessClasses);
4243
}
4344

44-
public Class<?> getClassFromBinaryName(String painlessType) {
45-
return canonicalClassNamesToClasses.get(painlessType.replace('$', '.'));
45+
public boolean isValidCanonicalClassName(String canonicalClassName) {
46+
Objects.requireNonNull(canonicalClassName);
47+
48+
return canonicalClassNamesToClasses.containsKey(canonicalClassName);
4649
}
4750

48-
public boolean isSimplePainlessType(String painlessType) {
49-
return canonicalClassNamesToClasses.containsKey(painlessType);
51+
public Class<?> canonicalTypeNameToType(String painlessType) {
52+
Objects.requireNonNull(painlessType);
53+
54+
return PainlessLookupUtility.canonicalTypeNameToType(painlessType, canonicalClassNamesToClasses);
5055
}
5156

52-
public PainlessClass getPainlessStructFromJavaClass(Class<?> clazz) {
53-
return classesToPainlessClasses.get(clazz);
57+
public Set<Class<?>> getClasses() {
58+
return classesToPainlessClasses.keySet();
5459
}
5560

56-
public Class<?> getJavaClassFromPainlessType(String painlessType) {
57-
return PainlessLookupUtility.canonicalTypeNameToType(painlessType, canonicalClassNamesToClasses);
61+
public PainlessClass lookupPainlessClass(Class<?> targetClass) {
62+
return classesToPainlessClasses.get(targetClass);
63+
}
64+
65+
public PainlessConstructor lookupPainlessConstructor(Class<?> targetClass, int constructorArity) {
66+
Objects.requireNonNull(targetClass);
67+
68+
PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass);
69+
String painlessConstructorKey = buildPainlessConstructorKey(constructorArity);
70+
71+
if (targetPainlessClass == null) {
72+
throw new IllegalArgumentException("target class [" + typeToCanonicalTypeName(targetClass) + "] " +
73+
"not found for constructor [" + painlessConstructorKey + "]");
74+
}
75+
76+
PainlessConstructor painlessConstructor = targetPainlessClass.constructors.get(painlessConstructorKey);
77+
78+
if (painlessConstructor == null) {
79+
throw new IllegalArgumentException(
80+
"constructor [" + typeToCanonicalTypeName(targetClass) + ", " + painlessConstructorKey + "] not found");
81+
}
82+
83+
return painlessConstructor;
84+
}
85+
86+
public PainlessMethod lookupPainlessMethod(Class<?> targetClass, boolean isStatic, String methodName, int methodArity) {
87+
Objects.requireNonNull(targetClass);
88+
Objects.requireNonNull(methodName);
89+
90+
if (targetClass.isPrimitive()) {
91+
targetClass = PainlessLookupUtility.typeToBoxedType(targetClass);
92+
}
93+
94+
PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass);
95+
String painlessMethodKey = buildPainlessMethodKey(methodName, methodArity);
96+
97+
if (targetPainlessClass == null) {
98+
throw new IllegalArgumentException(
99+
"target class [" + typeToCanonicalTypeName(targetClass) + "] not found for method [" + painlessMethodKey + "]");
100+
}
101+
102+
PainlessMethod painlessMethod = isStatic ?
103+
targetPainlessClass.staticMethods.get(painlessMethodKey) :
104+
targetPainlessClass.methods.get(painlessMethodKey);
105+
106+
if (painlessMethod == null) {
107+
throw new IllegalArgumentException(
108+
"method [" + typeToCanonicalTypeName(targetClass) + ", " + painlessMethodKey + "] not found");
109+
}
110+
111+
return painlessMethod;
112+
}
113+
114+
public PainlessField lookupPainlessField(Class<?> targetClass, boolean isStatic, String fieldName) {
115+
Objects.requireNonNull(targetClass);
116+
Objects.requireNonNull(fieldName);
117+
118+
PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass);
119+
String painlessFieldKey = buildPainlessFieldKey(fieldName);
120+
121+
if (targetPainlessClass == null) {
122+
throw new IllegalArgumentException(
123+
"target class [" + typeToCanonicalTypeName(targetClass) + "] not found for field [" + painlessFieldKey + "]");
124+
}
125+
126+
PainlessField painlessField = isStatic ?
127+
targetPainlessClass.staticFields.get(painlessFieldKey) :
128+
targetPainlessClass.fields.get(painlessFieldKey);
129+
130+
if (painlessField == null) {
131+
throw new IllegalArgumentException(
132+
"field [" + typeToCanonicalTypeName(targetClass) + ", " + painlessFieldKey + "] not found");
133+
}
134+
135+
return painlessField;
58136
}
59137
}

modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void extractVariables(Set<String> variables) {
5050
@Override
5151
void analyze(Locals locals) {
5252
try {
53-
actual = locals.getPainlessLookup().getJavaClassFromPainlessType(type);
53+
actual = locals.getPainlessLookup().canonicalTypeNameToType(type);
5454
} catch (IllegalArgumentException exception) {
5555
throw createError(new IllegalArgumentException("Not a type [" + type + "]."));
5656
}

modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ void analyze(Locals locals) {
6666
try {
6767
if ("this".equals(type)) {
6868
// user's own function
69-
PainlessMethod interfaceMethod = locals.getPainlessLookup().getPainlessStructFromJavaClass(expected).functionalMethod;
69+
PainlessMethod interfaceMethod = locals.getPainlessLookup().lookupPainlessClass(expected).functionalMethod;
7070
if (interfaceMethod == null) {
7171
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
7272
"to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface");

modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ void analyze(Locals locals) {
5858

5959
// ensure the specified type is part of the definition
6060
try {
61-
clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type);
61+
clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type);
6262
} catch (IllegalArgumentException exception) {
6363
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
6464
}

modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ void analyze(Locals locals) {
121121
}
122122
} else {
123123
// we know the method statically, infer return type and any unknown/def types
124-
interfaceMethod = locals.getPainlessLookup().getPainlessStructFromJavaClass(expected).functionalMethod;
124+
interfaceMethod = locals.getPainlessLookup().lookupPainlessClass(expected).functionalMethod;
125125
if (interfaceMethod == null) {
126126
throw createError(new IllegalArgumentException("Cannot pass lambda to " +
127127
"[" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface"));

modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.elasticsearch.painless.Location;
2525
import org.elasticsearch.painless.MethodWriter;
2626
import org.elasticsearch.painless.lookup.PainlessConstructor;
27-
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
2827
import org.elasticsearch.painless.lookup.PainlessMethod;
2928
import org.elasticsearch.painless.lookup.def;
3029
import org.objectweb.asm.Type;
@@ -64,18 +63,16 @@ void analyze(Locals locals) {
6463

6564
actual = ArrayList.class;
6665

67-
constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(
68-
PainlessLookupUtility.buildPainlessConstructorKey(0));
69-
70-
if (constructor == null) {
71-
throw createError(new IllegalStateException("Illegal tree structure."));
66+
try {
67+
constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, 0);
68+
} catch (IllegalArgumentException iae) {
69+
throw createError(iae);
7270
}
7371

74-
method = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).methods
75-
.get(PainlessLookupUtility.buildPainlessMethodKey("add", 1));
76-
77-
if (method == null) {
78-
throw createError(new IllegalStateException("Illegal tree structure."));
72+
try {
73+
method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "add", 1);
74+
} catch (IllegalArgumentException iae) {
75+
throw createError(iae);
7976
}
8077

8178
for (int index = 0; index < values.size(); ++index) {

0 commit comments

Comments
 (0)