1
0
Эх сурвалжийг харах

Painless: Clean up PainlessMethod (#32476)

Renames and removes variables from PainlessMethod to follow the new naming 
convention. Generates methodtypes at compile-time instead of using a method at run-
time. Moves write method to MethodWriter.
Jack Conradson 7 жил өмнө
parent
commit
09e38f2f59
20 өөрчлөгдсөн 280 нэмэгдсэн , 259 устгасан
  1. 8 7
      modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java
  2. 76 53
      modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java
  3. 33 9
      modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java
  4. 24 0
      modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java
  5. 15 20
      modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupBuilder.java
  6. 14 53
      modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethod.java
  7. 6 5
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java
  8. 4 4
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java
  9. 7 7
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java
  10. 13 10
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java
  11. 1 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java
  12. 1 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java
  13. 4 4
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java
  14. 11 14
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubListShortcut.java
  15. 14 18
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubMapShortcut.java
  16. 12 12
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java
  17. 14 18
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java
  18. 6 6
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java
  19. 1 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java
  20. 16 16
      modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java

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

@@ -238,7 +238,7 @@ public final class Def {
          int numArguments = callSiteType.parameterCount();
          // simple case: no lambdas
          if (recipeString.isEmpty()) {
-             return lookupMethodInternal(painlessLookup, receiverClass, name, numArguments - 1).handle;
+             return lookupMethodInternal(painlessLookup, receiverClass, name, numArguments - 1).methodHandle;
          }
 
          // convert recipe string to a bitset for convenience (the code below should be refactored...)
@@ -262,7 +262,7 @@ public final class Def {
          // lookup the method with the proper arity, then we know everything (e.g. interface types of parameters).
          // based on these we can finally link any remaining lambdas that were deferred.
          PainlessMethod method = lookupMethodInternal(painlessLookup, receiverClass, name, arity);
-         MethodHandle handle = method.handle;
+         MethodHandle handle = method.methodHandle;
 
          int replaced = 0;
          upTo = 1;
@@ -281,7 +281,7 @@ public final class Def {
                      captures[capture] = callSiteType.parameterType(i + 1 + capture);
                  }
                  MethodHandle filter;
-                 Class<?> interfaceType = method.arguments.get(i - 1 - replaced);
+                 Class<?> interfaceType = method.typeParameters.get(i - 1 - replaced);
                  if (signature.charAt(0) == 'S') {
                      // the implementation is strongly typed, now that we know the interface type,
                      // we have everything.
@@ -331,10 +331,11 @@ public final class Def {
          if (interfaceMethod == null) {
              throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
          }
-         int arity = interfaceMethod.arguments.size();
+         int arity = interfaceMethod.typeParameters.size();
          PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity);
         return lookupReferenceInternal(painlessLookup, methodHandlesLookup, interfaceType,
-                PainlessLookupUtility.typeToCanonicalTypeName(implMethod.target), implMethod.name, receiverClass);
+                PainlessLookupUtility.typeToCanonicalTypeName(implMethod.targetClass),
+                implMethod.javaMethod.getName(), receiverClass);
      }
 
      /** Returns a method handle to an implementation of clazz, given method reference signature. */
@@ -349,7 +350,7 @@ public final class Def {
                  throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
                          "to [" + PainlessLookupUtility.typeToCanonicalTypeName(clazz) + "], not a functional interface");
              }
-             int arity = interfaceMethod.arguments.size() + captures.length;
+             int arity = interfaceMethod.typeParameters.size() + captures.length;
              final MethodHandle handle;
              try {
                  MethodHandle accessor = methodHandlesLookup.findStaticGetter(methodHandlesLookup.lookupClass(),
@@ -360,7 +361,7 @@ public final class Def {
                  // is it a synthetic method? If we generated the method ourselves, be more helpful. It can only fail
                  // because the arity does not match the expected interface type.
                  if (call.contains("$")) {
-                     throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name +
+                     throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.javaMethod.getName() +
                                                         "] in [" + clazz + "]");
                  }
                  throw new IllegalArgumentException("Unknown call [" + call + "] with [" + arity + "] arguments.");

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

@@ -19,6 +19,7 @@
 
 package org.elasticsearch.painless;
 
+import org.elasticsearch.painless.Locals.LocalMethod;
 import org.elasticsearch.painless.lookup.PainlessClass;
 import org.elasticsearch.painless.lookup.PainlessConstructor;
 import org.elasticsearch.painless.lookup.PainlessLookup;
@@ -108,24 +109,24 @@ public class FunctionRef {
         Constructor<?> javaConstructor = delegateConstructor.javaConstructor;
         MethodType delegateMethodType = delegateConstructor.methodType;
 
-        interfaceMethodName = interfaceMethod.name;
-        factoryMethodType = MethodType.methodType(expected,
+        this.interfaceMethodName = interfaceMethod.javaMethod.getName();
+        this.factoryMethodType = MethodType.methodType(expected,
                 delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount()));
-        interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
+        this.interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
 
-        delegateClassName = javaConstructor.getDeclaringClass().getName();
-        isDelegateInterface = false;
-        delegateInvokeType = H_NEWINVOKESPECIAL;
-        delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME;
+        this.delegateClassName = javaConstructor.getDeclaringClass().getName();
+        this.isDelegateInterface = false;
+        this.delegateInvokeType = H_NEWINVOKESPECIAL;
+        this.delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME;
         this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures);
 
         this.interfaceMethod = interfaceMethod;
-        delegateTypeParameters = delegateConstructor.typeParameters;
-        delegateReturnType = void.class;
+        this.delegateTypeParameters = delegateConstructor.typeParameters;
+        this.delegateReturnType = void.class;
 
-        factoryDescriptor = factoryMethodType.toMethodDescriptorString();
-        interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString());
-        delegateType = Type.getMethodType(this.delegateMethodType.toMethodDescriptorString());
+        this.factoryDescriptor = factoryMethodType.toMethodDescriptorString();
+        this.interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString());
+        this.delegateType = Type.getMethodType(this.delegateMethodType.toMethodDescriptorString());
     }
 
     /**
@@ -138,41 +139,63 @@ public class FunctionRef {
     public FunctionRef(Class<?> expected, PainlessMethod interfaceMethod, PainlessMethod delegateMethod, int numCaptures) {
         MethodType delegateMethodType = delegateMethod.methodType;
 
-        interfaceMethodName = interfaceMethod.name;
-        factoryMethodType = MethodType.methodType(expected,
+        this.interfaceMethodName = interfaceMethod.javaMethod.getName();
+        this.factoryMethodType = MethodType.methodType(expected,
                 delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount()));
-        interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
-
-        // the Painless$Script class can be inferred if owner is null
-        if (delegateMethod.target == null) {
-            delegateClassName = CLASS_NAME;
-            isDelegateInterface = false;
-        } else if (delegateMethod.augmentation != null) {
-            delegateClassName = delegateMethod.augmentation.getName();
-            isDelegateInterface = delegateMethod.augmentation.isInterface();
-        } else {
-            delegateClassName = delegateMethod.target.getName();
-            isDelegateInterface = delegateMethod.target.isInterface();
-        }
+        this.interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
+
+        this.delegateClassName = delegateMethod.javaMethod.getDeclaringClass().getName();
+        this.isDelegateInterface = delegateMethod.javaMethod.getDeclaringClass().isInterface();
 
-        if (Modifier.isStatic(delegateMethod.modifiers)) {
-            delegateInvokeType = H_INVOKESTATIC;
-        } else if (delegateMethod.target.isInterface()) {
-            delegateInvokeType = H_INVOKEINTERFACE;
+        if (Modifier.isStatic(delegateMethod.javaMethod.getModifiers())) {
+            this.delegateInvokeType = H_INVOKESTATIC;
+        } else if (delegateMethod.javaMethod.getDeclaringClass().isInterface()) {
+            this.delegateInvokeType = H_INVOKEINTERFACE;
         } else {
-            delegateInvokeType = H_INVOKEVIRTUAL;
+            this.delegateInvokeType = H_INVOKEVIRTUAL;
         }
 
-        delegateMethodName = delegateMethod.name;
+        this.delegateMethodName = delegateMethod.javaMethod.getName();
+        this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures);
+
+        this.interfaceMethod = interfaceMethod;
+        this.delegateTypeParameters = delegateMethod.typeParameters;
+        this.delegateReturnType = delegateMethod.returnType;
+
+        this.factoryDescriptor = factoryMethodType.toMethodDescriptorString();
+        this.interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString());
+        this.delegateType = Type.getMethodType(this.delegateMethodType.toMethodDescriptorString());
+    }
+
+    /**
+     * Creates a new FunctionRef (already resolved)
+     * @param expected functional interface type to implement
+     * @param interfaceMethod functional interface method
+     * @param delegateMethod implementation method
+     * @param numCaptures number of captured arguments
+     */
+    public FunctionRef(Class<?> expected, PainlessMethod interfaceMethod, LocalMethod delegateMethod, int numCaptures) {
+        MethodType delegateMethodType = delegateMethod.methodType;
+
+        this.interfaceMethodName = interfaceMethod.javaMethod.getName();
+        this.factoryMethodType = MethodType.methodType(expected,
+                delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount()));
+        this.interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
+
+        this.delegateClassName = CLASS_NAME;
+        this.isDelegateInterface = false;
+        this.delegateInvokeType = H_INVOKESTATIC;
+
+        this.delegateMethodName = delegateMethod.name;
         this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures);
 
         this.interfaceMethod = interfaceMethod;
-        delegateTypeParameters = delegateMethod.arguments;
-        delegateReturnType = delegateMethod.rtn;
+        this.delegateTypeParameters = delegateMethod.typeParameters;
+        this.delegateReturnType = delegateMethod.returnType;
 
-        factoryDescriptor = factoryMethodType.toMethodDescriptorString();
-        interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString());
-        delegateType = Type.getMethodType(this.delegateMethodType.toMethodDescriptorString());
+        this.factoryDescriptor = factoryMethodType.toMethodDescriptorString();
+        this.interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString());
+        this.delegateType = Type.getMethodType(this.delegateMethodType.toMethodDescriptorString());
     }
 
     /**
@@ -181,24 +204,24 @@ public class FunctionRef {
      */
     public FunctionRef(Class<?> expected,
                        PainlessMethod interfaceMethod, String delegateMethodName, MethodType delegateMethodType, int numCaptures) {
-        interfaceMethodName = interfaceMethod.name;
-        factoryMethodType = MethodType.methodType(expected,
+        this.interfaceMethodName = interfaceMethod.javaMethod.getName();
+        this.factoryMethodType = MethodType.methodType(expected,
             delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount()));
-        interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
+        this.interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1);
 
-        delegateClassName = CLASS_NAME;
-        delegateInvokeType = H_INVOKESTATIC;
+        this.delegateClassName = CLASS_NAME;
+        this.delegateInvokeType = H_INVOKESTATIC;
         this.delegateMethodName = delegateMethodName;
         this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures);
-        isDelegateInterface = false;
+        this.isDelegateInterface = false;
 
         this.interfaceMethod = null;
-        delegateTypeParameters = null;
-        delegateReturnType = null;
+        this.delegateTypeParameters = null;
+        this.delegateReturnType = null;
 
-        factoryDescriptor = null;
-        interfaceType = null;
-        delegateType = null;
+        this.factoryDescriptor = null;
+        this.interfaceType = null;
+        this.delegateType = null;
     }
 
     /**
@@ -215,7 +238,7 @@ public class FunctionRef {
 
         // lookup requested constructor
         PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.getJavaClassFromPainlessType(type));
-        PainlessConstructor impl = struct.constructors.get(PainlessLookupUtility.buildPainlessConstructorKey(method.arguments.size()));
+        PainlessConstructor impl = struct.constructors.get(PainlessLookupUtility.buildPainlessConstructorKey(method.typeParameters.size()));
 
         if (impl == null) {
             throw new IllegalArgumentException("Unknown reference [" + type + "::new] matching [" + expected + "]");
@@ -242,16 +265,16 @@ public class FunctionRef {
         final PainlessMethod impl;
         // look for a static impl first
         PainlessMethod staticImpl =
-                struct.staticMethods.get(PainlessLookupUtility.buildPainlessMethodKey(call, method.arguments.size()));
+                struct.staticMethods.get(PainlessLookupUtility.buildPainlessMethodKey(call, method.typeParameters.size()));
         if (staticImpl == null) {
             // otherwise a virtual impl
             final int arity;
             if (receiverCaptured) {
                 // receiver captured
-                arity = method.arguments.size();
+                arity = method.typeParameters.size();
             } else {
                 // receiver passed
-                arity = method.arguments.size() - 1;
+                arity = method.typeParameters.size() - 1;
             }
             impl = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey(call, arity));
         } else {

+ 33 - 9
modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java

@@ -22,8 +22,8 @@ package org.elasticsearch.painless;
 import org.elasticsearch.painless.ScriptClassInfo.MethodArgument;
 import org.elasticsearch.painless.lookup.PainlessLookup;
 import org.elasticsearch.painless.lookup.PainlessLookupUtility;
-import org.elasticsearch.painless.lookup.PainlessMethod;
 
+import java.lang.invoke.MethodType;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -38,6 +38,30 @@ import java.util.Set;
  */
 public final class Locals {
 
+    /**
+     * Constructs a local method key used to lookup local methods from a painless class.
+     */
+    public static String buildLocalMethodKey(String methodName, int methodArity) {
+        return methodName + "/" + methodArity;
+    }
+
+    /**
+     * Stores information about methods directly callable on the generated script class.
+     */
+    public static class LocalMethod {
+        public final String name;
+        public final Class<?> returnType;
+        public final List<Class<?>> typeParameters;
+        public final MethodType methodType;
+
+        public LocalMethod(String name, Class<?> returnType, List<Class<?>> typeParameters, MethodType methodType) {
+            this.name = name;
+            this.returnType = returnType;
+            this.typeParameters = typeParameters;
+            this.methodType = methodType;
+        }
+    }
+
     /** Reserved word: loop counter */
     public static final String LOOP   = "#loop";
     /** Reserved word: unused */
@@ -110,9 +134,9 @@ public final class Locals {
     }
 
     /** Creates a new program scope: the list of methods. It is the parent for all methods */
-    public static Locals newProgramScope(PainlessLookup painlessLookup, Collection<PainlessMethod> methods) {
+    public static Locals newProgramScope(PainlessLookup painlessLookup, Collection<LocalMethod> methods) {
         Locals locals = new Locals(null, painlessLookup, null, null);
-        for (PainlessMethod method : methods) {
+        for (LocalMethod method : methods) {
             locals.addMethod(method);
         }
         return locals;
@@ -143,8 +167,8 @@ public final class Locals {
     }
 
     /** Looks up a method. Returns null if the method does not exist. */
-    public PainlessMethod getMethod(String key) {
-        PainlessMethod method = lookupMethod(key);
+    public LocalMethod getMethod(String key) {
+        LocalMethod method = lookupMethod(key);
         if (method != null) {
             return method;
         }
@@ -199,7 +223,7 @@ public final class Locals {
     // variable name -> variable
     private Map<String,Variable> variables;
     // method name+arity -> methods
-    private Map<String,PainlessMethod> methods;
+    private Map<String,LocalMethod> methods;
 
     /**
      * Create a new Locals
@@ -237,7 +261,7 @@ public final class Locals {
     }
 
     /** Looks up a method at this scope only. Returns null if the method does not exist. */
-    private PainlessMethod lookupMethod(String key) {
+    private LocalMethod lookupMethod(String key) {
         if (methods == null) {
             return null;
         }
@@ -256,11 +280,11 @@ public final class Locals {
         return variable;
     }
 
-    private void addMethod(PainlessMethod method) {
+    private void addMethod(LocalMethod method) {
         if (methods == null) {
             methods = new HashMap<>();
         }
-        methods.put(PainlessLookupUtility.buildPainlessMethodKey(method.name, method.arguments.size()), method);
+        methods.put(buildLocalMethodKey(method.name, method.typeParameters.size()), method);
         // TODO: check result
     }
 

+ 24 - 0
modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java

@@ -20,6 +20,7 @@
 package org.elasticsearch.painless;
 
 import org.elasticsearch.painless.lookup.PainlessCast;
+import org.elasticsearch.painless.lookup.PainlessMethod;
 import org.elasticsearch.painless.lookup.def;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.Label;
@@ -28,6 +29,7 @@ import org.objectweb.asm.Type;
 import org.objectweb.asm.commons.GeneratorAdapter;
 import org.objectweb.asm.commons.Method;
 
+import java.lang.reflect.Modifier;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -415,4 +417,26 @@ public final class MethodWriter extends GeneratorAdapter {
         System.arraycopy(params, 0, args, 2, params.length);
         invokeDynamic(name, methodType.getDescriptor(), DEF_BOOTSTRAP_HANDLE, args);
     }
+
+    public void invokeMethodCall(PainlessMethod painlessMethod) {
+        Type type = Type.getType(painlessMethod.javaMethod.getDeclaringClass());
+        Method method = Method.getMethod(painlessMethod.javaMethod);
+
+        if (Modifier.isStatic(painlessMethod.javaMethod.getModifiers())) {
+            // invokeStatic assumes that the owner class is not an interface, so this is a
+            // special case for interfaces where the interface method boolean needs to be set to
+            // true to reference the appropriate class constant when calling a static interface
+            // method since java 8 did not check, but java 9 and 10 do
+            if (painlessMethod.javaMethod.getDeclaringClass().isInterface()) {
+                visitMethodInsn(Opcodes.INVOKESTATIC, type.getInternalName(),
+                        painlessMethod.javaMethod.getName(), painlessMethod.methodType.toMethodDescriptorString(), true);
+            } else {
+                invokeStatic(type, method);
+            }
+        } else if (painlessMethod.javaMethod.getDeclaringClass().isInterface()) {
+            invokeInterface(type, method);
+        } else {
+            invokeVirtual(type, method);
+        }
+    }
 }

+ 15 - 20
modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupBuilder.java

@@ -540,7 +540,6 @@ public final class PainlessLookupBuilder {
             PainlessMethod painlessMethod = painlessClassBuilder.staticMethods.get(painlessMethodKey);
 
             if (painlessMethod == null) {
-                org.objectweb.asm.commons.Method asmMethod = org.objectweb.asm.commons.Method.getMethod(javaMethod);
                 MethodHandle methodHandle;
 
                 try {
@@ -554,19 +553,17 @@ public final class PainlessLookupBuilder {
 
                 painlessMethod = painlessMethodCache.computeIfAbsent(
                         new PainlessMethodCacheKey(targetClass, methodName, typeParameters),
-                        key -> new PainlessMethod(methodName, targetClass, null, returnType,
-                                typeParameters, asmMethod, javaMethod.getModifiers(), methodHandle, methodType));
+                        key -> new PainlessMethod(javaMethod, targetClass, returnType, typeParameters, methodHandle, methodType));
 
                 painlessClassBuilder.staticMethods.put(painlessMethodKey, painlessMethod);
-            } else if ((painlessMethod.name.equals(methodName) && painlessMethod.rtn == returnType &&
-                    painlessMethod.arguments.equals(typeParameters)) == false) {
+            } else if (painlessMethod.returnType == returnType && painlessMethod.typeParameters.equals(typeParameters) == false) {
                 throw new IllegalArgumentException("cannot have static methods " +
                         "[[" + targetCanonicalClassName + "], [" + methodName + "], " +
                         "[" + typeToCanonicalTypeName(returnType) + "], " +
                         typesToCanonicalTypeNames(typeParameters) + "] and " +
                         "[[" + targetCanonicalClassName + "], [" + methodName + "], " +
-                        "[" + typeToCanonicalTypeName(painlessMethod.rtn) + "], " +
-                        typesToCanonicalTypeNames(painlessMethod.arguments) + "] " +
+                        "[" + typeToCanonicalTypeName(painlessMethod.returnType) + "], " +
+                        typesToCanonicalTypeNames(painlessMethod.typeParameters) + "] " +
                         "with the same arity and different return type or type parameters");
             }
         } else {
@@ -597,19 +594,17 @@ public final class PainlessLookupBuilder {
 
                 painlessMethod = painlessMethodCache.computeIfAbsent(
                         new PainlessMethodCacheKey(targetClass, methodName, typeParameters),
-                        key -> new PainlessMethod(methodName, targetClass, augmentedClass, returnType,
-                                typeParameters, asmMethod, javaMethod.getModifiers(), methodHandle, methodType));
+                        key -> new PainlessMethod(javaMethod, targetClass, returnType, typeParameters, methodHandle, methodType));
 
                 painlessClassBuilder.methods.put(painlessMethodKey, painlessMethod);
-            } else if ((painlessMethod.name.equals(methodName) && painlessMethod.rtn == returnType &&
-                    painlessMethod.arguments.equals(typeParameters)) == false) {
+            } else if (painlessMethod.returnType == returnType && painlessMethod.typeParameters.equals(typeParameters) == false) {
                 throw new IllegalArgumentException("cannot have methods " +
                         "[[" + targetCanonicalClassName + "], [" + methodName + "], " +
                         "[" + typeToCanonicalTypeName(returnType) + "], " +
                         typesToCanonicalTypeNames(typeParameters) + "] and " +
                         "[[" + targetCanonicalClassName + "], [" + methodName + "], " +
-                        "[" + typeToCanonicalTypeName(painlessMethod.rtn) + "], " +
-                        typesToCanonicalTypeNames(painlessMethod.arguments) + "] " +
+                        "[" + typeToCanonicalTypeName(painlessMethod.returnType) + "], " +
+                        typesToCanonicalTypeNames(painlessMethod.typeParameters) + "] " +
                         "with the same arity and different return type or type parameters");
             }
         }
@@ -806,8 +801,8 @@ public final class PainlessLookupBuilder {
             PainlessMethod newPainlessMethod = painlessMethodEntry.getValue();
             PainlessMethod existingPainlessMethod = targetPainlessClassBuilder.methods.get(painlessMethodKey);
 
-            if (existingPainlessMethod == null || existingPainlessMethod.target != newPainlessMethod.target &&
-                    existingPainlessMethod.target.isAssignableFrom(newPainlessMethod.target)) {
+            if (existingPainlessMethod == null || existingPainlessMethod.targetClass != newPainlessMethod.targetClass &&
+                    existingPainlessMethod.targetClass.isAssignableFrom(newPainlessMethod.targetClass)) {
                 targetPainlessClassBuilder.methods.put(painlessMethodKey, newPainlessMethod);
             }
         }
@@ -832,21 +827,21 @@ public final class PainlessLookupBuilder {
 
     private void cacheRuntimeHandles(PainlessClassBuilder painlessClassBuilder) {
         for (PainlessMethod painlessMethod : painlessClassBuilder.methods.values()) {
-            String methodName = painlessMethod.name;
-            int typeParametersSize = painlessMethod.arguments.size();
+            String methodName = painlessMethod.javaMethod.getName();
+            int typeParametersSize = painlessMethod.typeParameters.size();
 
             if (typeParametersSize == 0 && methodName.startsWith("get") && methodName.length() > 3 &&
                     Character.isUpperCase(methodName.charAt(3))) {
                 painlessClassBuilder.getterMethodHandles.putIfAbsent(
-                        Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4), painlessMethod.handle);
+                        Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4), painlessMethod.methodHandle);
             } else if (typeParametersSize == 0 && methodName.startsWith("is") && methodName.length() > 2 &&
                     Character.isUpperCase(methodName.charAt(2))) {
                 painlessClassBuilder.getterMethodHandles.putIfAbsent(
-                        Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3), painlessMethod.handle);
+                        Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3), painlessMethod.methodHandle);
             } else if (typeParametersSize == 1 && methodName.startsWith("set") && methodName.length() > 3 &&
                     Character.isUpperCase(methodName.charAt(3))) {
                 painlessClassBuilder.setterMethodHandles.putIfAbsent(
-                        Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4), painlessMethod.handle);
+                        Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4), painlessMethod.methodHandle);
             }
         }
 

+ 14 - 53
modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethod.java

@@ -19,67 +19,28 @@
 
 package org.elasticsearch.painless.lookup;
 
-import org.elasticsearch.painless.MethodWriter;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodType;
-import java.lang.reflect.Modifier;
+import java.lang.reflect.Method;
 import java.util.Collections;
 import java.util.List;
 
 public class PainlessMethod {
-    public final String name;
-    public final Class<?> target;
-    public final Class<?> augmentation;
-    public final Class<?> rtn;
-    public final List<Class<?>> arguments;
-    public final org.objectweb.asm.commons.Method method;
-    public final int modifiers;
-    public final MethodHandle handle;
+    public final Method javaMethod;
+    public final Class<?> targetClass;
+    public final Class<?> returnType;
+    public final List<Class<?>> typeParameters;
+    public final MethodHandle methodHandle;
     public final MethodType methodType;
 
-    public PainlessMethod(String name, Class<?> target, Class<?> augmentation, Class<?> rtn, List<Class<?>> arguments,
-                          org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle, MethodType methodType) {
-        this.name = name;
-        this.augmentation = augmentation;
-        this.target = target;
-        this.rtn = rtn;
-        this.arguments = Collections.unmodifiableList(arguments);
-        this.method = method;
-        this.modifiers = modifiers;
-        this.handle = handle;
-        this.methodType = methodType;
-    }
+    public PainlessMethod(Method javaMethod, Class<?> targetClass, Class<?> returnType, List<Class<?>> typeParameters,
+            MethodHandle methodHandle, MethodType methodType) {
 
-    public void write(MethodWriter writer) {
-        final org.objectweb.asm.Type type;
-        final Class<?> clazz;
-        if (augmentation != null) {
-            assert Modifier.isStatic(modifiers);
-            clazz = augmentation;
-            type = org.objectweb.asm.Type.getType(augmentation);
-        } else {
-            clazz = target;
-            type = Type.getType(target);
-        }
-
-        if (Modifier.isStatic(modifiers)) {
-            // invokeStatic assumes that the owner class is not an interface, so this is a
-            // special case for interfaces where the interface method boolean needs to be set to
-            // true to reference the appropriate class constant when calling a static interface
-            // method since java 8 did not check, but java 9 and 10 do
-            if (Modifier.isInterface(clazz.getModifiers())) {
-                writer.visitMethodInsn(Opcodes.INVOKESTATIC,
-                        type.getInternalName(), name, methodType.toMethodDescriptorString(), true);
-            } else {
-                writer.invokeStatic(type, method);
-            }
-        } else if (Modifier.isInterface(clazz.getModifiers())) {
-            writer.invokeInterface(type, method);
-        } else {
-            writer.invokeVirtual(type, method);
-        }
+        this.javaMethod = javaMethod;
+        this.targetClass = targetClass;
+        this.returnType = returnType;
+        this.typeParameters = Collections.unmodifiableList(typeParameters);
+        this.methodHandle = methodHandle;
+        this.methodType = methodType;
     }
 }

+ 6 - 5
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java

@@ -21,10 +21,11 @@ package org.elasticsearch.painless.node;
 
 import org.elasticsearch.painless.Globals;
 import org.elasticsearch.painless.Locals;
+import org.elasticsearch.painless.Locals.LocalMethod;
 import org.elasticsearch.painless.Location;
 import org.elasticsearch.painless.MethodWriter;
 import org.elasticsearch.painless.lookup.PainlessLookupUtility;
-import org.elasticsearch.painless.lookup.PainlessMethod;
+import org.objectweb.asm.commons.Method;
 
 import java.util.List;
 import java.util.Objects;
@@ -40,7 +41,7 @@ public final class ECallLocal extends AExpression {
     private final String name;
     private final List<AExpression> arguments;
 
-    private PainlessMethod method = null;
+    private LocalMethod method = null;
 
     public ECallLocal(Location location, String name, List<AExpression> arguments) {
         super(location);
@@ -68,14 +69,14 @@ public final class ECallLocal extends AExpression {
         for (int argument = 0; argument < arguments.size(); ++argument) {
             AExpression expression = arguments.get(argument);
 
-            expression.expected = method.arguments.get(argument);
+            expression.expected = method.typeParameters.get(argument);
             expression.internal = true;
             expression.analyze(locals);
             arguments.set(argument, expression.cast(locals));
         }
 
         statement = true;
-        actual = method.rtn;
+        actual = method.returnType;
     }
 
     @Override
@@ -86,7 +87,7 @@ public final class ECallLocal extends AExpression {
             argument.write(writer, globals);
         }
 
-        writer.invokeStatic(CLASS_TYPE, method.method);
+        writer.invokeStatic(CLASS_TYPE, new Method(method.name, method.methodType.toMethodDescriptorString()));
     }
 
     @Override

+ 4 - 4
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java

@@ -81,14 +81,14 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
                             PainlessLookupUtility.typeToCanonicalTypeName(captured.clazz), call, 1);
 
                     // check casts between the interface method and the delegate method are legal
-                    for (int i = 0; i < ref.interfaceMethod.arguments.size(); ++i) {
-                        Class<?> from = ref.interfaceMethod.arguments.get(i);
+                    for (int i = 0; i < ref.interfaceMethod.typeParameters.size(); ++i) {
+                        Class<?> from = ref.interfaceMethod.typeParameters.get(i);
                         Class<?> to = ref.delegateTypeParameters.get(i);
                         AnalyzerCaster.getLegalCast(location, from, to, false, true);
                     }
 
-                    if (ref.interfaceMethod.rtn != void.class) {
-                        AnalyzerCaster.getLegalCast(location, ref.delegateReturnType, ref.interfaceMethod.rtn, false, true);
+                    if (ref.interfaceMethod.returnType != void.class) {
+                        AnalyzerCaster.getLegalCast(location, ref.delegateReturnType, ref.interfaceMethod.returnType, false, true);
                     }
                 } catch (IllegalArgumentException e) {
                     throw createError(e);

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

@@ -23,6 +23,7 @@ import org.elasticsearch.painless.AnalyzerCaster;
 import org.elasticsearch.painless.FunctionRef;
 import org.elasticsearch.painless.Globals;
 import org.elasticsearch.painless.Locals;
+import org.elasticsearch.painless.Locals.LocalMethod;
 import org.elasticsearch.painless.Location;
 import org.elasticsearch.painless.MethodWriter;
 import org.elasticsearch.painless.lookup.PainlessLookupUtility;
@@ -70,8 +71,7 @@ public final class EFunctionRef extends AExpression implements ILambda {
                         throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
                                 "to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface");
                     }
-                    PainlessMethod delegateMethod =
-                            locals.getMethod(PainlessLookupUtility.buildPainlessMethodKey(call, interfaceMethod.arguments.size()));
+                    LocalMethod delegateMethod = locals.getMethod(Locals.buildLocalMethodKey(call, interfaceMethod.typeParameters.size()));
                     if (delegateMethod == null) {
                         throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
                                 "to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], function not found");
@@ -79,14 +79,14 @@ public final class EFunctionRef extends AExpression implements ILambda {
                     ref = new FunctionRef(expected, interfaceMethod, delegateMethod, 0);
 
                     // check casts between the interface method and the delegate method are legal
-                    for (int i = 0; i < interfaceMethod.arguments.size(); ++i) {
-                        Class<?> from = interfaceMethod.arguments.get(i);
-                        Class<?> to = delegateMethod.arguments.get(i);
+                    for (int i = 0; i < interfaceMethod.typeParameters.size(); ++i) {
+                        Class<?> from = interfaceMethod.typeParameters.get(i);
+                        Class<?> to = delegateMethod.typeParameters.get(i);
                         AnalyzerCaster.getLegalCast(location, from, to, false, true);
                     }
 
-                    if (interfaceMethod.rtn != void.class) {
-                        AnalyzerCaster.getLegalCast(location, delegateMethod.rtn, interfaceMethod.rtn, false, true);
+                    if (interfaceMethod.returnType != void.class) {
+                        AnalyzerCaster.getLegalCast(location, delegateMethod.returnType, interfaceMethod.returnType, false, true);
                     }
                 } else {
                     // whitelist lookup

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

@@ -23,6 +23,7 @@ import org.elasticsearch.painless.AnalyzerCaster;
 import org.elasticsearch.painless.FunctionRef;
 import org.elasticsearch.painless.Globals;
 import org.elasticsearch.painless.Locals;
+import org.elasticsearch.painless.Locals.LocalMethod;
 import org.elasticsearch.painless.Locals.Variable;
 import org.elasticsearch.painless.Location;
 import org.elasticsearch.painless.MethodWriter;
@@ -126,21 +127,21 @@ public final class ELambda extends AExpression implements ILambda {
                         "[" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface"));
             }
             // check arity before we manipulate parameters
-            if (interfaceMethod.arguments.size() != paramTypeStrs.size())
-                throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name +
+            if (interfaceMethod.typeParameters.size() != paramTypeStrs.size())
+                throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.javaMethod.getName() +
                         "] in [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "]");
             // for method invocation, its allowed to ignore the return value
-            if (interfaceMethod.rtn == void.class) {
+            if (interfaceMethod.returnType == void.class) {
                 returnType = def.class;
             } else {
-                returnType = interfaceMethod.rtn;
+                returnType = interfaceMethod.returnType;
             }
             // replace any null types with the actual type
             actualParamTypeStrs = new ArrayList<>(paramTypeStrs.size());
             for (int i = 0; i < paramTypeStrs.size(); i++) {
                 String paramType = paramTypeStrs.get(i);
                 if (paramType == null) {
-                    actualParamTypeStrs.add(PainlessLookupUtility.typeToCanonicalTypeName(interfaceMethod.arguments.get(i)));
+                    actualParamTypeStrs.add(PainlessLookupUtility.typeToCanonicalTypeName(interfaceMethod.typeParameters.get(i)));
                 } else {
                     actualParamTypeStrs.add(paramType);
                 }
@@ -183,20 +184,22 @@ public final class ELambda extends AExpression implements ILambda {
         } else {
             defPointer = null;
             try {
-                ref = new FunctionRef(expected, interfaceMethod, desugared.method, captures.size());
+                LocalMethod localMethod =
+                        new LocalMethod(desugared.name, desugared.returnType, desugared.typeParameters, desugared.methodType);
+                ref = new FunctionRef(expected, interfaceMethod, localMethod, captures.size());
             } catch (IllegalArgumentException e) {
                 throw createError(e);
             }
 
             // check casts between the interface method and the delegate method are legal
-            for (int i = 0; i < interfaceMethod.arguments.size(); ++i) {
-                Class<?> from = interfaceMethod.arguments.get(i);
+            for (int i = 0; i < interfaceMethod.typeParameters.size(); ++i) {
+                Class<?> from = interfaceMethod.typeParameters.get(i);
                 Class<?> to = desugared.parameters.get(i + captures.size()).clazz;
                 AnalyzerCaster.getLegalCast(location, from, to, false, true);
             }
 
-            if (interfaceMethod.rtn != void.class) {
-                AnalyzerCaster.getLegalCast(location, desugared.rtnType, interfaceMethod.rtn, false, true);
+            if (interfaceMethod.returnType != void.class) {
+                AnalyzerCaster.getLegalCast(location, desugared.returnType, interfaceMethod.returnType, false, true);
             }
 
             actual = expected;

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

@@ -100,7 +100,7 @@ public final class EListInit extends AExpression {
         for (AExpression value : values) {
             writer.dup();
             value.write(writer, globals);
-            method.write(writer);
+            writer.invokeMethodCall(method);
             writer.pop();
         }
     }

+ 1 - 1
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java

@@ -123,7 +123,7 @@ public final class EMapInit extends AExpression {
             writer.dup();
             key.write(writer, globals);
             value.write(writer, globals);
-            method.write(writer);
+            writer.invokeMethodCall(method);
             writer.pop();
         }
     }

+ 4 - 4
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java

@@ -56,14 +56,14 @@ final class PSubCallInvoke extends AExpression {
         for (int argument = 0; argument < arguments.size(); ++argument) {
             AExpression expression = arguments.get(argument);
 
-            expression.expected = method.arguments.get(argument);
+            expression.expected = method.typeParameters.get(argument);
             expression.internal = true;
             expression.analyze(locals);
             arguments.set(argument, expression.cast(locals));
         }
 
         statement = true;
-        actual = method.rtn;
+        actual = method.returnType;
     }
 
     @Override
@@ -78,11 +78,11 @@ final class PSubCallInvoke extends AExpression {
             argument.write(writer, globals);
         }
 
-        method.write(writer);
+        writer.invokeMethodCall(method);
     }
 
     @Override
     public String toString() {
-        return singleLineToStringWithOptionalArgs(arguments, prefix, method.name);
+        return singleLineToStringWithOptionalArgs(arguments, prefix, method.javaMethod.getName());
     }
 }

+ 11 - 14
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubListShortcut.java

@@ -62,17 +62,17 @@ final class PSubListShortcut extends AStoreable {
         getter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey("get", 1));
         setter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey("set", 2));
 
-        if (getter != null && (getter.rtn == void.class || getter.arguments.size() != 1 ||
-            getter.arguments.get(0) != int.class)) {
+        if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1 ||
+            getter.typeParameters.get(0) != int.class)) {
             throw createError(new IllegalArgumentException("Illegal list get shortcut for type [" + canonicalClassName + "]."));
         }
 
-        if (setter != null && (setter.arguments.size() != 2 || setter.arguments.get(0) != int.class)) {
+        if (setter != null && (setter.typeParameters.size() != 2 || setter.typeParameters.get(0) != int.class)) {
             throw createError(new IllegalArgumentException("Illegal list set shortcut for type [" + canonicalClassName + "]."));
         }
 
-        if (getter != null && setter != null && (!getter.arguments.get(0).equals(setter.arguments.get(0))
-            || !getter.rtn.equals(setter.arguments.get(1)))) {
+        if (getter != null && setter != null && (!getter.typeParameters.get(0).equals(setter.typeParameters.get(0))
+            || !getter.returnType.equals(setter.typeParameters.get(1)))) {
             throw createError(new IllegalArgumentException("Shortcut argument types must match."));
         }
 
@@ -81,7 +81,7 @@ final class PSubListShortcut extends AStoreable {
             index.analyze(locals);
             index = index.cast(locals);
 
-            actual = setter != null ? setter.arguments.get(1) : getter.rtn;
+            actual = setter != null ? setter.typeParameters.get(1) : getter.returnType;
         } else {
             throw createError(new IllegalArgumentException("Illegal list shortcut for type [" + canonicalClassName + "]."));
         }
@@ -119,21 +119,18 @@ final class PSubListShortcut extends AStoreable {
     @Override
     void load(MethodWriter writer, Globals globals) {
         writer.writeDebugInfo(location);
+        writer.invokeMethodCall(getter);
 
-        getter.write(writer);
-
-        if (getter.rtn == getter.handle.type().returnType()) {
-            writer.checkCast(MethodWriter.getType(getter.rtn));
+        if (getter.returnType == getter.javaMethod.getReturnType()) {
+            writer.checkCast(MethodWriter.getType(getter.returnType));
         }
     }
 
     @Override
     void store(MethodWriter writer, Globals globals) {
         writer.writeDebugInfo(location);
-
-        setter.write(writer);
-
-        writer.writePop(MethodWriter.getType(setter.rtn).getSize());
+        writer.invokeMethodCall(setter);
+        writer.writePop(MethodWriter.getType(setter.returnType).getSize());
     }
 
     @Override

+ 14 - 18
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubMapShortcut.java

@@ -61,25 +61,25 @@ final class PSubMapShortcut extends AStoreable {
         getter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey("get", 1));
         setter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey("put", 2));
 
-        if (getter != null && (getter.rtn == void.class || getter.arguments.size() != 1)) {
+        if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1)) {
             throw createError(new IllegalArgumentException("Illegal map get shortcut for type [" + canonicalClassName + "]."));
         }
 
-        if (setter != null && setter.arguments.size() != 2) {
+        if (setter != null && setter.typeParameters.size() != 2) {
             throw createError(new IllegalArgumentException("Illegal map set shortcut for type [" + canonicalClassName + "]."));
         }
 
-        if (getter != null && setter != null &&
-            (!getter.arguments.get(0).equals(setter.arguments.get(0)) || !getter.rtn.equals(setter.arguments.get(1)))) {
+        if (getter != null && setter != null && (!getter.typeParameters.get(0).equals(setter.typeParameters.get(0)) ||
+                !getter.returnType.equals(setter.typeParameters.get(1)))) {
             throw createError(new IllegalArgumentException("Shortcut argument types must match."));
         }
 
         if ((read || write) && (!read || getter != null) && (!write || setter != null)) {
-            index.expected = setter != null ? setter.arguments.get(0) : getter.arguments.get(0);
+            index.expected = setter != null ? setter.typeParameters.get(0) : getter.typeParameters.get(0);
             index.analyze(locals);
             index = index.cast(locals);
 
-            actual = setter != null ? setter.arguments.get(1) : getter.rtn;
+            actual = setter != null ? setter.typeParameters.get(1) : getter.returnType;
         } else {
             throw createError(new IllegalArgumentException("Illegal map shortcut for type [" + canonicalClassName + "]."));
         }
@@ -90,11 +90,10 @@ final class PSubMapShortcut extends AStoreable {
         index.write(writer, globals);
 
         writer.writeDebugInfo(location);
+        writer.invokeMethodCall(getter);
 
-        getter.write(writer);
-
-        if (getter.rtn != getter.handle.type().returnType()) {
-            writer.checkCast(MethodWriter.getType(getter.rtn));
+        if (getter.returnType != getter.javaMethod.getReturnType()) {
+            writer.checkCast(MethodWriter.getType(getter.returnType));
         }
     }
 
@@ -121,21 +120,18 @@ final class PSubMapShortcut extends AStoreable {
     @Override
     void load(MethodWriter writer, Globals globals) {
         writer.writeDebugInfo(location);
+        writer.invokeMethodCall(getter);
 
-        getter.write(writer);
-
-        if (getter.rtn != getter.handle.type().returnType()) {
-            writer.checkCast(MethodWriter.getType(getter.rtn));
+        if (getter.returnType != getter.javaMethod.getReturnType()) {
+            writer.checkCast(MethodWriter.getType(getter.returnType));
         }
     }
 
     @Override
     void store(MethodWriter writer, Globals globals) {
         writer.writeDebugInfo(location);
-
-        setter.write(writer);
-
-        writer.writePop(MethodWriter.getType(setter.rtn).getSize());
+        writer.invokeMethodCall(setter);
+        writer.writePop(MethodWriter.getType(setter.returnType).getSize());
     }
 
     @Override

+ 12 - 12
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java

@@ -53,22 +53,22 @@ final class PSubShortcut extends AStoreable {
 
     @Override
     void analyze(Locals locals) {
-        if (getter != null && (getter.rtn == void.class || !getter.arguments.isEmpty())) {
+        if (getter != null && (getter.returnType == void.class || !getter.typeParameters.isEmpty())) {
             throw createError(new IllegalArgumentException(
                 "Illegal get shortcut on field [" + value + "] for type [" + type + "]."));
         }
 
-        if (setter != null && (setter.rtn != void.class || setter.arguments.size() != 1)) {
+        if (setter != null && (setter.returnType != void.class || setter.typeParameters.size() != 1)) {
             throw createError(new IllegalArgumentException(
                 "Illegal set shortcut on field [" + value + "] for type [" + type + "]."));
         }
 
-        if (getter != null && setter != null && setter.arguments.get(0) != getter.rtn) {
+        if (getter != null && setter != null && setter.typeParameters.get(0) != getter.returnType) {
             throw createError(new IllegalArgumentException("Shortcut argument types must match."));
         }
 
         if ((getter != null || setter != null) && (!read || getter != null) && (!write || setter != null)) {
-            actual = setter != null ? setter.arguments.get(0) : getter.rtn;
+            actual = setter != null ? setter.typeParameters.get(0) : getter.returnType;
         } else {
             throw createError(new IllegalArgumentException("Illegal shortcut on field [" + value + "] for type [" + type + "]."));
         }
@@ -78,10 +78,10 @@ final class PSubShortcut extends AStoreable {
     void write(MethodWriter writer, Globals globals) {
         writer.writeDebugInfo(location);
 
-        getter.write(writer);
+        writer.invokeMethodCall(getter);
 
-        if (!getter.rtn.equals(getter.handle.type().returnType())) {
-            writer.checkCast(MethodWriter.getType(getter.rtn));
+        if (!getter.returnType.equals(getter.javaMethod.getReturnType())) {
+            writer.checkCast(MethodWriter.getType(getter.returnType));
         }
     }
 
@@ -109,10 +109,10 @@ final class PSubShortcut extends AStoreable {
     void load(MethodWriter writer, Globals globals) {
         writer.writeDebugInfo(location);
 
-        getter.write(writer);
+        writer.invokeMethodCall(getter);
 
-        if (getter.rtn != getter.handle.type().returnType()) {
-            writer.checkCast(MethodWriter.getType(getter.rtn));
+        if (getter.returnType != getter.javaMethod.getReturnType()) {
+            writer.checkCast(MethodWriter.getType(getter.returnType));
         }
     }
 
@@ -120,9 +120,9 @@ final class PSubShortcut extends AStoreable {
     void store(MethodWriter writer, Globals globals) {
         writer.writeDebugInfo(location);
 
-        setter.write(writer);
+        writer.invokeMethodCall(setter);
 
-        writer.writePop(MethodWriter.getType(setter.rtn).getSize());
+        writer.writePop(MethodWriter.getType(setter.returnType).getSize());
     }
 
     @Override

+ 14 - 18
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java

@@ -31,14 +31,12 @@ import org.elasticsearch.painless.MethodWriter;
 import org.elasticsearch.painless.WriterConstants;
 import org.elasticsearch.painless.lookup.PainlessLookup;
 import org.elasticsearch.painless.lookup.PainlessLookupUtility;
-import org.elasticsearch.painless.lookup.PainlessMethod;
 import org.elasticsearch.painless.node.SSource.Reserved;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.Handle;
 import org.objectweb.asm.Opcodes;
 
 import java.lang.invoke.MethodType;
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
@@ -92,9 +90,12 @@ public final class SFunction extends AStatement {
     private final List<AStatement> statements;
     public final boolean synthetic;
 
-    Class<?> rtnType = null;
+    Class<?> returnType;
+    List<Class<?>> typeParameters;
+    MethodType methodType;
+
+    org.objectweb.asm.commons.Method method;
     List<Parameter> parameters = new ArrayList<>();
-    PainlessMethod method = null;
 
     private Variable loop = null;
 
@@ -120,7 +121,7 @@ public final class SFunction extends AStatement {
 
     void generateSignature(PainlessLookup painlessLookup) {
         try {
-            rtnType = painlessLookup.getJavaClassFromPainlessType(rtnTypeStr);
+            returnType = painlessLookup.getJavaClassFromPainlessType(rtnTypeStr);
         } catch (IllegalArgumentException exception) {
             throw createError(new IllegalArgumentException("Illegal return type [" + rtnTypeStr + "] for function [" + name + "]."));
         }
@@ -145,11 +146,10 @@ public final class SFunction extends AStatement {
             }
         }
 
-        int modifiers = Modifier.STATIC | Modifier.PRIVATE;
-        org.objectweb.asm.commons.Method method = new org.objectweb.asm.commons.Method(name, MethodType.methodType(
-                PainlessLookupUtility.typeToJavaType(rtnType), paramClasses).toMethodDescriptorString());
-        MethodType methodType = MethodType.methodType(PainlessLookupUtility.typeToJavaType(rtnType), paramClasses);
-        this.method = new PainlessMethod(name, null, null, rtnType, paramTypes, method, modifiers, null, methodType);
+        typeParameters = paramTypes;
+        methodType = MethodType.methodType(PainlessLookupUtility.typeToJavaType(returnType), paramClasses);
+        method = new org.objectweb.asm.commons.Method(name, MethodType.methodType(
+                PainlessLookupUtility.typeToJavaType(returnType), paramClasses).toMethodDescriptorString());
     }
 
     @Override
@@ -177,7 +177,7 @@ public final class SFunction extends AStatement {
             allEscape = statement.allEscape;
         }
 
-        if (!methodEscape && rtnType != void.class) {
+        if (!methodEscape && returnType != void.class) {
             throw createError(new IllegalArgumentException("Not all paths provide a return value for method [" + name + "]."));
         }
 
@@ -192,7 +192,7 @@ public final class SFunction extends AStatement {
         if (synthetic) {
             access |= Opcodes.ACC_SYNTHETIC;
         }
-        final MethodWriter function = new MethodWriter(access, method.method, writer, globals.getStatements(), settings);
+        final MethodWriter function = new MethodWriter(access, method, writer, globals.getStatements(), settings);
         function.visitCode();
         write(function, globals);
         function.endMethod();
@@ -212,7 +212,7 @@ public final class SFunction extends AStatement {
         }
 
         if (!methodEscape) {
-            if (rtnType == void.class) {
+            if (returnType == void.class) {
                 function.returnValue();
             } else {
                 throw createError(new IllegalStateException("Illegal tree structure."));
@@ -225,11 +225,7 @@ public final class SFunction extends AStatement {
     }
 
     private void initializeConstant(MethodWriter writer) {
-        final Handle handle = new Handle(Opcodes.H_INVOKESTATIC,
-                CLASS_TYPE.getInternalName(),
-                name,
-                method.method.getDescriptor(),
-                false);
+        final Handle handle = new Handle(Opcodes.H_INVOKESTATIC, CLASS_TYPE.getInternalName(), name, method.getDescriptor(), false);
         writer.push(handle);
     }
 

+ 6 - 6
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java

@@ -23,6 +23,7 @@ import org.elasticsearch.painless.CompilerSettings;
 import org.elasticsearch.painless.Constant;
 import org.elasticsearch.painless.Globals;
 import org.elasticsearch.painless.Locals;
+import org.elasticsearch.painless.Locals.LocalMethod;
 import org.elasticsearch.painless.Locals.Variable;
 import org.elasticsearch.painless.Location;
 import org.elasticsearch.painless.MethodWriter;
@@ -30,8 +31,6 @@ import org.elasticsearch.painless.ScriptClassInfo;
 import org.elasticsearch.painless.SimpleChecksAdapter;
 import org.elasticsearch.painless.WriterConstants;
 import org.elasticsearch.painless.lookup.PainlessLookup;
-import org.elasticsearch.painless.lookup.PainlessLookupUtility;
-import org.elasticsearch.painless.lookup.PainlessMethod;
 import org.elasticsearch.painless.node.SFunction.FunctionReserved;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.ClassWriter;
@@ -165,14 +164,15 @@ public final class SSource extends AStatement {
     }
 
     public void analyze(PainlessLookup painlessLookup) {
-        Map<String, PainlessMethod> methods = new HashMap<>();
+        Map<String, LocalMethod> methods = new HashMap<>();
 
         for (SFunction function : functions) {
             function.generateSignature(painlessLookup);
 
-            String key = PainlessLookupUtility.buildPainlessMethodKey(function.name, function.parameters.size());
+            String key = Locals.buildLocalMethodKey(function.name, function.parameters.size());
 
-            if (methods.put(key, function.method) != null) {
+            if (methods.put(key,
+                    new LocalMethod(function.name, function.returnType, function.typeParameters, function.methodType)) != null) {
                 throw createError(new IllegalArgumentException("Duplicate functions with name [" + function.name + "]."));
             }
         }
@@ -184,7 +184,7 @@ public final class SSource extends AStatement {
     void analyze(Locals program) {
         for (SFunction function : functions) {
             Locals functionLocals =
-                Locals.newFunctionScope(program, function.rtnType, function.parameters, function.reserved.getMaxLoopCounter());
+                Locals.newFunctionScope(program, function.returnType, function.parameters, function.reserved.getMaxLoopCounter());
             function.analyze(functionLocals);
         }
 

+ 1 - 1
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java

@@ -99,7 +99,7 @@ final class SSubEachIterable extends AStatement {
                     .getMethodType(org.objectweb.asm.Type.getType(Iterator.class), org.objectweb.asm.Type.getType(Object.class));
             writer.invokeDefCall("iterator", methodType, DefBootstrap.ITERATOR);
         } else {
-            method.write(writer);
+            writer.invokeMethodCall(method);
         }
 
         writer.visitVarInsn(MethodWriter.getType(iterator.clazz).getOpcode(Opcodes.ISTORE), iterator.getSlot());

+ 16 - 16
modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java

@@ -57,8 +57,8 @@ public class PainlessDocGenerator {
     private static final PainlessLookup PAINLESS_LOOKUP = PainlessLookupBuilder.buildFromWhitelists(Whitelist.BASE_WHITELISTS);
     private static final Logger logger = ESLoggerFactory.getLogger(PainlessDocGenerator.class);
     private static final Comparator<PainlessField> FIELD_NAME = comparing(f -> f.name);
-    private static final Comparator<PainlessMethod> METHOD_NAME = comparing(m -> m.name);
-    private static final Comparator<PainlessMethod> METHOD_NUMBER_OF_PARAMS = comparing(m -> m.arguments.size());
+    private static final Comparator<PainlessMethod> METHOD_NAME = comparing(m -> m.javaMethod.getName());
+    private static final Comparator<PainlessMethod> METHOD_NUMBER_OF_PARAMS = comparing(m -> m.typeParameters.size());
     private static final Comparator<PainlessConstructor> CONSTRUCTOR_NUMBER_OF_PARAMS = comparing(m -> m.typeParameters.size());
 
     public static void main(String[] args) throws IOException {
@@ -114,10 +114,10 @@ public class PainlessDocGenerator {
                     struct.constructors.values().stream().sorted(CONSTRUCTOR_NUMBER_OF_PARAMS).forEach(documentConstructor);
                     Map<String, Class<?>> inherited = new TreeMap<>();
                     struct.methods.values().stream().sorted(METHOD_NAME.thenComparing(METHOD_NUMBER_OF_PARAMS)).forEach(method -> {
-                        if (method.target == clazz) {
+                        if (method.targetClass == clazz) {
                             documentMethod(typeStream, method);
                         } else {
-                            inherited.put(canonicalClassName, method.target);
+                            inherited.put(canonicalClassName, method.targetClass);
                         }
                     });
 
@@ -212,11 +212,11 @@ public class PainlessDocGenerator {
         emitAnchor(stream, method);
         stream.print("]]");
 
-        if (null == method.augmentation && Modifier.isStatic(method.modifiers)) {
+        if (method.targetClass == method.javaMethod.getDeclaringClass() && Modifier.isStatic(method.javaMethod.getModifiers())) {
             stream.print("static ");
         }
 
-        emitType(stream, method.rtn);
+        emitType(stream, method.returnType);
         stream.print(' ');
 
         String javadocRoot = javadocRoot(method);
@@ -227,7 +227,7 @@ public class PainlessDocGenerator {
 
         stream.print("](");
         boolean first = true;
-        for (Class<?> arg : method.arguments) {
+        for (Class<?> arg : method.typeParameters) {
             if (first) {
                 first = false;
             } else {
@@ -269,11 +269,11 @@ public class PainlessDocGenerator {
      * Anchor text for a {@link PainlessMethod}.
      */
     private static void emitAnchor(PrintStream stream, PainlessMethod method) {
-        emitAnchor(stream, method.target);
+        emitAnchor(stream, method.targetClass);
         stream.print('-');
         stream.print(methodName(method));
         stream.print('-');
-        stream.print(method.arguments.size());
+        stream.print(method.typeParameters.size());
     }
 
     /**
@@ -290,7 +290,7 @@ public class PainlessDocGenerator {
     }
 
     private static String methodName(PainlessMethod method) {
-        return PainlessLookupUtility.typeToCanonicalTypeName(method.target);
+        return PainlessLookupUtility.typeToCanonicalTypeName(method.targetClass);
     }
 
     /**
@@ -359,16 +359,16 @@ public class PainlessDocGenerator {
         stream.print("link:{");
         stream.print(root);
         stream.print("-javadoc}/");
-        stream.print(classUrlPath(method.augmentation != null ? method.augmentation : method.target));
+        stream.print(classUrlPath(method.javaMethod.getDeclaringClass()));
         stream.print(".html#");
         stream.print(methodName(method));
         stream.print("%2D");
         boolean first = true;
-        if (method.augmentation != null) {
+        if (method.targetClass != method.javaMethod.getDeclaringClass()) {
             first = false;
-            stream.print(method.target.getName());
+            stream.print(method.javaMethod.getDeclaringClass().getName());
         }
-        for (Class<?> clazz: method.arguments) {
+        for (Class<?> clazz: method.typeParameters) {
             if (first) {
                 first = false;
             } else {
@@ -400,10 +400,10 @@ public class PainlessDocGenerator {
      * Pick the javadoc root for a {@link PainlessMethod}.
      */
     private static String javadocRoot(PainlessMethod method) {
-        if (method.augmentation != null) {
+        if (method.targetClass != method.javaMethod.getDeclaringClass()) {
             return "painless";
         }
-        return javadocRoot(method.target);
+        return javadocRoot(method.targetClass);
     }
 
     /**