فهرست منبع

Remove RuntimeClass from Painless Definition in favor of just Painless Struct. (#28486)

Jack Conradson 7 سال پیش
والد
کامیت
90c74a7e09

+ 9 - 9
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);

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

@@ -307,9 +307,12 @@ public final class Definition {
         public final Map<String, Field> staticMembers;
         public final Map<String, Field> members;
 
-        private final SetOnce<Method> functionalMethod;
+        public final Map<String, MethodHandle> getters;
+        public final Map<String, MethodHandle> setters;
+
+        public final Method functionalMethod;
 
-        private Struct(final String name, final Class<?> clazz, final org.objectweb.asm.Type type) {
+        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<MethodKey, Method> methods;
-        public final Map<String, MethodHandle> getters;
-        public final Map<String, MethodHandle> setters;
-
-        private RuntimeClass(final Struct struct, final Map<MethodKey, Method> methods,
-                             final Map<String, MethodHandle> getters, final Map<String, MethodHandle> 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<Class<?>, RuntimeClass> runtimeMap;
     private final Map<String, Struct> structsMap;
     private final Map<String, Type> simpleTypesMap;
 
     public Definition(List<Whitelist> whitelists) {
         structsMap = new HashMap<>();
         simpleTypesMap = new HashMap<>();
-        runtimeMap = new HashMap<>();
 
         Map<Class<?>, 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<MethodKey, Method> methods = struct.methods;
-        final Map<String, MethodHandle> getters = new HashMap<>();
-        final Map<String, MethodHandle> setters = new HashMap<>();
-
-        // add all members
-        for (final Map.Entry<String, Field> 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<MethodKey, Method> method : methods.entrySet()) {
-            final String name = method.getKey().name;
-            final Method m = method.getValue();
+        for (Map.Entry<MethodKey, Method> 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<String, Field> 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 */

+ 2 - 2
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");

+ 3 - 3
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;
             }
         }
 

+ 2 - 3
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));
     }

+ 1 - 1
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");

+ 1 - 1
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"));

+ 1 - 2
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");