Skip to content

Commit 90c74a7

Browse files
authored
Remove RuntimeClass from Painless Definition in favor of just Painless Struct. (#28486)
1 parent ab8f5ea commit 90c74a7

File tree

8 files changed

+58
-95
lines changed

8 files changed

+58
-95
lines changed

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
@@ -20,7 +20,7 @@
2020
package org.elasticsearch.painless;
2121

2222
import org.elasticsearch.painless.Definition.Method;
23-
import org.elasticsearch.painless.Definition.RuntimeClass;
23+
import org.elasticsearch.painless.Definition.Struct;
2424

2525
import java.lang.invoke.CallSite;
2626
import java.lang.invoke.MethodHandle;
@@ -185,7 +185,7 @@ static Method lookupMethodInternal(Definition definition, Class<?> receiverClass
185185
Definition.MethodKey key = new Definition.MethodKey(name, arity);
186186
// check whitelist for matching method
187187
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
188-
RuntimeClass struct = definition.getRuntimeClass(clazz);
188+
Struct struct = definition.RuntimeClassToStruct(clazz);
189189

190190
if (struct != null) {
191191
Method method = struct.methods.get(key);
@@ -195,7 +195,7 @@ static Method lookupMethodInternal(Definition definition, Class<?> receiverClass
195195
}
196196

197197
for (Class<?> iface : clazz.getInterfaces()) {
198-
struct = definition.getRuntimeClass(iface);
198+
struct = definition.RuntimeClassToStruct(iface);
199199

200200
if (struct != null) {
201201
Method method = struct.methods.get(key);
@@ -325,7 +325,7 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp
325325
static MethodHandle lookupReference(Definition definition, Lookup lookup, String interfaceClass,
326326
Class<?> receiverClass, String name) throws Throwable {
327327
Definition.Type interfaceType = definition.getType(interfaceClass);
328-
Method interfaceMethod = interfaceType.struct.getFunctionalMethod();
328+
Method interfaceMethod = interfaceType.struct.functionalMethod;
329329
if (interfaceMethod == null) {
330330
throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
331331
}
@@ -342,7 +342,7 @@ private static MethodHandle lookupReferenceInternal(Definition definition, Looku
342342
final FunctionRef ref;
343343
if ("this".equals(type)) {
344344
// user written method
345-
Method interfaceMethod = clazz.struct.getFunctionalMethod();
345+
Method interfaceMethod = clazz.struct.functionalMethod;
346346
if (interfaceMethod == null) {
347347
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
348348
"to [" + clazz.name + "], not a functional interface");
@@ -415,7 +415,7 @@ public static String getUserFunctionHandleFieldName(String name, int arity) {
415415
static MethodHandle lookupGetter(Definition definition, Class<?> receiverClass, String name) {
416416
// first try whitelist
417417
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
418-
RuntimeClass struct = definition.getRuntimeClass(clazz);
418+
Struct struct = definition.RuntimeClassToStruct(clazz);
419419

420420
if (struct != null) {
421421
MethodHandle handle = struct.getters.get(name);
@@ -425,7 +425,7 @@ static MethodHandle lookupGetter(Definition definition, Class<?> receiverClass,
425425
}
426426

427427
for (final Class<?> iface : clazz.getInterfaces()) {
428-
struct = definition.getRuntimeClass(iface);
428+
struct = definition.RuntimeClassToStruct(iface);
429429

430430
if (struct != null) {
431431
MethodHandle handle = struct.getters.get(name);
@@ -486,7 +486,7 @@ static MethodHandle lookupGetter(Definition definition, Class<?> receiverClass,
486486
static MethodHandle lookupSetter(Definition definition, Class<?> receiverClass, String name) {
487487
// first try whitelist
488488
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
489-
RuntimeClass struct = definition.getRuntimeClass(clazz);
489+
Struct struct = definition.RuntimeClassToStruct(clazz);
490490

491491
if (struct != null) {
492492
MethodHandle handle = struct.setters.get(name);
@@ -496,7 +496,7 @@ static MethodHandle lookupSetter(Definition definition, Class<?> receiverClass,
496496
}
497497

498498
for (final Class<?> iface : clazz.getInterfaces()) {
499-
struct = definition.getRuntimeClass(iface);
499+
struct = definition.RuntimeClassToStruct(iface);
500500

501501
if (struct != null) {
502502
MethodHandle handle = struct.setters.get(name);

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

Lines changed: 39 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,12 @@ public static final class Struct {
307307
public final Map<String, Field> staticMembers;
308308
public final Map<String, Field> members;
309309

310-
private final SetOnce<Method> functionalMethod;
310+
public final Map<String, MethodHandle> getters;
311+
public final Map<String, MethodHandle> setters;
312+
313+
public final Method functionalMethod;
311314

312-
private Struct(final String name, final Class<?> clazz, final org.objectweb.asm.Type type) {
315+
private Struct(String name, Class<?> clazz, org.objectweb.asm.Type type) {
313316
this.name = name;
314317
this.clazz = clazz;
315318
this.type = type;
@@ -321,10 +324,13 @@ private Struct(final String name, final Class<?> clazz, final org.objectweb.asm.
321324
staticMembers = new HashMap<>();
322325
members = new HashMap<>();
323326

324-
functionalMethod = new SetOnce<>();
327+
getters = new HashMap<>();
328+
setters = new HashMap<>();
329+
330+
functionalMethod = null;
325331
}
326332

327-
private Struct(final Struct struct) {
333+
private Struct(Struct struct, Method functionalMethod) {
328334
name = struct.name;
329335
clazz = struct.clazz;
330336
type = struct.type;
@@ -336,11 +342,14 @@ private Struct(final Struct struct) {
336342
staticMembers = Collections.unmodifiableMap(struct.staticMembers);
337343
members = Collections.unmodifiableMap(struct.members);
338344

339-
functionalMethod = struct.functionalMethod;
345+
getters = Collections.unmodifiableMap(struct.getters);
346+
setters = Collections.unmodifiableMap(struct.setters);
347+
348+
this.functionalMethod = functionalMethod;
340349
}
341350

342-
private Struct freeze() {
343-
return new Struct(this);
351+
private Struct freeze(Method functionalMethod) {
352+
return new Struct(this, functionalMethod);
344353
}
345354

346355
@Override
@@ -362,14 +371,6 @@ public boolean equals(Object object) {
362371
public int hashCode() {
363372
return name.hashCode();
364373
}
365-
366-
/**
367-
* If this class is a functional interface according to JLS, returns its method.
368-
* Otherwise returns null.
369-
*/
370-
public Method getFunctionalMethod() {
371-
return functionalMethod.get();
372-
}
373374
}
374375

375376
public static class Cast {
@@ -418,25 +419,6 @@ private Cast(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxFrom, C
418419
}
419420
}
420421

421-
public static final class RuntimeClass {
422-
private final Struct struct;
423-
public final Map<MethodKey, Method> methods;
424-
public final Map<String, MethodHandle> getters;
425-
public final Map<String, MethodHandle> setters;
426-
427-
private RuntimeClass(final Struct struct, final Map<MethodKey, Method> methods,
428-
final Map<String, MethodHandle> getters, final Map<String, MethodHandle> setters) {
429-
this.struct = struct;
430-
this.methods = Collections.unmodifiableMap(methods);
431-
this.getters = Collections.unmodifiableMap(getters);
432-
this.setters = Collections.unmodifiableMap(setters);
433-
}
434-
435-
public Struct getStruct() {
436-
return struct;
437-
}
438-
}
439-
440422
/** Returns whether or not a non-array type exists. */
441423
public boolean isSimpleType(final String name) {
442424
return structsMap.containsKey(name);
@@ -569,7 +551,9 @@ public static Class<?> defClassToObjectClass(Class<?> clazz) {
569551
}
570552

571553
public static String ClassToName(Class<?> clazz) {
572-
if (clazz.isArray()) {
554+
if (clazz.isLocalClass() || clazz.isAnonymousClass()) {
555+
return null;
556+
} else if (clazz.isArray()) {
573557
Class<?> component = clazz.getComponentType();
574558
int dimensions = 1;
575559

@@ -609,7 +593,7 @@ public Type ClassToType(Class<?> clazz) {
609593
if (component == def.class) {
610594
return getType(structsMap.get(def.class.getSimpleName()), dimensions);
611595
} else {
612-
return getType(runtimeMap.get(component).struct, dimensions);
596+
return getType(structsMap.get(ClassToName(component)), dimensions);
613597
}
614598
} else if (clazz == def.class) {
615599
return getType(structsMap.get(def.class.getSimpleName()), 0);
@@ -618,6 +602,10 @@ public Type ClassToType(Class<?> clazz) {
618602
return getType(structsMap.get(ClassToName(clazz)), 0);
619603
}
620604

605+
public Struct RuntimeClassToStruct(Class<?> clazz) {
606+
return structsMap.get(ClassToName(clazz));
607+
}
608+
621609
public static Class<?> TypeToClass(Type type) {
622610
if (def.class.getSimpleName().equals(type.struct.name)) {
623611
return ObjectClassTodefClass(type.clazz);
@@ -626,10 +614,6 @@ public static Class<?> TypeToClass(Type type) {
626614
return type.clazz;
627615
}
628616

629-
public RuntimeClass getRuntimeClass(Class<?> clazz) {
630-
return runtimeMap.get(clazz);
631-
}
632-
633617
public Class<?> getClassFromBinaryName(String name) {
634618
Struct struct = structsMap.get(name.replace('$', '.'));
635619

@@ -659,14 +643,12 @@ private static String buildFieldCacheKey(String structName, String fieldName, St
659643

660644
// INTERNAL IMPLEMENTATION:
661645

662-
private final Map<Class<?>, RuntimeClass> runtimeMap;
663646
private final Map<String, Struct> structsMap;
664647
private final Map<String, Type> simpleTypesMap;
665648

666649
public Definition(List<Whitelist> whitelists) {
667650
structsMap = new HashMap<>();
668651
simpleTypesMap = new HashMap<>();
669-
runtimeMap = new HashMap<>();
670652

671653
Map<Class<?>, Struct> javaClassesToPainlessStructs = new HashMap<>();
672654
String origin = null;
@@ -787,17 +769,6 @@ public Definition(List<Whitelist> whitelists) {
787769
}
788770
}
789771

790-
// mark functional interfaces (or set null, to mark class is not)
791-
for (String painlessStructName : structsMap.keySet()) {
792-
Struct painlessStruct = structsMap.get(painlessStructName);
793-
794-
if (painlessStruct.name.equals(painlessStructName) == false) {
795-
continue;
796-
}
797-
798-
painlessStruct.functionalMethod.set(computeFunctionalInterfaceMethod(painlessStruct));
799-
}
800-
801772
// precompute runtime classes
802773
for (String painlessStructName : structsMap.keySet()) {
803774
Struct painlessStruct = structsMap.get(painlessStructName);
@@ -815,7 +786,7 @@ public Definition(List<Whitelist> whitelists) {
815786
continue;
816787
}
817788

818-
entry.setValue(entry.getValue().freeze());
789+
entry.setValue(entry.getValue().freeze(computeFunctionalInterfaceMethod(entry.getValue())));
819790
}
820791

821792
voidType = getType("void");
@@ -1272,51 +1243,45 @@ private void copyStruct(String struct, List<String> children) {
12721243
* Precomputes a more efficient structure for dynamic method/field access.
12731244
*/
12741245
private void addRuntimeClass(final Struct struct) {
1275-
final Map<MethodKey, Method> methods = struct.methods;
1276-
final Map<String, MethodHandle> getters = new HashMap<>();
1277-
final Map<String, MethodHandle> setters = new HashMap<>();
1278-
1279-
// add all members
1280-
for (final Map.Entry<String, Field> member : struct.members.entrySet()) {
1281-
getters.put(member.getKey(), member.getValue().getter);
1282-
setters.put(member.getKey(), member.getValue().setter);
1283-
}
1284-
12851246
// add all getters/setters
1286-
for (final Map.Entry<MethodKey, Method> method : methods.entrySet()) {
1287-
final String name = method.getKey().name;
1288-
final Method m = method.getValue();
1247+
for (Map.Entry<MethodKey, Method> method : struct.methods.entrySet()) {
1248+
String name = method.getKey().name;
1249+
Method m = method.getValue();
12891250

12901251
if (m.arguments.size() == 0 &&
12911252
name.startsWith("get") &&
12921253
name.length() > 3 &&
12931254
Character.isUpperCase(name.charAt(3))) {
1294-
final StringBuilder newName = new StringBuilder();
1255+
StringBuilder newName = new StringBuilder();
12951256
newName.append(Character.toLowerCase(name.charAt(3)));
12961257
newName.append(name.substring(4));
1297-
getters.putIfAbsent(newName.toString(), m.handle);
1258+
struct.getters.putIfAbsent(newName.toString(), m.handle);
12981259
} else if (m.arguments.size() == 0 &&
12991260
name.startsWith("is") &&
13001261
name.length() > 2 &&
13011262
Character.isUpperCase(name.charAt(2))) {
1302-
final StringBuilder newName = new StringBuilder();
1263+
StringBuilder newName = new StringBuilder();
13031264
newName.append(Character.toLowerCase(name.charAt(2)));
13041265
newName.append(name.substring(3));
1305-
getters.putIfAbsent(newName.toString(), m.handle);
1266+
struct.getters.putIfAbsent(newName.toString(), m.handle);
13061267
}
13071268

13081269
if (m.arguments.size() == 1 &&
13091270
name.startsWith("set") &&
13101271
name.length() > 3 &&
13111272
Character.isUpperCase(name.charAt(3))) {
1312-
final StringBuilder newName = new StringBuilder();
1273+
StringBuilder newName = new StringBuilder();
13131274
newName.append(Character.toLowerCase(name.charAt(3)));
13141275
newName.append(name.substring(4));
1315-
setters.putIfAbsent(newName.toString(), m.handle);
1276+
struct.setters.putIfAbsent(newName.toString(), m.handle);
13161277
}
13171278
}
13181279

1319-
runtimeMap.put(struct.clazz, new RuntimeClass(struct, methods, getters, setters));
1280+
// add all members
1281+
for (Map.Entry<String, Field> member : struct.members.entrySet()) {
1282+
struct.getters.put(member.getKey(), member.getValue().getter);
1283+
struct.setters.put(member.getKey(), member.getValue().setter);
1284+
}
13201285
}
13211286

13221287
/** computes the functional interface method for a class, or returns null */

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public class FunctionRef {
7575
* @param numCaptures number of captured arguments
7676
*/
7777
public FunctionRef(Definition definition, Class<?> expected, String type, String call, int numCaptures) {
78-
this(expected, definition.ClassToType(expected).struct.getFunctionalMethod(),
78+
this(expected, definition.ClassToType(expected).struct.functionalMethod,
7979
lookup(definition, expected, type, call, numCaptures > 0), numCaptures);
8080
}
8181

@@ -155,7 +155,7 @@ private static Definition.Method lookup(Definition definition, Class<?> expected
155155
String type, String call, boolean receiverCaptured) {
156156
// check its really a functional interface
157157
// for e.g. Comparable
158-
Method method = definition.ClassToType(expected).struct.getFunctionalMethod();
158+
Method method = definition.ClassToType(expected).struct.functionalMethod;
159159
if (method == null) {
160160
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
161161
"to [" + Definition.ClassToName(expected) + "], not a functional interface");

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ public Map<String, List<String>> getHeaders(Definition definition) {
5454
if (objectToExplain != null) {
5555
toString = objectToExplain.toString();
5656
javaClassName = objectToExplain.getClass().getName();
57-
Definition.RuntimeClass runtimeClass = definition.getRuntimeClass(objectToExplain.getClass());
58-
if (runtimeClass != null) {
59-
painlessClassName = runtimeClass.getStruct().name;
57+
Definition.Struct struct = definition.ClassToType(objectToExplain.getClass()).struct;
58+
if (struct != null) {
59+
painlessClassName = struct.name;
6060
}
6161
}
6262

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,10 @@ private static Class<?> definitionTypeForClass(Definition definition, Class<?> t
193193
if (componentType == Object.class) {
194194
struct = definition.getType("def").struct;
195195
} else {
196-
Definition.RuntimeClass runtimeClass = definition.getRuntimeClass(componentType);
197-
if (runtimeClass == null) {
196+
if (definition.RuntimeClassToStruct(componentType) == null) {
198197
throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType));
199198
}
200-
struct = runtimeClass.getStruct();
199+
struct = definition.RuntimeClassToStruct(componentType);
201200
}
202201
return Definition.TypeToClass(definition.getType(struct, dimensions));
203202
}

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-
Method interfaceMethod = locals.getDefinition().ClassToType(expected).struct.getFunctionalMethod();
69+
Method interfaceMethod = locals.getDefinition().ClassToType(expected).struct.functionalMethod;
7070
if (interfaceMethod == null) {
7171
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
7272
"to [" + Definition.ClassToName(expected) + "], not a functional interface");

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
@@ -120,7 +120,7 @@ void analyze(Locals locals) {
120120
}
121121
} else {
122122
// we know the method statically, infer return type and any unknown/def types
123-
interfaceMethod = locals.getDefinition().ClassToType(expected).struct.getFunctionalMethod();
123+
interfaceMethod = locals.getDefinition().ClassToType(expected).struct.functionalMethod;
124124
if (interfaceMethod == null) {
125125
throw createError(new IllegalArgumentException("Cannot pass lambda to [" + Definition.ClassToName(expected) +
126126
"], not a functional interface"));

0 commit comments

Comments
 (0)