From 90c74a7e09f596102af23898a21c0d36eed05be9 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Fri, 2 Feb 2018 10:26:02 -0800 Subject: [PATCH] Remove RuntimeClass from Painless Definition in favor of just Painless Struct. (#28486) --- .../java/org/elasticsearch/painless/Def.java | 18 +-- .../elasticsearch/painless/Definition.java | 113 ++++++------------ .../elasticsearch/painless/FunctionRef.java | 4 +- .../painless/PainlessExplainError.java | 6 +- .../painless/ScriptClassInfo.java | 5 +- .../painless/node/EFunctionRef.java | 2 +- .../elasticsearch/painless/node/ELambda.java | 2 +- .../painless/node/NodeToStringTests.java | 3 +- 8 files changed, 58 insertions(+), 95 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index deba8b39de9..661af1b6c91 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java @@ -20,7 +20,7 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.RuntimeClass; +import org.elasticsearch.painless.Definition.Struct; import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; @@ -185,7 +185,7 @@ public final class Def { Definition.MethodKey key = new Definition.MethodKey(name, arity); // check whitelist for matching method for (Class clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) { - RuntimeClass struct = definition.getRuntimeClass(clazz); + Struct struct = definition.RuntimeClassToStruct(clazz); if (struct != null) { Method method = struct.methods.get(key); @@ -195,7 +195,7 @@ public final class Def { } for (Class iface : clazz.getInterfaces()) { - struct = definition.getRuntimeClass(iface); + struct = definition.RuntimeClassToStruct(iface); if (struct != null) { Method method = struct.methods.get(key); @@ -325,7 +325,7 @@ public final class Def { static MethodHandle lookupReference(Definition definition, Lookup lookup, String interfaceClass, Class receiverClass, String name) throws Throwable { Definition.Type interfaceType = definition.getType(interfaceClass); - Method interfaceMethod = interfaceType.struct.getFunctionalMethod(); + Method interfaceMethod = interfaceType.struct.functionalMethod; if (interfaceMethod == null) { throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface"); } @@ -342,7 +342,7 @@ public final class Def { final FunctionRef ref; if ("this".equals(type)) { // user written method - Method interfaceMethod = clazz.struct.getFunctionalMethod(); + Method interfaceMethod = clazz.struct.functionalMethod; if (interfaceMethod == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + "to [" + clazz.name + "], not a functional interface"); @@ -415,7 +415,7 @@ public final class Def { static MethodHandle lookupGetter(Definition definition, Class receiverClass, String name) { // first try whitelist for (Class clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) { - RuntimeClass struct = definition.getRuntimeClass(clazz); + Struct struct = definition.RuntimeClassToStruct(clazz); if (struct != null) { MethodHandle handle = struct.getters.get(name); @@ -425,7 +425,7 @@ public final class Def { } for (final Class iface : clazz.getInterfaces()) { - struct = definition.getRuntimeClass(iface); + struct = definition.RuntimeClassToStruct(iface); if (struct != null) { MethodHandle handle = struct.getters.get(name); @@ -486,7 +486,7 @@ public final class Def { static MethodHandle lookupSetter(Definition definition, Class receiverClass, String name) { // first try whitelist for (Class clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) { - RuntimeClass struct = definition.getRuntimeClass(clazz); + Struct struct = definition.RuntimeClassToStruct(clazz); if (struct != null) { MethodHandle handle = struct.setters.get(name); @@ -496,7 +496,7 @@ public final class Def { } for (final Class iface : clazz.getInterfaces()) { - struct = definition.getRuntimeClass(iface); + struct = definition.RuntimeClassToStruct(iface); if (struct != null) { MethodHandle handle = struct.setters.get(name); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java index c01f5d74e9b..6e37e5be0bb 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java @@ -307,9 +307,12 @@ public final class Definition { public final Map staticMembers; public final Map members; - private final SetOnce functionalMethod; + public final Map getters; + public final Map setters; - private Struct(final String name, final Class clazz, final org.objectweb.asm.Type type) { + public final Method functionalMethod; + + private Struct(String name, Class clazz, org.objectweb.asm.Type type) { this.name = name; this.clazz = clazz; this.type = type; @@ -321,10 +324,13 @@ public final class Definition { staticMembers = new HashMap<>(); members = new HashMap<>(); - functionalMethod = new SetOnce<>(); + getters = new HashMap<>(); + setters = new HashMap<>(); + + functionalMethod = null; } - private Struct(final Struct struct) { + private Struct(Struct struct, Method functionalMethod) { name = struct.name; clazz = struct.clazz; type = struct.type; @@ -336,11 +342,14 @@ public final class Definition { staticMembers = Collections.unmodifiableMap(struct.staticMembers); members = Collections.unmodifiableMap(struct.members); - functionalMethod = struct.functionalMethod; + getters = Collections.unmodifiableMap(struct.getters); + setters = Collections.unmodifiableMap(struct.setters); + + this.functionalMethod = functionalMethod; } - private Struct freeze() { - return new Struct(this); + private Struct freeze(Method functionalMethod) { + return new Struct(this, functionalMethod); } @Override @@ -362,14 +371,6 @@ public final class Definition { public int hashCode() { return name.hashCode(); } - - /** - * If this class is a functional interface according to JLS, returns its method. - * Otherwise returns null. - */ - public Method getFunctionalMethod() { - return functionalMethod.get(); - } } public static class Cast { @@ -418,25 +419,6 @@ public final class Definition { } } - public static final class RuntimeClass { - private final Struct struct; - public final Map methods; - public final Map getters; - public final Map setters; - - private RuntimeClass(final Struct struct, final Map methods, - final Map getters, final Map setters) { - this.struct = struct; - this.methods = Collections.unmodifiableMap(methods); - this.getters = Collections.unmodifiableMap(getters); - this.setters = Collections.unmodifiableMap(setters); - } - - public Struct getStruct() { - return struct; - } - } - /** Returns whether or not a non-array type exists. */ public boolean isSimpleType(final String name) { return structsMap.containsKey(name); @@ -569,7 +551,9 @@ public final class Definition { } public static String ClassToName(Class clazz) { - if (clazz.isArray()) { + if (clazz.isLocalClass() || clazz.isAnonymousClass()) { + return null; + } else if (clazz.isArray()) { Class component = clazz.getComponentType(); int dimensions = 1; @@ -609,7 +593,7 @@ public final class Definition { if (component == def.class) { return getType(structsMap.get(def.class.getSimpleName()), dimensions); } else { - return getType(runtimeMap.get(component).struct, dimensions); + return getType(structsMap.get(ClassToName(component)), dimensions); } } else if (clazz == def.class) { return getType(structsMap.get(def.class.getSimpleName()), 0); @@ -618,6 +602,10 @@ public final class Definition { return getType(structsMap.get(ClassToName(clazz)), 0); } + public Struct RuntimeClassToStruct(Class clazz) { + return structsMap.get(ClassToName(clazz)); + } + public static Class TypeToClass(Type type) { if (def.class.getSimpleName().equals(type.struct.name)) { return ObjectClassTodefClass(type.clazz); @@ -626,10 +614,6 @@ public final class Definition { return type.clazz; } - public RuntimeClass getRuntimeClass(Class clazz) { - return runtimeMap.get(clazz); - } - public Class getClassFromBinaryName(String name) { Struct struct = structsMap.get(name.replace('$', '.')); @@ -659,14 +643,12 @@ public final class Definition { // INTERNAL IMPLEMENTATION: - private final Map, RuntimeClass> runtimeMap; private final Map structsMap; private final Map simpleTypesMap; public Definition(List whitelists) { structsMap = new HashMap<>(); simpleTypesMap = new HashMap<>(); - runtimeMap = new HashMap<>(); Map, Struct> javaClassesToPainlessStructs = new HashMap<>(); String origin = null; @@ -787,17 +769,6 @@ public final class Definition { } } - // mark functional interfaces (or set null, to mark class is not) - for (String painlessStructName : structsMap.keySet()) { - Struct painlessStruct = structsMap.get(painlessStructName); - - if (painlessStruct.name.equals(painlessStructName) == false) { - continue; - } - - painlessStruct.functionalMethod.set(computeFunctionalInterfaceMethod(painlessStruct)); - } - // precompute runtime classes for (String painlessStructName : structsMap.keySet()) { Struct painlessStruct = structsMap.get(painlessStructName); @@ -815,7 +786,7 @@ public final class Definition { continue; } - entry.setValue(entry.getValue().freeze()); + entry.setValue(entry.getValue().freeze(computeFunctionalInterfaceMethod(entry.getValue()))); } voidType = getType("void"); @@ -1272,51 +1243,45 @@ public final class Definition { * Precomputes a more efficient structure for dynamic method/field access. */ private void addRuntimeClass(final Struct struct) { - final Map methods = struct.methods; - final Map getters = new HashMap<>(); - final Map setters = new HashMap<>(); - - // add all members - for (final Map.Entry member : struct.members.entrySet()) { - getters.put(member.getKey(), member.getValue().getter); - setters.put(member.getKey(), member.getValue().setter); - } - // add all getters/setters - for (final Map.Entry method : methods.entrySet()) { - final String name = method.getKey().name; - final Method m = method.getValue(); + for (Map.Entry method : struct.methods.entrySet()) { + String name = method.getKey().name; + Method m = method.getValue(); if (m.arguments.size() == 0 && name.startsWith("get") && name.length() > 3 && Character.isUpperCase(name.charAt(3))) { - final StringBuilder newName = new StringBuilder(); + StringBuilder newName = new StringBuilder(); newName.append(Character.toLowerCase(name.charAt(3))); newName.append(name.substring(4)); - getters.putIfAbsent(newName.toString(), m.handle); + struct.getters.putIfAbsent(newName.toString(), m.handle); } else if (m.arguments.size() == 0 && name.startsWith("is") && name.length() > 2 && Character.isUpperCase(name.charAt(2))) { - final StringBuilder newName = new StringBuilder(); + StringBuilder newName = new StringBuilder(); newName.append(Character.toLowerCase(name.charAt(2))); newName.append(name.substring(3)); - getters.putIfAbsent(newName.toString(), m.handle); + struct.getters.putIfAbsent(newName.toString(), m.handle); } if (m.arguments.size() == 1 && name.startsWith("set") && name.length() > 3 && Character.isUpperCase(name.charAt(3))) { - final StringBuilder newName = new StringBuilder(); + StringBuilder newName = new StringBuilder(); newName.append(Character.toLowerCase(name.charAt(3))); newName.append(name.substring(4)); - setters.putIfAbsent(newName.toString(), m.handle); + struct.setters.putIfAbsent(newName.toString(), m.handle); } } - runtimeMap.put(struct.clazz, new RuntimeClass(struct, methods, getters, setters)); + // add all members + for (Map.Entry member : struct.members.entrySet()) { + struct.getters.put(member.getKey(), member.getValue().getter); + struct.setters.put(member.getKey(), member.getValue().setter); + } } /** computes the functional interface method for a class, or returns null */ diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java index 9e381b33bac..66cf78e8572 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java @@ -75,7 +75,7 @@ public class FunctionRef { * @param numCaptures number of captured arguments */ public FunctionRef(Definition definition, Class expected, String type, String call, int numCaptures) { - this(expected, definition.ClassToType(expected).struct.getFunctionalMethod(), + this(expected, definition.ClassToType(expected).struct.functionalMethod, lookup(definition, expected, type, call, numCaptures > 0), numCaptures); } @@ -155,7 +155,7 @@ public class FunctionRef { String type, String call, boolean receiverCaptured) { // check its really a functional interface // for e.g. Comparable - Method method = definition.ClassToType(expected).struct.getFunctionalMethod(); + Method method = definition.ClassToType(expected).struct.functionalMethod; if (method == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + "to [" + Definition.ClassToName(expected) + "], not a functional interface"); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java index 291d852bdde..0b2fdf35890 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java @@ -54,9 +54,9 @@ public class PainlessExplainError extends Error { if (objectToExplain != null) { toString = objectToExplain.toString(); javaClassName = objectToExplain.getClass().getName(); - Definition.RuntimeClass runtimeClass = definition.getRuntimeClass(objectToExplain.getClass()); - if (runtimeClass != null) { - painlessClassName = runtimeClass.getStruct().name; + Definition.Struct struct = definition.ClassToType(objectToExplain.getClass()).struct; + if (struct != null) { + painlessClassName = struct.name; } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java index ee36ac57303..d5b5b4646bd 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java @@ -193,11 +193,10 @@ public class ScriptClassInfo { if (componentType == Object.class) { struct = definition.getType("def").struct; } else { - Definition.RuntimeClass runtimeClass = definition.getRuntimeClass(componentType); - if (runtimeClass == null) { + if (definition.RuntimeClassToStruct(componentType) == null) { throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType)); } - struct = runtimeClass.getStruct(); + struct = definition.RuntimeClassToStruct(componentType); } return Definition.TypeToClass(definition.getType(struct, dimensions)); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java index a432457abb8..636623004c9 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java @@ -66,7 +66,7 @@ public final class EFunctionRef extends AExpression implements ILambda { try { if ("this".equals(type)) { // user's own function - Method interfaceMethod = locals.getDefinition().ClassToType(expected).struct.getFunctionalMethod(); + Method interfaceMethod = locals.getDefinition().ClassToType(expected).struct.functionalMethod; if (interfaceMethod == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + "to [" + Definition.ClassToName(expected) + "], not a functional interface"); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java index 79a914f4284..c37ff435f56 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java @@ -120,7 +120,7 @@ public final class ELambda extends AExpression implements ILambda { } } else { // we know the method statically, infer return type and any unknown/def types - interfaceMethod = locals.getDefinition().ClassToType(expected).struct.getFunctionalMethod(); + interfaceMethod = locals.getDefinition().ClassToType(expected).struct.functionalMethod; if (interfaceMethod == null) { throw createError(new IllegalArgumentException("Cannot pass lambda to [" + Definition.ClassToName(expected) + "], not a functional interface")); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java index 00be123e199..6dbe480d4b5 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java @@ -25,7 +25,6 @@ import org.elasticsearch.painless.Definition.Cast; import org.elasticsearch.painless.Definition.Field; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Definition.MethodKey; -import org.elasticsearch.painless.Definition.RuntimeClass; import org.elasticsearch.painless.Definition.Struct; import org.elasticsearch.painless.FeatureTest; import org.elasticsearch.painless.GenericElasticsearchScript; @@ -404,7 +403,7 @@ public class NodeToStringTests extends ESTestCase { public void testPSubCallInvoke() { Location l = new Location(getTestName(), 0); - RuntimeClass c = definition.getRuntimeClass(Integer.class); + Struct c = definition.ClassToType(Integer.class).struct; Method m = c.methods.get(new MethodKey("toString", 0)); PSubCallInvoke node = new PSubCallInvoke(l, m, null, emptyList()); node.prefix = new EVariable(l, "a");