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 9be2559e67a27..91c25b7cfecb2 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 @@ -238,7 +238,7 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lo 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 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lo // 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 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lo 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 @@ static MethodHandle lookupReference(PainlessLookup painlessLookup, MethodHandles 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 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku 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 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku // 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."); 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 0da91438326c5..d4671f05b6c9d 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 @@ -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 FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessCo 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 FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessCo 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 FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessMe */ 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 @@ private static PainlessConstructor lookup(PainlessLookup painlessLookup, Class exp 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 { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java index 804f6aa2b689c..f2c7e02c637c1 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java @@ -22,8 +22,8 @@ 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 @@ */ 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> typeParameters; + public final MethodType methodType; + + public LocalMethod(String name, Class returnType, List> 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 static Locals newMainMethodScope(ScriptClassInfo scriptClassInfo, Locals } /** Creates a new program scope: the list of methods. It is the parent for all methods */ - public static Locals newProgramScope(PainlessLookup painlessLookup, Collection methods) { + public static Locals newProgramScope(PainlessLookup painlessLookup, Collection 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 Variable getVariable(Location location, String name) { } /** 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 PainlessLookup getPainlessLookup() { // variable name -> variable private Map variables; // method name+arity -> methods - private Map methods; + private Map methods; /** * Create a new Locals @@ -237,7 +261,7 @@ private Variable lookupVariable(Location location, String name) { } /** 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 @@ private Variable defineVariable(Location location, Class type, String name, b 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 } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java index c339e7bfb2613..72435562a3bd0 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java +++ b/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.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 void invokeDefCall(String name, Type methodType, int flavor, Object... pa 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); + } + } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupBuilder.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupBuilder.java index b0a11dea58a7e..a4dbe1006d61b 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupBuilder.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupBuilder.java @@ -540,7 +540,6 @@ public void addPainlessMethod(Class targetClass, Class augmentedClass, Str 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 void addPainlessMethod(Class targetClass, Class augmentedClass, Str 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 void addPainlessMethod(Class targetClass, Class augmentedClass, Str 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 @@ private void copyPainlessClassMembers(Class originalClass, Class targetCla 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 @@ private void cacheRuntimeHandles() { 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); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethod.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethod.java index 904cf06907fde..9dd143a402865 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethod.java +++ b/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> 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> typeParameters; + public final MethodHandle methodHandle; public final MethodType methodType; - public PainlessMethod(String name, Class target, Class augmentation, Class rtn, List> 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> 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; } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java index 098c75386e1a6..7605a0c9f7f40 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java @@ -21,10 +21,11 @@ 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 arguments; - private PainlessMethod method = null; + private LocalMethod method = null; public ECallLocal(Location location, String name, List arguments) { super(location); @@ -68,14 +69,14 @@ void analyze(Locals locals) { 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 @@ void write(MethodWriter writer, Globals globals) { argument.write(writer, globals); } - writer.invokeStatic(CLASS_TYPE, method.method); + writer.invokeStatic(CLASS_TYPE, new Method(method.name, method.methodType.toMethodDescriptorString())); } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java index a4f7b1300452a..e78b3c67210b8 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java @@ -81,14 +81,14 @@ void analyze(Locals locals) { 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); 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 23fb8a4180e48..782991e295836 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 @@ -23,6 +23,7 @@ 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 @@ void analyze(Locals locals) { 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 @@ void analyze(Locals locals) { 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 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 ab1442be805eb..6fc4a3a648039 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 @@ -23,6 +23,7 @@ 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 @@ void analyze(Locals locals) { "[" + 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 @@ void analyze(Locals locals) { } 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; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java index bd4bea95ea0ef..01a4878266e3e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java @@ -100,7 +100,7 @@ void write(MethodWriter writer, Globals globals) { for (AExpression value : values) { writer.dup(); value.write(writer, globals); - method.write(writer); + writer.invokeMethodCall(method); writer.pop(); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java index 5bef62a4b1812..73afe7f0dc53f 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java @@ -123,7 +123,7 @@ void write(MethodWriter writer, Globals globals) { writer.dup(); key.write(writer, globals); value.write(writer, globals); - method.write(writer); + writer.invokeMethodCall(method); writer.pop(); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java index 237efa61ffa7d..fe2ae52603b57 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java @@ -56,14 +56,14 @@ void analyze(Locals locals) { 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 @@ void write(MethodWriter writer, Globals globals) { 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()); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubListShortcut.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubListShortcut.java index 509aad6415349..0738f55c2cf84 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubListShortcut.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubListShortcut.java @@ -62,17 +62,17 @@ void analyze(Locals locals) { 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 @@ void analyze(Locals locals) { 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 @@ void setup(MethodWriter writer, Globals globals) { @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 diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubMapShortcut.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubMapShortcut.java index 2d7f2250c6c38..04ccbc9f534a0 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubMapShortcut.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubMapShortcut.java @@ -61,25 +61,25 @@ void analyze(Locals locals) { 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 @@ void write(MethodWriter writer, Globals globals) { 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 @@ void setup(MethodWriter writer, Globals globals) { @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 diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java index eb5668c554c20..1697566047798 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java @@ -53,22 +53,22 @@ void extractVariables(Set variables) { @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 @@ void analyze(Locals locals) { 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 @@ void setup(MethodWriter writer, Globals globals) { 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 @@ void load(MethodWriter writer, Globals globals) { 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 diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java index 600ca95504ac6..d61a424f83ddb 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java @@ -31,14 +31,12 @@ 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 int getMaxLoopCounter() { private final List statements; public final boolean synthetic; - Class rtnType = null; + Class returnType; + List> typeParameters; + MethodType methodType; + + org.objectweb.asm.commons.Method method; List parameters = new ArrayList<>(); - PainlessMethod method = null; private Variable loop = null; @@ -120,7 +121,7 @@ void extractVariables(Set variables) { 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 @@ void generateSignature(PainlessLookup painlessLookup) { } } - 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 @@ void analyze(Locals locals) { 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 @@ void write (ClassVisitor writer, CompilerSettings settings, Globals globals) { 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 @@ void write(MethodWriter function, Globals globals) { } if (!methodEscape) { - if (rtnType == void.class) { + if (returnType == void.class) { function.returnValue(); } else { throw createError(new IllegalStateException("Illegal tree structure.")); @@ -225,11 +225,7 @@ void write(MethodWriter function, Globals globals) { } 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); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java index c354e78a961a3..fe735c0db313e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java @@ -23,6 +23,7 @@ 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.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 @@ void extractVariables(Set variables) { } public void analyze(PainlessLookup painlessLookup) { - Map methods = new HashMap<>(); + Map 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 void analyze(PainlessLookup painlessLookup) { 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); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java index 12e3154eb562e..5450f690f6c1c 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java @@ -99,7 +99,7 @@ void write(MethodWriter writer, Globals globals) { .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()); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java index 654291543017f..1f8410092a052 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java +++ b/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 FIELD_NAME = comparing(f -> f.name); - private static final Comparator METHOD_NAME = comparing(m -> m.name); - private static final Comparator METHOD_NUMBER_OF_PARAMS = comparing(m -> m.arguments.size()); + private static final Comparator METHOD_NAME = comparing(m -> m.javaMethod.getName()); + private static final Comparator METHOD_NUMBER_OF_PARAMS = comparing(m -> m.typeParameters.size()); private static final Comparator CONSTRUCTOR_NUMBER_OF_PARAMS = comparing(m -> m.typeParameters.size()); public static void main(String[] args) throws IOException { @@ -114,10 +114,10 @@ public static void main(String[] args) throws IOException { struct.constructors.values().stream().sorted(CONSTRUCTOR_NUMBER_OF_PARAMS).forEach(documentConstructor); Map> 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 @@ private static void documentMethod(PrintStream stream, PainlessMethod method) { 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 @@ private static void documentMethod(PrintStream stream, PainlessMethod method) { stream.print("]("); boolean first = true; - for (Class arg : method.arguments) { + for (Class arg : method.typeParameters) { if (first) { first = false; } else { @@ -269,11 +269,11 @@ private static void emitAnchor(PrintStream stream, PainlessConstructor construct * 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 @@ private static String constructorName(PainlessConstructor constructor) { } private static String methodName(PainlessMethod method) { - return PainlessLookupUtility.typeToCanonicalTypeName(method.target); + return PainlessLookupUtility.typeToCanonicalTypeName(method.targetClass); } /** @@ -359,16 +359,16 @@ private static void emitJavadocLink(PrintStream stream, String root, PainlessMet 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 @@ private static void emitJavadocLink(PrintStream stream, String root, PainlessFie * 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); } /**