From 1419751abcc6e9dd475ec776cd3ea7ebe415f236 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Thu, 26 Jul 2018 13:22:10 -0700 Subject: [PATCH 01/33] Parially added constructor. --- .../elasticsearch/painless/FunctionRef.java | 76 +++++++++++++++++-- .../painless/lookup/PainlessClass.java | 6 +- .../painless/lookup/PainlessClassBuilder.java | 4 +- .../painless/lookup/PainlessConstructor.java | 36 +++++++++ .../lookup/PainlessLookupBuilder.java | 56 ++++++++++---- .../painless/lookup/PainlessMethod.java | 48 +----------- .../painless/node/ECapturingFunctionRef.java | 4 +- .../painless/node/EListInit.java | 10 ++- .../elasticsearch/painless/node/EMapInit.java | 10 ++- .../elasticsearch/painless/node/ENewObj.java | 18 +++-- 10 files changed, 179 insertions(+), 89 deletions(-) create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessConstructor.java 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 aa72724b93029..aff367fc62318 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 @@ -20,6 +20,7 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.lookup.PainlessClass; +import org.elasticsearch.painless.lookup.PainlessConstructor; import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; @@ -27,6 +28,7 @@ import java.lang.invoke.MethodType; import java.lang.reflect.Modifier; +import java.util.List; import static org.elasticsearch.painless.WriterConstants.CLASS_NAME; import static org.objectweb.asm.Opcodes.H_INVOKEINTERFACE; @@ -59,8 +61,10 @@ public class FunctionRef { /** interface method */ public final PainlessMethod interfaceMethod; - /** delegate method */ - public final PainlessMethod delegateMethod; + /** delegate method type parameters */ + public final List> delegateTypeParameters; + /** delegate method return type */ + public final Class delegateReturnType; /** factory method type descriptor */ public final String factoryDescriptor; @@ -93,12 +97,12 @@ public FunctionRef(PainlessLookup painlessLookup, Class expected, String type * @param numCaptures number of captured arguments */ public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessMethod delegateMethod, int numCaptures) { - MethodType delegateMethodType = delegateMethod.getMethodType(); + MethodType delegateMethodType = getMethodType(delegateMethod); interfaceMethodName = interfaceMethod.name; factoryMethodType = MethodType.methodType(expected, delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); - interfaceMethodType = interfaceMethod.getMethodType().dropParameterTypes(0, 1); + interfaceMethodType = getMethodType(delegateMethod).dropParameterTypes(0, 1); // the Painless$Script class can be inferred if owner is null if (delegateMethod.target == null) { @@ -126,7 +130,8 @@ public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessMe this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures); this.interfaceMethod = interfaceMethod; - this.delegateMethod = delegateMethod; + delegateTypeParameters = delegateMethod.arguments; + delegateReturnType = delegateMethod.rtn; factoryDescriptor = factoryMethodType.toMethodDescriptorString(); interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString()); @@ -142,7 +147,7 @@ public FunctionRef(Class expected, interfaceMethodName = interfaceMethod.name; factoryMethodType = MethodType.methodType(expected, delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); - interfaceMethodType = interfaceMethod.getMethodType().dropParameterTypes(0, 1); + interfaceMethodType = getMethodType(interfaceMethod).dropParameterTypes(0, 1); delegateClassName = CLASS_NAME; delegateInvokeType = H_INVOKESTATIC; @@ -151,7 +156,8 @@ public FunctionRef(Class expected, isDelegateInterface = false; this.interfaceMethod = null; - delegateMethod = null; + delegateTypeParameters = null; + delegateReturnType = null; factoryDescriptor = null; interfaceType = null; @@ -176,7 +182,7 @@ private static PainlessMethod lookup(PainlessLookup painlessLookup, Class exp final PainlessMethod impl; // ctor ref if ("new".equals(call)) { - impl = struct.constructors.get(PainlessLookupUtility.buildPainlessMethodKey("", method.arguments.size())); + impl = null;//TODO: struct.constructors.get(PainlessLookupUtility.buildPainlessMethodKey("", method.arguments.size())); } else { // look for a static impl first PainlessMethod staticImpl = @@ -202,4 +208,58 @@ private static PainlessMethod lookup(PainlessLookup painlessLookup, Class exp } return impl; } + + /** + * Returns MethodType for this constructor. + */ + private MethodType getMethodType(PainlessConstructor painlessConstructor) { + // constructor: returns the owner class + final Class params[] = new Class[painlessConstructor.typeParameters.size()]; + for (int i = 0; i < painlessConstructor.typeParameters.size(); i++) { + params[i] = PainlessLookupUtility.typeToJavaType(painlessConstructor.typeParameters.get(i)); + } + Class returnValue = painlessConstructor.javaConstructor.getDeclaringClass(); + return MethodType.methodType(returnValue, params); + } + + /** + * Returns MethodType for this method. + *

+ * This works even for user-defined Methods (where the MethodHandle is null). + */ + private MethodType getMethodType(PainlessMethod painlessMethod) { + // we have a methodhandle already (e.g. whitelisted class) + // just return its type + if (painlessMethod.handle != null) { + return painlessMethod.handle.type(); + } + // otherwise compute it + final Class params[]; + final Class returnValue; + if (painlessMethod.augmentation != null) { + // static method disguised as virtual/interface method + params = new Class[1 + painlessMethod.arguments.size()]; + params[0] = painlessMethod.augmentation; + for (int i = 0; i < painlessMethod.arguments.size(); i++) { + params[i + 1] = PainlessLookupUtility.typeToJavaType(painlessMethod.arguments.get(i)); + } + returnValue = PainlessLookupUtility.typeToJavaType(painlessMethod.rtn); + } else if (Modifier.isStatic(painlessMethod.modifiers)) { + // static method: straightforward copy + params = new Class[painlessMethod.arguments.size()]; + for (int i = 0; i < painlessMethod.arguments.size(); i++) { + params[i] = PainlessLookupUtility.typeToJavaType(painlessMethod.arguments.get(i)); + } + returnValue = PainlessLookupUtility.typeToJavaType(painlessMethod.rtn); + } else { + // virtual/interface method: add receiver class + params = new Class[1 + painlessMethod.arguments.size()]; + params[0] = painlessMethod.target; + for (int i = 0; i < painlessMethod.arguments.size(); i++) { + params[i + 1] = PainlessLookupUtility.typeToJavaType(painlessMethod.arguments.get(i)); + } + returnValue = PainlessLookupUtility.typeToJavaType(painlessMethod.rtn); + } + return MethodType.methodType(returnValue, params); + } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java index 24dcf0ebdba87..a10413acb8498 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java @@ -24,7 +24,8 @@ import java.util.Map; public final class PainlessClass { - public final Map constructors; + public final Map constructors; + public final Map staticMethods; public final Map methods; @@ -36,13 +37,14 @@ public final class PainlessClass { public final PainlessMethod functionalMethod; - PainlessClass(Map constructors, + PainlessClass(Map constructors, Map staticMethods, Map methods, Map staticFields, Map fields, Map getterMethodHandles, Map setterMethodHandles, PainlessMethod functionalMethod) { this.constructors = Collections.unmodifiableMap(constructors); + this.staticMethods = Collections.unmodifiableMap(staticMethods); this.methods = Collections.unmodifiableMap(methods); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java index 2f41ed5dca8f1..9cc4e0fdbb7fe 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java @@ -24,7 +24,8 @@ import java.util.Map; final class PainlessClassBuilder { - final Map constructors; + final Map constructors; + final Map staticMethods; final Map methods; @@ -38,6 +39,7 @@ final class PainlessClassBuilder { PainlessClassBuilder() { constructors = new HashMap<>(); + staticMethods = new HashMap<>(); methods = new HashMap<>(); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessConstructor.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessConstructor.java new file mode 100644 index 0000000000000..cf59ca0725265 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessConstructor.java @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.painless.lookup; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Constructor; +import java.util.List; + +public class PainlessConstructor { + public final Constructor javaConstructor; + public final List> typeParameters; + public final MethodHandle methodHandle; + + PainlessConstructor(Constructor javaConstructor, List> typeParameters, MethodHandle methodHandle) { + this.javaConstructor = javaConstructor; + this.typeParameters = typeParameters; + this.methodHandle = methodHandle; + } +} 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 3675cc7cd0f20..392c6acc5aa84 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 @@ -47,7 +47,39 @@ import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToJavaType; import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typesToCanonicalTypeNames; -public class PainlessLookupBuilder { +public final class PainlessLookupBuilder { + + private static class PainlessConstructorCacheKey { + + private final Class targetType; + private final List> typeParameters; + + private PainlessConstructorCacheKey(Class targetType, List> typeParameters) { + this.targetType = targetType; + this.typeParameters = Collections.unmodifiableList(typeParameters); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + PainlessMethodCacheKey that = (PainlessMethodCacheKey)object; + + return Objects.equals(targetType, that.targetType) && + Objects.equals(typeParameters, that.typeParameters); + } + + @Override + public int hashCode() { + return Objects.hash(targetType, typeParameters); + } + } private static class PainlessMethodCacheKey { @@ -119,8 +151,9 @@ public int hashCode() { } } - private static final Map painlessMethodCache = new HashMap<>(); - private static final Map painlessFieldCache = new HashMap<>(); + private static final Map painlessConstuctorCache = new HashMap<>(); + private static final Map painlessMethodCache = new HashMap<>(); + private static final Map painlessFieldCache = new HashMap<>(); private static final Pattern CLASS_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][._a-zA-Z0-9]*$"); private static final Pattern METHOD_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][_a-zA-Z0-9]*$"); @@ -342,11 +375,9 @@ public void addPainlessConstructor(Class targetClass, List> typePara "[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(typeParameters) + "] not found", nsme); } - String painlessMethodKey = buildPainlessMethodKey(CONSTRUCTOR_NAME, typeParametersSize); - PainlessMethod painlessConstructor = painlessClassBuilder.constructors.get(painlessMethodKey); + PainlessConstructor painlessConstructor = painlessClassBuilder.constructors.get(typeParametersSize); if (painlessConstructor == null) { - org.objectweb.asm.commons.Method asmConstructor = org.objectweb.asm.commons.Method.getMethod(javaConstructor); MethodHandle methodHandle; try { @@ -356,17 +387,16 @@ public void addPainlessConstructor(Class targetClass, List> typePara "[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(typeParameters) + "] not found", iae); } - painlessConstructor = painlessMethodCache.computeIfAbsent( - new PainlessMethodCacheKey(targetClass, CONSTRUCTOR_NAME, typeParameters), - key -> new PainlessMethod(CONSTRUCTOR_NAME, targetClass, null, void.class, typeParameters, - asmConstructor, javaConstructor.getModifiers(), methodHandle) + painlessConstructor = painlessConstuctorCache.computeIfAbsent( + new PainlessConstructorCacheKey(targetClass, typeParameters), + key -> new PainlessConstructor(javaConstructor, typeParameters, methodHandle) ); - painlessClassBuilder.constructors.put(painlessMethodKey, painlessConstructor); - } else if (painlessConstructor.arguments.equals(typeParameters) == false){ + painlessClassBuilder.constructors.put(typeParametersSize, painlessConstructor); + } else if (painlessConstructor.typeParameters.equals(typeParameters) == false){ throw new IllegalArgumentException("cannot have constructors " + "[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(typeParameters) + "] and " + - "[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(painlessConstructor.arguments) + "] " + + "[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(painlessConstructor.typeParameters) + "] " + "with the same arity and different type parameters"); } } 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 3321de94a267f..adeeb31b3accd 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 @@ -51,53 +51,7 @@ public PainlessMethod(String name, Class target, Class augmentation, Class this.handle = handle; } - /** - * Returns MethodType for this method. - *

- * This works even for user-defined Methods (where the MethodHandle is null). - */ - public MethodType getMethodType() { - // we have a methodhandle already (e.g. whitelisted class) - // just return its type - if (handle != null) { - return handle.type(); - } - // otherwise compute it - final Class params[]; - final Class returnValue; - if (augmentation != null) { - // static method disguised as virtual/interface method - params = new Class[1 + arguments.size()]; - params[0] = augmentation; - for (int i = 0; i < arguments.size(); i++) { - params[i + 1] = PainlessLookupUtility.typeToJavaType(arguments.get(i)); - } - returnValue = PainlessLookupUtility.typeToJavaType(rtn); - } else if (Modifier.isStatic(modifiers)) { - // static method: straightforward copy - params = new Class[arguments.size()]; - for (int i = 0; i < arguments.size(); i++) { - params[i] = PainlessLookupUtility.typeToJavaType(arguments.get(i)); - } - returnValue = PainlessLookupUtility.typeToJavaType(rtn); - } else if ("".equals(name)) { - // constructor: returns the owner class - params = new Class[arguments.size()]; - for (int i = 0; i < arguments.size(); i++) { - params[i] = PainlessLookupUtility.typeToJavaType(arguments.get(i)); - } - returnValue = target; - } else { - // virtual/interface method: add receiver class - params = new Class[1 + arguments.size()]; - params[0] = target; - for (int i = 0; i < arguments.size(); i++) { - params[i + 1] = PainlessLookupUtility.typeToJavaType(arguments.get(i)); - } - returnValue = PainlessLookupUtility.typeToJavaType(rtn); - } - return MethodType.methodType(returnValue, params); - } + public void write(MethodWriter writer) { final org.objectweb.asm.Type type; 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 7b35bc1b48ee5..986cb0def68dd 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 @@ -83,12 +83,12 @@ void analyze(Locals locals) { // 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); - Class to = ref.delegateMethod.arguments.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.delegateMethod.rtn, ref.interfaceMethod.rtn, false, true); + AnalyzerCaster.getLegalCast(location, ref.delegateReturnType, ref.interfaceMethod.rtn, false, true); } } catch (IllegalArgumentException e) { throw createError(e); 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 e0af653d2098a..1dec938adeb50 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 @@ -23,10 +23,12 @@ import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; +import org.elasticsearch.painless.lookup.PainlessConstructor; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.lookup.def; import org.objectweb.asm.Type; +import org.objectweb.asm.commons.Method; import java.util.ArrayList; import java.util.List; @@ -38,7 +40,7 @@ public final class EListInit extends AExpression { private final List values; - private PainlessMethod constructor = null; + private PainlessConstructor constructor = null; private PainlessMethod method = null; public EListInit(Location location, List values) { @@ -62,8 +64,7 @@ void analyze(Locals locals) { actual = ArrayList.class; - constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors - .get(PainlessLookupUtility.buildPainlessMethodKey("", 0)); + constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(0); if (constructor == null) { throw createError(new IllegalStateException("Illegal tree structure.")); @@ -92,7 +93,8 @@ void write(MethodWriter writer, Globals globals) { writer.newInstance(MethodWriter.getType(actual)); writer.dup(); - writer.invokeConstructor(Type.getType(constructor.target), constructor.method); + writer.invokeConstructor( + Type.getType(constructor.javaConstructor.getDeclaringClass()), Method.getMethod(constructor.javaConstructor)); for (AExpression value : values) { writer.dup(); 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 d81f08dc3cc54..5013cc302c5b5 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 @@ -23,10 +23,12 @@ import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; +import org.elasticsearch.painless.lookup.PainlessConstructor; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.lookup.def; import org.objectweb.asm.Type; +import org.objectweb.asm.commons.Method; import java.util.HashMap; import java.util.List; @@ -39,7 +41,7 @@ public final class EMapInit extends AExpression { private final List keys; private final List values; - private PainlessMethod constructor = null; + private PainlessConstructor constructor = null; private PainlessMethod method = null; public EMapInit(Location location, List keys, List values) { @@ -68,8 +70,7 @@ void analyze(Locals locals) { actual = HashMap.class; - constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors - .get(PainlessLookupUtility.buildPainlessMethodKey("", 0)); + constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(0); if (constructor == null) { throw createError(new IllegalStateException("Illegal tree structure.")); @@ -111,7 +112,8 @@ void write(MethodWriter writer, Globals globals) { writer.newInstance(MethodWriter.getType(actual)); writer.dup(); - writer.invokeConstructor(Type.getType(constructor.target), constructor.method); + writer.invokeConstructor( + Type.getType(constructor.javaConstructor.getDeclaringClass()), Method.getMethod(constructor.javaConstructor)); for (int index = 0; index < keys.size(); ++index) { AExpression key = keys.get(index); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java index f092a17c9fc4a..f9d882f39efec 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java @@ -24,9 +24,10 @@ import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; import org.elasticsearch.painless.lookup.PainlessClass; +import org.elasticsearch.painless.lookup.PainlessConstructor; import org.elasticsearch.painless.lookup.PainlessLookupUtility; -import org.elasticsearch.painless.lookup.PainlessMethod; import org.objectweb.asm.Type; +import org.objectweb.asm.commons.Method; import java.util.List; import java.util.Objects; @@ -40,7 +41,7 @@ public final class ENewObj extends AExpression { private final String type; private final List arguments; - private PainlessMethod constructor; + private PainlessConstructor constructor; public ENewObj(Location location, String type, List arguments) { super(location); @@ -65,16 +66,16 @@ void analyze(Locals locals) { } PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual); - constructor = struct.constructors.get(PainlessLookupUtility.buildPainlessMethodKey("", arguments.size())); + constructor = struct.constructors.get(arguments.size()); if (constructor != null) { - Class[] types = new Class[constructor.arguments.size()]; - constructor.arguments.toArray(types); + Class[] types = new Class[constructor.typeParameters.size()]; + constructor.typeParameters.toArray(types); - if (constructor.arguments.size() != arguments.size()) { + if (constructor.typeParameters.size() != arguments.size()) { throw createError(new IllegalArgumentException( "When calling constructor on type [" + PainlessLookupUtility.typeToCanonicalTypeName(actual) + "] " + - "expected [" + constructor.arguments.size() + "] arguments, but found [" + arguments.size() + "].")); + "expected [" + constructor.typeParameters.size() + "] arguments, but found [" + arguments.size() + "].")); } for (int argument = 0; argument < arguments.size(); ++argument) { @@ -107,7 +108,8 @@ void write(MethodWriter writer, Globals globals) { argument.write(writer, globals); } - writer.invokeConstructor(Type.getType(constructor.target), constructor.method); + writer.invokeConstructor( + Type.getType(constructor.javaConstructor.getDeclaringClass()), Method.getMethod(constructor.javaConstructor)); } @Override From 6469598db78a22dadcc53159c8b1a9e76ffc85d3 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Fri, 27 Jul 2018 09:43:31 -0700 Subject: [PATCH 02/33] Add method type to method. --- .../elasticsearch/painless/FunctionRef.java | 6 +-- .../lookup/PainlessLookupBuilder.java | 13 +++-- .../painless/lookup/PainlessMethod.java | 54 ++----------------- .../painless/node/SFunction.java | 4 +- 4 files changed, 20 insertions(+), 57 deletions(-) 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 aa72724b93029..7b7e8d25f392d 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 @@ -93,12 +93,12 @@ public FunctionRef(PainlessLookup painlessLookup, Class expected, String type * @param numCaptures number of captured arguments */ public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessMethod delegateMethod, int numCaptures) { - MethodType delegateMethodType = delegateMethod.getMethodType(); + MethodType delegateMethodType = delegateMethod.methodType; interfaceMethodName = interfaceMethod.name; factoryMethodType = MethodType.methodType(expected, delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); - interfaceMethodType = interfaceMethod.getMethodType().dropParameterTypes(0, 1); + interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1); // the Painless$Script class can be inferred if owner is null if (delegateMethod.target == null) { @@ -142,7 +142,7 @@ public FunctionRef(Class expected, interfaceMethodName = interfaceMethod.name; factoryMethodType = MethodType.methodType(expected, delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); - interfaceMethodType = interfaceMethod.getMethodType().dropParameterTypes(0, 1); + interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1); delegateClassName = CLASS_NAME; delegateInvokeType = H_INVOKESTATIC; 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 3675cc7cd0f20..ba48fbea734cf 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 @@ -27,6 +27,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -356,10 +357,12 @@ public void addPainlessConstructor(Class targetClass, List> typePara "[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(typeParameters) + "] not found", iae); } + MethodType methodType = methodHandle.type(); + painlessConstructor = painlessMethodCache.computeIfAbsent( new PainlessMethodCacheKey(targetClass, CONSTRUCTOR_NAME, typeParameters), key -> new PainlessMethod(CONSTRUCTOR_NAME, targetClass, null, void.class, typeParameters, - asmConstructor, javaConstructor.getModifiers(), methodHandle) + asmConstructor, javaConstructor.getModifiers(), methodHandle, methodType) ); painlessClassBuilder.constructors.put(painlessMethodKey, painlessConstructor); @@ -516,10 +519,12 @@ public void addPainlessMethod(Class targetClass, Class augmentedClass, Str "[" + methodName + "], " + typesToCanonicalTypeNames(typeParameters) + "] not found", iae); } + MethodType methodType = methodHandle.type(); + painlessMethod = painlessMethodCache.computeIfAbsent( new PainlessMethodCacheKey(targetClass, methodName, typeParameters), key -> new PainlessMethod(methodName, targetClass, null, returnType, - typeParameters, asmMethod, javaMethod.getModifiers(), methodHandle)); + typeParameters, asmMethod, javaMethod.getModifiers(), methodHandle, methodType)); painlessClassBuilder.staticMethods.put(painlessMethodKey, painlessMethod); } else if ((painlessMethod.name.equals(methodName) && painlessMethod.rtn == returnType && @@ -557,10 +562,12 @@ public void addPainlessMethod(Class targetClass, Class augmentedClass, Str } } + MethodType methodType = methodHandle.type(); + painlessMethod = painlessMethodCache.computeIfAbsent( new PainlessMethodCacheKey(targetClass, methodName, typeParameters), key -> new PainlessMethod(methodName, targetClass, augmentedClass, returnType, - typeParameters, asmMethod, javaMethod.getModifiers(), methodHandle)); + typeParameters, asmMethod, javaMethod.getModifiers(), methodHandle, methodType)); painlessClassBuilder.methods.put(painlessMethodKey, painlessMethod); } else if ((painlessMethod.name.equals(methodName) && painlessMethod.rtn == returnType && 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 3321de94a267f..904cf06907fde 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 @@ -38,9 +38,10 @@ public class PainlessMethod { public final org.objectweb.asm.commons.Method method; public final int modifiers; public final MethodHandle handle; + 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) { + org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle, MethodType methodType) { this.name = name; this.augmentation = augmentation; this.target = target; @@ -49,54 +50,7 @@ public PainlessMethod(String name, Class target, Class augmentation, Class this.method = method; this.modifiers = modifiers; this.handle = handle; - } - - /** - * Returns MethodType for this method. - *

- * This works even for user-defined Methods (where the MethodHandle is null). - */ - public MethodType getMethodType() { - // we have a methodhandle already (e.g. whitelisted class) - // just return its type - if (handle != null) { - return handle.type(); - } - // otherwise compute it - final Class params[]; - final Class returnValue; - if (augmentation != null) { - // static method disguised as virtual/interface method - params = new Class[1 + arguments.size()]; - params[0] = augmentation; - for (int i = 0; i < arguments.size(); i++) { - params[i + 1] = PainlessLookupUtility.typeToJavaType(arguments.get(i)); - } - returnValue = PainlessLookupUtility.typeToJavaType(rtn); - } else if (Modifier.isStatic(modifiers)) { - // static method: straightforward copy - params = new Class[arguments.size()]; - for (int i = 0; i < arguments.size(); i++) { - params[i] = PainlessLookupUtility.typeToJavaType(arguments.get(i)); - } - returnValue = PainlessLookupUtility.typeToJavaType(rtn); - } else if ("".equals(name)) { - // constructor: returns the owner class - params = new Class[arguments.size()]; - for (int i = 0; i < arguments.size(); i++) { - params[i] = PainlessLookupUtility.typeToJavaType(arguments.get(i)); - } - returnValue = target; - } else { - // virtual/interface method: add receiver class - params = new Class[1 + arguments.size()]; - params[0] = target; - for (int i = 0; i < arguments.size(); i++) { - params[i + 1] = PainlessLookupUtility.typeToJavaType(arguments.get(i)); - } - returnValue = PainlessLookupUtility.typeToJavaType(rtn); - } - return MethodType.methodType(returnValue, params); + this.methodType = methodType; } public void write(MethodWriter writer) { @@ -118,7 +72,7 @@ public void write(MethodWriter writer) { // 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, getMethodType().toMethodDescriptorString(), true); + type.getInternalName(), name, methodType.toMethodDescriptorString(), true); } else { writer.invokeStatic(type, method); } 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 7c243e296c7e3..600ca95504ac6 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 @@ -145,9 +145,11 @@ 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()); - this.method = new PainlessMethod(name, null, null, rtnType, paramTypes, method, Modifier.STATIC | Modifier.PRIVATE, null); + MethodType methodType = MethodType.methodType(PainlessLookupUtility.typeToJavaType(rtnType), paramClasses); + this.method = new PainlessMethod(name, null, null, rtnType, paramTypes, method, modifiers, null, methodType); } @Override From 641c383af51da6389b76b866dd59a66a8041110a Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Fri, 27 Jul 2018 10:42:59 -0700 Subject: [PATCH 03/33] Add PainlessConstructor. --- .../java/org/elasticsearch/painless/Def.java | 2 +- .../elasticsearch/painless/FunctionRef.java | 104 +++++++++++++----- .../lookup/PainlessLookupBuilder.java | 9 +- .../lookup/PainlessLookupUtility.java | 7 ++ .../painless/node/ECapturingFunctionRef.java | 2 +- .../painless/node/EFunctionRef.java | 2 +- .../painless/PainlessDocGenerator.java | 99 +++++++++++++++-- 7 files changed, 184 insertions(+), 41 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index 8a90f53b4fdfa..863f973a5c2f3 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 @@ -368,7 +368,7 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku ref = new FunctionRef(clazz, interfaceMethod, call, handle.type(), captures.length); } else { // whitelist lookup - ref = new FunctionRef(painlessLookup, clazz, type, call, captures.length); + ref = FunctionRef.getFunctionRef(painlessLookup, clazz, type, call, captures.length); } final CallSite callSite = LambdaBootstrap.lambdaBootstrap( methodHandlesLookup, 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 a3ec943e78a88..2752b046f8e24 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 @@ -20,12 +20,14 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.lookup.PainlessClass; +import org.elasticsearch.painless.lookup.PainlessConstructor; import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; import org.objectweb.asm.Type; import java.lang.invoke.MethodType; +import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.util.List; @@ -83,9 +85,45 @@ public class FunctionRef { * @param call the right hand side of a method reference expression * @param numCaptures number of captured arguments */ - public FunctionRef(PainlessLookup painlessLookup, Class expected, String type, String call, int numCaptures) { - this(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod, - lookup(painlessLookup, expected, type, call, numCaptures > 0), numCaptures); + public static FunctionRef getFunctionRef(PainlessLookup painlessLookup, Class expected, String type, String call, int numCaptures) { + if ("new".equals(call)) { + return new FunctionRef(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod, + lookup(painlessLookup, expected, type), numCaptures); + } else { + return new FunctionRef(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod, + lookup(painlessLookup, expected, type, call, numCaptures > 0), numCaptures); + } + } + + /** + * Creates a new FunctionRef (already resolved) + * @param expected functional interface type to implement + * @param interfaceMethod functional interface method + * @param delegateConstructor implementation constructor + * @param numCaptures number of captured arguments + */ + public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessConstructor delegateConstructor, int numCaptures) { + Constructor javaConstructor = delegateConstructor.javaConstructor; + MethodType delegateMethodType = delegateConstructor.methodType; + + interfaceMethodName = interfaceMethod.name; + factoryMethodType = MethodType.methodType(expected, + delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); + interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1); + + delegateClassName = javaConstructor.getDeclaringClass().getName(); + isDelegateInterface = false; + delegateInvokeType = H_NEWINVOKESPECIAL; + delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME; + this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures); + + this.interfaceMethod = interfaceMethod; + delegateTypeParameters = delegateConstructor.typeParameters; + delegateReturnType = void.class; + + factoryDescriptor = factoryMethodType.toMethodDescriptorString(); + interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString()); + delegateType = Type.getMethodType(this.delegateMethodType.toMethodDescriptorString()); } /** @@ -115,9 +153,7 @@ public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessMe isDelegateInterface = delegateMethod.target.isInterface(); } - if ("".equals(delegateMethod.name)) { - delegateInvokeType = H_NEWINVOKESPECIAL; - } else if (Modifier.isStatic(delegateMethod.modifiers)) { + if (Modifier.isStatic(delegateMethod.modifiers)) { delegateInvokeType = H_INVOKESTATIC; } else if (delegateMethod.target.isInterface()) { delegateInvokeType = H_INVOKEINTERFACE; @@ -163,6 +199,29 @@ public FunctionRef(Class expected, delegateType = null; } + /** + * Looks up {@code type} from the whitelist, and returns a matching constructor. + */ + private static PainlessConstructor lookup(PainlessLookup painlessLookup, Class expected, String type) { + // check its really a functional interface + // for e.g. Comparable + PainlessMethod method = painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod; + if (method == null) { + throw new IllegalArgumentException("Cannot convert function reference [" + type + "::new] " + + "to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface"); + } + + // lookup requested constructor + PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.getJavaClassFromPainlessType(type)); + PainlessConstructor impl = struct.constructors.get(PainlessLookupUtility.buildPainlessConstructorKey(method.arguments.size())); + + if (impl == null) { + throw new IllegalArgumentException("Unknown reference [" + type + "::new] matching [" + expected + "]"); + } + + return impl; + } + /** * Looks up {@code type::call} from the whitelist, and returns a matching method. */ @@ -179,27 +238,22 @@ private static PainlessMethod lookup(PainlessLookup painlessLookup, Class exp // lookup requested method PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.getJavaClassFromPainlessType(type)); final PainlessMethod impl; - // ctor ref - if ("new".equals(call)) { - impl = null;//TODO: struct.constructors.get(PainlessLookupUtility.buildPainlessMethodKey("", method.arguments.size())); - } else { - // look for a static impl first - PainlessMethod staticImpl = - struct.staticMethods.get(PainlessLookupUtility.buildPainlessMethodKey(call, method.arguments.size())); - if (staticImpl == null) { - // otherwise a virtual impl - final int arity; - if (receiverCaptured) { - // receiver captured - arity = method.arguments.size(); - } else { - // receiver passed - arity = method.arguments.size() - 1; - } - impl = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey(call, arity)); + // look for a static impl first + PainlessMethod staticImpl = + struct.staticMethods.get(PainlessLookupUtility.buildPainlessMethodKey(call, method.arguments.size())); + if (staticImpl == null) { + // otherwise a virtual impl + final int arity; + if (receiverCaptured) { + // receiver captured + arity = method.arguments.size(); } else { - impl = staticImpl; + // receiver passed + arity = method.arguments.size() - 1; } + impl = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey(call, arity)); + } else { + impl = staticImpl; } if (impl == null) { throw new IllegalArgumentException("Unknown reference [" + type + "::" + call + "] matching " + 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 1ea98c50fc67c..28ee228601a22 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 @@ -40,8 +40,8 @@ import java.util.Objects; import java.util.regex.Pattern; -import static org.elasticsearch.painless.lookup.PainlessLookupUtility.CONSTRUCTOR_NAME; import static org.elasticsearch.painless.lookup.PainlessLookupUtility.DEF_CLASS_NAME; +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessConstructorKey; import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessFieldKey; import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessMethodKey; import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName; @@ -70,7 +70,7 @@ public boolean equals(Object object) { return false; } - PainlessMethodCacheKey that = (PainlessMethodCacheKey)object; + PainlessConstructorCacheKey that = (PainlessConstructorCacheKey)object; return Objects.equals(targetType, that.targetType) && Objects.equals(typeParameters, that.typeParameters); @@ -376,7 +376,8 @@ public void addPainlessConstructor(Class targetClass, List> typePara "[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(typeParameters) + "] not found", nsme); } - PainlessConstructor painlessConstructor = painlessClassBuilder.constructors.get(typeParametersSize); + int painlessConstructorKey = buildPainlessConstructorKey(typeParametersSize); + PainlessConstructor painlessConstructor = painlessClassBuilder.constructors.get(painlessConstructorKey); if (painlessConstructor == null) { MethodHandle methodHandle; @@ -395,7 +396,7 @@ public void addPainlessConstructor(Class targetClass, List> typePara key -> new PainlessConstructor(javaConstructor, typeParameters, methodHandle, methodType) ); - painlessClassBuilder.constructors.put(typeParametersSize, painlessConstructor); + painlessClassBuilder.constructors.put(painlessConstructorKey, painlessConstructor); } else if (painlessConstructor.typeParameters.equals(typeParameters) == false){ throw new IllegalArgumentException("cannot have constructors " + "[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(typeParameters) + "] and " + diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java index 86d3f87663867..7a4c951235e13 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java @@ -336,6 +336,13 @@ public static boolean isConstantType(Class type) { type == String.class; } + /** + * Constructs a painless constructor key used to lookup painless constructors from a painless class. + */ + public static int buildPainlessConstructorKey(int constructorArity) { + return constructorArity; + } + /** * Constructs a painless method key used to lookup painless methods from a painless class. */ 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 986cb0def68dd..50b685c1ca52f 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 @@ -77,7 +77,7 @@ void analyze(Locals locals) { // static case if (captured.clazz != def.class) { try { - ref = new FunctionRef(locals.getPainlessLookup(), expected, + ref = FunctionRef.getFunctionRef(locals.getPainlessLookup(), expected, PainlessLookupUtility.typeToCanonicalTypeName(captured.clazz), call, 1); // check casts between the interface method and the delegate method are legal 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 d787db5d41c92..437fb84b79e2f 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 @@ -90,7 +90,7 @@ void analyze(Locals locals) { } } else { // whitelist lookup - ref = new FunctionRef(locals.getPainlessLookup(), expected, type, call, 0); + ref = FunctionRef.getFunctionRef(locals.getPainlessLookup(), expected, type, call, 0); } } catch (IllegalArgumentException e) { 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 ff0d423117564..654291543017f 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 @@ -24,6 +24,7 @@ import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.painless.lookup.PainlessClass; +import org.elasticsearch.painless.lookup.PainlessConstructor; import org.elasticsearch.painless.lookup.PainlessField; import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.lookup.PainlessLookupBuilder; @@ -57,7 +58,8 @@ public class PainlessDocGenerator { 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 NUMBER_OF_ARGS = comparing(m -> m.arguments.size()); + private static final Comparator METHOD_NUMBER_OF_PARAMS = comparing(m -> m.arguments.size()); + private static final Comparator CONSTRUCTOR_NUMBER_OF_PARAMS = comparing(m -> m.typeParameters.size()); public static void main(String[] args) throws IOException { Path apiRootPath = PathUtils.get(args[0]); @@ -103,12 +105,15 @@ public static void main(String[] args) throws IOException { Consumer documentField = field -> PainlessDocGenerator.documentField(typeStream, field); Consumer documentMethod = method -> PainlessDocGenerator.documentMethod(typeStream, method); + Consumer documentConstructor = + constructor -> PainlessDocGenerator.documentConstructor(typeStream, constructor); struct.staticFields.values().stream().sorted(FIELD_NAME).forEach(documentField); struct.fields.values().stream().sorted(FIELD_NAME).forEach(documentField); - struct.staticMethods.values().stream().sorted(METHOD_NAME.thenComparing(NUMBER_OF_ARGS)).forEach(documentMethod); - struct.constructors.values().stream().sorted(NUMBER_OF_ARGS).forEach(documentMethod); + struct.staticMethods.values().stream().sorted( + METHOD_NAME.thenComparing(METHOD_NUMBER_OF_PARAMS)).forEach(documentMethod); + struct.constructors.values().stream().sorted(CONSTRUCTOR_NUMBER_OF_PARAMS).forEach(documentConstructor); Map> inherited = new TreeMap<>(); - struct.methods.values().stream().sorted(METHOD_NAME.thenComparing(NUMBER_OF_ARGS)).forEach(method -> { + struct.methods.values().stream().sorted(METHOD_NAME.thenComparing(METHOD_NUMBER_OF_PARAMS)).forEach(method -> { if (method.target == clazz) { documentMethod(typeStream, method); } else { @@ -164,6 +169,41 @@ private static void documentField(PrintStream stream, PainlessField field) { stream.println(); } + /** + * Document a constructor. + */ + private static void documentConstructor(PrintStream stream, PainlessConstructor constructor) { + stream.print("* ++[["); + emitAnchor(stream, constructor); + stream.print("]]"); + + String javadocRoot = javadocRoot(constructor.javaConstructor.getDeclaringClass()); + emitJavadocLink(stream, javadocRoot, constructor); + stream.print('['); + + stream.print(constructorName(constructor)); + + stream.print("]("); + boolean first = true; + for (Class arg : constructor.typeParameters) { + if (first) { + first = false; + } else { + stream.print(", "); + } + emitType(stream, arg); + } + stream.print(")++"); + + if (javadocRoot.equals("java8")) { + stream.print(" ("); + emitJavadocLink(stream, "java9", constructor); + stream.print("[java 9])"); + } + + stream.println(); + } + /** * Document a method. */ @@ -176,10 +216,8 @@ private static void documentMethod(PrintStream stream, PainlessMethod method) { stream.print("static "); } - if (false == method.name.equals("")) { - emitType(stream, method.rtn); - stream.print(' '); - } + emitType(stream, method.rtn); + stream.print(' '); String javadocRoot = javadocRoot(method); emitJavadocLink(stream, javadocRoot, method); @@ -216,6 +254,17 @@ private static void emitAnchor(PrintStream stream, Class clazz) { stream.print(PainlessLookupUtility.typeToCanonicalTypeName(clazz).replace('.', '-')); } + /** + * Anchor text for a {@link PainlessConstructor}. + */ + private static void emitAnchor(PrintStream stream, PainlessConstructor constructor) { + emitAnchor(stream, constructor.javaConstructor.getDeclaringClass()); + stream.print('-'); + stream.print(constructorName(constructor)); + stream.print('-'); + stream.print(constructor.typeParameters.size()); + } + /** * Anchor text for a {@link PainlessMethod}. */ @@ -236,8 +285,12 @@ private static void emitAnchor(PrintStream stream, PainlessField field) { stream.print(field.name); } + private static String constructorName(PainlessConstructor constructor) { + return PainlessLookupUtility.typeToCanonicalTypeName(constructor.javaConstructor.getDeclaringClass()); + } + private static String methodName(PainlessMethod method) { - return method.name.equals("") ? PainlessLookupUtility.typeToCanonicalTypeName(method.target) : method.name; + return PainlessLookupUtility.typeToCanonicalTypeName(method.target); } /** @@ -269,6 +322,34 @@ private static void emitStruct(PrintStream stream, Class clazz) { } } + /** + * Emit an external link to Javadoc for a {@link PainlessMethod}. + * + * @param root name of the root uri variable + */ + private static void emitJavadocLink(PrintStream stream, String root, PainlessConstructor constructor) { + stream.print("link:{"); + stream.print(root); + stream.print("-javadoc}/"); + stream.print(classUrlPath(constructor.javaConstructor.getDeclaringClass())); + stream.print(".html#"); + stream.print(constructorName(constructor)); + stream.print("%2D"); + boolean first = true; + for (Class clazz: constructor.typeParameters) { + if (first) { + first = false; + } else { + stream.print("%2D"); + } + stream.print(clazz.getName()); + if (clazz.isArray()) { + stream.print(":A"); + } + } + stream.print("%2D"); + } + /** * Emit an external link to Javadoc for a {@link PainlessMethod}. * From 5b449d891cbb4f949c8c4d189cd6e7771900dd90 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Fri, 27 Jul 2018 14:17:59 -0700 Subject: [PATCH 04/33] Clean up method. --- .../java/org/elasticsearch/painless/Def.java | 15 +++-- .../elasticsearch/painless/FunctionRef.java | 67 +++++++++++++------ .../org/elasticsearch/painless/Locals.java | 42 +++++++++--- .../elasticsearch/painless/MethodWriter.java | 24 +++++++ .../lookup/PainlessLookupBuilder.java | 35 +++++----- .../painless/lookup/PainlessMethod.java | 67 ++++--------------- .../painless/node/ECallLocal.java | 11 +-- .../painless/node/ECapturingFunctionRef.java | 8 +-- .../painless/node/EFunctionRef.java | 14 ++-- .../elasticsearch/painless/node/ELambda.java | 23 ++++--- .../painless/node/EListInit.java | 2 +- .../elasticsearch/painless/node/EMapInit.java | 2 +- .../painless/node/PSubCallInvoke.java | 8 +-- .../painless/node/PSubListShortcut.java | 25 +++---- .../painless/node/PSubMapShortcut.java | 32 ++++----- .../painless/node/PSubShortcut.java | 24 +++---- .../painless/node/SFunction.java | 31 ++++----- .../elasticsearch/painless/node/SSource.java | 12 ++-- .../painless/node/SSubEachIterable.java | 2 +- .../painless/PainlessDocGenerator.java | 32 ++++----- 20 files changed, 248 insertions(+), 228 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index 863f973a5c2f3..e4e4414afb9c5 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.javaMethod.getDeclaringClass()), + 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 2752b046f8e24..dff5ff90cf22b 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; @@ -106,7 +107,7 @@ public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessCo Constructor javaConstructor = delegateConstructor.javaConstructor; MethodType delegateMethodType = delegateConstructor.methodType; - interfaceMethodName = interfaceMethod.name; + interfaceMethodName = interfaceMethod.javaMethod.getName(); factoryMethodType = MethodType.methodType(expected, delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1); @@ -136,37 +137,59 @@ public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessCo public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessMethod delegateMethod, int numCaptures) { MethodType delegateMethodType = delegateMethod.methodType; - interfaceMethodName = interfaceMethod.name; + interfaceMethodName = interfaceMethod.javaMethod.getName(); 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(); - } + delegateClassName = delegateMethod.javaMethod.getName(); + isDelegateInterface = delegateMethod.javaMethod.getDeclaringClass().isInterface(); - if (Modifier.isStatic(delegateMethod.modifiers)) { + if (Modifier.isStatic(delegateMethod.javaMethod.getModifiers())) { delegateInvokeType = H_INVOKESTATIC; - } else if (delegateMethod.target.isInterface()) { + } else if (delegateMethod.javaMethod.getDeclaringClass().isInterface()) { delegateInvokeType = H_INVOKEINTERFACE; } else { delegateInvokeType = H_INVOKEVIRTUAL; } + delegateMethodName = delegateMethod.javaMethod.getName(); + this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures); + + this.interfaceMethod = interfaceMethod; + delegateTypeParameters = delegateMethod.typeParameters; + delegateReturnType = delegateMethod.returnType; + + factoryDescriptor = factoryMethodType.toMethodDescriptorString(); + interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString()); + 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; + + interfaceMethodName = interfaceMethod.javaMethod.getName(); + factoryMethodType = MethodType.methodType(expected, + delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); + interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1); + + delegateClassName = CLASS_NAME; + isDelegateInterface = false; + delegateInvokeType = H_INVOKESTATIC; + delegateMethodName = delegateMethod.name; this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures); this.interfaceMethod = interfaceMethod; - delegateTypeParameters = delegateMethod.arguments; - delegateReturnType = delegateMethod.rtn; + delegateTypeParameters = delegateMethod.typeParameters; + delegateReturnType = delegateMethod.returnType; factoryDescriptor = factoryMethodType.toMethodDescriptorString(); interfaceType = Type.getMethodType(interfaceMethodType.toMethodDescriptorString()); @@ -179,7 +202,7 @@ public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessMe */ public FunctionRef(Class expected, PainlessMethod interfaceMethod, String delegateMethodName, MethodType delegateMethodType, int numCaptures) { - interfaceMethodName = interfaceMethod.name; + interfaceMethodName = interfaceMethod.javaMethod.getName(); factoryMethodType = MethodType.methodType(expected, delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1); @@ -213,7 +236,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 28ee228601a22..12581e27c9701 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 50b685c1ca52f..b0c8ad96e8337 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 437fb84b79e2f..91a92aa7e7f6c 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 1dec938adeb50..d6b0c592bf10b 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 @@ -99,7 +99,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 5013cc302c5b5..5c3c3e0ad4baa 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 @@ -122,7 +122,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..b9336fb5aac25 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,9 @@ 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); + 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 +176,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 +191,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 +211,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 +224,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); } /** From 266a92d23f38644218caa2557584e91ff7a5422a Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Fri, 27 Jul 2018 14:49:10 -0700 Subject: [PATCH 05/33] Fixes. --- .../src/main/java/org/elasticsearch/painless/Def.java | 2 +- .../src/main/java/org/elasticsearch/painless/FunctionRef.java | 2 +- .../main/java/org/elasticsearch/painless/node/SFunction.java | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index e4e4414afb9c5..5c9cb9c747531 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 @@ -334,7 +334,7 @@ static MethodHandle lookupReference(PainlessLookup painlessLookup, MethodHandles int arity = interfaceMethod.typeParameters.size(); PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity); return lookupReferenceInternal(painlessLookup, methodHandlesLookup, interfaceType, - PainlessLookupUtility.typeToCanonicalTypeName(implMethod.javaMethod.getDeclaringClass()), + PainlessLookupUtility.typeToCanonicalTypeName(implMethod.targetClass), implMethod.javaMethod.getName(), receiverClass); } 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 dff5ff90cf22b..304b03562409c 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 @@ -142,7 +142,7 @@ public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessMe delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1); - delegateClassName = delegateMethod.javaMethod.getName(); + delegateClassName = delegateMethod.javaMethod.getDeclaringClass().getName(); isDelegateInterface = delegateMethod.javaMethod.getDeclaringClass().isInterface(); if (Modifier.isStatic(delegateMethod.javaMethod.getModifiers())) { 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 b9336fb5aac25..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 @@ -146,6 +146,7 @@ void generateSignature(PainlessLookup painlessLookup) { } } + typeParameters = paramTypes; methodType = MethodType.methodType(PainlessLookupUtility.typeToJavaType(returnType), paramClasses); method = new org.objectweb.asm.commons.Method(name, MethodType.methodType( PainlessLookupUtility.typeToJavaType(returnType), paramClasses).toMethodDescriptorString()); From 2875c485247b31fa291d4f4c0432cd77bceaf4f6 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Fri, 27 Jul 2018 15:31:36 -0700 Subject: [PATCH 06/33] Clean up fields. --- .../painless/lookup/PainlessField.java | 28 +++++----- .../lookup/PainlessLookupBuilder.java | 51 +++++++++---------- .../painless/node/PSubField.java | 34 ++++++++----- .../painless/PainlessDocGenerator.java | 18 +++---- 4 files changed, 66 insertions(+), 65 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessField.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessField.java index f316e1438ecb9..a55d6c3730ebd 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessField.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessField.java @@ -20,24 +20,20 @@ package org.elasticsearch.painless.lookup; import java.lang.invoke.MethodHandle; +import java.lang.reflect.Field; public final class PainlessField { - public final String name; - public final Class target; - public final Class clazz; - public final String javaName; - public final int modifiers; - public final MethodHandle getter; - public final MethodHandle setter; + public final Field javaField; + public final Class typeParameter; - PainlessField(String name, String javaName, Class target, Class clazz, int modifiers, - MethodHandle getter, MethodHandle setter) { - this.name = name; - this.javaName = javaName; - this.target = target; - this.clazz = clazz; - this.modifiers = modifiers; - this.getter = getter; - this.setter = setter; + public final MethodHandle getterMethodHandle; + public final MethodHandle setterMethodHandle; + + PainlessField(Field javaField, Class typeParameter, MethodHandle getterMethodHandle, MethodHandle setterMethodHandle) { + this.javaField = javaField; + this.typeParameter = typeParameter; + + this.getterMethodHandle = getterMethodHandle; + this.setterMethodHandle = setterMethodHandle; } } 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 12581e27c9701..a00ca3d885dff 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 @@ -679,11 +679,20 @@ public void addPainlessField(Class targetClass, String fieldName, Class ty "for field [[" + targetCanonicalClassName + "], [" + fieldName + "]"); } + MethodHandle methodHandleGetter; + + try { + methodHandleGetter = MethodHandles.publicLookup().unreflectGetter(javaField); + } catch (IllegalAccessException iae) { + throw new IllegalArgumentException( + "getter method handle not found for field [[" + targetCanonicalClassName + "], [" + fieldName + "]]"); + } + String painlessFieldKey = buildPainlessFieldKey(fieldName); if (Modifier.isStatic(javaField.getModifiers())) { if (Modifier.isFinal(javaField.getModifiers()) == false) { - throw new IllegalArgumentException("static field [[" + targetCanonicalClassName + "]. [" + fieldName + "]] must be final"); + throw new IllegalArgumentException("static field [[" + targetCanonicalClassName + "], [" + fieldName + "]] must be final"); } PainlessField painlessField = painlessClassBuilder.staticFields.get(painlessFieldKey); @@ -691,28 +700,18 @@ public void addPainlessField(Class targetClass, String fieldName, Class ty if (painlessField == null) { painlessField = painlessFieldCache.computeIfAbsent( new PainlessFieldCacheKey(targetClass, fieldName, typeParameter), - key -> new PainlessField(fieldName, javaField.getName(), targetClass, - typeParameter, javaField.getModifiers(), null, null)); + key -> new PainlessField(javaField, typeParameter, methodHandleGetter, null)); painlessClassBuilder.staticFields.put(painlessFieldKey, painlessField); - } else if (painlessField.clazz != typeParameter) { + } else if (painlessField.typeParameter != typeParameter) { throw new IllegalArgumentException("cannot have static fields " + "[[" + targetCanonicalClassName + "], [" + fieldName + "], [" + typeToCanonicalTypeName(typeParameter) + "] and " + - "[[" + targetCanonicalClassName + "], [" + painlessField.name + "], " + - typeToCanonicalTypeName(painlessField.clazz) + "] " + - "with the same and different type parameters"); + "[[" + targetCanonicalClassName + "], [" + painlessField.javaField.getName() + "], " + + typeToCanonicalTypeName(painlessField.typeParameter) + "] " + + "with the same name and different type parameters"); } } else { - MethodHandle methodHandleGetter; - - try { - methodHandleGetter = MethodHandles.publicLookup().unreflectGetter(javaField); - } catch (IllegalAccessException iae) { - throw new IllegalArgumentException( - "getter method handle not found for field [[" + targetCanonicalClassName + "], [" + fieldName + "]]"); - } - MethodHandle methodHandleSetter; try { @@ -727,17 +726,16 @@ public void addPainlessField(Class targetClass, String fieldName, Class ty if (painlessField == null) { painlessField = painlessFieldCache.computeIfAbsent( new PainlessFieldCacheKey(targetClass, painlessFieldKey, typeParameter), - key -> new PainlessField(fieldName, javaField.getName(), targetClass, - typeParameter, javaField.getModifiers(), methodHandleGetter, methodHandleSetter)); + key -> new PainlessField(javaField, typeParameter, methodHandleGetter, methodHandleSetter)); painlessClassBuilder.fields.put(fieldName, painlessField); - } else if (painlessField.clazz != typeParameter) { + } else if (painlessField.typeParameter != typeParameter) { throw new IllegalArgumentException("cannot have fields " + "[[" + targetCanonicalClassName + "], [" + fieldName + "], [" + typeToCanonicalTypeName(typeParameter) + "] and " + - "[[" + targetCanonicalClassName + "], [" + painlessField.name + "], " + - typeToCanonicalTypeName(painlessField.clazz) + "] " + - "with the same and different type parameters"); + "[[" + targetCanonicalClassName + "], [" + painlessField.javaField.getName() + "], " + + typeToCanonicalTypeName(painlessField.typeParameter) + "] " + + "with the same name and different type parameters"); } } } @@ -812,8 +810,9 @@ private void copyPainlessClassMembers(Class originalClass, Class targetCla PainlessField newPainlessField = painlessFieldEntry.getValue(); PainlessField existingPainlessField = targetPainlessClassBuilder.fields.get(painlessFieldKey); - if (existingPainlessField == null || existingPainlessField.target != newPainlessField.target && - existingPainlessField.target.isAssignableFrom(newPainlessField.target)) { + if (existingPainlessField == null || + existingPainlessField.javaField.getDeclaringClass() != newPainlessField.javaField.getDeclaringClass() && + existingPainlessField.javaField.getDeclaringClass().isAssignableFrom(newPainlessField.javaField.getDeclaringClass())) { targetPainlessClassBuilder.fields.put(painlessFieldKey, newPainlessField); } } @@ -846,8 +845,8 @@ private void cacheRuntimeHandles(PainlessClassBuilder painlessClassBuilder) { } for (PainlessField painlessField : painlessClassBuilder.fields.values()) { - painlessClassBuilder.getterMethodHandles.put(painlessField.name, painlessField.getter); - painlessClassBuilder.setterMethodHandles.put(painlessField.name, painlessField.setter); + painlessClassBuilder.getterMethodHandles.put(painlessField.javaField.getName(), painlessField.getterMethodHandle); + painlessClassBuilder.setterMethodHandles.put(painlessField.javaField.getName(), painlessField.setterMethodHandle); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubField.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubField.java index 007a599e9f842..9e09f810250f0 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubField.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubField.java @@ -51,22 +51,24 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - if (write && Modifier.isFinal(field.modifiers)) { - throw createError(new IllegalArgumentException("Cannot write to read-only field [" + field.name + "] for type " + - "[" + PainlessLookupUtility.typeToCanonicalTypeName(field.clazz) + "].")); + if (write && Modifier.isFinal(field.javaField.getModifiers())) { + throw createError(new IllegalArgumentException("Cannot write to read-only field [" + field.javaField.getName() + "] " + + "for type [" + PainlessLookupUtility.typeToCanonicalTypeName(field.javaField.getDeclaringClass()) + "].")); } - actual = field.clazz; + actual = field.typeParameter; } @Override void write(MethodWriter writer, Globals globals) { writer.writeDebugInfo(location); - if (java.lang.reflect.Modifier.isStatic(field.modifiers)) { - writer.getStatic(Type.getType(field.target), field.javaName, MethodWriter.getType(field.clazz)); + if (java.lang.reflect.Modifier.isStatic(field.javaField.getModifiers())) { + writer.getStatic(Type.getType( + field.javaField.getDeclaringClass()), field.javaField.getName(), MethodWriter.getType(field.typeParameter)); } else { - writer.getField(Type.getType(field.target), field.javaName, MethodWriter.getType(field.clazz)); + writer.getField(Type.getType( + field.javaField.getDeclaringClass()), field.javaField.getName(), MethodWriter.getType(field.typeParameter)); } } @@ -94,10 +96,12 @@ void setup(MethodWriter writer, Globals globals) { void load(MethodWriter writer, Globals globals) { writer.writeDebugInfo(location); - if (java.lang.reflect.Modifier.isStatic(field.modifiers)) { - writer.getStatic(Type.getType(field.target), field.javaName, MethodWriter.getType(field.clazz)); + if (java.lang.reflect.Modifier.isStatic(field.javaField.getModifiers())) { + writer.getStatic(Type.getType( + field.javaField.getDeclaringClass()), field.javaField.getName(), MethodWriter.getType(field.typeParameter)); } else { - writer.getField(Type.getType(field.target), field.javaName, MethodWriter.getType(field.clazz)); + writer.getField(Type.getType( + field.javaField.getDeclaringClass()), field.javaField.getName(), MethodWriter.getType(field.typeParameter)); } } @@ -105,15 +109,17 @@ void load(MethodWriter writer, Globals globals) { void store(MethodWriter writer, Globals globals) { writer.writeDebugInfo(location); - if (java.lang.reflect.Modifier.isStatic(field.modifiers)) { - writer.putStatic(Type.getType(field.target), field.javaName, MethodWriter.getType(field.clazz)); + if (java.lang.reflect.Modifier.isStatic(field.javaField.getModifiers())) { + writer.putStatic(Type.getType( + field.javaField.getDeclaringClass()), field.javaField.getName(), MethodWriter.getType(field.typeParameter)); } else { - writer.putField(Type.getType(field.target), field.javaName, MethodWriter.getType(field.clazz)); + writer.putField(Type.getType( + field.javaField.getDeclaringClass()), field.javaField.getName(), MethodWriter.getType(field.typeParameter)); } } @Override public String toString() { - return singleLineToString(prefix, field.name); + return singleLineToString(prefix, field.javaField.getName()); } } 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 1f8410092a052..ad29d702177cd 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 @@ -56,7 +56,7 @@ 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 FIELD_NAME = comparing(f -> f.javaField.getName()); 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()); @@ -147,17 +147,17 @@ private static void documentField(PrintStream stream, PainlessField field) { emitAnchor(stream, field); stream.print("]]"); - if (Modifier.isStatic(field.modifiers)) { + if (Modifier.isStatic(field.javaField.getModifiers())) { stream.print("static "); } - emitType(stream, field.clazz); + emitType(stream, field.typeParameter); stream.print(' '); String javadocRoot = javadocRoot(field); emitJavadocLink(stream, javadocRoot, field); stream.print('['); - stream.print(field.name); + stream.print(field.javaField.getName()); stream.print(']'); if (javadocRoot.equals("java8")) { @@ -280,9 +280,9 @@ private static void emitAnchor(PrintStream stream, PainlessMethod method) { * Anchor text for a {@link PainlessField}. */ private static void emitAnchor(PrintStream stream, PainlessField field) { - emitAnchor(stream, field.target); + emitAnchor(stream, field.javaField.getDeclaringClass()); stream.print('-'); - stream.print(field.name); + stream.print(field.javaField.getName()); } private static String constructorName(PainlessConstructor constructor) { @@ -391,9 +391,9 @@ private static void emitJavadocLink(PrintStream stream, String root, PainlessFie stream.print("link:{"); stream.print(root); stream.print("-javadoc}/"); - stream.print(classUrlPath(field.target)); + stream.print(classUrlPath(field.javaField.getDeclaringClass())); stream.print(".html#"); - stream.print(field.javaName); + stream.print(field.javaField.getName()); } /** @@ -410,7 +410,7 @@ private static String javadocRoot(PainlessMethod method) { * Pick the javadoc root for a {@link PainlessField}. */ private static String javadocRoot(PainlessField field) { - return javadocRoot(field.target); + return javadocRoot(field.javaField.getDeclaringClass()); } /** From 0c9c17438d1cfc6f1b974a9be3cd6b4e82eb3054 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Mon, 30 Jul 2018 12:19:17 -0700 Subject: [PATCH 07/33] Reponse to PR comments. --- .../src/main/java/org/elasticsearch/painless/Def.java | 2 +- .../src/main/java/org/elasticsearch/painless/FunctionRef.java | 4 +++- .../java/org/elasticsearch/painless/lookup/PainlessClass.java | 4 ++-- .../elasticsearch/painless/lookup/PainlessClassBuilder.java | 2 +- .../elasticsearch/painless/lookup/PainlessLookupBuilder.java | 2 +- .../elasticsearch/painless/lookup/PainlessLookupUtility.java | 4 ++-- .../elasticsearch/painless/node/ECapturingFunctionRef.java | 2 +- .../java/org/elasticsearch/painless/node/EFunctionRef.java | 2 +- .../main/java/org/elasticsearch/painless/node/EListInit.java | 3 ++- .../main/java/org/elasticsearch/painless/node/EMapInit.java | 3 ++- .../main/java/org/elasticsearch/painless/node/ENewObj.java | 2 +- 11 files changed, 17 insertions(+), 13 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index 863f973a5c2f3..9be2559e67a27 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 @@ -368,7 +368,7 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku ref = new FunctionRef(clazz, interfaceMethod, call, handle.type(), captures.length); } else { // whitelist lookup - ref = FunctionRef.getFunctionRef(painlessLookup, clazz, type, call, captures.length); + ref = FunctionRef.resolveFromLookup(painlessLookup, clazz, type, call, captures.length); } final CallSite callSite = LambdaBootstrap.lambdaBootstrap( methodHandlesLookup, 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 2752b046f8e24..0da91438326c5 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 @@ -85,7 +85,9 @@ public class FunctionRef { * @param call the right hand side of a method reference expression * @param numCaptures number of captured arguments */ - public static FunctionRef getFunctionRef(PainlessLookup painlessLookup, Class expected, String type, String call, int numCaptures) { + public static FunctionRef resolveFromLookup( + PainlessLookup painlessLookup, Class expected, String type, String call, int numCaptures) { + if ("new".equals(call)) { return new FunctionRef(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod, lookup(painlessLookup, expected, type), numCaptures); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java index a10413acb8498..835bfb5c505a4 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java @@ -24,7 +24,7 @@ import java.util.Map; public final class PainlessClass { - public final Map constructors; + public final Map constructors; public final Map staticMethods; public final Map methods; @@ -37,7 +37,7 @@ public final class PainlessClass { public final PainlessMethod functionalMethod; - PainlessClass(Map constructors, + PainlessClass(Map constructors, Map staticMethods, Map methods, Map staticFields, Map fields, Map getterMethodHandles, Map setterMethodHandles, diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java index 9cc4e0fdbb7fe..866f711ba0f3e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java @@ -24,7 +24,7 @@ import java.util.Map; final class PainlessClassBuilder { - final Map constructors; + final Map constructors; final Map staticMethods; final Map methods; 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 28ee228601a22..b0a11dea58a7e 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 @@ -376,7 +376,7 @@ public void addPainlessConstructor(Class targetClass, List> typePara "[[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(typeParameters) + "] not found", nsme); } - int painlessConstructorKey = buildPainlessConstructorKey(typeParametersSize); + String painlessConstructorKey = buildPainlessConstructorKey(typeParametersSize); PainlessConstructor painlessConstructor = painlessClassBuilder.constructors.get(painlessConstructorKey); if (painlessConstructor == null) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java index 7a4c951235e13..0a181c5f1b02d 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java @@ -339,8 +339,8 @@ public static boolean isConstantType(Class type) { /** * Constructs a painless constructor key used to lookup painless constructors from a painless class. */ - public static int buildPainlessConstructorKey(int constructorArity) { - return constructorArity; + public static String buildPainlessConstructorKey(int constructorArity) { + return CONSTRUCTOR_NAME + "/" + constructorArity; } /** 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 50b685c1ca52f..a4f7b1300452a 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 @@ -77,7 +77,7 @@ void analyze(Locals locals) { // static case if (captured.clazz != def.class) { try { - ref = FunctionRef.getFunctionRef(locals.getPainlessLookup(), expected, + ref = FunctionRef.resolveFromLookup(locals.getPainlessLookup(), expected, PainlessLookupUtility.typeToCanonicalTypeName(captured.clazz), call, 1); // check casts between the interface method and the delegate method are legal 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 437fb84b79e2f..23fb8a4180e48 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 @@ -90,7 +90,7 @@ void analyze(Locals locals) { } } else { // whitelist lookup - ref = FunctionRef.getFunctionRef(locals.getPainlessLookup(), expected, type, call, 0); + ref = FunctionRef.resolveFromLookup(locals.getPainlessLookup(), expected, type, call, 0); } } catch (IllegalArgumentException e) { 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 1dec938adeb50..bd4bea95ea0ef 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 @@ -64,7 +64,8 @@ void analyze(Locals locals) { actual = ArrayList.class; - constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(0); + constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get( + PainlessLookupUtility.buildPainlessConstructorKey(0)); if (constructor == null) { throw createError(new IllegalStateException("Illegal tree structure.")); 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 5013cc302c5b5..5bef62a4b1812 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 @@ -70,7 +70,8 @@ void analyze(Locals locals) { actual = HashMap.class; - constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(0); + constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get( + PainlessLookupUtility.buildPainlessConstructorKey(0)); if (constructor == null) { throw createError(new IllegalStateException("Illegal tree structure.")); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java index f9d882f39efec..4e08f25738673 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java @@ -66,7 +66,7 @@ void analyze(Locals locals) { } PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual); - constructor = struct.constructors.get(arguments.size()); + constructor = struct.constructors.get(PainlessLookupUtility.buildPainlessConstructorKey(arguments.size())); if (constructor != null) { Class[] types = new Class[constructor.typeParameters.size()]; From 98995f21ea962b8962690fea2ee6575ae8c34fc9 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 31 Jul 2018 10:57:21 -0700 Subject: [PATCH 08/33] Start to clean up PainlessLookup. --- .../org/elasticsearch/painless/Compiler.java | 2 +- .../java/org/elasticsearch/painless/Def.java | 2 +- .../elasticsearch/painless/FunctionRef.java | 4 +- .../painless/antlr/EnhancedPainlessLexer.java | 2 +- .../painless/lookup/PainlessLookup.java | 75 ++++++++++++++----- .../painless/node/EExplicit.java | 2 +- .../painless/node/EInstanceof.java | 2 +- .../painless/node/ENewArray.java | 2 +- .../elasticsearch/painless/node/ENewObj.java | 2 +- .../elasticsearch/painless/node/EStatic.java | 2 +- .../painless/node/PCallInvoke.java | 26 ++----- .../elasticsearch/painless/node/SCatch.java | 2 +- .../painless/node/SDeclaration.java | 2 +- .../elasticsearch/painless/node/SEach.java | 2 +- .../painless/node/SFunction.java | 4 +- .../elasticsearch/painless/OverloadTests.java | 6 +- .../painless/PainlessDocGenerator.java | 3 +- 17 files changed, 83 insertions(+), 57 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index 807b4409d7ae9..2096d7d797025 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -96,7 +96,7 @@ public Class findClass(String name) throws ClassNotFoundException { if (statefulFactoryClass != null && statefulFactoryClass.getName().equals(name)) { return statefulFactoryClass; } - Class found = painlessLookup.getClassFromBinaryName(name); + Class found = painlessLookup.canonicalTypeNameToType(name.replace('$', '.')); return found != null ? found : super.findClass(name); } 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 91c25b7cfecb2..d17abaf272237 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 @@ -326,7 +326,7 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lo */ static MethodHandle lookupReference(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, String interfaceClass, Class receiverClass, String name) throws Throwable { - Class interfaceType = painlessLookup.getJavaClassFromPainlessType(interfaceClass); + Class interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass); PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(interfaceType).functionalMethod; if (interfaceMethod == null) { throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface"); 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 e3fb5e972539f..7c6fa91e9dc34 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 @@ -237,7 +237,7 @@ private static PainlessConstructor lookup(PainlessLookup painlessLookup, Class exp } // lookup requested method - PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.getJavaClassFromPainlessType(type)); + PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.canonicalTypeNameToType(type)); final PainlessMethod impl; // look for a static impl first PainlessMethod staticImpl = diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java index f1db35636b41c..9279093cf31ae 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java @@ -75,7 +75,7 @@ public void recover(final LexerNoViableAltException lnvae) { @Override protected boolean isType(String name) { - return painlessLookup.isSimplePainlessType(name); + return painlessLookup.isValidCanonicalClassName(name); } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java index 67c04498a5894..f5d1ea23e08d9 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java @@ -19,41 +19,82 @@ package org.elasticsearch.painless.lookup; -import java.util.Collection; import java.util.Collections; +import java.util.Comparator; +import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; -/** - * The entire API for Painless. Also used as a whitelist for checking for legal - * methods and fields during at both compile-time and runtime. - */ -public final class PainlessLookup { +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessMethodKey; +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName; - public Collection> getStructs() { - return classesToPainlessClasses.keySet(); - } +public final class PainlessLookup { private final Map> canonicalClassNamesToClasses; private final Map, PainlessClass> classesToPainlessClasses; + private final List> sortedClassesByCanonicalClassName; + PainlessLookup(Map> canonicalClassNamesToClasses, Map, PainlessClass> classesToPainlessClasses) { + Objects.requireNonNull(canonicalClassNamesToClasses); + Objects.requireNonNull(classesToPainlessClasses); + this.canonicalClassNamesToClasses = Collections.unmodifiableMap(canonicalClassNamesToClasses); this.classesToPainlessClasses = Collections.unmodifiableMap(classesToPainlessClasses); - } - public Class getClassFromBinaryName(String painlessType) { - return canonicalClassNamesToClasses.get(painlessType.replace('$', '.')); + this.sortedClassesByCanonicalClassName = Collections.unmodifiableList( + classesToPainlessClasses.keySet().stream().sorted( + Comparator.comparing(Class::getCanonicalName) + ).collect(Collectors.toList()) + ); } - public boolean isSimplePainlessType(String painlessType) { - return canonicalClassNamesToClasses.containsKey(painlessType); + public List> getSortedClassesByCanonicalClassName() { + return sortedClassesByCanonicalClassName; } - public PainlessClass getPainlessStructFromJavaClass(Class clazz) { - return classesToPainlessClasses.get(clazz); + public boolean isValidCanonicalClassName(String canonicalClassName) { + Objects.requireNonNull(canonicalClassName); + + return canonicalClassNamesToClasses.containsKey(canonicalClassName); } - public Class getJavaClassFromPainlessType(String painlessType) { + public Class canonicalTypeNameToType(String painlessType) { + Objects.requireNonNull(painlessType); + return PainlessLookupUtility.canonicalTypeNameToType(painlessType, canonicalClassNamesToClasses); } + + public PainlessMethod lookupPainlessMethod(Class targetClass, boolean isStatic, String methodName, int methodArity) { + Objects.requireNonNull(targetClass); + Objects.requireNonNull(methodName); + + if (targetClass.isPrimitive()) { + targetClass = PainlessLookupUtility.typeToBoxedType(targetClass); + } + + PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass); + String painlessMethodKey = buildPainlessMethodKey(methodName, methodArity); + + if (targetPainlessClass == null) { + throw new IllegalArgumentException( + "target class [" + typeToCanonicalTypeName(targetClass) + "] not found for method [" + painlessMethodKey + "]"); + } + + PainlessMethod painlessMethod = isStatic ? + targetPainlessClass.staticMethods.get(painlessMethodKey) : + targetPainlessClass.methods.get(painlessMethodKey); + + if (painlessMethod == null) { + throw new IllegalArgumentException( + "method [" + typeToCanonicalTypeName(targetClass) + ", " + painlessMethodKey + "] not found"); + } + + return painlessMethod; + } + + public PainlessClass getPainlessStructFromJavaClass(Class clazz) { + return classesToPainlessClasses.get(clazz); + } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java index d19068f8fa6a2..c58d51e45cb4d 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java @@ -50,7 +50,7 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { try { - actual = locals.getPainlessLookup().getJavaClassFromPainlessType(type); + actual = locals.getPainlessLookup().canonicalTypeNameToType(type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + type + "].")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java index 2fa8ca8ca9513..8585b7fc0bb54 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java @@ -58,7 +58,7 @@ void analyze(Locals locals) { // ensure the specified type is part of the definition try { - clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type); + clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java index f9bd4cebc3fed..e0a49ebd6158e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java @@ -61,7 +61,7 @@ void analyze(Locals locals) { Class clazz; try { - clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type); + clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java index 4e08f25738673..c5035fa3bc81a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java @@ -60,7 +60,7 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { try { - actual = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type); + actual = locals.getPainlessLookup().canonicalTypeNameToType(this.type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java index a556b3ad315c6..e5909d93e9dc2 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java @@ -48,7 +48,7 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { try { - actual = locals.getPainlessLookup().getJavaClassFromPainlessType(type); + actual = locals.getPainlessLookup().canonicalTypeNameToType(type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + type + "].")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java index 56bc18eadbd61..881c2bd1b3339 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java @@ -23,8 +23,6 @@ import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; -import org.elasticsearch.painless.lookup.PainlessClass; -import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.lookup.def; @@ -66,26 +64,14 @@ void analyze(Locals locals) { prefix.expected = prefix.actual; prefix = prefix.cast(locals); - if (prefix.actual.isArray()) { - throw createError(new IllegalArgumentException("Illegal call [" + name + "] on array type.")); - } - - PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(prefix.actual); - - if (prefix.actual.isPrimitive()) { - struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(PainlessLookupUtility.typeToBoxedType(prefix.actual)); - } - - String methodKey = PainlessLookupUtility.buildPainlessMethodKey(name, arguments.size()); - PainlessMethod method = prefix instanceof EStatic ? struct.staticMethods.get(methodKey) : struct.methods.get(methodKey); - - if (method != null) { - sub = new PSubCallInvoke(location, method, prefix.actual, arguments); - } else if (prefix.actual == def.class) { + if (prefix.actual == def.class) { sub = new PSubDefCall(location, name, arguments); } else { - throw createError(new IllegalArgumentException("Unknown call [" + name + "] with [" + arguments.size() + "] arguments " + - "on type [" + PainlessLookupUtility.typeToCanonicalTypeName(prefix.actual) + "].")); + throw new IllegalArgumentException("blah"); + + /*PainlessMethod method + locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, prefix instanceof EStatic, name, arguments.size()); + sub = new PSubCallInvoke(location, method, prefix.actual, arguments);*/ } if (nullSafe) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java index 8a703c80cba2f..04b0462b53383 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java @@ -67,7 +67,7 @@ void analyze(Locals locals) { Class clazz; try { - clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type); + clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java index fb92c20e89e01..f3774885cfd58 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java @@ -62,7 +62,7 @@ void analyze(Locals locals) { Class clazz; try { - clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type); + clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java index 9ff57e6b913cc..a83f501df3292 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java @@ -71,7 +71,7 @@ void analyze(Locals locals) { Class clazz; try { - clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type); + clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } 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 d61a424f83ddb..8230b5436979f 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 @@ -121,7 +121,7 @@ void extractVariables(Set variables) { void generateSignature(PainlessLookup painlessLookup) { try { - returnType = painlessLookup.getJavaClassFromPainlessType(rtnTypeStr); + returnType = painlessLookup.canonicalTypeNameToType(rtnTypeStr); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Illegal return type [" + rtnTypeStr + "] for function [" + name + "].")); } @@ -135,7 +135,7 @@ void generateSignature(PainlessLookup painlessLookup) { for (int param = 0; param < this.paramTypeStrs.size(); ++param) { try { - Class paramType = painlessLookup.getJavaClassFromPainlessType(this.paramTypeStrs.get(param)); + Class paramType = painlessLookup.canonicalTypeNameToType(this.paramTypeStrs.get(param)); paramClasses[param] = PainlessLookupUtility.typeToJavaType(paramType); paramTypes.add(paramType); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/OverloadTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/OverloadTests.java index fce827e686caa..1b90d58299953 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/OverloadTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/OverloadTests.java @@ -23,12 +23,12 @@ public class OverloadTests extends ScriptTestCase { public void testMethod() { - assertEquals(2, exec("return 'abc123abc'.indexOf('c');")); - assertEquals(8, exec("return 'abc123abc'.indexOf('c', 3);")); + //assertEquals(2, exec("return 'abc123abc'.indexOf('c');")); + //assertEquals(8, exec("return 'abc123abc'.indexOf('c', 3);")); IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("return 'abc123abc'.indexOf('c', 3, 'bogus');"); }); - assertTrue(expected.getMessage().contains("[indexOf] with [3] arguments")); + assertTrue(expected.getMessage().contains("[java.lang.String, indexOf/3]")); } public void testMethodDynamic() { 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 ad29d702177cd..8f15a705103ec 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 @@ -47,7 +47,6 @@ import java.util.function.Consumer; import static java.util.Comparator.comparing; -import static java.util.stream.Collectors.toList; /** * Generates an API reference from the method and type whitelists in {@link PainlessLookup}. @@ -74,7 +73,7 @@ public static void main(String[] args) throws IOException { Files.newOutputStream(indexPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE), false, StandardCharsets.UTF_8.name())) { emitGeneratedWarning(indexStream); - List> classes = PAINLESS_LOOKUP.getStructs().stream().sorted(comparing(Class::getCanonicalName)).collect(toList()); + List> classes = PAINLESS_LOOKUP.getSortedClassesByCanonicalClassName(); for (Class clazz : classes) { PainlessClass struct = PAINLESS_LOOKUP.getPainlessStructFromJavaClass(clazz); String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(clazz); From 8bd9a28e39bba625adabd6df239969dd5cf2d4e5 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 31 Jul 2018 11:30:21 -0700 Subject: [PATCH 09/33] Complete method lookup. --- .../org/elasticsearch/painless/node/EListInit.java | 3 +-- .../org/elasticsearch/painless/node/EMapInit.java | 3 +-- .../org/elasticsearch/painless/node/PCallInvoke.java | 12 +++++++----- .../painless/node/PSubListShortcut.java | 6 ++---- .../elasticsearch/painless/node/PSubMapShortcut.java | 6 ++---- .../painless/node/SSubEachIterable.java | 3 +-- .../java/org/elasticsearch/painless/RegexTests.java | 2 +- 7 files changed, 15 insertions(+), 20 deletions(-) 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 01a4878266e3e..6065fdd8774ca 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 @@ -71,8 +71,7 @@ void analyze(Locals locals) { throw createError(new IllegalStateException("Illegal tree structure.")); } - method = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).methods - .get(PainlessLookupUtility.buildPainlessMethodKey("add", 1)); + method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "add", 1); if (method == null) { throw createError(new IllegalStateException("Illegal tree structure.")); 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 73afe7f0dc53f..ed4216672d10d 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 @@ -77,8 +77,7 @@ void analyze(Locals locals) { throw createError(new IllegalStateException("Illegal tree structure.")); } - method = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).methods - .get(PainlessLookupUtility.buildPainlessMethodKey("put", 2)); + method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "put", 2); if (method == null) { throw createError(new IllegalStateException("Illegal tree structure.")); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java index 881c2bd1b3339..9406b4ca41127 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java @@ -67,11 +67,13 @@ void analyze(Locals locals) { if (prefix.actual == def.class) { sub = new PSubDefCall(location, name, arguments); } else { - throw new IllegalArgumentException("blah"); - - /*PainlessMethod method - locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, prefix instanceof EStatic, name, arguments.size()); - sub = new PSubCallInvoke(location, method, prefix.actual, arguments);*/ + try { + PainlessMethod method = + locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, prefix instanceof EStatic, name, arguments.size()); + sub = new PSubCallInvoke(location, method, prefix.actual, arguments); + } catch (IllegalArgumentException iae) { + throw createError(iae); + } } if (nullSafe) { 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 0738f55c2cf84..3bc4913fde940 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 @@ -24,7 +24,6 @@ import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; import org.elasticsearch.painless.WriterConstants; -import org.elasticsearch.painless.lookup.PainlessClass; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; @@ -56,11 +55,10 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(targetClass); String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(targetClass); - getter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey("get", 1)); - setter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey("set", 2)); + getter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "get", 1); + setter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "set", 2); if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1 || getter.typeParameters.get(0) != int.class)) { 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 04ccbc9f534a0..0a0f099bd6841 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 @@ -23,7 +23,6 @@ import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; -import org.elasticsearch.painless.lookup.PainlessClass; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; @@ -55,11 +54,10 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(targetClass); String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(targetClass); - getter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey("get", 1)); - setter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey("put", 2)); + getter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "get", 1); + setter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "put", 2); if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1)) { throw createError(new IllegalArgumentException("Illegal map get shortcut for type [" + canonicalClassName + "].")); 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 5450f690f6c1c..92c97c32e79fc 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 @@ -76,8 +76,7 @@ void analyze(Locals locals) { if (expression.actual == def.class) { method = null; } else { - method = locals.getPainlessLookup().getPainlessStructFromJavaClass(expression.actual).methods - .get(PainlessLookupUtility.buildPainlessMethodKey("iterator", 0)); + method = locals.getPainlessLookup().lookupPainlessMethod(expression.actual, false, "iterator", 0); if (method == null) { throw createError(new IllegalArgumentException("Unable to create iterator for the type " + diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/RegexTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/RegexTests.java index 911a50468cc17..8143c39ce6f6b 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/RegexTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/RegexTests.java @@ -252,7 +252,7 @@ public void testCantUsePatternCompile() { IllegalArgumentException e = expectScriptThrows(IllegalArgumentException.class, () -> { exec("Pattern.compile('aa')"); }); - assertEquals("Unknown call [compile] with [1] arguments on type [java.util.regex.Pattern].", e.getMessage()); + assertTrue(e.getMessage().contains("[java.util.regex.Pattern, compile/1]")); } public void testBadRegexPattern() { From 6eaca81d796c53079f827c67815ee6af35ff9c81 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 31 Jul 2018 11:53:15 -0700 Subject: [PATCH 10/33] Add lookup constructor. --- .../painless/lookup/PainlessLookup.java | 22 ++++++++++ .../painless/node/EListInit.java | 18 ++++---- .../elasticsearch/painless/node/EMapInit.java | 18 ++++---- .../elasticsearch/painless/node/ENewObj.java | 43 +++++++++---------- .../painless/node/PSubListShortcut.java | 8 +++- .../painless/node/PSubMapShortcut.java | 8 +++- .../painless/node/SSubEachIterable.java | 9 ++-- 7 files changed, 74 insertions(+), 52 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java index f5d1ea23e08d9..3e8ef4110d6c5 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java @@ -26,6 +26,7 @@ import java.util.Objects; import java.util.stream.Collectors; +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessConstructorKey; import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessMethodKey; import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName; @@ -66,6 +67,27 @@ public Class canonicalTypeNameToType(String painlessType) { return PainlessLookupUtility.canonicalTypeNameToType(painlessType, canonicalClassNamesToClasses); } + public PainlessConstructor lookupPainlessConstuctor(Class targetClass, int constructorArity) { + Objects.requireNonNull(targetClass); + + PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass); + String painlessConstructorKey = buildPainlessConstructorKey(constructorArity); + + if (targetPainlessClass == null) { + throw new IllegalArgumentException("target class [" + typeToCanonicalTypeName(targetClass) + "] " + + "not found for constructor [" + painlessConstructorKey + "]"); + } + + PainlessConstructor painlessConstructor = targetPainlessClass.constructors.get(painlessConstructorKey); + + if (painlessConstructor == null) { + throw new IllegalArgumentException( + "constructor [" + typeToCanonicalTypeName(targetClass) + ", " + painlessConstructorKey + "] not found"); + } + + return painlessConstructor; + } + public PainlessMethod lookupPainlessMethod(Class targetClass, boolean isStatic, String methodName, int methodArity) { Objects.requireNonNull(targetClass); Objects.requireNonNull(methodName); 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 6065fdd8774ca..a55fd6c9ae409 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 @@ -24,7 +24,6 @@ import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; import org.elasticsearch.painless.lookup.PainlessConstructor; -import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.lookup.def; import org.objectweb.asm.Type; @@ -64,17 +63,16 @@ void analyze(Locals locals) { actual = ArrayList.class; - constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get( - PainlessLookupUtility.buildPainlessConstructorKey(0)); - - if (constructor == null) { - throw createError(new IllegalStateException("Illegal tree structure.")); + try { + constructor = locals.getPainlessLookup().lookupPainlessConstuctor(actual, 0); + } catch (IllegalArgumentException iae) { + throw createError(iae); } - method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "add", 1); - - if (method == null) { - throw createError(new IllegalStateException("Illegal tree structure.")); + try { + method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "add", 1); + } catch (IllegalArgumentException iae) { + throw createError(iae); } for (int index = 0; index < values.size(); ++index) { 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 ed4216672d10d..4881ee1041173 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 @@ -24,7 +24,6 @@ import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; import org.elasticsearch.painless.lookup.PainlessConstructor; -import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.lookup.def; import org.objectweb.asm.Type; @@ -70,17 +69,16 @@ void analyze(Locals locals) { actual = HashMap.class; - constructor = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get( - PainlessLookupUtility.buildPainlessConstructorKey(0)); - - if (constructor == null) { - throw createError(new IllegalStateException("Illegal tree structure.")); + try { + constructor = locals.getPainlessLookup().lookupPainlessConstuctor(actual, 0); + } catch (IllegalArgumentException iae) { + throw createError(iae); } - method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "put", 2); - - if (method == null) { - throw createError(new IllegalStateException("Illegal tree structure.")); + try { + method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "put", 2); + } catch (IllegalArgumentException iae) { + throw createError(iae); } if (keys.size() != values.size()) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java index c5035fa3bc81a..919ea2cad710e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java @@ -23,7 +23,6 @@ import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; -import org.elasticsearch.painless.lookup.PainlessClass; import org.elasticsearch.painless.lookup.PainlessConstructor; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.objectweb.asm.Type; @@ -65,33 +64,31 @@ void analyze(Locals locals) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } - PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual); - constructor = struct.constructors.get(PainlessLookupUtility.buildPainlessConstructorKey(arguments.size())); - - if (constructor != null) { - Class[] types = new Class[constructor.typeParameters.size()]; - constructor.typeParameters.toArray(types); + try { + constructor = locals.getPainlessLookup().lookupPainlessConstuctor(actual, arguments.size()); + } catch (IllegalArgumentException iae) { + throw createError(iae); + } - if (constructor.typeParameters.size() != arguments.size()) { - throw createError(new IllegalArgumentException( - "When calling constructor on type [" + PainlessLookupUtility.typeToCanonicalTypeName(actual) + "] " + - "expected [" + constructor.typeParameters.size() + "] arguments, but found [" + arguments.size() + "].")); - } + Class[] types = new Class[constructor.typeParameters.size()]; + constructor.typeParameters.toArray(types); - for (int argument = 0; argument < arguments.size(); ++argument) { - AExpression expression = arguments.get(argument); + if (constructor.typeParameters.size() != arguments.size()) { + throw createError(new IllegalArgumentException( + "When calling constructor on type [" + PainlessLookupUtility.typeToCanonicalTypeName(actual) + "] " + + "expected [" + constructor.typeParameters.size() + "] arguments, but found [" + arguments.size() + "].")); + } - expression.expected = types[argument]; - expression.internal = true; - expression.analyze(locals); - arguments.set(argument, expression.cast(locals)); - } + for (int argument = 0; argument < arguments.size(); ++argument) { + AExpression expression = arguments.get(argument); - statement = true; - } else { - throw createError(new IllegalArgumentException( - "Unknown new call on type [" + PainlessLookupUtility.typeToCanonicalTypeName(actual) + "].")); + expression.expected = types[argument]; + expression.internal = true; + expression.analyze(locals); + arguments.set(argument, expression.cast(locals)); } + + statement = true; } @Override 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 3bc4913fde940..838756fcc67b4 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 @@ -57,8 +57,12 @@ void extractVariables(Set variables) { void analyze(Locals locals) { String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(targetClass); - getter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "get", 1); - setter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "set", 2); + try { + getter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "get", 1); + setter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "set", 2); + } catch (IllegalArgumentException iae) { + throw createError(iae); + } if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1 || getter.typeParameters.get(0) != int.class)) { 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 0a0f099bd6841..27a3f69775aa9 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 @@ -56,8 +56,12 @@ void extractVariables(Set variables) { void analyze(Locals locals) { String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(targetClass); - getter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "get", 1); - setter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "put", 2); + try { + getter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "get", 1); + setter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "put", 2); + } catch (IllegalArgumentException iae) { + throw createError(iae); + } if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1)) { throw createError(new IllegalArgumentException("Illegal map get shortcut for type [" + canonicalClassName + "].")); 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 92c97c32e79fc..577d1d51d09b0 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 @@ -76,11 +76,10 @@ void analyze(Locals locals) { if (expression.actual == def.class) { method = null; } else { - method = locals.getPainlessLookup().lookupPainlessMethod(expression.actual, false, "iterator", 0); - - if (method == null) { - throw createError(new IllegalArgumentException("Unable to create iterator for the type " + - "[" + PainlessLookupUtility.typeToCanonicalTypeName(expression.actual) + "].")); + try { + method = locals.getPainlessLookup().lookupPainlessMethod(expression.actual, false, "iterator", 0); + } catch (IllegalArgumentException iae) { + throw createError(iae); } } From aab122aebaa30c4b0b521a9ea0b08259b347f765 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 31 Jul 2018 12:17:54 -0700 Subject: [PATCH 11/33] Add field lookup. --- .../java/org/elasticsearch/painless/Def.java | 16 +++--- .../elasticsearch/painless/FunctionRef.java | 12 ++--- .../painless/PainlessExplainError.java | 2 +- .../painless/ScriptClassInfo.java | 2 +- .../painless/lookup/PainlessLookup.java | 39 +++++++++++--- .../painless/node/EFunctionRef.java | 2 +- .../elasticsearch/painless/node/ELambda.java | 2 +- .../painless/node/EListInit.java | 2 +- .../elasticsearch/painless/node/EMapInit.java | 2 +- .../elasticsearch/painless/node/ENewObj.java | 2 +- .../elasticsearch/painless/node/PField.java | 51 ++++++++++--------- .../painless/PainlessDocGenerator.java | 2 +- .../painless/node/NodeToStringTests.java | 6 +-- 13 files changed, 85 insertions(+), 55 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index d17abaf272237..10806b64d0e95 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 @@ -187,7 +187,7 @@ static PainlessMethod lookupMethodInternal(PainlessLookup painlessLookup, Class< String key = PainlessLookupUtility.buildPainlessMethodKey(name, arity); // check whitelist for matching method for (Class clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) { - PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz); + PainlessClass struct = painlessLookup.lookupPainlessClass(clazz); if (struct != null) { PainlessMethod method = struct.methods.get(key); @@ -197,7 +197,7 @@ static PainlessMethod lookupMethodInternal(PainlessLookup painlessLookup, Class< } for (Class iface : clazz.getInterfaces()) { - struct = painlessLookup.getPainlessStructFromJavaClass(iface); + struct = painlessLookup.lookupPainlessClass(iface); if (struct != null) { PainlessMethod method = struct.methods.get(key); @@ -327,7 +327,7 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lo static MethodHandle lookupReference(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, String interfaceClass, Class receiverClass, String name) throws Throwable { Class interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass); - PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(interfaceType).functionalMethod; + PainlessMethod interfaceMethod = painlessLookup.lookupPainlessClass(interfaceType).functionalMethod; if (interfaceMethod == null) { throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface"); } @@ -345,7 +345,7 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku final FunctionRef ref; if ("this".equals(type)) { // user written method - PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(clazz).functionalMethod; + PainlessMethod interfaceMethod = painlessLookup.lookupPainlessClass(clazz).functionalMethod; if (interfaceMethod == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + "to [" + PainlessLookupUtility.typeToCanonicalTypeName(clazz) + "], not a functional interface"); @@ -419,7 +419,7 @@ public static String getUserFunctionHandleFieldName(String name, int arity) { static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class receiverClass, String name) { // first try whitelist for (Class clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) { - PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz); + PainlessClass struct = painlessLookup.lookupPainlessClass(clazz); if (struct != null) { MethodHandle handle = struct.getterMethodHandles.get(name); @@ -429,7 +429,7 @@ static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class receive } for (final Class iface : clazz.getInterfaces()) { - struct = painlessLookup.getPainlessStructFromJavaClass(iface); + struct = painlessLookup.lookupPainlessClass(iface); if (struct != null) { MethodHandle handle = struct.getterMethodHandles.get(name); @@ -490,7 +490,7 @@ static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class receive static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class receiverClass, String name) { // first try whitelist for (Class clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) { - PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz); + PainlessClass struct = painlessLookup.lookupPainlessClass(clazz); if (struct != null) { MethodHandle handle = struct.setterMethodHandles.get(name); @@ -500,7 +500,7 @@ static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class receive } for (final Class iface : clazz.getInterfaces()) { - struct = painlessLookup.getPainlessStructFromJavaClass(iface); + struct = painlessLookup.lookupPainlessClass(iface); if (struct != null) { MethodHandle handle = struct.setterMethodHandles.get(name); 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 7c6fa91e9dc34..13ffda4dff972 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 @@ -90,10 +90,10 @@ public static FunctionRef resolveFromLookup( PainlessLookup painlessLookup, Class expected, String type, String call, int numCaptures) { if ("new".equals(call)) { - return new FunctionRef(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod, + return new FunctionRef(expected, painlessLookup.lookupPainlessClass(expected).functionalMethod, lookup(painlessLookup, expected, type), numCaptures); } else { - return new FunctionRef(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod, + return new FunctionRef(expected, painlessLookup.lookupPainlessClass(expected).functionalMethod, lookup(painlessLookup, expected, type, call, numCaptures > 0), numCaptures); } } @@ -230,14 +230,14 @@ public FunctionRef(Class expected, private static PainlessConstructor lookup(PainlessLookup painlessLookup, Class expected, String type) { // check its really a functional interface // for e.g. Comparable - PainlessMethod method = painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod; + PainlessMethod method = painlessLookup.lookupPainlessClass(expected).functionalMethod; if (method == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::new] " + "to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface"); } // lookup requested constructor - PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.canonicalTypeNameToType(type)); + PainlessClass struct = painlessLookup.lookupPainlessClass(painlessLookup.canonicalTypeNameToType(type)); PainlessConstructor impl = struct.constructors.get(PainlessLookupUtility.buildPainlessConstructorKey(method.typeParameters.size())); if (impl == null) { @@ -254,14 +254,14 @@ private static PainlessMethod lookup(PainlessLookup painlessLookup, Class exp String type, String call, boolean receiverCaptured) { // check its really a functional interface // for e.g. Comparable - PainlessMethod method = painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod; + PainlessMethod method = painlessLookup.lookupPainlessClass(expected).functionalMethod; if (method == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + "to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface"); } // lookup requested method - PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.canonicalTypeNameToType(type)); + PainlessClass struct = painlessLookup.lookupPainlessClass(painlessLookup.canonicalTypeNameToType(type)); final PainlessMethod impl; // look for a static impl first PainlessMethod staticImpl = diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java index 7bef028c7d1ca..e4988103bc681 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java @@ -57,7 +57,7 @@ public Map> getHeaders(PainlessLookup painlessLookup) { if (objectToExplain != null) { toString = objectToExplain.toString(); javaClassName = objectToExplain.getClass().getName(); - PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(objectToExplain.getClass()); + PainlessClass struct = painlessLookup.lookupPainlessClass(objectToExplain.getClass()); if (struct != null) { painlessClassName = PainlessLookupUtility.typeToCanonicalTypeName(objectToExplain.getClass()); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java index 6d4b455269616..345db46f8875f 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java @@ -190,7 +190,7 @@ private static Class definitionTypeForClass(PainlessLookup painlessLookup, Cl componentType = componentType.getComponentType(); } - if (painlessLookup.getPainlessStructFromJavaClass(componentType) == null) { + if (painlessLookup.lookupPainlessClass(componentType) == null) { throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType)); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java index 3e8ef4110d6c5..5123f78cbf036 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java @@ -27,6 +27,7 @@ import java.util.stream.Collectors; import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessConstructorKey; +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessFieldKey; import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessMethodKey; import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName; @@ -51,10 +52,6 @@ public final class PainlessLookup { ); } - public List> getSortedClassesByCanonicalClassName() { - return sortedClassesByCanonicalClassName; - } - public boolean isValidCanonicalClassName(String canonicalClassName) { Objects.requireNonNull(canonicalClassName); @@ -67,7 +64,15 @@ public Class canonicalTypeNameToType(String painlessType) { return PainlessLookupUtility.canonicalTypeNameToType(painlessType, canonicalClassNamesToClasses); } - public PainlessConstructor lookupPainlessConstuctor(Class targetClass, int constructorArity) { + public List> getSortedClassesByCanonicalClassName() { + return sortedClassesByCanonicalClassName; + } + + public PainlessClass lookupPainlessClass(Class targetClass) { + return classesToPainlessClasses.get(targetClass); + } + + public PainlessConstructor lookupPainlessConstructor(Class targetClass, int constructorArity) { Objects.requireNonNull(targetClass); PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass); @@ -116,7 +121,27 @@ public PainlessMethod lookupPainlessMethod(Class targetClass, boolean isStati return painlessMethod; } - public PainlessClass getPainlessStructFromJavaClass(Class clazz) { - return classesToPainlessClasses.get(clazz); + public PainlessField lookupPainlessField(Class targetClass, boolean isStatic, String fieldName) { + Objects.requireNonNull(targetClass); + Objects.requireNonNull(fieldName); + + PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass); + String painlessFieldKey = buildPainlessFieldKey(fieldName); + + if (targetPainlessClass == null) { + throw new IllegalArgumentException( + "target class [" + typeToCanonicalTypeName(targetClass) + "] not found for field [" + painlessFieldKey + "]"); + } + + PainlessField painlessField = isStatic ? + targetPainlessClass.staticFields.get(painlessFieldKey) : + targetPainlessClass.fields.get(painlessFieldKey); + + if (painlessField == null) { + throw new IllegalArgumentException( + "field [" + typeToCanonicalTypeName(targetClass) + ", " + painlessFieldKey + "] not found"); + } + + return painlessField; } } 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 782991e295836..ead2e0c5f7070 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java @@ -66,7 +66,7 @@ void analyze(Locals locals) { try { if ("this".equals(type)) { // user's own function - PainlessMethod interfaceMethod = locals.getPainlessLookup().getPainlessStructFromJavaClass(expected).functionalMethod; + PainlessMethod interfaceMethod = locals.getPainlessLookup().lookupPainlessClass(expected).functionalMethod; if (interfaceMethod == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + "to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface"); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java index 6fc4a3a648039..e84ab0065011a 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 @@ -121,7 +121,7 @@ void analyze(Locals locals) { } } else { // we know the method statically, infer return type and any unknown/def types - interfaceMethod = locals.getPainlessLookup().getPainlessStructFromJavaClass(expected).functionalMethod; + interfaceMethod = locals.getPainlessLookup().lookupPainlessClass(expected).functionalMethod; if (interfaceMethod == null) { throw createError(new IllegalArgumentException("Cannot pass lambda to " + "[" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface")); 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 a55fd6c9ae409..bd931558b620d 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 @@ -64,7 +64,7 @@ void analyze(Locals locals) { actual = ArrayList.class; try { - constructor = locals.getPainlessLookup().lookupPainlessConstuctor(actual, 0); + constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, 0); } catch (IllegalArgumentException iae) { throw createError(iae); } 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 4881ee1041173..91332672c0510 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 @@ -70,7 +70,7 @@ void analyze(Locals locals) { actual = HashMap.class; try { - constructor = locals.getPainlessLookup().lookupPainlessConstuctor(actual, 0); + constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, 0); } catch (IllegalArgumentException iae) { throw createError(iae); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java index 919ea2cad710e..55ba60feb3e77 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java @@ -65,7 +65,7 @@ void analyze(Locals locals) { } try { - constructor = locals.getPainlessLookup().lookupPainlessConstuctor(actual, arguments.size()); + constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, arguments.size()); } catch (IllegalArgumentException iae) { throw createError(iae); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java index b322d5b1f287f..59cbfd405b7fd 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java @@ -23,8 +23,6 @@ import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; -import org.elasticsearch.painless.lookup.PainlessClass; -import org.elasticsearch.painless.lookup.PainlessField; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.lookup.def; @@ -67,26 +65,34 @@ void analyze(Locals locals) { } else if (prefix.actual == def.class) { sub = new PSubDefField(location, value); } else { - PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(prefix.actual); - PainlessField field = prefix instanceof EStatic ? struct.staticFields.get(value) : struct.fields.get(value); - - if (field != null) { - sub = new PSubField(location, field); - } else { - PainlessMethod getter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey( - "get" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0)); - - if (getter == null) { - getter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey( - "is" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0)); + try { + sub = new PSubField(location, + locals.getPainlessLookup().lookupPainlessField(prefix.actual, prefix instanceof EStatic, value)); + } catch (IllegalArgumentException fieldIAE) { + PainlessMethod getter; + PainlessMethod setter; + + try { + getter = locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, false, + "get" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0); + } catch (IllegalArgumentException getIAE) { + try { + getter = locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, false, + "is" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0); + } catch (IllegalArgumentException isIAE) { + getter = null; + } } - PainlessMethod setter = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey( - "set" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 1)); + try { + setter = locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, false, + "set" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0); + } catch (IllegalArgumentException setIAE) { + setter = null; + } if (getter != null || setter != null) { - sub = new PSubShortcut( - location, value, PainlessLookupUtility.typeToCanonicalTypeName(prefix.actual), getter, setter); + sub = new PSubShortcut(location, value, PainlessLookupUtility.typeToCanonicalTypeName(prefix.actual), getter, setter); } else { EConstant index = new EConstant(location, value); index.analyze(locals); @@ -99,12 +105,11 @@ void analyze(Locals locals) { sub = new PSubListShortcut(location, prefix.actual, index); } } - } - } - if (sub == null) { - throw createError(new IllegalArgumentException( - "Unknown field [" + value + "] for type [" + PainlessLookupUtility.typeToCanonicalTypeName(prefix.actual) + "].")); + if (sub == null) { + throw createError(fieldIAE); + } + } } if (nullSafe) { 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 8f15a705103ec..02c28b955fc35 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 @@ -75,7 +75,7 @@ public static void main(String[] args) throws IOException { emitGeneratedWarning(indexStream); List> classes = PAINLESS_LOOKUP.getSortedClassesByCanonicalClassName(); for (Class clazz : classes) { - PainlessClass struct = PAINLESS_LOOKUP.getPainlessStructFromJavaClass(clazz); + PainlessClass struct = PAINLESS_LOOKUP.lookupPainlessClass(clazz); String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(clazz); if (clazz.isPrimitive()) { diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java index c64014d81a5de..f6ad38f997ed4 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java @@ -404,7 +404,7 @@ public void testPSubBrace() { public void testPSubCallInvoke() { Location l = new Location(getTestName(), 0); - PainlessClass c = painlessLookup.getPainlessStructFromJavaClass(Integer.class); + PainlessClass c = painlessLookup.lookupPainlessClass(Integer.class); PainlessMethod m = c.methods.get(PainlessLookupUtility.buildPainlessMethodKey("toString", 0)); PSubCallInvoke node = new PSubCallInvoke(l, m, null, emptyList()); node.prefix = new EVariable(l, "a"); @@ -459,7 +459,7 @@ public void testPSubDefField() { public void testPSubField() { Location l = new Location(getTestName(), 0); - PainlessClass s = painlessLookup.getPainlessStructFromJavaClass(Boolean.class); + PainlessClass s = painlessLookup.lookupPainlessClass(Boolean.class); PainlessField f = s.staticFields.get("TRUE"); PSubField node = new PSubField(l, f); node.prefix = new EStatic(l, "Boolean"); @@ -497,7 +497,7 @@ public void testPSubMapShortcut() { public void testPSubShortcut() { Location l = new Location(getTestName(), 0); - PainlessClass s = painlessLookup.getPainlessStructFromJavaClass(FeatureTest.class); + PainlessClass s = painlessLookup.lookupPainlessClass(FeatureTest.class); PainlessMethod getter = s.methods.get(PainlessLookupUtility.buildPainlessMethodKey("getX", 0)); PainlessMethod setter = s.methods.get(PainlessLookupUtility.buildPainlessMethodKey("setX", 1)); PSubShortcut node = new PSubShortcut(l, "x", FeatureTest.class.getName(), getter, setter); From aff5c3e9be0cf89047d1c458228e6d97b26e5195 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 31 Jul 2018 14:00:55 -0700 Subject: [PATCH 12/33] Reponse to PR comments. --- .../elasticsearch/painless/FunctionRef.java | 98 +++++++++---------- 1 file changed, 49 insertions(+), 49 deletions(-) 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 e3fb5e972539f..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 @@ -109,24 +109,24 @@ public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessCo Constructor javaConstructor = delegateConstructor.javaConstructor; MethodType delegateMethodType = delegateConstructor.methodType; - interfaceMethodName = interfaceMethod.javaMethod.getName(); - 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()); } /** @@ -139,32 +139,32 @@ public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessCo public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessMethod delegateMethod, int numCaptures) { MethodType delegateMethodType = delegateMethod.methodType; - interfaceMethodName = interfaceMethod.javaMethod.getName(); - 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 = delegateMethod.javaMethod.getDeclaringClass().getName(); - isDelegateInterface = delegateMethod.javaMethod.getDeclaringClass().isInterface(); + this.delegateClassName = delegateMethod.javaMethod.getDeclaringClass().getName(); + this.isDelegateInterface = delegateMethod.javaMethod.getDeclaringClass().isInterface(); if (Modifier.isStatic(delegateMethod.javaMethod.getModifiers())) { - delegateInvokeType = H_INVOKESTATIC; + this.delegateInvokeType = H_INVOKESTATIC; } else if (delegateMethod.javaMethod.getDeclaringClass().isInterface()) { - delegateInvokeType = H_INVOKEINTERFACE; + this.delegateInvokeType = H_INVOKEINTERFACE; } else { - delegateInvokeType = H_INVOKEVIRTUAL; + this.delegateInvokeType = H_INVOKEVIRTUAL; } - delegateMethodName = delegateMethod.javaMethod.getName(); + this.delegateMethodName = delegateMethod.javaMethod.getName(); this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures); this.interfaceMethod = interfaceMethod; - delegateTypeParameters = delegateMethod.typeParameters; - delegateReturnType = delegateMethod.returnType; + 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()); } /** @@ -177,25 +177,25 @@ public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessMe public FunctionRef(Class expected, PainlessMethod interfaceMethod, LocalMethod delegateMethod, int numCaptures) { MethodType delegateMethodType = delegateMethod.methodType; - interfaceMethodName = interfaceMethod.javaMethod.getName(); - 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; - isDelegateInterface = false; - delegateInvokeType = H_INVOKESTATIC; + this.delegateClassName = CLASS_NAME; + this.isDelegateInterface = false; + this.delegateInvokeType = H_INVOKESTATIC; - delegateMethodName = delegateMethod.name; + this.delegateMethodName = delegateMethod.name; this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures); this.interfaceMethod = interfaceMethod; - delegateTypeParameters = delegateMethod.typeParameters; - delegateReturnType = delegateMethod.returnType; + 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()); } /** @@ -204,24 +204,24 @@ public FunctionRef(Class expected, PainlessMethod interfaceMethod, LocalMetho */ public FunctionRef(Class expected, PainlessMethod interfaceMethod, String delegateMethodName, MethodType delegateMethodType, int numCaptures) { - interfaceMethodName = interfaceMethod.javaMethod.getName(); - 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; } /** From 57e2a46e45f385c565f47a14e7f6421aa749af95 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 09:16:25 -0700 Subject: [PATCH 13/33] Add local methods to def bootstrap for consistency in look ups. --- .../org/elasticsearch/painless/Compiler.java | 3 +- .../java/org/elasticsearch/painless/Def.java | 38 +++++++---------- .../elasticsearch/painless/DefBootstrap.java | 26 ++++++++---- .../org/elasticsearch/painless/Locals.java | 42 +++++++++---------- .../painless/WriterConstants.java | 2 +- .../painless/node/ECallLocal.java | 4 +- .../painless/node/EFunctionRef.java | 2 +- .../elasticsearch/painless/node/ELambda.java | 3 +- .../painless/node/SFunction.java | 14 ------- .../elasticsearch/painless/node/SSource.java | 12 ++++-- .../painless/DefBootstrapTests.java | 10 +++++ 11 files changed, 78 insertions(+), 78 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index 2096d7d797025..857f2f3f4bea8 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -200,7 +200,7 @@ Constructor compile(Loader loader, MainMethodReserved reserved, String name, ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, scriptClass); SSource root = Walker.buildPainlessTree(scriptClassInfo, reserved, name, source, settings, painlessLookup, null); - root.analyze(painlessLookup); + Object localMethods = root.analyze(painlessLookup); root.write(); try { @@ -209,6 +209,7 @@ Constructor compile(Loader loader, MainMethodReserved reserved, String name, clazz.getField("$SOURCE").set(null, source); clazz.getField("$STATEMENTS").set(null, root.getStatements()); clazz.getField("$DEFINITION").set(null, painlessLookup); + clazz.getField("$LOCALS").set(null, localMethods); return clazz.getConstructors()[0]; } catch (Exception exception) { // Catch everything to let the user know this is something caused internally. 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 10806b64d0e95..1db1e4f7cac70 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 @@ -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.PainlessLookup; import org.elasticsearch.painless.lookup.PainlessLookupUtility; @@ -232,8 +233,10 @@ static PainlessMethod lookupMethodInternal(PainlessLookup painlessLookup, Class< * @throws IllegalArgumentException if no matching whitelisted method was found. * @throws Throwable if a method reference cannot be converted to an functional interface */ - static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, MethodType callSiteType, - Class receiverClass, String name, Object args[]) throws Throwable { + static MethodHandle lookupMethod(PainlessLookup painlessLookup, Map localMethods, + MethodHandles.Lookup methodHandlesLookup, MethodType callSiteType, Class receiverClass, String name, Object args[]) + throws Throwable { + String recipeString = (String) args[0]; int numArguments = callSiteType.parameterCount(); // simple case: no lambdas @@ -286,6 +289,7 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lo // the implementation is strongly typed, now that we know the interface type, // we have everything. filter = lookupReferenceInternal(painlessLookup, + localMethods, methodHandlesLookup, interfaceType, type, @@ -297,6 +301,7 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lo // this cache). It won't blow up since we never nest here (just references) MethodType nestedType = MethodType.methodType(interfaceType, captures); CallSite nested = DefBootstrap.bootstrap(painlessLookup, + localMethods, methodHandlesLookup, call, nestedType, @@ -324,8 +329,8 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lo * This is just like LambdaMetaFactory, only with a dynamic type. The interface type is known, * so we simply need to lookup the matching implementation method based on receiver type. */ - static MethodHandle lookupReference(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, String interfaceClass, - Class receiverClass, String name) throws Throwable { + static MethodHandle lookupReference(PainlessLookup painlessLookup, Map localMethods, + MethodHandles.Lookup methodHandlesLookup, String interfaceClass, Class receiverClass, String name) throws Throwable { Class interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass); PainlessMethod interfaceMethod = painlessLookup.lookupPainlessClass(interfaceType).functionalMethod; if (interfaceMethod == null) { @@ -333,15 +338,14 @@ static MethodHandle lookupReference(PainlessLookup painlessLookup, MethodHandles } int arity = interfaceMethod.typeParameters.size(); PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity); - return lookupReferenceInternal(painlessLookup, methodHandlesLookup, interfaceType, - PainlessLookupUtility.typeToCanonicalTypeName(implMethod.targetClass), + return lookupReferenceInternal(painlessLookup, localMethods, methodHandlesLookup, + interfaceType, PainlessLookupUtility.typeToCanonicalTypeName(implMethod.targetClass), implMethod.javaMethod.getName(), receiverClass); } /** Returns a method handle to an implementation of clazz, given method reference signature. */ - private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, - Class clazz, String type, String call, Class... captures) - throws Throwable { + private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLookup, Map localMethods, + MethodHandles.Lookup methodHandlesLookup, Class clazz, String type, String call, Class... captures) throws Throwable { final FunctionRef ref; if ("this".equals(type)) { // user written method @@ -351,13 +355,8 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku "to [" + PainlessLookupUtility.typeToCanonicalTypeName(clazz) + "], not a functional interface"); } int arity = interfaceMethod.typeParameters.size() + captures.length; - final MethodHandle handle; - try { - MethodHandle accessor = methodHandlesLookup.findStaticGetter(methodHandlesLookup.lookupClass(), - getUserFunctionHandleFieldName(call, arity), - MethodHandle.class); - handle = (MethodHandle)accessor.invokeExact(); - } catch (NoSuchFieldException | IllegalAccessException e) { + LocalMethod localMethod = localMethods.get(Locals.buildLocalMethodKey(call, arity)); + if (localMethod == null) { // 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("$")) { @@ -366,7 +365,7 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku } throw new IllegalArgumentException("Unknown call [" + call + "] with [" + arity + "] arguments."); } - ref = new FunctionRef(clazz, interfaceMethod, call, handle.type(), captures.length); + ref = new FunctionRef(clazz, interfaceMethod, call, localMethod.methodType, captures.length); } else { // whitelist lookup ref = FunctionRef.resolveFromLookup(painlessLookup, clazz, type, call, captures.length); @@ -385,11 +384,6 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku return callSite.dynamicInvoker().asType(MethodType.methodType(clazz, captures)); } - /** gets the field name used to lookup up the MethodHandle for a function. */ - public static String getUserFunctionHandleFieldName(String name, int arity) { - return "handle$" + name + "$" + arity; - } - /** * Looks up handle for a dynamic field getter (field load) *

diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java index 2fadaf30964a6..96ea64b8f5fa1 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java @@ -20,6 +20,7 @@ package org.elasticsearch.painless; import org.elasticsearch.common.SuppressForbidden; +import org.elasticsearch.painless.Locals.LocalMethod; import org.elasticsearch.painless.lookup.PainlessLookup; import java.lang.invoke.CallSite; @@ -28,6 +29,7 @@ import java.lang.invoke.MethodType; import java.lang.invoke.MutableCallSite; import java.lang.invoke.WrongMethodTypeException; +import java.util.Map; /** * Painless invokedynamic bootstrap for the call site. @@ -105,19 +107,21 @@ static final class PIC extends MutableCallSite { static final int MAX_DEPTH = 5; private final PainlessLookup painlessLookup; + private final Map localMethods; private final MethodHandles.Lookup methodHandlesLookup; private final String name; private final int flavor; private final Object[] args; int depth; // pkg-protected for testing - PIC(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, - String name, MethodType type, int initialDepth, int flavor, Object[] args) { + PIC(PainlessLookup painlessLookup, Map localMethods, + MethodHandles.Lookup methodHandlesLookup, String name, MethodType type, int initialDepth, int flavor, Object[] args) { super(type); if (type.parameterType(0) != Object.class) { throw new BootstrapMethodError("The receiver type (1st arg) of invokedynamic descriptor must be Object."); } this.painlessLookup = painlessLookup; + this.localMethods = localMethods; this.methodHandlesLookup = methodHandlesLookup; this.name = name; this.flavor = flavor; @@ -145,7 +149,7 @@ static boolean checkClass(Class clazz, Object receiver) { private MethodHandle lookup(int flavor, String name, Class receiver) throws Throwable { switch(flavor) { case METHOD_CALL: - return Def.lookupMethod(painlessLookup, methodHandlesLookup, type(), receiver, name, args); + return Def.lookupMethod(painlessLookup, localMethods, methodHandlesLookup, type(), receiver, name, args); case LOAD: return Def.lookupGetter(painlessLookup, receiver, name); case STORE: @@ -157,7 +161,7 @@ private MethodHandle lookup(int flavor, String name, Class receiver) throws T case ITERATOR: return Def.lookupIterator(receiver); case REFERENCE: - return Def.lookupReference(painlessLookup, methodHandlesLookup, (String) args[0], receiver, name); + return Def.lookupReference(painlessLookup, localMethods, methodHandlesLookup, (String) args[0], receiver, name); case INDEX_NORMALIZE: return Def.lookupIndexNormalize(receiver); default: throw new AssertionError(); @@ -432,8 +436,9 @@ static boolean checkBoth(Class left, Class right, Object leftObject, Objec *

* see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic */ - public static CallSite bootstrap(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, String name, - MethodType type, int initialDepth, int flavor, Object... args) { + @SuppressWarnings("unchecked") + public static CallSite bootstrap(PainlessLookup painlessLookup, Object localMethods, + MethodHandles.Lookup methodHandlesLookup, String name, MethodType type, int initialDepth, int flavor, Object... args) { // validate arguments switch(flavor) { // "function-call" like things get a polymorphic cache @@ -452,7 +457,8 @@ public static CallSite bootstrap(PainlessLookup painlessLookup, MethodHandles.Lo if (args.length != numLambdas + 1) { throw new BootstrapMethodError("Illegal number of parameters: expected " + numLambdas + " references"); } - return new PIC(painlessLookup, methodHandlesLookup, name, type, initialDepth, flavor, args); + return new PIC(painlessLookup, (Map)localMethods, + methodHandlesLookup, name, type, initialDepth, flavor, args); case LOAD: case STORE: case ARRAY_LOAD: @@ -462,7 +468,8 @@ public static CallSite bootstrap(PainlessLookup painlessLookup, MethodHandles.Lo if (args.length > 0) { throw new BootstrapMethodError("Illegal static bootstrap parameters for flavor: " + flavor); } - return new PIC(painlessLookup, methodHandlesLookup, name, type, initialDepth, flavor, args); + return new PIC(painlessLookup, (Map)localMethods, + methodHandlesLookup, name, type, initialDepth, flavor, args); case REFERENCE: if (args.length != 1) { throw new BootstrapMethodError("Invalid number of parameters for reference call"); @@ -470,7 +477,8 @@ public static CallSite bootstrap(PainlessLookup painlessLookup, MethodHandles.Lo if (args[0] instanceof String == false) { throw new BootstrapMethodError("Illegal parameter for reference call: " + args[0]); } - return new PIC(painlessLookup, methodHandlesLookup, name, type, initialDepth, flavor, args); + return new PIC(painlessLookup, (Map)localMethods, + methodHandlesLookup, name, type, initialDepth, flavor, args); // operators get monomorphic cache, with a generic impl for a fallback case UNARY_OPERATOR: 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 f2c7e02c637c1..e07c016ddd0e3 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 @@ -32,6 +32,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; + +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToJavaType; /** * Tracks user defined methods and variables across compilation phases. @@ -74,7 +77,10 @@ public LocalMethod(String name, Class returnType, List> typeParamete /** Creates a new local variable scope (e.g. loop) inside the current scope */ public static Locals newLocalScope(Locals currentScope) { - return new Locals(currentScope); + Locals locals = new Locals(currentScope); + locals.methods = currentScope.methods; + + return locals; } /** @@ -82,9 +88,13 @@ public static Locals newLocalScope(Locals currentScope) { *

* This is just like {@link #newFunctionScope}, except the captured parameters are made read-only. */ - public static Locals newLambdaScope(Locals programScope, Class returnType, List parameters, + public static Locals newLambdaScope(Locals programScope, String name, Class returnType, List parameters, int captureCount, int maxLoopCounter) { Locals locals = new Locals(programScope, programScope.painlessLookup, returnType, KEYWORDS); + locals.methods = programScope.methods; + List> typeParameters = parameters.stream().map(parameter -> typeToJavaType(parameter.clazz)).collect(Collectors.toList()); + locals.methods.put(buildLocalMethodKey(name, parameters.size()), new LocalMethod(name, returnType, typeParameters, + MethodType.methodType(typeToJavaType(returnType), typeParameters))); for (int i = 0; i < parameters.size(); i++) { Parameter parameter = parameters.get(i); // TODO: allow non-captures to be r/w: @@ -104,6 +114,7 @@ public static Locals newLambdaScope(Locals programScope, Class returnType, Li /** Creates a new function scope inside the current scope */ public static Locals newFunctionScope(Locals programScope, Class returnType, List parameters, int maxLoopCounter) { Locals locals = new Locals(programScope, programScope.painlessLookup, returnType, KEYWORDS); + locals.methods = programScope.methods; for (Parameter parameter : parameters) { locals.addVariable(parameter.location, parameter.clazz, parameter.name, false); } @@ -118,6 +129,7 @@ public static Locals newFunctionScope(Locals programScope, Class returnType, public static Locals newMainMethodScope(ScriptClassInfo scriptClassInfo, Locals programScope, int maxLoopCounter) { Locals locals = new Locals( programScope, programScope.painlessLookup, scriptClassInfo.getExecuteMethodReturnType(), KEYWORDS); + locals.methods = programScope.methods; // This reference. Internal use only. locals.defineVariable(null, Object.class, THIS, true); @@ -136,6 +148,7 @@ 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) { Locals locals = new Locals(null, painlessLookup, null, null); + locals.methods = new HashMap<>(); for (LocalMethod method : methods) { locals.addMethod(method); } @@ -167,15 +180,8 @@ public Variable getVariable(Location location, String name) { } /** Looks up a method. Returns null if the method does not exist. */ - public LocalMethod getMethod(String key) { - LocalMethod method = lookupMethod(key); - if (method != null) { - return method; - } - if (parent != null) { - return parent.getMethod(key); - } - return null; + public LocalMethod getMethod(String methodName, int methodArity) { + return methods.get(buildLocalMethodKey(methodName, methodArity)); } /** Creates a new variable. Throws IAE if the variable has already been defined (even in a parent) or reserved. */ @@ -260,15 +266,10 @@ private Variable lookupVariable(Location location, String name) { return variables.get(name); } - /** Looks up a method at this scope only. Returns null if the method does not exist. */ - private LocalMethod lookupMethod(String key) { - if (methods == null) { - return null; - } - return methods.get(key); + public Map getMethods() { + return Collections.unmodifiableMap(methods); } - /** Defines a variable at this scope internally. */ private Variable defineVariable(Location location, Class type, String name, boolean readonly) { if (variables == null) { @@ -281,14 +282,9 @@ private Variable defineVariable(Location location, Class type, String name, b } private void addMethod(LocalMethod method) { - if (methods == null) { - methods = new HashMap<>(); - } methods.put(buildLocalMethodKey(method.name, method.typeParameters.size()), method); - // TODO: check result } - private int getNextSlot() { return nextSlotNumber; } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java index db3aeff0483f6..72ec57de8013c 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java @@ -120,7 +120,7 @@ public final class WriterConstants { DEF_BOOTSTRAP_METHOD.getDescriptor(), false); public static final Type DEF_BOOTSTRAP_DELEGATE_TYPE = Type.getType(DefBootstrap.class); public static final Method DEF_BOOTSTRAP_DELEGATE_METHOD = getAsmMethod(CallSite.class, "bootstrap", PainlessLookup.class, - MethodHandles.Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class); + Object.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class); public static final Type DEF_UTIL_TYPE = Type.getType(Def.class); public static final Method DEF_TO_BOOLEAN = getAsmMethod(boolean.class, "DefToboolean" , Object.class); 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 7605a0c9f7f40..1f9973df19224 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 @@ -24,7 +24,6 @@ import org.elasticsearch.painless.Locals.LocalMethod; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; -import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.objectweb.asm.commons.Method; import java.util.List; @@ -59,8 +58,7 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - String methodKey = PainlessLookupUtility.buildPainlessMethodKey(name, arguments.size()); - method = locals.getMethod(methodKey); + method = locals.getMethod(name, arguments.size()); if (method == null) { throw createError(new IllegalArgumentException("Unknown call [" + name + "] with [" + arguments.size() + "] arguments.")); 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 ead2e0c5f7070..692581d811849 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 @@ -71,7 +71,7 @@ void analyze(Locals locals) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + "to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface"); } - LocalMethod delegateMethod = locals.getMethod(Locals.buildLocalMethodKey(call, interfaceMethod.typeParameters.size())); + LocalMethod delegateMethod = locals.getMethod(call, interfaceMethod.typeParameters.size()); if (delegateMethod == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + "to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], function not found"); 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 e84ab0065011a..ecd11ce1bf794 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 @@ -119,6 +119,7 @@ void analyze(Locals locals) { actualParamTypeStrs.add(type); } } + } else { // we know the method statically, infer return type and any unknown/def types interfaceMethod = locals.getPainlessLookup().lookupPainlessClass(expected).functionalMethod; @@ -173,7 +174,7 @@ void analyze(Locals locals) { desugared = new SFunction(reserved, location, PainlessLookupUtility.typeToCanonicalTypeName(returnType), name, paramTypes, paramNames, statements, true); desugared.generateSignature(locals.getPainlessLookup()); - desugared.analyze(Locals.newLambdaScope(locals.getProgramScope(), returnType, + desugared.analyze(Locals.newLambdaScope(locals.getProgramScope(), desugared.name, returnType, desugared.parameters, captures.size(), reserved.getMaxLoopCounter())); // setup method reference to synthetic method 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 8230b5436979f..4a844c7bc30ef 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 @@ -20,20 +20,16 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.CompilerSettings; -import org.elasticsearch.painless.Constant; -import org.elasticsearch.painless.Def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Parameter; import org.elasticsearch.painless.Locals.Variable; import org.elasticsearch.painless.Location; 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.node.SSource.Reserved; import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.Handle; import org.objectweb.asm.Opcodes; import java.lang.invoke.MethodType; @@ -46,7 +42,6 @@ import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableSet; -import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; /** * Represents a user-defined function. @@ -218,15 +213,6 @@ void write(MethodWriter function, Globals globals) { throw createError(new IllegalStateException("Illegal tree structure.")); } } - - String staticHandleFieldName = Def.getUserFunctionHandleFieldName(name, parameters.size()); - globals.addConstantInitializer(new Constant(location, WriterConstants.METHOD_HANDLE_TYPE, - staticHandleFieldName, this::initializeConstant)); - } - - private void initializeConstant(MethodWriter writer) { - final Handle handle = new Handle(Opcodes.H_INVOKESTATIC, CLASS_TYPE.getInternalName(), name, method.getDescriptor(), false); - writer.push(handle); } @Override 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 fe735c0db313e..6b18609bfc996 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 @@ -69,6 +69,7 @@ import static org.elasticsearch.painless.WriterConstants.GET_NAME_METHOD; import static org.elasticsearch.painless.WriterConstants.GET_SOURCE_METHOD; import static org.elasticsearch.painless.WriterConstants.GET_STATEMENTS_METHOD; +import static org.elasticsearch.painless.WriterConstants.OBJECT_TYPE; import static org.elasticsearch.painless.WriterConstants.OUT_OF_MEMORY_ERROR_TYPE; import static org.elasticsearch.painless.WriterConstants.PAINLESS_ERROR_TYPE; import static org.elasticsearch.painless.WriterConstants.PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD; @@ -163,7 +164,7 @@ void extractVariables(Set variables) { throw new IllegalStateException("Illegal tree structure."); } - public void analyze(PainlessLookup painlessLookup) { + public Map analyze(PainlessLookup painlessLookup) { Map methods = new HashMap<>(); for (SFunction function : functions) { @@ -177,7 +178,10 @@ public void analyze(PainlessLookup painlessLookup) { } } - analyze(Locals.newProgramScope(painlessLookup, methods.values())); + Locals locals = Locals.newProgramScope(painlessLookup, methods.values()); + analyze(locals); + + return locals.getMethods(); } @Override @@ -253,6 +257,7 @@ public void write() { globals.getStatements(), settings); bootstrapDef.visitCode(); bootstrapDef.getStatic(CLASS_TYPE, "$DEFINITION", DEFINITION_TYPE); + bootstrapDef.getStatic(CLASS_TYPE, "$LOCALS", OBJECT_TYPE); bootstrapDef.loadArgs(); bootstrapDef.invokeStatic(DEF_BOOTSTRAP_DELEGATE_TYPE, DEF_BOOTSTRAP_DELEGATE_METHOD); bootstrapDef.returnValue(); @@ -263,8 +268,9 @@ public void write() { visitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$SOURCE", STRING_TYPE.getDescriptor(), null, null).visitEnd(); visitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$STATEMENTS", BITSET_TYPE.getDescriptor(), null, null).visitEnd(); - // Write the static variable used by the method to bootstrap def calls + // Write the static variables used by the method to bootstrap def calls visitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$DEFINITION", DEFINITION_TYPE.getDescriptor(), null, null).visitEnd(); + visitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$LOCALS", OBJECT_TYPE.getDescriptor(), null, null).visitEnd(); org.objectweb.asm.commons.Method init; diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java index 1ef855d561c52..a9861341a84f5 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java @@ -38,6 +38,7 @@ public class DefBootstrapTests extends ESTestCase { /** calls toString() on integers, twice */ public void testOneType() throws Throwable { CallSite site = DefBootstrap.bootstrap(painlessLookup, + Collections.emptyMap(), MethodHandles.publicLookup(), "toString", MethodType.methodType(String.class, Object.class), @@ -58,6 +59,7 @@ public void testOneType() throws Throwable { public void testTwoTypes() throws Throwable { CallSite site = DefBootstrap.bootstrap(painlessLookup, + Collections.emptyMap(), MethodHandles.publicLookup(), "toString", MethodType.methodType(String.class, Object.class), @@ -83,6 +85,7 @@ public void testTooManyTypes() throws Throwable { // if this changes, test must be rewritten assertEquals(5, DefBootstrap.PIC.MAX_DEPTH); CallSite site = DefBootstrap.bootstrap(painlessLookup, + Collections.emptyMap(), MethodHandles.publicLookup(), "toString", MethodType.methodType(String.class, Object.class), @@ -109,6 +112,7 @@ public void testTooManyTypes() throws Throwable { /** test that we revert to the megamorphic classvalue cache and that it works as expected */ public void testMegamorphic() throws Throwable { DefBootstrap.PIC site = (DefBootstrap.PIC) DefBootstrap.bootstrap(painlessLookup, + Collections.emptyMap(), MethodHandles.publicLookup(), "size", MethodType.methodType(int.class, Object.class), @@ -141,6 +145,7 @@ public void testMegamorphic() throws Throwable { public void testNullGuardAdd() throws Throwable { DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(painlessLookup, + Collections.emptyMap(), MethodHandles.publicLookup(), "add", MethodType.methodType(Object.class, Object.class, Object.class), @@ -153,6 +158,7 @@ public void testNullGuardAdd() throws Throwable { public void testNullGuardAddWhenCached() throws Throwable { DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(painlessLookup, + Collections.emptyMap(), MethodHandles.publicLookup(), "add", MethodType.methodType(Object.class, Object.class, Object.class), @@ -166,6 +172,7 @@ public void testNullGuardAddWhenCached() throws Throwable { public void testNullGuardEq() throws Throwable { DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(painlessLookup, + Collections.emptyMap(), MethodHandles.publicLookup(), "eq", MethodType.methodType(boolean.class, Object.class, Object.class), @@ -179,6 +186,7 @@ public void testNullGuardEq() throws Throwable { public void testNullGuardEqWhenCached() throws Throwable { DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(painlessLookup, + Collections.emptyMap(), MethodHandles.publicLookup(), "eq", MethodType.methodType(boolean.class, Object.class, Object.class), @@ -197,6 +205,7 @@ public void testNullGuardEqWhenCached() throws Throwable { public void testNoNullGuardAdd() throws Throwable { DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(painlessLookup, + Collections.emptyMap(), MethodHandles.publicLookup(), "add", MethodType.methodType(Object.class, int.class, Object.class), @@ -211,6 +220,7 @@ public void testNoNullGuardAdd() throws Throwable { public void testNoNullGuardAddWhenCached() throws Throwable { DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(painlessLookup, + Collections.emptyMap(), MethodHandles.publicLookup(), "add", MethodType.methodType(Object.class, int.class, Object.class), From 4da7113c618e3022417e9f37637d076d9e51abf8 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 09:29:28 -0700 Subject: [PATCH 14/33] Add FunctionReference. --- .../painless/FunctionReference.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java new file mode 100644 index 0000000000000..06637d4dd4829 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java @@ -0,0 +1,58 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.painless; + +import java.lang.invoke.MethodType; + +public class FunctionReference { + + /** functional interface method name */ + public final String interfaceMethodName; + /** functional interface method signature */ + public final MethodType interfaceMethodType; + /** class of the delegate method to be called */ + public final String delegateClassName; + /** whether a call is made on a delegate interface */ + public final boolean isDelegateInterface; + /** the invocation type of the delegate method */ + public final int delegateInvokeType; + /** the name of the delegate method */ + public final String delegateMethodName; + /** delegate method signature */ + public final MethodType delegateMethodType; + /** factory (CallSite) method signature */ + public final MethodType factoryMethodType; + + public FunctionReference( + String interfaceMethodName, MethodType interfaceMethodType, + String delegateClassName, boolean isDelegateInterface, + int delegateInvokeType, String delegateMethodName, MethodType delegateMethodType, + MethodType factoryMethodType) { + + this.interfaceMethodName = interfaceMethodName; + this.interfaceMethodType = interfaceMethodType; + this.delegateClassName = delegateClassName; + this.isDelegateInterface = isDelegateInterface; + this.delegateInvokeType = delegateInvokeType; + this.delegateMethodName = delegateMethodName; + this.delegateMethodType = delegateMethodType; + this.factoryMethodType = factoryMethodType; + } +} From 1d8b54723de09533c45e296b08a910f01ed4217b Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 09:33:33 -0700 Subject: [PATCH 15/33] Progress. --- .../painless/lookup/PainlessClass.java | 6 +-- .../painless/lookup/PainlessClassBuilder.java | 6 +-- .../painless/lookup/PainlessLookup.java | 40 +++++++++++++++++++ .../lookup/PainlessLookupBuilder.java | 2 +- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java index 835bfb5c505a4..50bb79dcfbdf5 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java @@ -35,13 +35,13 @@ public final class PainlessClass { public final Map getterMethodHandles; public final Map setterMethodHandles; - public final PainlessMethod functionalMethod; + public final PainlessMethod functionalInterfaceMethod; PainlessClass(Map constructors, Map staticMethods, Map methods, Map staticFields, Map fields, Map getterMethodHandles, Map setterMethodHandles, - PainlessMethod functionalMethod) { + PainlessMethod functionalInterfaceMethod) { this.constructors = Collections.unmodifiableMap(constructors); @@ -54,6 +54,6 @@ public final class PainlessClass { this.getterMethodHandles = Collections.unmodifiableMap(getterMethodHandles); this.setterMethodHandles = Collections.unmodifiableMap(setterMethodHandles); - this.functionalMethod = functionalMethod; + this.functionalInterfaceMethod = functionalInterfaceMethod; } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java index 866f711ba0f3e..a61215e9ed749 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java @@ -35,7 +35,7 @@ final class PainlessClassBuilder { final Map getterMethodHandles; final Map setterMethodHandles; - PainlessMethod functionalMethod; + PainlessMethod functionalInterfaceMethod; PainlessClassBuilder() { constructors = new HashMap<>(); @@ -49,11 +49,11 @@ final class PainlessClassBuilder { getterMethodHandles = new HashMap<>(); setterMethodHandles = new HashMap<>(); - functionalMethod = null; + functionalInterfaceMethod = null; } PainlessClass build() { return new PainlessClass(constructors, staticMethods, methods, staticFields, fields, - getterMethodHandles, setterMethodHandles, functionalMethod); + getterMethodHandles, setterMethodHandles, functionalInterfaceMethod); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java index 5123f78cbf036..3918697af8292 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java @@ -72,6 +72,14 @@ public PainlessClass lookupPainlessClass(Class targetClass) { return classesToPainlessClasses.get(targetClass); } + public PainlessConstructor lookupPainlessConstructor(String targetClassName, int constructorArity) { + Objects.requireNonNull(targetClassName); + + Class targetClass = canonicalTypeNameToType(targetClassName); + + return lookupPainlessConstructor(targetClassName, constructorArity); + } + public PainlessConstructor lookupPainlessConstructor(Class targetClass, int constructorArity) { Objects.requireNonNull(targetClass); @@ -93,6 +101,14 @@ public PainlessConstructor lookupPainlessConstructor(Class targetClass, int c return painlessConstructor; } + public PainlessMethod lookupPainlessMethod(String targetClassName, boolean isStatic, String methodName, int methodArity) { + Objects.requireNonNull(targetClassName); + + Class targetClass = canonicalTypeNameToType(targetClassName); + + return lookupPainlessMethod(targetClass, isStatic, methodName, methodArity); + } + public PainlessMethod lookupPainlessMethod(Class targetClass, boolean isStatic, String methodName, int methodArity) { Objects.requireNonNull(targetClass); Objects.requireNonNull(methodName); @@ -121,6 +137,14 @@ public PainlessMethod lookupPainlessMethod(Class targetClass, boolean isStati return painlessMethod; } + public PainlessField lookupPainlessField(String targetClassName, boolean isStatic, String fieldName) { + Objects.requireNonNull(targetClassName); + + Class targetClass = canonicalTypeNameToType(targetClassName); + + return lookupPainlessField(targetClass, isStatic, fieldName); + } + public PainlessField lookupPainlessField(Class targetClass, boolean isStatic, String fieldName) { Objects.requireNonNull(targetClass); Objects.requireNonNull(fieldName); @@ -144,4 +168,20 @@ public PainlessField lookupPainlessField(Class targetClass, boolean isStatic, return painlessField; } + + public PainlessMethod lookupFunctionalInterfacePainlessMethod(Class targetClass) { + PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass); + + if (targetPainlessClass == null) { + throw new IllegalArgumentException("target class [" + typeToCanonicalTypeName(targetClass) + "] not found"); + } + + PainlessMethod functionalInterfacePainlessMethod = targetPainlessClass.functionalInterfaceMethod; + + if (functionalInterfacePainlessMethod == null) { + throw new IllegalArgumentException("target class [" + typeToCanonicalTypeName(targetClass) + "] is not a functional interface"); + } + + return functionalInterfacePainlessMethod; + } } 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 a0cab7f1a5bda..7f956b194b889 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 @@ -876,7 +876,7 @@ private void setFunctionalInterfaceMethod(Class targetClass, PainlessClassBui } else if (javaMethods.size() == 1) { java.lang.reflect.Method javaMethod = javaMethods.get(0); String painlessMethodKey = buildPainlessMethodKey(javaMethod.getName(), javaMethod.getParameterCount()); - painlessClassBuilder.functionalMethod = painlessClassBuilder.methods.get(painlessMethodKey); + painlessClassBuilder.functionalInterfaceMethod = painlessClassBuilder.methods.get(painlessMethodKey); } } } From e51f3df843ba3f28ddaf3eb4f01fad00f2a8b018 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 09:36:56 -0700 Subject: [PATCH 16/33] Progress. --- .../painless/FunctionReference.java | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java index 06637d4dd4829..99b00771c1451 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java @@ -19,10 +19,128 @@ package org.elasticsearch.painless; +import org.elasticsearch.painless.lookup.PainlessConstructor; +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.lang.reflect.Modifier; +import java.util.Objects; + +import static org.elasticsearch.painless.WriterConstants.CLASS_NAME; +import static org.objectweb.asm.Opcodes.H_INVOKEINTERFACE; +import static org.objectweb.asm.Opcodes.H_INVOKESTATIC; +import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL; public class FunctionReference { + /** + * Creates a new FunctionReference which will resolve {@code type::call} from the whitelist. + * @param painlessLookup the whitelist against which this script is being compiled + * @param locals the local state for a script instance + * @param targetClass functional interface type to implement. + * @param typeName the left hand side of a method reference expression + * @param methodName the right hand side of a method reference expression + * @param numCaptures number of captured arguments + */ + public static FunctionReference resolve(PainlessLookup painlessLookup, Locals locals, + Class targetClass, String typeName, String methodName, int numCaptures) { + + Objects.requireNonNull(painlessLookup); + Objects.requireNonNull(targetClass); + Objects.requireNonNull(typeName); + Objects.requireNonNull(methodName); + + String targetClassName = PainlessLookupUtility.typeToCanonicalTypeName(targetClass); + PainlessMethod interfaceMethod; + + try { + interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(targetClass); + } catch (IllegalArgumentException iae) { + throw new IllegalArgumentException("cannot convert function reference [" + typeName + "::" + methodName + "] " + + "to a non-functional interface [" + targetClassName + "]", iae); + } + + int typeParametersSize = interfaceMethod.typeParameters.size(); + + String interfaceMethodName = interfaceMethod.javaMethod.getName(); + MethodType interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1); + String delegateClassName; + boolean isDelegateInterface; + int delegateInvokeType; + String delegateMethodName; + MethodType delegateMethodType; + + if ("this".equals(typeName)) { + Objects.requireNonNull(locals); + + Locals.LocalMethod localMethod = locals.getMethod(Locals.buildLocalMethodKey(methodName, typeParametersSize)); + + if (localMethod == null) { + throw new IllegalArgumentException("function reference [this::" + methodName + "] not found"); + } + + delegateClassName = CLASS_NAME; + isDelegateInterface = false; + delegateInvokeType = H_INVOKESTATIC; + delegateMethodName = localMethod.name; + delegateMethodType = localMethod.methodType.dropParameterTypes(0, numCaptures); + } else if ("new".equals(methodName)) { + PainlessConstructor painlessConstructor; + + try { + painlessConstructor = painlessLookup.lookupPainlessConstructor(typeName, typeParametersSize); + } catch (IllegalArgumentException iae) { + throw new IllegalArgumentException( + "function reference [" + typeName + "::new] matching [" + targetClassName + "] not found", iae); + } + + delegateClassName = painlessConstructor.javaConstructor.getDeclaringClass().getName(); + isDelegateInterface = false; + delegateInvokeType = H_NEWINVOKESPECIAL; + delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME; + delegateMethodType = painlessConstructor.methodType.dropParameterTypes(0, numCaptures); + } else { + PainlessMethod painlessMethod; + + try { + painlessMethod = painlessLookup.lookupPainlessMethod(targetClassName, true, methodName, typeParametersSize); + } catch (IllegalArgumentException staticIAE) { + try { + painlessMethod = painlessLookup.lookupPainlessMethod( + targetClassName, false, methodName, numCaptures > 0 ? typeParametersSize : typeParametersSize - 1); + } catch (IllegalArgumentException iae) { + throw new IllegalArgumentException( + "function reference [" + typeName + "::" + methodName + "] matching [" + targetClassName + "] not found", iae); + } + } + + delegateClassName = painlessMethod.javaMethod.getDeclaringClass().getName(); + isDelegateInterface = painlessMethod.javaMethod.getDeclaringClass().isInterface(); + + if (Modifier.isStatic(painlessMethod.javaMethod.getModifiers())) { + delegateInvokeType = H_INVOKESTATIC; + } else if (isDelegateInterface) { + delegateInvokeType = H_INVOKEINTERFACE; + } else { + delegateInvokeType = H_INVOKEVIRTUAL; + } + + delegateMethodName = painlessMethod.javaMethod.getName(); + delegateMethodType = painlessMethod.methodType.dropParameterTypes(0, numCaptures); + } + + MethodType factoryMethodType = MethodType.methodType(targetClass, + delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); + + return new FunctionReference(interfaceMethodName, interfaceMethodType, + delegateClassName, isDelegateInterface, delegateInvokeType, delegateMethodName, delegateMethodType, + factoryMethodType + ); + } + /** functional interface method name */ public final String interfaceMethodName; /** functional interface method signature */ From a082624e96b125256ec4813ee2e608aefd99f0b5 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 10:49:05 -0700 Subject: [PATCH 17/33] Progress. --- .../java/org/elasticsearch/painless/Def.java | 69 ++--- .../elasticsearch/painless/FunctionRef.java | 289 ------------------ .../painless/FunctionReference.java | 165 ++++++---- .../elasticsearch/painless/MethodWriter.java | 15 + .../painless/node/ECapturingFunctionRef.java | 38 +-- .../painless/node/EFunctionRef.java | 56 +--- .../elasticsearch/painless/node/ELambda.java | 43 +-- 7 files changed, 153 insertions(+), 522 deletions(-) delete mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java 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 1db1e4f7cac70..eca9836d039ec 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 @@ -294,7 +294,7 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, Map localMethods, MethodHandles.Lookup methodHandlesLookup, String interfaceClass, Class receiverClass, String name) throws Throwable { - Class interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass); - PainlessMethod interfaceMethod = painlessLookup.lookupPainlessClass(interfaceType).functionalMethod; - if (interfaceMethod == null) { + Class interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass); + PainlessMethod interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(interfaceType); + if (interfaceMethod == null) { throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface"); - } - int arity = interfaceMethod.typeParameters.size(); - PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity); - return lookupReferenceInternal(painlessLookup, localMethods, methodHandlesLookup, + } + int arity = interfaceMethod.typeParameters.size(); + PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity); + return lookupReferenceInternal(painlessLookup, localMethods, methodHandlesLookup, interfaceType, PainlessLookupUtility.typeToCanonicalTypeName(implMethod.targetClass), - implMethod.javaMethod.getName(), receiverClass); + implMethod.javaMethod.getName(), 1); } /** Returns a method handle to an implementation of clazz, given method reference signature. */ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLookup, Map localMethods, - MethodHandles.Lookup methodHandlesLookup, Class clazz, String type, String call, Class... captures) throws Throwable { - final FunctionRef ref; - if ("this".equals(type)) { - // user written method - PainlessMethod interfaceMethod = painlessLookup.lookupPainlessClass(clazz).functionalMethod; - if (interfaceMethod == null) { - throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + - "to [" + PainlessLookupUtility.typeToCanonicalTypeName(clazz) + "], not a functional interface"); - } - int arity = interfaceMethod.typeParameters.size() + captures.length; - LocalMethod localMethod = localMethods.get(Locals.buildLocalMethodKey(call, arity)); - if (localMethod == null) { - // 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.javaMethod.getName() + - "] in [" + clazz + "]"); - } - throw new IllegalArgumentException("Unknown call [" + call + "] with [" + arity + "] arguments."); - } - ref = new FunctionRef(clazz, interfaceMethod, call, localMethod.methodType, captures.length); - } else { - // whitelist lookup - ref = FunctionRef.resolveFromLookup(painlessLookup, clazz, type, call, captures.length); - } - final CallSite callSite = LambdaBootstrap.lambdaBootstrap( - methodHandlesLookup, - ref.interfaceMethodName, - ref.factoryMethodType, - ref.interfaceMethodType, - ref.delegateClassName, - ref.delegateInvokeType, - ref.delegateMethodName, - ref.delegateMethodType, - ref.isDelegateInterface ? 1 : 0 - ); - return callSite.dynamicInvoker().asType(MethodType.methodType(clazz, captures)); + MethodHandles.Lookup methodHandlesLookup, Class clazz, String type, String call, int captures) throws Throwable { + final FunctionReference ref = FunctionReference.create(painlessLookup, localMethods, null, clazz, type, call, captures); + final CallSite callSite = LambdaBootstrap.lambdaBootstrap( + methodHandlesLookup, + ref.interfaceMethodName, + ref.factoryMethodType, + ref.interfaceMethodType, + ref.delegateClassName, + ref.delegateInvokeType, + ref.delegateMethodName, + ref.delegateMethodType, + ref.isDelegateInterface ? 1 : 0 + ); + return callSite.dynamicInvoker().asType(MethodType.methodType(clazz, ref.factoryMethodType.parameterArray())); } /** 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 deleted file mode 100644 index cc55848944636..0000000000000 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -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; -import org.elasticsearch.painless.lookup.PainlessLookupUtility; -import org.elasticsearch.painless.lookup.PainlessMethod; -import org.objectweb.asm.Type; - -import java.lang.invoke.MethodType; -import java.lang.reflect.Constructor; -import java.lang.reflect.Modifier; -import java.util.List; - -import static org.elasticsearch.painless.WriterConstants.CLASS_NAME; -import static org.objectweb.asm.Opcodes.H_INVOKEINTERFACE; -import static org.objectweb.asm.Opcodes.H_INVOKESTATIC; -import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL; -import static org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL; - -/** - * Reference to a function or lambda. - *

- * Once you have created one of these, you have "everything you need" to call {@link LambdaBootstrap} - * either statically from bytecode with invokedynamic, or at runtime from Java. - */ -public class FunctionRef { - - /** functional interface method name */ - public final String interfaceMethodName; - /** factory (CallSite) method signature */ - public final MethodType factoryMethodType; - /** functional interface method signature */ - public final MethodType interfaceMethodType; - /** class of the delegate method to be called */ - public final String delegateClassName; - /** the invocation type of the delegate method */ - public final int delegateInvokeType; - /** the name of the delegate method */ - public final String delegateMethodName; - /** delegate method signature */ - public final MethodType delegateMethodType; - - /** interface method */ - public final PainlessMethod interfaceMethod; - /** delegate method type parameters */ - public final List> delegateTypeParameters; - /** delegate method return type */ - public final Class delegateReturnType; - - /** factory method type descriptor */ - public final String factoryDescriptor; - /** functional interface method as type */ - public final Type interfaceType; - /** delegate method type method as type */ - public final Type delegateType; - - /** whether a call is made on a delegate interface */ - public final boolean isDelegateInterface; - - /** - * Creates a new FunctionRef, which will resolve {@code type::call} from the whitelist. - * @param painlessLookup the whitelist against which this script is being compiled - * @param expected functional interface type to implement. - * @param type the left hand side of a method reference expression - * @param call the right hand side of a method reference expression - * @param numCaptures number of captured arguments - */ - public static FunctionRef resolveFromLookup( - PainlessLookup painlessLookup, Class expected, String type, String call, int numCaptures) { - - if ("new".equals(call)) { - return new FunctionRef(expected, painlessLookup.lookupPainlessClass(expected).functionalMethod, - lookup(painlessLookup, expected, type), numCaptures); - } else { - return new FunctionRef(expected, painlessLookup.lookupPainlessClass(expected).functionalMethod, - lookup(painlessLookup, expected, type, call, numCaptures > 0), numCaptures); - } - } - - /** - * Creates a new FunctionRef (already resolved) - * @param expected functional interface type to implement - * @param interfaceMethod functional interface method - * @param delegateConstructor implementation constructor - * @param numCaptures number of captured arguments - */ - public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessConstructor delegateConstructor, int numCaptures) { - Constructor javaConstructor = delegateConstructor.javaConstructor; - MethodType delegateMethodType = delegateConstructor.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 = javaConstructor.getDeclaringClass().getName(); - this.isDelegateInterface = false; - this.delegateInvokeType = H_NEWINVOKESPECIAL; - this.delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME; - this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures); - - this.interfaceMethod = interfaceMethod; - this.delegateTypeParameters = delegateConstructor.typeParameters; - this.delegateReturnType = void.class; - - 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, PainlessMethod 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 = delegateMethod.javaMethod.getDeclaringClass().getName(); - this.isDelegateInterface = delegateMethod.javaMethod.getDeclaringClass().isInterface(); - - if (Modifier.isStatic(delegateMethod.javaMethod.getModifiers())) { - this.delegateInvokeType = H_INVOKESTATIC; - } else if (delegateMethod.javaMethod.getDeclaringClass().isInterface()) { - this.delegateInvokeType = H_INVOKEINTERFACE; - } else { - this.delegateInvokeType = H_INVOKEVIRTUAL; - } - - 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; - 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 (low level). - * It is for runtime use only. - */ - public FunctionRef(Class expected, - PainlessMethod interfaceMethod, String delegateMethodName, MethodType delegateMethodType, int numCaptures) { - 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.delegateInvokeType = H_INVOKESTATIC; - this.delegateMethodName = delegateMethodName; - this.delegateMethodType = delegateMethodType.dropParameterTypes(0, numCaptures); - this.isDelegateInterface = false; - - this.interfaceMethod = null; - this.delegateTypeParameters = null; - this.delegateReturnType = null; - - this.factoryDescriptor = null; - this.interfaceType = null; - this.delegateType = null; - } - - /** - * Looks up {@code type} from the whitelist, and returns a matching constructor. - */ - private static PainlessConstructor lookup(PainlessLookup painlessLookup, Class expected, String type) { - // check its really a functional interface - // for e.g. Comparable - PainlessMethod method = painlessLookup.lookupPainlessClass(expected).functionalMethod; - if (method == null) { - throw new IllegalArgumentException("Cannot convert function reference [" + type + "::new] " + - "to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface"); - } - - // lookup requested constructor - PainlessClass struct = painlessLookup.lookupPainlessClass(painlessLookup.canonicalTypeNameToType(type)); - PainlessConstructor impl = struct.constructors.get(PainlessLookupUtility.buildPainlessConstructorKey(method.typeParameters.size())); - - if (impl == null) { - throw new IllegalArgumentException("Unknown reference [" + type + "::new] matching [" + expected + "]"); - } - - return impl; - } - - /** - * Looks up {@code type::call} from the whitelist, and returns a matching method. - */ - private static PainlessMethod lookup(PainlessLookup painlessLookup, Class expected, - String type, String call, boolean receiverCaptured) { - // check its really a functional interface - // for e.g. Comparable - PainlessMethod method = painlessLookup.lookupPainlessClass(expected).functionalMethod; - if (method == null) { - throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + - "to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface"); - } - - // lookup requested method - PainlessClass struct = painlessLookup.lookupPainlessClass(painlessLookup.canonicalTypeNameToType(type)); - final PainlessMethod impl; - // look for a static impl first - PainlessMethod staticImpl = - 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.typeParameters.size(); - } else { - // receiver passed - arity = method.typeParameters.size() - 1; - } - impl = struct.methods.get(PainlessLookupUtility.buildPainlessMethodKey(call, arity)); - } else { - impl = staticImpl; - } - if (impl == null) { - throw new IllegalArgumentException("Unknown reference [" + type + "::" + call + "] matching " + - "[" + expected + "]"); - } - return impl; - } -} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java index 99b00771c1451..28318b5433486 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java @@ -19,6 +19,7 @@ package org.elasticsearch.painless; +import org.elasticsearch.painless.Locals.LocalMethod; import org.elasticsearch.painless.lookup.PainlessConstructor; import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.lookup.PainlessLookupUtility; @@ -26,6 +27,8 @@ import java.lang.invoke.MethodType; import java.lang.reflect.Modifier; +import java.util.List; +import java.util.Map; import java.util.Objects; import static org.elasticsearch.painless.WriterConstants.CLASS_NAME; @@ -39,13 +42,14 @@ public class FunctionReference { /** * Creates a new FunctionReference which will resolve {@code type::call} from the whitelist. * @param painlessLookup the whitelist against which this script is being compiled - * @param locals the local state for a script instance + * @param localMethods user-defined and synthetic methods generated directly on the script class + * @param location the character number within the script at compile-time * @param targetClass functional interface type to implement. * @param typeName the left hand side of a method reference expression * @param methodName the right hand side of a method reference expression * @param numCaptures number of captured arguments */ - public static FunctionReference resolve(PainlessLookup painlessLookup, Locals locals, + public static FunctionReference create(PainlessLookup painlessLookup, Map localMethods, Location location, Class targetClass, String typeName, String methodName, int numCaptures) { Objects.requireNonNull(painlessLookup); @@ -57,88 +61,119 @@ public static FunctionReference resolve(PainlessLookup painlessLookup, Locals lo PainlessMethod interfaceMethod; try { - interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(targetClass); - } catch (IllegalArgumentException iae) { - throw new IllegalArgumentException("cannot convert function reference [" + typeName + "::" + methodName + "] " + - "to a non-functional interface [" + targetClassName + "]", iae); - } - - int typeParametersSize = interfaceMethod.typeParameters.size(); + try { + interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(targetClass); + } catch (IllegalArgumentException iae) { + throw new IllegalArgumentException("cannot convert function reference [" + typeName + "::" + methodName + "] " + + "to a non-functional interface [" + targetClassName + "]", iae); + } - String interfaceMethodName = interfaceMethod.javaMethod.getName(); - MethodType interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1); - String delegateClassName; - boolean isDelegateInterface; - int delegateInvokeType; - String delegateMethodName; - MethodType delegateMethodType; + String interfaceMethodName = interfaceMethod.javaMethod.getName(); + MethodType interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1); + String delegateClassName; + boolean isDelegateInterface; + int delegateInvokeType; + String delegateMethodName; + MethodType delegateMethodType; - if ("this".equals(typeName)) { - Objects.requireNonNull(locals); + Class delegateMethodReturnType; + List> delegateMethodParameters; + int typeParametersSize = interfaceMethod.typeParameters.size(); - Locals.LocalMethod localMethod = locals.getMethod(Locals.buildLocalMethodKey(methodName, typeParametersSize)); + if ("this".equals(typeName)) { + Objects.requireNonNull(localMethods); - if (localMethod == null) { - throw new IllegalArgumentException("function reference [this::" + methodName + "] not found"); - } + LocalMethod localMethod = localMethods.get(Locals.buildLocalMethodKey(methodName, typeParametersSize)); - delegateClassName = CLASS_NAME; - isDelegateInterface = false; - delegateInvokeType = H_INVOKESTATIC; - delegateMethodName = localMethod.name; - delegateMethodType = localMethod.methodType.dropParameterTypes(0, numCaptures); - } else if ("new".equals(methodName)) { - PainlessConstructor painlessConstructor; + if (localMethod == null) { + throw new IllegalArgumentException("function reference [this::" + methodName + "] not found"); + } - try { - painlessConstructor = painlessLookup.lookupPainlessConstructor(typeName, typeParametersSize); - } catch (IllegalArgumentException iae) { - throw new IllegalArgumentException( - "function reference [" + typeName + "::new] matching [" + targetClassName + "] not found", iae); - } + delegateClassName = CLASS_NAME; + isDelegateInterface = false; + delegateInvokeType = H_INVOKESTATIC; + delegateMethodName = localMethod.name; + delegateMethodType = localMethod.methodType.dropParameterTypes(0, numCaptures); - delegateClassName = painlessConstructor.javaConstructor.getDeclaringClass().getName(); - isDelegateInterface = false; - delegateInvokeType = H_NEWINVOKESPECIAL; - delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME; - delegateMethodType = painlessConstructor.methodType.dropParameterTypes(0, numCaptures); - } else { - PainlessMethod painlessMethod; + delegateMethodReturnType = localMethod.returnType; + delegateMethodParameters = localMethod.typeParameters; + } else if ("new".equals(methodName)) { + PainlessConstructor painlessConstructor; - try { - painlessMethod = painlessLookup.lookupPainlessMethod(targetClassName, true, methodName, typeParametersSize); - } catch (IllegalArgumentException staticIAE) { try { - painlessMethod = painlessLookup.lookupPainlessMethod( - targetClassName, false, methodName, numCaptures > 0 ? typeParametersSize : typeParametersSize - 1); + painlessConstructor = painlessLookup.lookupPainlessConstructor(typeName, typeParametersSize); } catch (IllegalArgumentException iae) { throw new IllegalArgumentException( - "function reference [" + typeName + "::" + methodName + "] matching [" + targetClassName + "] not found", iae); + "function reference [" + typeName + "::new] matching [" + targetClassName + "] not found", iae); } - } - delegateClassName = painlessMethod.javaMethod.getDeclaringClass().getName(); - isDelegateInterface = painlessMethod.javaMethod.getDeclaringClass().isInterface(); + delegateClassName = painlessConstructor.javaConstructor.getDeclaringClass().getName(); + isDelegateInterface = false; + delegateInvokeType = H_NEWINVOKESPECIAL; + delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME; + delegateMethodType = painlessConstructor.methodType.dropParameterTypes(0, numCaptures); - if (Modifier.isStatic(painlessMethod.javaMethod.getModifiers())) { - delegateInvokeType = H_INVOKESTATIC; - } else if (isDelegateInterface) { - delegateInvokeType = H_INVOKEINTERFACE; + delegateMethodReturnType = void.class; + delegateMethodParameters = painlessConstructor.typeParameters; } else { - delegateInvokeType = H_INVOKEVIRTUAL; + PainlessMethod painlessMethod; + + try { + painlessMethod = painlessLookup.lookupPainlessMethod(targetClassName, true, methodName, typeParametersSize); + } catch (IllegalArgumentException staticIAE) { + try { + painlessMethod = painlessLookup.lookupPainlessMethod( + targetClassName, false, methodName, numCaptures > 0 ? typeParametersSize - 1 : typeParametersSize); + } catch (IllegalArgumentException iae) { + throw new IllegalArgumentException("function reference [" + typeName + "::" + methodName + "] " + + "matching [" + targetClassName + "] not found", iae); + } + } + + delegateClassName = painlessMethod.javaMethod.getDeclaringClass().getName(); + isDelegateInterface = painlessMethod.javaMethod.getDeclaringClass().isInterface(); + + if (Modifier.isStatic(painlessMethod.javaMethod.getModifiers())) { + delegateInvokeType = H_INVOKESTATIC; + } else if (isDelegateInterface) { + delegateInvokeType = H_INVOKEINTERFACE; + } else { + delegateInvokeType = H_INVOKEVIRTUAL; + } + + delegateMethodName = painlessMethod.javaMethod.getName(); + delegateMethodType = painlessMethod.methodType.dropParameterTypes(0, numCaptures); + + delegateMethodReturnType = painlessMethod.returnType; + delegateMethodParameters = painlessMethod.typeParameters; } - delegateMethodName = painlessMethod.javaMethod.getName(); - delegateMethodType = painlessMethod.methodType.dropParameterTypes(0, numCaptures); - } + if (location != null) { + for (int typeParameter = 0; typeParameter < typeParametersSize; ++typeParameter) { + Class from = interfaceMethod.typeParameters.get(typeParameter); + Class to = delegateMethodParameters.get(numCaptures + typeParameter); + AnalyzerCaster.getLegalCast(location, from, to, false, true); + } + + if (interfaceMethod.returnType != void.class) { + AnalyzerCaster.getLegalCast(location, delegateMethodReturnType, interfaceMethod.returnType, false, true); + } + } - MethodType factoryMethodType = MethodType.methodType(targetClass, - delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); + MethodType factoryMethodType = MethodType.methodType(targetClass, + delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); - return new FunctionReference(interfaceMethodName, interfaceMethodType, - delegateClassName, isDelegateInterface, delegateInvokeType, delegateMethodName, delegateMethodType, - factoryMethodType - ); + return new FunctionReference(interfaceMethodName, interfaceMethodType, + delegateClassName, isDelegateInterface, delegateInvokeType, delegateMethodName, delegateMethodType, + factoryMethodType + ); + } catch (IllegalArgumentException iae) { + if (location != null) { + throw location.createError(iae); + } + + throw iae; + } } /** functional interface method name */ 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 72435562a3bd0..c8e38467801bd 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 @@ -56,6 +56,7 @@ import static org.elasticsearch.painless.WriterConstants.DEF_TO_SHORT_IMPLICIT; import static org.elasticsearch.painless.WriterConstants.DEF_UTIL_TYPE; import static org.elasticsearch.painless.WriterConstants.INDY_STRING_CONCAT_BOOTSTRAP_HANDLE; +import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE; import static org.elasticsearch.painless.WriterConstants.MAX_INDY_STRING_CONCAT_ARGS; import static org.elasticsearch.painless.WriterConstants.PAINLESS_ERROR_TYPE; import static org.elasticsearch.painless.WriterConstants.STRINGBUILDER_APPEND_BOOLEAN; @@ -439,4 +440,18 @@ public void invokeMethodCall(PainlessMethod painlessMethod) { invokeVirtual(type, method); } } + + public void invokeLambdaCall(FunctionReference functionReference) { + invokeDynamic( + functionReference.interfaceMethodName, + functionReference.factoryMethodType.toMethodDescriptorString(), + LAMBDA_BOOTSTRAP_HANDLE, + Type.getMethodType(functionReference.interfaceMethodType.toMethodDescriptorString()), + functionReference.delegateClassName, + functionReference.delegateInvokeType, + functionReference.delegateMethodName, + Type.getMethodType(functionReference.delegateMethodType.toMethodDescriptorString()), + functionReference.isDelegateInterface ? 1 : 0 + ); + } } 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 e78b3c67210b8..5c1a9380ce109 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 @@ -19,9 +19,8 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.FunctionRef; +import org.elasticsearch.painless.FunctionReference; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Variable; @@ -35,8 +34,6 @@ import java.util.Objects; import java.util.Set; -import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE; - /** * Represents a capturing function reference. */ @@ -44,7 +41,7 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda private final String variable; private final String call; - private FunctionRef ref; + private FunctionReference ref; private Variable captured; private String defPointer; @@ -76,23 +73,8 @@ void analyze(Locals locals) { defPointer = null; // static case if (captured.clazz != def.class) { - try { - ref = FunctionRef.resolveFromLookup(locals.getPainlessLookup(), expected, - 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.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.returnType != void.class) { - AnalyzerCaster.getLegalCast(location, ref.delegateReturnType, ref.interfaceMethod.returnType, false, true); - } - } catch (IllegalArgumentException e) { - throw createError(e); - } + ref = FunctionReference.create(locals.getPainlessLookup(), locals.getMethods(), location, + expected, PainlessLookupUtility.typeToCanonicalTypeName(captured.clazz), call, 1); } actual = expected; } @@ -114,17 +96,7 @@ void write(MethodWriter writer, Globals globals) { } else { // typed interface, typed implementation writer.visitVarInsn(MethodWriter.getType(captured.clazz).getOpcode(Opcodes.ILOAD), captured.getSlot()); - writer.invokeDynamic( - ref.interfaceMethodName, - ref.factoryDescriptor, - LAMBDA_BOOTSTRAP_HANDLE, - ref.interfaceType, - ref.delegateClassName, - ref.delegateInvokeType, - ref.delegateMethodName, - ref.delegateType, - ref.isDelegateInterface ? 1 : 0 - ); + writer.invokeLambdaCall(ref); } } 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 692581d811849..8710707ac2a81 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 @@ -19,22 +19,16 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.AnalyzerCaster; -import org.elasticsearch.painless.FunctionRef; +import org.elasticsearch.painless.FunctionReference; 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.Type; import java.util.Objects; import java.util.Set; -import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE; - /** * Represents a function reference. */ @@ -42,7 +36,7 @@ public final class EFunctionRef extends AExpression implements ILambda { private final String type; private final String call; - private FunctionRef ref; + private FunctionReference ref; private String defPointer; public EFunctionRef(Location location, String type, String call) { @@ -63,39 +57,7 @@ void analyze(Locals locals) { defPointer = "S" + type + "." + call + ",0"; } else { defPointer = null; - try { - if ("this".equals(type)) { - // user's own function - PainlessMethod interfaceMethod = locals.getPainlessLookup().lookupPainlessClass(expected).functionalMethod; - if (interfaceMethod == null) { - throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + - "to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface"); - } - LocalMethod delegateMethod = locals.getMethod(call, interfaceMethod.typeParameters.size()); - if (delegateMethod == null) { - throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + - "to [" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], function not found"); - } - 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.typeParameters.size(); ++i) { - Class from = interfaceMethod.typeParameters.get(i); - Class to = delegateMethod.typeParameters.get(i); - AnalyzerCaster.getLegalCast(location, from, to, false, true); - } - - if (interfaceMethod.returnType != void.class) { - AnalyzerCaster.getLegalCast(location, delegateMethod.returnType, interfaceMethod.returnType, false, true); - } - } else { - // whitelist lookup - ref = FunctionRef.resolveFromLookup(locals.getPainlessLookup(), expected, type, call, 0); - } - - } catch (IllegalArgumentException e) { - throw createError(e); - } + ref = FunctionReference.create(locals.getPainlessLookup(), locals.getMethods(), location, expected, type, call, 0); actual = expected; } } @@ -104,17 +66,7 @@ void analyze(Locals locals) { void write(MethodWriter writer, Globals globals) { if (ref != null) { writer.writeDebugInfo(location); - writer.invokeDynamic( - ref.interfaceMethodName, - ref.factoryDescriptor, - LAMBDA_BOOTSTRAP_HANDLE, - ref.interfaceType, - ref.delegateClassName, - ref.delegateInvokeType, - ref.delegateMethodName, - ref.delegateType, - ref.isDelegateInterface ? 1 : 0 - ); + writer.invokeLambdaCall(ref); } else { // TODO: don't do this: its just to cutover :) writer.push((String)null); 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 ecd11ce1bf794..ead1d8d08fdb0 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 @@ -19,11 +19,9 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.AnalyzerCaster; -import org.elasticsearch.painless.FunctionRef; +import org.elasticsearch.painless.FunctionReference; 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; @@ -40,8 +38,6 @@ import java.util.Objects; import java.util.Set; -import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE; - /** * Lambda expression node. *

@@ -78,7 +74,7 @@ public final class ELambda extends AExpression implements ILambda { // captured variables private List captures; // static parent, static lambda - private FunctionRef ref; + private FunctionReference ref; // dynamic parent, deferred until link time private String defPointer; @@ -122,7 +118,7 @@ void analyze(Locals locals) { } else { // we know the method statically, infer return type and any unknown/def types - interfaceMethod = locals.getPainlessLookup().lookupPainlessClass(expected).functionalMethod; + interfaceMethod = locals.getPainlessLookup().lookupFunctionalInterfacePainlessMethod(expected); if (interfaceMethod == null) { throw createError(new IllegalArgumentException("Cannot pass lambda to " + "[" + PainlessLookupUtility.typeToCanonicalTypeName(expected) + "], not a functional interface")); @@ -184,25 +180,8 @@ void analyze(Locals locals) { defPointer = "Sthis." + name + "," + captures.size(); } else { defPointer = null; - try { - 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.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.returnType != void.class) { - AnalyzerCaster.getLegalCast(location, desugared.returnType, interfaceMethod.returnType, false, true); - } - + ref = FunctionReference.create( + locals.getPainlessLookup(), locals.getMethods(), location, expected, "this", desugared.name, captures.size()); actual = expected; } } @@ -218,17 +197,7 @@ void write(MethodWriter writer, Globals globals) { writer.visitVarInsn(MethodWriter.getType(capture.clazz).getOpcode(Opcodes.ILOAD), capture.getSlot()); } - writer.invokeDynamic( - ref.interfaceMethodName, - ref.factoryDescriptor, - LAMBDA_BOOTSTRAP_HANDLE, - ref.interfaceType, - ref.delegateClassName, - ref.delegateInvokeType, - ref.delegateMethodName, - ref.delegateType, - ref.isDelegateInterface ? 1 : 0 - ); + writer.invokeLambdaCall(ref); } else { // placeholder writer.push((String)null); From 89e936ea5862d5af2d4b9043ecd5596250ecfc7e Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 10:53:20 -0700 Subject: [PATCH 18/33] Fix. --- .../java/org/elasticsearch/painless/FunctionReference.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java index 28318b5433486..27c51fb7984e4 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java @@ -119,11 +119,11 @@ public static FunctionReference create(PainlessLookup painlessLookup, Map 0 ? typeParametersSize - 1 : typeParametersSize); + typeName, false, methodName, numCaptures > 0 ? typeParametersSize - 1 : typeParametersSize); } catch (IllegalArgumentException iae) { throw new IllegalArgumentException("function reference [" + typeName + "::" + methodName + "] " + "matching [" + targetClassName + "] not found", iae); From cf430a966b461b4fc25945cb5cbf424a427a59c3 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 11:31:03 -0700 Subject: [PATCH 19/33] Progress. --- .../elasticsearch/painless/FunctionReference.java | 13 ++++++++----- .../painless/lookup/PainlessLookup.java | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java index 27c51fb7984e4..41ca2fe252a2d 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java @@ -113,7 +113,7 @@ public static FunctionReference create(PainlessLookup painlessLookup, Map 0 ? typeParametersSize - 1 : typeParametersSize); + if (numCaptures == 0) { + --typeParametersSize; + } + + painlessMethod = painlessLookup.lookupPainlessMethod(typeName, false, methodName, typeParametersSize); } catch (IllegalArgumentException iae) { throw new IllegalArgumentException("function reference [" + typeName + "::" + methodName + "] " + "matching [" + targetClassName + "] not found", iae); @@ -148,7 +151,7 @@ public static FunctionReference create(PainlessLookup painlessLookup, Map from = interfaceMethod.typeParameters.get(typeParameter); Class to = delegateMethodParameters.get(numCaptures + typeParameter); @@ -158,7 +161,7 @@ public static FunctionReference create(PainlessLookup painlessLookup, Map targetClass = canonicalTypeNameToType(targetClassName); - return lookupPainlessConstructor(targetClassName, constructorArity); + return lookupPainlessConstructor(targetClass, constructorArity); } public PainlessConstructor lookupPainlessConstructor(Class targetClass, int constructorArity) { From c4e835701d3b317cf83ccd64a141448fb969b202 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 15:02:18 -0700 Subject: [PATCH 20/33] Large clean up of function reference. --- .../painless/FunctionReference.java | 79 +++++++++++++------ .../painless/FunctionRefTests.java | 23 +++--- .../elasticsearch/painless/LambdaTests.java | 4 +- 3 files changed, 70 insertions(+), 36 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java index 41ca2fe252a2d..64d08a9763f4d 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java @@ -27,6 +27,7 @@ import java.lang.invoke.MethodType; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -47,10 +48,10 @@ public class FunctionReference { * @param targetClass functional interface type to implement. * @param typeName the left hand side of a method reference expression * @param methodName the right hand side of a method reference expression - * @param numCaptures number of captured arguments + * @param numberOfCaptures number of captured arguments */ public static FunctionReference create(PainlessLookup painlessLookup, Map localMethods, Location location, - Class targetClass, String typeName, String methodName, int numCaptures) { + Class targetClass, String typeName, String methodName, int numberOfCaptures) { Objects.requireNonNull(painlessLookup); Objects.requireNonNull(targetClass); @@ -78,58 +79,79 @@ public static FunctionReference create(PainlessLookup painlessLookup, Map delegateMethodReturnType; List> delegateMethodParameters; - int typeParametersSize = interfaceMethod.typeParameters.size(); + int interfaceTypeParametersSize = interfaceMethod.typeParameters.size(); if ("this".equals(typeName)) { Objects.requireNonNull(localMethods); - LocalMethod localMethod = localMethods.get(Locals.buildLocalMethodKey(methodName, typeParametersSize)); + if (numberOfCaptures < 0) { + throw new IllegalStateException("internal error"); + } + + String localMethodKey = Locals.buildLocalMethodKey(methodName, numberOfCaptures + interfaceTypeParametersSize); + LocalMethod localMethod = localMethods.get(localMethodKey); if (localMethod == null) { - throw new IllegalArgumentException("function reference [this::" + methodName + "] not found"); + throw new IllegalArgumentException("function reference [this::" + localMethodKey + "] " + + "matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " + + "not found" + (localMethodKey.contains("$") ? " due to an incorrect number of arguments" : "") + ); } delegateClassName = CLASS_NAME; isDelegateInterface = false; delegateInvokeType = H_INVOKESTATIC; delegateMethodName = localMethod.name; - delegateMethodType = localMethod.methodType.dropParameterTypes(0, numCaptures); + delegateMethodType = localMethod.methodType; delegateMethodReturnType = localMethod.returnType; delegateMethodParameters = localMethod.typeParameters; } else if ("new".equals(methodName)) { + if (numberOfCaptures != 0) { + throw new IllegalStateException("internal error"); + } + PainlessConstructor painlessConstructor; try { - painlessConstructor = painlessLookup.lookupPainlessConstructor(typeName, typeParametersSize); + painlessConstructor = painlessLookup.lookupPainlessConstructor(typeName, interfaceTypeParametersSize); } catch (IllegalArgumentException iae) { - throw new IllegalArgumentException( - "function reference [" + typeName + "::new] matching [" + targetClassName + "] not found", iae); + throw new IllegalArgumentException("function reference [" + typeName + "::new/" + interfaceTypeParametersSize + "] " + + "matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " + + "not found", iae); } delegateClassName = painlessConstructor.javaConstructor.getDeclaringClass().getName(); isDelegateInterface = false; delegateInvokeType = H_NEWINVOKESPECIAL; delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME; - delegateMethodType = painlessConstructor.methodType.dropParameterTypes(0, numCaptures); + delegateMethodType = painlessConstructor.methodType; delegateMethodReturnType = painlessConstructor.javaConstructor.getDeclaringClass(); delegateMethodParameters = painlessConstructor.typeParameters; } else { + if (numberOfCaptures != 0 && numberOfCaptures != 1) { + throw new IllegalStateException("internal error"); + } + + boolean captured = numberOfCaptures == 1; PainlessMethod painlessMethod; try { - painlessMethod = painlessLookup.lookupPainlessMethod(typeName, true, methodName, typeParametersSize); + painlessMethod = painlessLookup.lookupPainlessMethod(typeName, true, methodName, interfaceTypeParametersSize); + + if (captured) { + throw new IllegalStateException("internal error"); + } } catch (IllegalArgumentException staticIAE) { try { - if (numCaptures == 0) { - --typeParametersSize; - } - - painlessMethod = painlessLookup.lookupPainlessMethod(typeName, false, methodName, typeParametersSize); + painlessMethod = painlessLookup.lookupPainlessMethod(typeName, false, methodName, + captured ? interfaceTypeParametersSize : interfaceTypeParametersSize - 1); } catch (IllegalArgumentException iae) { - throw new IllegalArgumentException("function reference [" + typeName + "::" + methodName + "] " + - "matching [" + targetClassName + "] not found", iae); + throw new IllegalArgumentException( + "function reference " + "[" + typeName + "::" + methodName + "/" + interfaceTypeParametersSize + "] " + + "matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " + + "not found", iae); } } @@ -145,26 +167,33 @@ public static FunctionReference create(PainlessLookup painlessLookup, Map painlessMethod.typeParameters.size()) { + delegateMethodParameters = new ArrayList<>(painlessMethod.typeParameters); + delegateMethodParameters.add(0, delegateMethodType.parameterType(0)); + } else { + delegateMethodParameters = painlessMethod.typeParameters; + } } - /*if (location != null) { - for (int typeParameter = 0; typeParameter < typeParametersSize; ++typeParameter) { + if (location != null) { + for (int typeParameter = 0; typeParameter < interfaceTypeParametersSize; ++typeParameter) { Class from = interfaceMethod.typeParameters.get(typeParameter); - Class to = delegateMethodParameters.get(numCaptures + typeParameter); + Class to = delegateMethodParameters.get(numberOfCaptures + typeParameter); AnalyzerCaster.getLegalCast(location, from, to, false, true); } if (interfaceMethod.returnType != void.class) { AnalyzerCaster.getLegalCast(location, delegateMethodReturnType, interfaceMethod.returnType, false, true); } - }*/ + } MethodType factoryMethodType = MethodType.methodType(targetClass, - delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); + delegateMethodType.dropParameterTypes(numberOfCaptures, delegateMethodType.parameterCount())); + delegateMethodType = delegateMethodType.dropParameterTypes(0, numberOfCaptures); return new FunctionReference(interfaceMethodName, interfaceMethodType, delegateClassName, isDelegateInterface, delegateInvokeType, delegateMethodName, delegateMethodType, diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java index fd47db6b83d41..5829593f52441 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionRefTests.java @@ -27,7 +27,6 @@ import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.startsWith; public class FunctionRefTests extends ScriptTestCase { @@ -193,14 +192,15 @@ public void testMethodMissing() { Exception e = expectScriptThrows(IllegalArgumentException.class, () -> { exec("List l = [2, 1]; l.sort(Integer::bogus); return l.get(0);"); }); - assertThat(e.getMessage(), startsWith("Unknown reference")); + assertThat(e.getMessage(), containsString("function reference [Integer::bogus/2] matching [java.util.Comparator")); } public void testQualifiedMethodMissing() { Exception e = expectScriptThrows(IllegalArgumentException.class, () -> { exec("List l = [2, 1]; l.sort(org.joda.time.ReadableDateTime::bogus); return l.get(0);", false); }); - assertThat(e.getMessage(), startsWith("Unknown reference")); + assertThat(e.getMessage(), + containsString("function reference [org.joda.time.ReadableDateTime::bogus/2] matching [java.util.Comparator")); } public void testClassMissing() { @@ -223,11 +223,12 @@ public void testNotFunctionalInterface() { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("List l = new ArrayList(); l.add(2); l.add(1); l.add(Integer::bogus); return l.get(0);"); }); - assertThat(expected.getMessage(), containsString("Cannot convert function reference")); + assertThat(expected.getMessage(), + containsString("cannot convert function reference [Integer::bogus] to a non-functional interface [def]")); } public void testIncompatible() { - expectScriptThrows(BootstrapMethodError.class, () -> { + expectScriptThrows(ClassCastException.class, () -> { exec("List l = new ArrayList(); l.add(2); l.add(1); l.sort(String::startsWith); return l.get(0);"); }); } @@ -236,28 +237,32 @@ public void testWrongArity() { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("Optional.empty().orElseGet(String::startsWith);"); }); - assertThat(expected.getMessage(), containsString("Unknown reference")); + assertThat(expected.getMessage(), + containsString("function reference [String::startsWith/0] matching [java.util.function.Supplier")); } public void testWrongArityNotEnough() { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("List l = new ArrayList(); l.add(2); l.add(1); l.sort(String::isEmpty);"); }); - assertTrue(expected.getMessage().contains("Unknown reference")); + assertThat(expected.getMessage(), containsString( + "function reference [String::isEmpty/2] matching [java.util.Comparator")); } public void testWrongArityDef() { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("def y = Optional.empty(); return y.orElseGet(String::startsWith);"); }); - assertThat(expected.getMessage(), containsString("Unknown reference")); + assertThat(expected.getMessage(), + containsString("function reference [String::startsWith/0] matching [java.util.function.Supplier")); } public void testWrongArityNotEnoughDef() { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("def l = new ArrayList(); l.add(2); l.add(1); l.sort(String::isEmpty);"); }); - assertThat(expected.getMessage(), containsString("Unknown reference")); + assertThat(expected.getMessage(), + containsString("function reference [String::isEmpty/2] matching [java.util.Comparator")); } public void testReturnVoid() { diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/LambdaTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/LambdaTests.java index 20e257e574709..1f1a6f95b3608 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/LambdaTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/LambdaTests.java @@ -184,7 +184,7 @@ public void testWrongArityDef() { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("def y = Optional.empty(); return y.orElseGet(x -> x);"); }); - assertTrue(expected.getMessage(), expected.getMessage().contains("Incorrect number of parameters")); + assertTrue(expected.getMessage(), expected.getMessage().contains("due to an incorrect number of arguments")); } public void testWrongArityNotEnough() { @@ -200,7 +200,7 @@ public void testWrongArityNotEnoughDef() { exec("def l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(() -> 5).sum();"); }); - assertTrue(expected.getMessage().contains("Incorrect number of parameters")); + assertTrue(expected.getMessage(), expected.getMessage().contains("due to an incorrect number of arguments")); } public void testLambdaInFunction() { From bde26b40a2040db5e7427f91dc1044bd79b49b57 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 15:13:02 -0700 Subject: [PATCH 21/33] A bit more clean up. --- .../src/main/java/org/elasticsearch/painless/Def.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index eca9836d039ec..587106e8628f1 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 @@ -279,10 +279,6 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, Map captures[] = new Class[numCaptures]; - for (int capture = 0; capture < captures.length; capture++) { - captures[capture] = callSiteType.parameterType(i + 1 + capture); - } MethodHandle filter; Class interfaceType = method.typeParameters.get(i - 1 - replaced); if (signature.charAt(0) == 'S') { @@ -294,11 +290,15 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, Map captures[] = new Class[numCaptures]; + for (int capture = 0; capture < captures.length; capture++) { + captures[capture] = callSiteType.parameterType(i + 1 + capture); + } MethodType nestedType = MethodType.methodType(interfaceType, captures); CallSite nested = DefBootstrap.bootstrap(painlessLookup, localMethods, From 02ab981a1432f42e93ffe588b42ea258fb8bfde1 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 16:02:11 -0700 Subject: [PATCH 22/33] Move runtime method lookup to PainlessLookup. --- .../java/org/elasticsearch/painless/Def.java | 52 ++----------------- .../painless/lookup/PainlessLookup.java | 40 ++++++++++++++ .../painless/DefBootstrapTests.java | 2 +- .../elasticsearch/painless/OverloadTests.java | 2 +- .../painless/WhenThingsGoWrongTests.java | 2 +- 5 files changed, 46 insertions(+), 52 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index 587106e8628f1..fa21401c4727d 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 @@ -167,52 +167,6 @@ static MethodHandle arrayLengthGetter(Class arrayType) { } } - /** - * Looks up method entry for a dynamic method call. - *

- * A dynamic method call for variable {@code x} of type {@code def} looks like: - * {@code x.method(args...)} - *

- * This method traverses {@code recieverClass}'s class hierarchy (including interfaces) - * until it finds a matching whitelisted method. If one is not found, it throws an exception. - * Otherwise it returns the matching method. - *

- * @params painlessLookup the whitelist - * @param receiverClass Class of the object to invoke the method on. - * @param name Name of the method. - * @param arity arity of method - * @return matching method to invoke. never returns null. - * @throws IllegalArgumentException if no matching whitelisted method was found. - */ - static PainlessMethod lookupMethodInternal(PainlessLookup painlessLookup, Class receiverClass, String name, int arity) { - String key = PainlessLookupUtility.buildPainlessMethodKey(name, arity); - // check whitelist for matching method - for (Class clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) { - PainlessClass struct = painlessLookup.lookupPainlessClass(clazz); - - if (struct != null) { - PainlessMethod method = struct.methods.get(key); - if (method != null) { - return method; - } - } - - for (Class iface : clazz.getInterfaces()) { - struct = painlessLookup.lookupPainlessClass(iface); - - if (struct != null) { - PainlessMethod method = struct.methods.get(key); - if (method != null) { - return method; - } - } - } - } - - throw new IllegalArgumentException("Unable to find dynamic method [" + name + "] with [" + arity + "] arguments " + - "for class [" + receiverClass.getCanonicalName() + "]."); - } - /** * Looks up handle for a dynamic method call, with lambda replacement *

@@ -241,7 +195,7 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, Map targetCla return functionalInterfacePainlessMethod; } + + public PainlessMethod lookupRuntimePainlessMethod(Class originalTargetClass, String methodName, int methodArity) { + String painlessMethodKey = PainlessLookupUtility.buildPainlessMethodKey(methodName, methodArity); + Class currentTargetClass = originalTargetClass; + + while (currentTargetClass != null) { + PainlessClass targetPainlessClass = classesToPainlessClasses.get(currentTargetClass); + + if (targetPainlessClass != null) { + PainlessMethod painlessMethod = targetPainlessClass.methods.get(painlessMethodKey); + + if (painlessMethod != null) { + return painlessMethod; + } + } + + currentTargetClass = currentTargetClass.getSuperclass(); + } + + currentTargetClass = originalTargetClass; + + while (currentTargetClass != null) { + for (Class targetInterface : currentTargetClass.getInterfaces()) { + PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetInterface); + + if (targetPainlessClass != null) { + PainlessMethod painlessMethod = targetPainlessClass.methods.get(painlessMethodKey); + + if (painlessMethod != null) { + return painlessMethod; + } + } + } + + currentTargetClass = currentTargetClass.getSuperclass(); + } + + throw new IllegalArgumentException( + "dynamic method [" + typeToCanonicalTypeName(originalTargetClass) + ", " + painlessMethodKey + "] not found"); + } } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java index a9861341a84f5..88d257a067205 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java @@ -134,7 +134,7 @@ public void testMegamorphic() throws Throwable { final IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> { Integer.toString((int)handle.invokeExact(new Object())); }); - assertEquals("Unable to find dynamic method [size] with [0] arguments for class [java.lang.Object].", iae.getMessage()); + assertEquals("dynamic method [java.lang.Object, size/0] not found", iae.getMessage()); assertTrue("Does not fail inside ClassValue.computeValue()", Arrays.stream(iae.getStackTrace()).anyMatch(e -> { return e.getMethodName().equals("computeValue") && e.getClassName().startsWith("org.elasticsearch.painless.DefBootstrap$PIC$"); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/OverloadTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/OverloadTests.java index 1b90d58299953..52c28799fae34 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/OverloadTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/OverloadTests.java @@ -37,7 +37,7 @@ public void testMethodDynamic() { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("def x = 'abc123abc'; return x.indexOf('c', 3, 'bogus');"); }); - assertTrue(expected.getMessage().contains("dynamic method [indexOf]")); + assertTrue(expected.getMessage().contains("dynamic method [java.lang.String, indexOf/3] not found")); } public void testConstructor() { diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/WhenThingsGoWrongTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/WhenThingsGoWrongTests.java index 8eeb25c9676c7..f2d93aa759d07 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/WhenThingsGoWrongTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/WhenThingsGoWrongTests.java @@ -219,7 +219,7 @@ public void testIllegalDynamicMethod() { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("def x = 'test'; return x.getClass().toString()"); }); - assertTrue(expected.getMessage().contains("Unable to find dynamic method")); + assertTrue(expected.getMessage().contains("dynamic method [java.lang.String, getClass/0] not found")); } public void testDynamicNPE() { From 551dc4e50f3dea5d18bd160a7b9b368440b209ec Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 16:40:05 -0700 Subject: [PATCH 23/33] Add getter/setter lookup methods to PainlessLookup. --- .../java/org/elasticsearch/painless/Def.java | 115 ++++++------------ .../painless/FunctionReference.java | 2 +- .../painless/lookup/PainlessLookup.java | 55 +++++++-- .../lookup/PainlessLookupUtility.java | 9 +- 4 files changed, 93 insertions(+), 88 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index fa21401c4727d..2382fe4a74793 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java @@ -20,7 +20,6 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.Locals.LocalMethod; -import org.elasticsearch.painless.lookup.PainlessClass; import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; @@ -343,49 +342,31 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku */ static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class receiverClass, String name) { // first try whitelist - for (Class clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) { - PainlessClass struct = painlessLookup.lookupPainlessClass(clazz); - - if (struct != null) { - MethodHandle handle = struct.getterMethodHandles.get(name); - if (handle != null) { - return handle; + try { + return painlessLookup.lookupRuntimeGetter(receiverClass, name); + } catch (IllegalArgumentException iae) { + // special case: arrays, maps, and lists + if (receiverClass.isArray() && "length".equals(name)) { + // arrays expose .length as a read-only getter + return arrayLengthGetter(receiverClass); + } else if (Map.class.isAssignableFrom(receiverClass)) { + // maps allow access like mymap.key + // wire 'key' as a parameter, its a constant in painless + return MethodHandles.insertArguments(MAP_GET, 1, name); + } else if (List.class.isAssignableFrom(receiverClass)) { + // lists allow access like mylist.0 + // wire '0' (index) as a parameter, its a constant. this also avoids + // parsing the same integer millions of times! + try { + int index = Integer.parseInt(name); + return MethodHandles.insertArguments(LIST_GET, 1, index); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException("Illegal list shortcut value [" + name + "]."); } } - for (final Class iface : clazz.getInterfaces()) { - struct = painlessLookup.lookupPainlessClass(iface); - - if (struct != null) { - MethodHandle handle = struct.getterMethodHandles.get(name); - if (handle != null) { - return handle; - } - } - } + throw iae; } - // special case: arrays, maps, and lists - if (receiverClass.isArray() && "length".equals(name)) { - // arrays expose .length as a read-only getter - return arrayLengthGetter(receiverClass); - } else if (Map.class.isAssignableFrom(receiverClass)) { - // maps allow access like mymap.key - // wire 'key' as a parameter, its a constant in painless - return MethodHandles.insertArguments(MAP_GET, 1, name); - } else if (List.class.isAssignableFrom(receiverClass)) { - // lists allow access like mylist.0 - // wire '0' (index) as a parameter, its a constant. this also avoids - // parsing the same integer millions of times! - try { - int index = Integer.parseInt(name); - return MethodHandles.insertArguments(LIST_GET, 1, index); - } catch (NumberFormatException exception) { - throw new IllegalArgumentException( "Illegal list shortcut value [" + name + "]."); - } - } - - throw new IllegalArgumentException("Unable to find dynamic field [" + name + "] " + - "for class [" + receiverClass.getCanonicalName() + "]."); } /** @@ -414,46 +395,28 @@ static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class receive */ static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class receiverClass, String name) { // first try whitelist - for (Class clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) { - PainlessClass struct = painlessLookup.lookupPainlessClass(clazz); - - if (struct != null) { - MethodHandle handle = struct.setterMethodHandles.get(name); - if (handle != null) { - return handle; + try { + return painlessLookup.lookupRuntimeSetter(receiverClass, name); + } catch (IllegalArgumentException iae) { + // special case: maps, and lists + if (Map.class.isAssignableFrom(receiverClass)) { + // maps allow access like mymap.key + // wire 'key' as a parameter, its a constant in painless + return MethodHandles.insertArguments(MAP_PUT, 1, name); + } else if (List.class.isAssignableFrom(receiverClass)) { + // lists allow access like mylist.0 + // wire '0' (index) as a parameter, its a constant. this also avoids + // parsing the same integer millions of times! + try { + int index = Integer.parseInt(name); + return MethodHandles.insertArguments(LIST_SET, 1, index); + } catch (final NumberFormatException exception) { + throw new IllegalArgumentException("Illegal list shortcut value [" + name + "]."); } } - for (final Class iface : clazz.getInterfaces()) { - struct = painlessLookup.lookupPainlessClass(iface); - - if (struct != null) { - MethodHandle handle = struct.setterMethodHandles.get(name); - if (handle != null) { - return handle; - } - } - } + throw iae; } - // special case: maps, and lists - if (Map.class.isAssignableFrom(receiverClass)) { - // maps allow access like mymap.key - // wire 'key' as a parameter, its a constant in painless - return MethodHandles.insertArguments(MAP_PUT, 1, name); - } else if (List.class.isAssignableFrom(receiverClass)) { - // lists allow access like mylist.0 - // wire '0' (index) as a parameter, its a constant. this also avoids - // parsing the same integer millions of times! - try { - int index = Integer.parseInt(name); - return MethodHandles.insertArguments(LIST_SET, 1, index); - } catch (final NumberFormatException exception) { - throw new IllegalArgumentException( "Illegal list shortcut value [" + name + "]."); - } - } - - throw new IllegalArgumentException("Unable to find dynamic field [" + name + "] " + - "for class [" + receiverClass.getCanonicalName() + "]."); } /** diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java index 64d08a9763f4d..f09904018d691 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java @@ -225,7 +225,7 @@ public static FunctionReference create(PainlessLookup painlessLookup, Map targetClass, boolean isStati Objects.requireNonNull(methodName); if (targetClass.isPrimitive()) { - targetClass = PainlessLookupUtility.typeToBoxedType(targetClass); + targetClass = typeToBoxedType(targetClass); } PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass); @@ -186,17 +189,50 @@ public PainlessMethod lookupFunctionalInterfacePainlessMethod(Class targetCla } public PainlessMethod lookupRuntimePainlessMethod(Class originalTargetClass, String methodName, int methodArity) { - String painlessMethodKey = PainlessLookupUtility.buildPainlessMethodKey(methodName, methodArity); + Objects.requireNonNull(originalTargetClass); + Objects.requireNonNull(methodName); + + String painlessMethodKey = buildPainlessMethodKey(methodName, methodArity); + Function objectLookup = targetPainlessClass -> targetPainlessClass.methods.get(painlessMethodKey); + String notFoundErrorMessage = + "dynamic method [" + typeToCanonicalTypeName(originalTargetClass) + ", " + painlessMethodKey + "] not found"; + + return lookupRuntimePainlessObject(originalTargetClass, objectLookup, notFoundErrorMessage); + } + + public MethodHandle lookupRuntimeGetter(Class originalTargetClass, String getterName) { + Objects.requireNonNull(originalTargetClass); + Objects.requireNonNull(getterName); + + Function objectLookup = targetPainlessClass -> targetPainlessClass.getterMethodHandles.get(getterName); + String notFoundErrorMessage = "dynamic getter [" + typeToCanonicalTypeName(originalTargetClass) + ", " + getterName + "] not found"; + + return lookupRuntimePainlessObject(originalTargetClass, objectLookup, notFoundErrorMessage); + } + + public MethodHandle lookupRuntimeSetter(Class originalTargetClass, String setterName) { + Objects.requireNonNull(originalTargetClass); + Objects.requireNonNull(setterName); + + Function objectLookup = targetPainlessClass -> targetPainlessClass.setterMethodHandles.get(setterName); + String notFoundErrorMessage = "dynamic setter [" + typeToCanonicalTypeName(originalTargetClass) + ", " + setterName + "] not found"; + + return lookupRuntimePainlessObject(originalTargetClass, objectLookup, notFoundErrorMessage); + } + + private T lookupRuntimePainlessObject( + Class originalTargetClass, Function objectLookup, String notFoundErrorMessage) { + Class currentTargetClass = originalTargetClass; while (currentTargetClass != null) { PainlessClass targetPainlessClass = classesToPainlessClasses.get(currentTargetClass); if (targetPainlessClass != null) { - PainlessMethod painlessMethod = targetPainlessClass.methods.get(painlessMethodKey); + T painlessObject = objectLookup.apply(targetPainlessClass); - if (painlessMethod != null) { - return painlessMethod; + if (painlessObject != null) { + return painlessObject; } } @@ -210,10 +246,10 @@ public PainlessMethod lookupRuntimePainlessMethod(Class originalTargetClass, PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetInterface); if (targetPainlessClass != null) { - PainlessMethod painlessMethod = targetPainlessClass.methods.get(painlessMethodKey); + T painlessObject = objectLookup.apply(targetPainlessClass); - if (painlessMethod != null) { - return painlessMethod; + if (painlessObject != null) { + return painlessObject; } } } @@ -221,7 +257,6 @@ public PainlessMethod lookupRuntimePainlessMethod(Class originalTargetClass, currentTargetClass = currentTargetClass.getSuperclass(); } - throw new IllegalArgumentException( - "dynamic method [" + typeToCanonicalTypeName(originalTargetClass) + ", " + painlessMethodKey + "] not found"); + throw new IllegalArgumentException(notFoundErrorMessage); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java index 0a181c5f1b02d..3aefe8d9694cd 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java @@ -152,7 +152,9 @@ public static String typeToCanonicalTypeName(Class type) { String canonicalTypeName = type.getCanonicalName(); - if (canonicalTypeName.startsWith(def.class.getCanonicalName())) { + if (canonicalTypeName == null) { + canonicalTypeName = ANONYMOUS_CLASS_NAME; + } else if (canonicalTypeName.startsWith(def.class.getCanonicalName())) { canonicalTypeName = canonicalTypeName.replace(def.class.getCanonicalName(), DEF_CLASS_NAME); } @@ -357,6 +359,11 @@ public static String buildPainlessFieldKey(String fieldName) { return fieldName; } + /** + * The name for an anonymous class. + */ + public static final String ANONYMOUS_CLASS_NAME = "$anonymous"; + /** * The def type name as specified in the source for a script. */ From c1d9d250c47dc5f2fd1ff963442a9eb662b001c7 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 16:41:53 -0700 Subject: [PATCH 24/33] Rename. --- .../src/main/java/org/elasticsearch/painless/Def.java | 4 ++-- .../org/elasticsearch/painless/lookup/PainlessLookup.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index 2382fe4a74793..f125034779cd6 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 @@ -343,7 +343,7 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class receiverClass, String name) { // first try whitelist try { - return painlessLookup.lookupRuntimeGetter(receiverClass, name); + return painlessLookup.lookupRuntimeGetterMethodHandle(receiverClass, name); } catch (IllegalArgumentException iae) { // special case: arrays, maps, and lists if (receiverClass.isArray() && "length".equals(name)) { @@ -396,7 +396,7 @@ static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class receive static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class receiverClass, String name) { // first try whitelist try { - return painlessLookup.lookupRuntimeSetter(receiverClass, name); + return painlessLookup.lookupRuntimeSetterMethodHandle(receiverClass, name); } catch (IllegalArgumentException iae) { // special case: maps, and lists if (Map.class.isAssignableFrom(receiverClass)) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java index a7d5627e04d6e..4ef5a21af31bb 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java @@ -200,7 +200,7 @@ public PainlessMethod lookupRuntimePainlessMethod(Class originalTargetClass, return lookupRuntimePainlessObject(originalTargetClass, objectLookup, notFoundErrorMessage); } - public MethodHandle lookupRuntimeGetter(Class originalTargetClass, String getterName) { + public MethodHandle lookupRuntimeGetterMethodHandle(Class originalTargetClass, String getterName) { Objects.requireNonNull(originalTargetClass); Objects.requireNonNull(getterName); @@ -210,7 +210,7 @@ public MethodHandle lookupRuntimeGetter(Class originalTargetClass, String get return lookupRuntimePainlessObject(originalTargetClass, objectLookup, notFoundErrorMessage); } - public MethodHandle lookupRuntimeSetter(Class originalTargetClass, String setterName) { + public MethodHandle lookupRuntimeSetterMethodHandle(Class originalTargetClass, String setterName) { Objects.requireNonNull(originalTargetClass); Objects.requireNonNull(setterName); From 73bfbdf832f0cf75b84fe3920510440b3cf7ffc4 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 1 Aug 2018 17:01:06 -0700 Subject: [PATCH 25/33] Mechanical change to rename variables in PainlessCast. --- .../painless/AnalyzerCaster.java | 388 +++++++++--------- .../elasticsearch/painless/MethodWriter.java | 66 +-- .../painless/lookup/PainlessCast.java | 69 ++-- .../elasticsearch/painless/node/ECast.java | 2 +- .../painless/AnalyzerCasterTests.java | 8 +- .../painless/node/NodeToStringTests.java | 4 +- 6 files changed, 273 insertions(+), 264 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java index fe53a3c11001c..588fe8ef5f7cf 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java @@ -41,421 +41,421 @@ public static PainlessCast getLegalCast(Location location, Class actual, Clas if (actual == def.class) { if (expected == boolean.class) { - return PainlessCast.unboxTo(def.class, Boolean.class, explicit, boolean.class); + return PainlessCast.unboxTargetType(def.class, Boolean.class, explicit, boolean.class); } else if (expected == byte.class) { - return PainlessCast.unboxTo(def.class, Byte.class, explicit, byte.class); + return PainlessCast.unboxTargetType(def.class, Byte.class, explicit, byte.class); } else if (expected == short.class) { - return PainlessCast.unboxTo(def.class, Short.class, explicit, short.class); + return PainlessCast.unboxTargetType(def.class, Short.class, explicit, short.class); } else if (expected == char.class) { - return PainlessCast.unboxTo(def.class, Character.class, explicit, char.class); + return PainlessCast.unboxTargetType(def.class, Character.class, explicit, char.class); } else if (expected == int.class) { - return PainlessCast.unboxTo(def.class, Integer.class, explicit, int.class); + return PainlessCast.unboxTargetType(def.class, Integer.class, explicit, int.class); } else if (expected == long.class) { - return PainlessCast.unboxTo(def.class, Long.class, explicit, long.class); + return PainlessCast.unboxTargetType(def.class, Long.class, explicit, long.class); } else if (expected == float.class) { - return PainlessCast.unboxTo(def.class, Float.class, explicit, float.class); + return PainlessCast.unboxTargetType(def.class, Float.class, explicit, float.class); } else if (expected == double.class) { - return PainlessCast.unboxTo(def.class, Double.class, explicit, double.class); + return PainlessCast.unboxTargetType(def.class, Double.class, explicit, double.class); } } else if (actual == Object.class) { if (expected == byte.class && explicit && internal) { - return PainlessCast.unboxTo(Object.class, Byte.class, true, byte.class); + return PainlessCast.unboxTargetType(Object.class, Byte.class, true, byte.class); } else if (expected == short.class && explicit && internal) { - return PainlessCast.unboxTo(Object.class, Short.class, true, short.class); + return PainlessCast.unboxTargetType(Object.class, Short.class, true, short.class); } else if (expected == char.class && explicit && internal) { - return PainlessCast.unboxTo(Object.class, Character.class, true, char.class); + return PainlessCast.unboxTargetType(Object.class, Character.class, true, char.class); } else if (expected == int.class && explicit && internal) { - return PainlessCast.unboxTo(Object.class, Integer.class, true, int.class); + return PainlessCast.unboxTargetType(Object.class, Integer.class, true, int.class); } else if (expected == long.class && explicit && internal) { - return PainlessCast.unboxTo(Object.class, Long.class, true, long.class); + return PainlessCast.unboxTargetType(Object.class, Long.class, true, long.class); } else if (expected == float.class && explicit && internal) { - return PainlessCast.unboxTo(Object.class, Float.class, true, float.class); + return PainlessCast.unboxTargetType(Object.class, Float.class, true, float.class); } else if (expected == double.class && explicit && internal) { - return PainlessCast.unboxTo(Object.class, Double.class, true, double.class); + return PainlessCast.unboxTargetType(Object.class, Double.class, true, double.class); } } else if (actual == Number.class) { if (expected == byte.class && explicit && internal) { - return PainlessCast.unboxTo(Number.class, Byte.class, true, byte.class); + return PainlessCast.unboxTargetType(Number.class, Byte.class, true, byte.class); } else if (expected == short.class && explicit && internal) { - return PainlessCast.unboxTo(Number.class, Short.class, true, short.class); + return PainlessCast.unboxTargetType(Number.class, Short.class, true, short.class); } else if (expected == char.class && explicit && internal) { - return PainlessCast.unboxTo(Number.class, Character.class, true, char.class); + return PainlessCast.unboxTargetType(Number.class, Character.class, true, char.class); } else if (expected == int.class && explicit && internal) { - return PainlessCast.unboxTo(Number.class, Integer.class, true, int.class); + return PainlessCast.unboxTargetType(Number.class, Integer.class, true, int.class); } else if (expected == long.class && explicit && internal) { - return PainlessCast.unboxTo(Number.class, Long.class, true, long.class); + return PainlessCast.unboxTargetType(Number.class, Long.class, true, long.class); } else if (expected == float.class && explicit && internal) { - return PainlessCast.unboxTo(Number.class, Float.class, true, float.class); + return PainlessCast.unboxTargetType(Number.class, Float.class, true, float.class); } else if (expected == double.class && explicit && internal) { - return PainlessCast.unboxTo(Number.class, Double.class, true, double.class); + return PainlessCast.unboxTargetType(Number.class, Double.class, true, double.class); } } else if (actual == String.class) { if (expected == char.class && explicit) { - return PainlessCast.standard(String.class, char.class, true); + return PainlessCast.originalTypetoTargetType(String.class, char.class, true); } } else if (actual == boolean.class) { if (expected == def.class) { - return PainlessCast.boxFrom(Boolean.class, def.class, explicit, boolean.class); + return PainlessCast.boxOriginalType(Boolean.class, def.class, explicit, boolean.class); } else if (expected == Object.class && internal) { - return PainlessCast.boxFrom(Boolean.class, Object.class, explicit, boolean.class); + return PainlessCast.boxOriginalType(Boolean.class, Object.class, explicit, boolean.class); } else if (expected == Boolean.class && internal) { - return PainlessCast.boxTo(boolean.class, boolean.class, explicit, boolean.class); + return PainlessCast.boxTargetType(boolean.class, boolean.class, explicit, boolean.class); } } else if (actual == byte.class) { if (expected == def.class) { - return PainlessCast.boxFrom(Byte.class, def.class, explicit, byte.class); + return PainlessCast.boxOriginalType(Byte.class, def.class, explicit, byte.class); } else if (expected == Object.class && internal) { - return PainlessCast.boxFrom(Byte.class, Object.class, explicit, byte.class); + return PainlessCast.boxOriginalType(Byte.class, Object.class, explicit, byte.class); } else if (expected == Number.class && internal) { - return PainlessCast.boxFrom(Byte.class, Number.class, explicit, byte.class); + return PainlessCast.boxOriginalType(Byte.class, Number.class, explicit, byte.class); } else if (expected == short.class) { - return PainlessCast.standard(byte.class, short.class, explicit); + return PainlessCast.originalTypetoTargetType(byte.class, short.class, explicit); } else if (expected == char.class && explicit) { - return PainlessCast.standard(byte.class, char.class, true); + return PainlessCast.originalTypetoTargetType(byte.class, char.class, true); } else if (expected == int.class) { - return PainlessCast.standard(byte.class, int.class, explicit); + return PainlessCast.originalTypetoTargetType(byte.class, int.class, explicit); } else if (expected == long.class) { - return PainlessCast.standard(byte.class, long.class, explicit); + return PainlessCast.originalTypetoTargetType(byte.class, long.class, explicit); } else if (expected == float.class) { - return PainlessCast.standard(byte.class, float.class, explicit); + return PainlessCast.originalTypetoTargetType(byte.class, float.class, explicit); } else if (expected == double.class) { - return PainlessCast.standard(byte.class, double.class, explicit); + return PainlessCast.originalTypetoTargetType(byte.class, double.class, explicit); } else if (expected == Byte.class && internal) { - return PainlessCast.boxTo(byte.class, byte.class, explicit, byte.class); + return PainlessCast.boxTargetType(byte.class, byte.class, explicit, byte.class); } else if (expected == Short.class && internal) { - return PainlessCast.boxTo(byte.class, short.class, explicit, short.class); + return PainlessCast.boxTargetType(byte.class, short.class, explicit, short.class); } else if (expected == Character.class && explicit && internal) { - return PainlessCast.boxTo(byte.class, char.class, true, char.class); + return PainlessCast.boxTargetType(byte.class, char.class, true, char.class); } else if (expected == Integer.class && internal) { - return PainlessCast.boxTo(byte.class, int.class, explicit, int.class); + return PainlessCast.boxTargetType(byte.class, int.class, explicit, int.class); } else if (expected == Long.class && internal) { - return PainlessCast.boxTo(byte.class, long.class, explicit, long.class); + return PainlessCast.boxTargetType(byte.class, long.class, explicit, long.class); } else if (expected == Float.class && internal) { - return PainlessCast.boxTo(byte.class, float.class, explicit, float.class); + return PainlessCast.boxTargetType(byte.class, float.class, explicit, float.class); } else if (expected == Double.class && internal) { - return PainlessCast.boxTo(byte.class, double.class, explicit, double.class); + return PainlessCast.boxTargetType(byte.class, double.class, explicit, double.class); } } else if (actual == short.class) { if (expected == def.class) { - return PainlessCast.boxFrom(Short.class, def.class, explicit, short.class); + return PainlessCast.boxOriginalType(Short.class, def.class, explicit, short.class); } else if (expected == Object.class && internal) { - return PainlessCast.boxFrom(Short.class, Object.class, explicit, short.class); + return PainlessCast.boxOriginalType(Short.class, Object.class, explicit, short.class); } else if (expected == Number.class && internal) { - return PainlessCast.boxFrom(Short.class, Number.class, explicit, short.class); + return PainlessCast.boxOriginalType(Short.class, Number.class, explicit, short.class); } else if (expected == byte.class && explicit) { - return PainlessCast.standard(short.class, byte.class, true); + return PainlessCast.originalTypetoTargetType(short.class, byte.class, true); } else if (expected == char.class && explicit) { - return PainlessCast.standard(short.class, char.class, true); + return PainlessCast.originalTypetoTargetType(short.class, char.class, true); } else if (expected == int.class) { - return PainlessCast.standard(short.class, int.class, explicit); + return PainlessCast.originalTypetoTargetType(short.class, int.class, explicit); } else if (expected == long.class) { - return PainlessCast.standard(short.class, long.class, explicit); + return PainlessCast.originalTypetoTargetType(short.class, long.class, explicit); } else if (expected == float.class) { - return PainlessCast.standard(short.class, float.class, explicit); + return PainlessCast.originalTypetoTargetType(short.class, float.class, explicit); } else if (expected == double.class) { - return PainlessCast.standard(short.class, double.class, explicit); + return PainlessCast.originalTypetoTargetType(short.class, double.class, explicit); } else if (expected == Byte.class && explicit && internal) { - return PainlessCast.boxTo(short.class, byte.class, true, byte.class); + return PainlessCast.boxTargetType(short.class, byte.class, true, byte.class); } else if (expected == Short.class && internal) { - return PainlessCast.boxTo(short.class, short.class, explicit, short.class); + return PainlessCast.boxTargetType(short.class, short.class, explicit, short.class); } else if (expected == Character.class && explicit && internal) { - return PainlessCast.boxTo(short.class, char.class, true, char.class); + return PainlessCast.boxTargetType(short.class, char.class, true, char.class); } else if (expected == Integer.class && internal) { - return PainlessCast.boxTo(short.class, int.class, explicit, int.class); + return PainlessCast.boxTargetType(short.class, int.class, explicit, int.class); } else if (expected == Long.class && internal) { - return PainlessCast.boxTo(short.class, long.class, explicit, long.class); + return PainlessCast.boxTargetType(short.class, long.class, explicit, long.class); } else if (expected == Float.class && internal) { - return PainlessCast.boxTo(short.class, float.class, explicit, float.class); + return PainlessCast.boxTargetType(short.class, float.class, explicit, float.class); } else if (expected == Double.class && internal) { - return PainlessCast.boxTo(short.class, double.class, explicit, double.class); + return PainlessCast.boxTargetType(short.class, double.class, explicit, double.class); } } else if (actual == char.class) { if (expected == def.class) { - return PainlessCast.boxFrom(Character.class, def.class, explicit, char.class); + return PainlessCast.boxOriginalType(Character.class, def.class, explicit, char.class); } else if (expected == Object.class && internal) { - return PainlessCast.boxFrom(Character.class, Object.class, explicit, char.class); + return PainlessCast.boxOriginalType(Character.class, Object.class, explicit, char.class); } else if (expected == Number.class && internal) { - return PainlessCast.boxFrom(Character.class, Number.class, explicit, char.class); + return PainlessCast.boxOriginalType(Character.class, Number.class, explicit, char.class); } else if (expected == String.class) { - return PainlessCast.standard(char.class, String.class, explicit); + return PainlessCast.originalTypetoTargetType(char.class, String.class, explicit); } else if (expected == byte.class && explicit) { - return PainlessCast.standard(char.class, byte.class, true); + return PainlessCast.originalTypetoTargetType(char.class, byte.class, true); } else if (expected == short.class && explicit) { - return PainlessCast.standard(char.class, short.class, true); + return PainlessCast.originalTypetoTargetType(char.class, short.class, true); } else if (expected == int.class) { - return PainlessCast.standard(char.class, int.class, explicit); + return PainlessCast.originalTypetoTargetType(char.class, int.class, explicit); } else if (expected == long.class) { - return PainlessCast.standard(char.class, long.class, explicit); + return PainlessCast.originalTypetoTargetType(char.class, long.class, explicit); } else if (expected == float.class) { - return PainlessCast.standard(char.class, float.class, explicit); + return PainlessCast.originalTypetoTargetType(char.class, float.class, explicit); } else if (expected == double.class) { - return PainlessCast.standard(char.class, double.class, explicit); + return PainlessCast.originalTypetoTargetType(char.class, double.class, explicit); } else if (expected == Byte.class && explicit && internal) { - return PainlessCast.boxTo(char.class, byte.class, true, byte.class); + return PainlessCast.boxTargetType(char.class, byte.class, true, byte.class); } else if (expected == Short.class && internal) { - return PainlessCast.boxTo(char.class, short.class, explicit, short.class); + return PainlessCast.boxTargetType(char.class, short.class, explicit, short.class); } else if (expected == Character.class && internal) { - return PainlessCast.boxTo(char.class, char.class, true, char.class); + return PainlessCast.boxTargetType(char.class, char.class, true, char.class); } else if (expected == Integer.class && internal) { - return PainlessCast.boxTo(char.class, int.class, explicit, int.class); + return PainlessCast.boxTargetType(char.class, int.class, explicit, int.class); } else if (expected == Long.class && internal) { - return PainlessCast.boxTo(char.class, long.class, explicit, long.class); + return PainlessCast.boxTargetType(char.class, long.class, explicit, long.class); } else if (expected == Float.class && internal) { - return PainlessCast.boxTo(char.class, float.class, explicit, float.class); + return PainlessCast.boxTargetType(char.class, float.class, explicit, float.class); } else if (expected == Double.class && internal) { - return PainlessCast.boxTo(char.class, double.class, explicit, double.class); + return PainlessCast.boxTargetType(char.class, double.class, explicit, double.class); } } else if (actual == int.class) { if (expected == def.class) { - return PainlessCast.boxFrom(Integer.class, def.class, explicit, int.class); + return PainlessCast.boxOriginalType(Integer.class, def.class, explicit, int.class); } else if (expected == Object.class && internal) { - return PainlessCast.boxFrom(Integer.class, Object.class, explicit, int.class); + return PainlessCast.boxOriginalType(Integer.class, Object.class, explicit, int.class); } else if (expected == Number.class && internal) { - return PainlessCast.boxFrom(Integer.class, Number.class, explicit, int.class); + return PainlessCast.boxOriginalType(Integer.class, Number.class, explicit, int.class); } else if (expected == byte.class && explicit) { - return PainlessCast.standard(int.class, byte.class, true); + return PainlessCast.originalTypetoTargetType(int.class, byte.class, true); } else if (expected == char.class && explicit) { - return PainlessCast.standard(int.class, char.class, true); + return PainlessCast.originalTypetoTargetType(int.class, char.class, true); } else if (expected == short.class && explicit) { - return PainlessCast.standard(int.class, short.class, true); + return PainlessCast.originalTypetoTargetType(int.class, short.class, true); } else if (expected == long.class) { - return PainlessCast.standard(int.class, long.class, explicit); + return PainlessCast.originalTypetoTargetType(int.class, long.class, explicit); } else if (expected == float.class) { - return PainlessCast.standard(int.class, float.class, explicit); + return PainlessCast.originalTypetoTargetType(int.class, float.class, explicit); } else if (expected == double.class) { - return PainlessCast.standard(int.class, double.class, explicit); + return PainlessCast.originalTypetoTargetType(int.class, double.class, explicit); } else if (expected == Byte.class && explicit && internal) { - return PainlessCast.boxTo(int.class, byte.class, true, byte.class); + return PainlessCast.boxTargetType(int.class, byte.class, true, byte.class); } else if (expected == Short.class && explicit && internal) { - return PainlessCast.boxTo(int.class, short.class, true, short.class); + return PainlessCast.boxTargetType(int.class, short.class, true, short.class); } else if (expected == Character.class && explicit && internal) { - return PainlessCast.boxTo(int.class, char.class, true, char.class); + return PainlessCast.boxTargetType(int.class, char.class, true, char.class); } else if (expected == Integer.class && internal) { - return PainlessCast.boxTo(int.class, int.class, explicit, int.class); + return PainlessCast.boxTargetType(int.class, int.class, explicit, int.class); } else if (expected == Long.class && internal) { - return PainlessCast.boxTo(int.class, long.class, explicit, long.class); + return PainlessCast.boxTargetType(int.class, long.class, explicit, long.class); } else if (expected == Float.class && internal) { - return PainlessCast.boxTo(int.class, float.class, explicit, float.class); + return PainlessCast.boxTargetType(int.class, float.class, explicit, float.class); } else if (expected == Double.class && internal) { - return PainlessCast.boxTo(int.class, double.class, explicit, double.class); + return PainlessCast.boxTargetType(int.class, double.class, explicit, double.class); } } else if (actual == long.class) { if (expected == def.class) { - return PainlessCast.boxFrom(Long.class, def.class, explicit, long.class); + return PainlessCast.boxOriginalType(Long.class, def.class, explicit, long.class); } else if (expected == Object.class && internal) { - return PainlessCast.boxFrom(Long.class, Object.class, explicit, long.class); + return PainlessCast.boxOriginalType(Long.class, Object.class, explicit, long.class); } else if (expected == Number.class && internal) { - return PainlessCast.boxFrom(Long.class, Number.class, explicit, long.class); + return PainlessCast.boxOriginalType(Long.class, Number.class, explicit, long.class); } else if (expected == byte.class && explicit) { - return PainlessCast.standard(long.class, byte.class, true); + return PainlessCast.originalTypetoTargetType(long.class, byte.class, true); } else if (expected == char.class && explicit) { - return PainlessCast.standard(long.class, char.class, true); + return PainlessCast.originalTypetoTargetType(long.class, char.class, true); } else if (expected == short.class && explicit) { - return PainlessCast.standard(long.class, short.class, true); + return PainlessCast.originalTypetoTargetType(long.class, short.class, true); } else if (expected == int.class && explicit) { - return PainlessCast.standard(long.class, int.class, true); + return PainlessCast.originalTypetoTargetType(long.class, int.class, true); } else if (expected == float.class) { - return PainlessCast.standard(long.class, float.class, explicit); + return PainlessCast.originalTypetoTargetType(long.class, float.class, explicit); } else if (expected == double.class) { - return PainlessCast.standard(long.class, double.class, explicit); + return PainlessCast.originalTypetoTargetType(long.class, double.class, explicit); } else if (expected == Byte.class && explicit && internal) { - return PainlessCast.boxTo(long.class, byte.class, true, byte.class); + return PainlessCast.boxTargetType(long.class, byte.class, true, byte.class); } else if (expected == Short.class && explicit && internal) { - return PainlessCast.boxTo(long.class, short.class, true, short.class); + return PainlessCast.boxTargetType(long.class, short.class, true, short.class); } else if (expected == Character.class && explicit && internal) { - return PainlessCast.boxTo(long.class, char.class, true, char.class); + return PainlessCast.boxTargetType(long.class, char.class, true, char.class); } else if (expected == Integer.class && explicit && internal) { - return PainlessCast.boxTo(long.class, int.class, true, int.class); + return PainlessCast.boxTargetType(long.class, int.class, true, int.class); } else if (expected == Long.class && internal) { - return PainlessCast.boxTo(long.class, long.class, explicit, long.class); + return PainlessCast.boxTargetType(long.class, long.class, explicit, long.class); } else if (expected == Float.class && internal) { - return PainlessCast.boxTo(long.class, float.class, explicit, float.class); + return PainlessCast.boxTargetType(long.class, float.class, explicit, float.class); } else if (expected == Double.class && internal) { - return PainlessCast.boxTo(long.class, double.class, explicit, double.class); + return PainlessCast.boxTargetType(long.class, double.class, explicit, double.class); } } else if (actual == float.class) { if (expected == def.class) { - return PainlessCast.boxFrom(Float.class, def.class, explicit, float.class); + return PainlessCast.boxOriginalType(Float.class, def.class, explicit, float.class); } else if (expected == Object.class && internal) { - return PainlessCast.boxFrom(Float.class, Object.class, explicit, float.class); + return PainlessCast.boxOriginalType(Float.class, Object.class, explicit, float.class); } else if (expected == Number.class && internal) { - return PainlessCast.boxFrom(Float.class, Number.class, explicit, float.class); + return PainlessCast.boxOriginalType(Float.class, Number.class, explicit, float.class); } else if (expected == byte.class && explicit) { - return PainlessCast.standard(float.class, byte.class, true); + return PainlessCast.originalTypetoTargetType(float.class, byte.class, true); } else if (expected == char.class && explicit) { - return PainlessCast.standard(float.class, char.class, true); + return PainlessCast.originalTypetoTargetType(float.class, char.class, true); } else if (expected == short.class && explicit) { - return PainlessCast.standard(float.class, short.class, true); + return PainlessCast.originalTypetoTargetType(float.class, short.class, true); } else if (expected == int.class && explicit) { - return PainlessCast.standard(float.class, int.class, true); + return PainlessCast.originalTypetoTargetType(float.class, int.class, true); } else if (expected == long.class && explicit) { - return PainlessCast.standard(float.class, long.class, true); + return PainlessCast.originalTypetoTargetType(float.class, long.class, true); } else if (expected == double.class) { - return PainlessCast.standard(float.class, double.class, explicit); + return PainlessCast.originalTypetoTargetType(float.class, double.class, explicit); } else if (expected == Byte.class && explicit && internal) { - return PainlessCast.boxTo(float.class, byte.class, true, byte.class); + return PainlessCast.boxTargetType(float.class, byte.class, true, byte.class); } else if (expected == Short.class && explicit && internal) { - return PainlessCast.boxTo(float.class, short.class, true, short.class); + return PainlessCast.boxTargetType(float.class, short.class, true, short.class); } else if (expected == Character.class && explicit && internal) { - return PainlessCast.boxTo(float.class, char.class, true, char.class); + return PainlessCast.boxTargetType(float.class, char.class, true, char.class); } else if (expected == Integer.class && explicit && internal) { - return PainlessCast.boxTo(float.class, int.class, true, int.class); + return PainlessCast.boxTargetType(float.class, int.class, true, int.class); } else if (expected == Long.class && explicit && internal) { - return PainlessCast.boxTo(float.class, long.class, true, long.class); + return PainlessCast.boxTargetType(float.class, long.class, true, long.class); } else if (expected == Float.class && internal) { - return PainlessCast.boxTo(float.class, float.class, explicit, float.class); + return PainlessCast.boxTargetType(float.class, float.class, explicit, float.class); } else if (expected == Double.class && internal) { - return PainlessCast.boxTo(float.class, double.class, explicit, double.class); + return PainlessCast.boxTargetType(float.class, double.class, explicit, double.class); } } else if (actual == double.class) { if (expected == def.class) { - return PainlessCast.boxFrom(Double.class, def.class, explicit, double.class); + return PainlessCast.boxOriginalType(Double.class, def.class, explicit, double.class); } else if (expected == Object.class && internal) { - return PainlessCast.boxFrom(Double.class, Object.class, explicit, double.class); + return PainlessCast.boxOriginalType(Double.class, Object.class, explicit, double.class); } else if (expected == Number.class && internal) { - return PainlessCast.boxFrom(Double.class, Number.class, explicit, double.class); + return PainlessCast.boxOriginalType(Double.class, Number.class, explicit, double.class); } else if (expected == byte.class && explicit) { - return PainlessCast.standard(double.class, byte.class, true); + return PainlessCast.originalTypetoTargetType(double.class, byte.class, true); } else if (expected == char.class && explicit) { - return PainlessCast.standard(double.class, char.class, true); + return PainlessCast.originalTypetoTargetType(double.class, char.class, true); } else if (expected == short.class && explicit) { - return PainlessCast.standard(double.class, short.class, true); + return PainlessCast.originalTypetoTargetType(double.class, short.class, true); } else if (expected == int.class && explicit) { - return PainlessCast.standard(double.class, int.class, true); + return PainlessCast.originalTypetoTargetType(double.class, int.class, true); } else if (expected == long.class && explicit) { - return PainlessCast.standard(double.class, long.class, true); + return PainlessCast.originalTypetoTargetType(double.class, long.class, true); } else if (expected == float.class && explicit) { - return PainlessCast.standard(double.class, float.class, true); + return PainlessCast.originalTypetoTargetType(double.class, float.class, true); } else if (expected == Byte.class && explicit && internal) { - return PainlessCast.boxTo(double.class, byte.class, true, byte.class); + return PainlessCast.boxTargetType(double.class, byte.class, true, byte.class); } else if (expected == Short.class && explicit && internal) { - return PainlessCast.boxTo(double.class, short.class, true, short.class); + return PainlessCast.boxTargetType(double.class, short.class, true, short.class); } else if (expected == Character.class && explicit && internal) { - return PainlessCast.boxTo(double.class, char.class, true, char.class); + return PainlessCast.boxTargetType(double.class, char.class, true, char.class); } else if (expected == Integer.class && explicit && internal) { - return PainlessCast.boxTo(double.class, int.class, true, int.class); + return PainlessCast.boxTargetType(double.class, int.class, true, int.class); } else if (expected == Long.class && explicit && internal) { - return PainlessCast.boxTo(double.class, long.class, true, long.class); + return PainlessCast.boxTargetType(double.class, long.class, true, long.class); } else if (expected == Float.class && explicit && internal) { - return PainlessCast.boxTo(double.class, float.class, true, float.class); + return PainlessCast.boxTargetType(double.class, float.class, true, float.class); } else if (expected == Double.class && internal) { - return PainlessCast.boxTo(double.class, double.class, explicit, double.class); + return PainlessCast.boxTargetType(double.class, double.class, explicit, double.class); } } else if (actual == Boolean.class) { if (expected == boolean.class && internal) { - return PainlessCast.unboxFrom(boolean.class, boolean.class, explicit, boolean.class); + return PainlessCast.unboxOriginalType(boolean.class, boolean.class, explicit, boolean.class); } } else if (actual == Byte.class) { if (expected == byte.class && internal) { - return PainlessCast.unboxFrom(byte.class, byte.class, explicit, byte.class); + return PainlessCast.unboxOriginalType(byte.class, byte.class, explicit, byte.class); } else if (expected == short.class && internal) { - return PainlessCast.unboxFrom(byte.class, short.class, explicit, byte.class); + return PainlessCast.unboxOriginalType(byte.class, short.class, explicit, byte.class); } else if (expected == char.class && explicit && internal) { - return PainlessCast.unboxFrom(byte.class, char.class, true, byte.class); + return PainlessCast.unboxOriginalType(byte.class, char.class, true, byte.class); } else if (expected == int.class && internal) { - return PainlessCast.unboxFrom(byte.class, int.class, explicit, byte.class); + return PainlessCast.unboxOriginalType(byte.class, int.class, explicit, byte.class); } else if (expected == long.class && internal) { - return PainlessCast.unboxFrom(byte.class, long.class, explicit, byte.class); + return PainlessCast.unboxOriginalType(byte.class, long.class, explicit, byte.class); } else if (expected == float.class && internal) { - return PainlessCast.unboxFrom(byte.class, float.class, explicit, byte.class); + return PainlessCast.unboxOriginalType(byte.class, float.class, explicit, byte.class); } else if (expected == double.class && internal) { - return PainlessCast.unboxFrom(byte.class, double.class, explicit, byte.class); + return PainlessCast.unboxOriginalType(byte.class, double.class, explicit, byte.class); } } else if (actual == Short.class) { if (expected == byte.class && explicit && internal) { - return PainlessCast.unboxFrom(short.class, byte.class, true, short.class); + return PainlessCast.unboxOriginalType(short.class, byte.class, true, short.class); } else if (expected == short.class && internal) { - return PainlessCast.unboxFrom(short.class, short.class, explicit, short.class); + return PainlessCast.unboxOriginalType(short.class, short.class, explicit, short.class); } else if (expected == char.class && explicit && internal) { - return PainlessCast.unboxFrom(short.class, char.class, true, short.class); + return PainlessCast.unboxOriginalType(short.class, char.class, true, short.class); } else if (expected == int.class && internal) { - return PainlessCast.unboxFrom(short.class, int.class, explicit, short.class); + return PainlessCast.unboxOriginalType(short.class, int.class, explicit, short.class); } else if (expected == long.class && internal) { - return PainlessCast.unboxFrom(short.class, long.class, explicit, short.class); + return PainlessCast.unboxOriginalType(short.class, long.class, explicit, short.class); } else if (expected == float.class && internal) { - return PainlessCast.unboxFrom(short.class, float.class, explicit, short.class); + return PainlessCast.unboxOriginalType(short.class, float.class, explicit, short.class); } else if (expected == double.class && internal) { - return PainlessCast.unboxFrom(short.class, double.class, explicit, short.class); + return PainlessCast.unboxOriginalType(short.class, double.class, explicit, short.class); } } else if (actual == Character.class) { if (expected == byte.class && explicit && internal) { - return PainlessCast.unboxFrom(char.class, byte.class, true, char.class); + return PainlessCast.unboxOriginalType(char.class, byte.class, true, char.class); } else if (expected == short.class && explicit && internal) { - return PainlessCast.unboxFrom(char.class, short.class, true, char.class); + return PainlessCast.unboxOriginalType(char.class, short.class, true, char.class); } else if (expected == char.class && internal) { - return PainlessCast.unboxFrom(char.class, char.class, explicit, char.class); + return PainlessCast.unboxOriginalType(char.class, char.class, explicit, char.class); } else if (expected == int.class && internal) { - return PainlessCast.unboxFrom(char.class, int.class, explicit, char.class); + return PainlessCast.unboxOriginalType(char.class, int.class, explicit, char.class); } else if (expected == long.class && internal) { - return PainlessCast.unboxFrom(char.class, long.class, explicit, char.class); + return PainlessCast.unboxOriginalType(char.class, long.class, explicit, char.class); } else if (expected == float.class && internal) { - return PainlessCast.unboxFrom(char.class, float.class, explicit, char.class); + return PainlessCast.unboxOriginalType(char.class, float.class, explicit, char.class); } else if (expected == double.class && internal) { - return PainlessCast.unboxFrom(char.class, double.class, explicit, char.class); + return PainlessCast.unboxOriginalType(char.class, double.class, explicit, char.class); } } else if (actual == Integer.class) { if (expected == byte.class && explicit && internal) { - return PainlessCast.unboxFrom(int.class, byte.class, true, int.class); + return PainlessCast.unboxOriginalType(int.class, byte.class, true, int.class); } else if (expected == short.class && explicit && internal) { - return PainlessCast.unboxFrom(int.class, short.class, true, int.class); + return PainlessCast.unboxOriginalType(int.class, short.class, true, int.class); } else if (expected == char.class && explicit && internal) { - return PainlessCast.unboxFrom(int.class, char.class, true, int.class); + return PainlessCast.unboxOriginalType(int.class, char.class, true, int.class); } else if (expected == int.class && internal) { - return PainlessCast.unboxFrom(int.class, int.class, explicit, int.class); + return PainlessCast.unboxOriginalType(int.class, int.class, explicit, int.class); } else if (expected == long.class && internal) { - return PainlessCast.unboxFrom(int.class, long.class, explicit, int.class); + return PainlessCast.unboxOriginalType(int.class, long.class, explicit, int.class); } else if (expected == float.class && internal) { - return PainlessCast.unboxFrom(int.class, float.class, explicit, int.class); + return PainlessCast.unboxOriginalType(int.class, float.class, explicit, int.class); } else if (expected == double.class && internal) { - return PainlessCast.unboxFrom(int.class, double.class, explicit, int.class); + return PainlessCast.unboxOriginalType(int.class, double.class, explicit, int.class); } } else if (actual == Long.class) { if (expected == byte.class && explicit && internal) { - return PainlessCast.unboxFrom(long.class, byte.class, true, long.class); + return PainlessCast.unboxOriginalType(long.class, byte.class, true, long.class); } else if (expected == short.class && explicit && internal) { - return PainlessCast.unboxFrom(long.class, short.class, true, long.class); + return PainlessCast.unboxOriginalType(long.class, short.class, true, long.class); } else if (expected == char.class && explicit && internal) { - return PainlessCast.unboxFrom(long.class, char.class, true, long.class); + return PainlessCast.unboxOriginalType(long.class, char.class, true, long.class); } else if (expected == int.class && explicit && internal) { - return PainlessCast.unboxFrom(long.class, int.class, true, long.class); + return PainlessCast.unboxOriginalType(long.class, int.class, true, long.class); } else if (expected == long.class && internal) { - return PainlessCast.unboxFrom(long.class, long.class, explicit, long.class); + return PainlessCast.unboxOriginalType(long.class, long.class, explicit, long.class); } else if (expected == float.class && internal) { - return PainlessCast.unboxFrom(long.class, float.class, explicit, long.class); + return PainlessCast.unboxOriginalType(long.class, float.class, explicit, long.class); } else if (expected == double.class && internal) { - return PainlessCast.unboxFrom(long.class, double.class, explicit, long.class); + return PainlessCast.unboxOriginalType(long.class, double.class, explicit, long.class); } } else if (actual == Float.class) { if (expected == byte.class && explicit && internal) { - return PainlessCast.unboxFrom(float.class, byte.class, true, float.class); + return PainlessCast.unboxOriginalType(float.class, byte.class, true, float.class); } else if (expected == short.class && explicit && internal) { - return PainlessCast.unboxFrom(float.class, short.class, true, float.class); + return PainlessCast.unboxOriginalType(float.class, short.class, true, float.class); } else if (expected == char.class && explicit && internal) { - return PainlessCast.unboxFrom(float.class, char.class, true, float.class); + return PainlessCast.unboxOriginalType(float.class, char.class, true, float.class); } else if (expected == int.class && explicit && internal) { - return PainlessCast.unboxFrom(float.class, int.class, true, float.class); + return PainlessCast.unboxOriginalType(float.class, int.class, true, float.class); } else if (expected == long.class && explicit && internal) { - return PainlessCast.unboxFrom(float.class, long.class, true, float.class); + return PainlessCast.unboxOriginalType(float.class, long.class, true, float.class); } else if (expected == float.class && internal) { - return PainlessCast.unboxFrom(float.class, float.class, explicit, float.class); + return PainlessCast.unboxOriginalType(float.class, float.class, explicit, float.class); } else if (expected == double.class && internal) { - return PainlessCast.unboxFrom(float.class, double.class, explicit, float.class); + return PainlessCast.unboxOriginalType(float.class, double.class, explicit, float.class); } } else if (actual == Double.class) { if (expected == byte.class && explicit && internal) { - return PainlessCast.unboxFrom(double.class, byte.class, true, double.class); + return PainlessCast.unboxOriginalType(double.class, byte.class, true, double.class); } else if (expected == short.class && explicit && internal) { - return PainlessCast.unboxFrom(double.class, short.class, true, double.class); + return PainlessCast.unboxOriginalType(double.class, short.class, true, double.class); } else if (expected == char.class && explicit && internal) { - return PainlessCast.unboxFrom(double.class, char.class, true, double.class); + return PainlessCast.unboxOriginalType(double.class, char.class, true, double.class); } else if (expected == int.class && explicit && internal) { - return PainlessCast.unboxFrom(double.class, int.class, true, double.class); + return PainlessCast.unboxOriginalType(double.class, int.class, true, double.class); } else if (expected == long.class && explicit && internal) { - return PainlessCast.unboxFrom(double.class, long.class, true, double.class); + return PainlessCast.unboxOriginalType(double.class, long.class, true, double.class); } else if (expected == float.class && explicit && internal) { - return PainlessCast.unboxFrom(double.class, float.class, true, double.class); + return PainlessCast.unboxOriginalType(double.class, float.class, true, double.class); } else if (expected == double.class && internal) { - return PainlessCast.unboxFrom(double.class, double.class, explicit, double.class); + return PainlessCast.unboxOriginalType(double.class, double.class, explicit, double.class); } } @@ -463,7 +463,7 @@ public static PainlessCast getLegalCast(Location location, Class actual, Clas (actual != void.class && expected == def.class) || expected.isAssignableFrom(actual) || (actual.isAssignableFrom(expected) && explicit)) { - return PainlessCast.standard(actual, expected, explicit); + return PainlessCast.originalTypetoTargetType(actual, expected, explicit); } else { throw location.createError(new ClassCastException("Cannot cast from " + "[" + PainlessLookupUtility.typeToCanonicalTypeName(actual) + "] to " + @@ -472,8 +472,8 @@ public static PainlessCast getLegalCast(Location location, Class actual, Clas } public static Object constCast(Location location, Object constant, PainlessCast cast) { - Class fsort = cast.from; - Class tsort = cast.to; + Class fsort = cast.originalType; + Class tsort = cast.targetType; if (fsort == tsort) { return constant; @@ -499,11 +499,11 @@ public static Object constCast(Location location, Object constant, PainlessCast else if (tsort == double.class) return number.doubleValue(); else { throw location.createError(new IllegalStateException("Cannot cast from " + - "[" + cast.from.getCanonicalName() + "] to [" + cast.to.getCanonicalName() + "].")); + "[" + cast.originalType.getCanonicalName() + "] to [" + cast.targetType.getCanonicalName() + "].")); } } else { throw location.createError(new IllegalStateException("Cannot cast from " + - "[" + cast.from.getCanonicalName() + "] to [" + cast.to.getCanonicalName() + "].")); + "[" + cast.originalType.getCanonicalName() + "] to [" + cast.targetType.getCanonicalName() + "].")); } } 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 c8e38467801bd..b872ba36d41cc 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 @@ -135,52 +135,52 @@ public void writeLoopCounter(int slot, int count, Location location) { public void writeCast(PainlessCast cast) { if (cast != null) { - if (cast.from == char.class && cast.to == String.class) { + if (cast.originalType == char.class && cast.targetType == String.class) { invokeStatic(UTILITY_TYPE, CHAR_TO_STRING); - } else if (cast.from == String.class && cast.to == char.class) { + } else if (cast.originalType == String.class && cast.targetType == char.class) { invokeStatic(UTILITY_TYPE, STRING_TO_CHAR); - } else if (cast.unboxFrom != null) { - unbox(getType(cast.unboxFrom)); - writeCast(cast.from, cast.to); - } else if (cast.unboxTo != null) { - if (cast.from == def.class) { - if (cast.explicit) { - if (cast.to == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN); - else if (cast.to == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_EXPLICIT); - else if (cast.to == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_EXPLICIT); - else if (cast.to == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_EXPLICIT); - else if (cast.to == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_EXPLICIT); - else if (cast.to == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_EXPLICIT); - else if (cast.to == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_EXPLICIT); - else if (cast.to == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_EXPLICIT); + } else if (cast.unboxOriginalType != null) { + unbox(getType(cast.unboxOriginalType)); + writeCast(cast.originalType, cast.targetType); + } else if (cast.unboxTargetType != null) { + if (cast.originalType == def.class) { + if (cast.explicitCast) { + if (cast.targetType == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN); + else if (cast.targetType == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_EXPLICIT); + else if (cast.targetType == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_EXPLICIT); + else if (cast.targetType == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_EXPLICIT); + else if (cast.targetType == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_EXPLICIT); + else if (cast.targetType == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_EXPLICIT); + else if (cast.targetType == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_EXPLICIT); + else if (cast.targetType == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_EXPLICIT); else { throw new IllegalStateException("Illegal tree structure."); } } else { - if (cast.to == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN); - else if (cast.to == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_IMPLICIT); - else if (cast.to == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_IMPLICIT); - else if (cast.to == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_IMPLICIT); - else if (cast.to == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_IMPLICIT); - else if (cast.to == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_IMPLICIT); - else if (cast.to == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_IMPLICIT); - else if (cast.to == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_IMPLICIT); + if (cast.targetType == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN); + else if (cast.targetType == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_IMPLICIT); + else if (cast.targetType == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_IMPLICIT); + else if (cast.targetType == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_IMPLICIT); + else if (cast.targetType == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_IMPLICIT); + else if (cast.targetType == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_IMPLICIT); + else if (cast.targetType == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_IMPLICIT); + else if (cast.targetType == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_IMPLICIT); else { throw new IllegalStateException("Illegal tree structure."); } } } else { - writeCast(cast.from, cast.to); - unbox(getType(cast.unboxTo)); + writeCast(cast.originalType, cast.targetType); + unbox(getType(cast.unboxTargetType)); } - } else if (cast.boxFrom != null) { - box(getType(cast.boxFrom)); - writeCast(cast.from, cast.to); - } else if (cast.boxTo != null) { - writeCast(cast.from, cast.to); - box(getType(cast.boxTo)); + } else if (cast.boxOriginalType != null) { + box(getType(cast.boxOriginalType)); + writeCast(cast.originalType, cast.targetType); + } else if (cast.boxTargetType != null) { + writeCast(cast.originalType, cast.targetType); + box(getType(cast.boxTargetType)); } else { - writeCast(cast.from, cast.to); + writeCast(cast.originalType, cast.targetType); } } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessCast.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessCast.java index 2440fb45d4dfb..f87f8a134b8c4 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessCast.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessCast.java @@ -22,46 +22,55 @@ public class PainlessCast { /** Create a standard cast with no boxing/unboxing. */ - public static PainlessCast standard(Class from, Class to, boolean explicit) { - return new PainlessCast(from, to, explicit, null, null, null, null); + public static PainlessCast originalTypetoTargetType(Class originalType, Class targetType, boolean explicitCast) { + return new PainlessCast(originalType, targetType, explicitCast, null, null, null, null); } - /** Create a cast where the from type will be unboxed, and then the cast will be performed. */ - public static PainlessCast unboxFrom(Class from, Class to, boolean explicit, Class unboxFrom) { - return new PainlessCast(from, to, explicit, unboxFrom, null, null, null); + /** Create a cast where the original type will be unboxed, and then the cast will be performed. */ + public static PainlessCast unboxOriginalType( + Class originalType, Class targetType, boolean explicitCast, Class unboxOriginalType) { + + return new PainlessCast(originalType, targetType, explicitCast, unboxOriginalType, null, null, null); } - /** Create a cast where the to type will be unboxed, and then the cast will be performed. */ - public static PainlessCast unboxTo(Class from, Class to, boolean explicit, Class unboxTo) { - return new PainlessCast(from, to, explicit, null, unboxTo, null, null); + /** Create a cast where the target type will be unboxed, and then the cast will be performed. */ + public static PainlessCast unboxTargetType( + Class originalType, Class targetType, boolean explicitCast, Class unboxTargetType) { + + return new PainlessCast(originalType, targetType, explicitCast, null, unboxTargetType, null, null); } - /** Create a cast where the from type will be boxed, and then the cast will be performed. */ - public static PainlessCast boxFrom(Class from, Class to, boolean explicit, Class boxFrom) { - return new PainlessCast(from, to, explicit, null, null, boxFrom, null); + /** Create a cast where the original type will be boxed, and then the cast will be performed. */ + public static PainlessCast boxOriginalType( + Class originalType, Class targetType, boolean explicitCast, Class boxOriginalType) { + + return new PainlessCast(originalType, targetType, explicitCast, null, null, boxOriginalType, null); } - /** Create a cast where the to type will be boxed, and then the cast will be performed. */ - public static PainlessCast boxTo(Class from, Class to, boolean explicit, Class boxTo) { - return new PainlessCast(from, to, explicit, null, null, null, boxTo); + /** Create a cast where the target type will be boxed, and then the cast will be performed. */ + public static PainlessCast boxTargetType( + Class originalType, Class targetType, boolean explicitCast, Class boxTargetType) { + + return new PainlessCast(originalType, targetType, explicitCast, null, null, null, boxTargetType); } - public final Class from; - public final Class to; - public final boolean explicit; - public final Class unboxFrom; - public final Class unboxTo; - public final Class boxFrom; - public final Class boxTo; + public final Class originalType; + public final Class targetType; + public final boolean explicitCast; + public final Class unboxOriginalType; + public final Class unboxTargetType; + public final Class boxOriginalType; + public final Class boxTargetType; + + private PainlessCast(Class originalType, Class targetType, boolean explicitCast, + Class unboxOriginalType, Class unboxTargetType, Class boxOriginalType, Class boxTargetType) { - private PainlessCast(Class from, Class to, boolean explicit, - Class unboxFrom, Class unboxTo, Class boxFrom, Class boxTo) { - this.from = from; - this.to = to; - this.explicit = explicit; - this.unboxFrom = unboxFrom; - this.unboxTo = unboxTo; - this.boxFrom = boxFrom; - this.boxTo = boxTo; + this.originalType = originalType; + this.targetType = targetType; + this.explicitCast = explicitCast; + this.unboxOriginalType = unboxOriginalType; + this.unboxTargetType = unboxTargetType; + this.boxOriginalType = boxOriginalType; + this.boxTargetType = boxTargetType; } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECast.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECast.java index b07613714b8ef..08236a965fe52 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECast.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECast.java @@ -63,6 +63,6 @@ void write(MethodWriter writer, Globals globals) { @Override public String toString() { - return singleLineToString(PainlessLookupUtility.typeToCanonicalTypeName(cast.to), child); + return singleLineToString(PainlessLookupUtility.typeToCanonicalTypeName(cast.targetType), child); } } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/AnalyzerCasterTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/AnalyzerCasterTests.java index 34bc2c78de662..58864d73c4120 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/AnalyzerCasterTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/AnalyzerCasterTests.java @@ -35,8 +35,8 @@ private static void assertCast(Class actual, Class expected, boolean mustB } PainlessCast cast = AnalyzerCaster.getLegalCast(location, actual, expected, true, false); - assertEquals(actual, cast.from); - assertEquals(expected, cast.to); + assertEquals(actual, cast.originalType); + assertEquals(expected, cast.targetType); if (mustBeExplicit) { ClassCastException error = expectThrows(ClassCastException.class, @@ -44,8 +44,8 @@ private static void assertCast(Class actual, Class expected, boolean mustB assertTrue(error.getMessage().startsWith("Cannot cast")); } else { cast = AnalyzerCaster.getLegalCast(location, actual, expected, false, false); - assertEquals(actual, cast.from); - assertEquals(expected, cast.to); + assertEquals(actual, cast.originalType); + assertEquals(expected, cast.targetType); } } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java index f6ad38f997ed4..12d57fab11d98 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java @@ -162,12 +162,12 @@ public void testECapturingFunctionRef() { public void testECast() { Location l = new Location(getTestName(), 0); AExpression child = new EConstant(l, "test"); - PainlessCast cast = PainlessCast.standard(String.class, Integer.class, true); + PainlessCast cast = PainlessCast.originalTypetoTargetType(String.class, Integer.class, true); assertEquals("(ECast java.lang.Integer (EConstant String 'test'))", new ECast(l, child, cast).toString()); l = new Location(getTestName(), 1); child = new EBinary(l, Operation.ADD, new EConstant(l, "test"), new EConstant(l, 12)); - cast = PainlessCast.standard(Integer.class, Boolean.class, true); + cast = PainlessCast.originalTypetoTargetType(Integer.class, Boolean.class, true); assertEquals("(ECast java.lang.Boolean (EBinary (EConstant String 'test') + (EConstant Integer 12)))", new ECast(l, child, cast).toString()); } From 9d5f02fa403884ccda12cb8221a8f6c2fc6e783b Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Thu, 2 Aug 2018 08:50:41 -0700 Subject: [PATCH 26/33] Remove unnecessary sorted classes list. --- .../painless/lookup/PainlessLookup.java | 16 +++------------- .../painless/PainlessDocGenerator.java | 4 +++- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java index 5123f78cbf036..786248f726982 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java @@ -20,11 +20,9 @@ package org.elasticsearch.painless.lookup; import java.util.Collections; -import java.util.Comparator; -import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.stream.Collectors; +import java.util.Set; import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessConstructorKey; import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessFieldKey; @@ -36,20 +34,12 @@ public final class PainlessLookup { private final Map> canonicalClassNamesToClasses; private final Map, PainlessClass> classesToPainlessClasses; - private final List> sortedClassesByCanonicalClassName; - PainlessLookup(Map> canonicalClassNamesToClasses, Map, PainlessClass> classesToPainlessClasses) { Objects.requireNonNull(canonicalClassNamesToClasses); Objects.requireNonNull(classesToPainlessClasses); this.canonicalClassNamesToClasses = Collections.unmodifiableMap(canonicalClassNamesToClasses); this.classesToPainlessClasses = Collections.unmodifiableMap(classesToPainlessClasses); - - this.sortedClassesByCanonicalClassName = Collections.unmodifiableList( - classesToPainlessClasses.keySet().stream().sorted( - Comparator.comparing(Class::getCanonicalName) - ).collect(Collectors.toList()) - ); } public boolean isValidCanonicalClassName(String canonicalClassName) { @@ -64,8 +54,8 @@ public Class canonicalTypeNameToType(String painlessType) { return PainlessLookupUtility.canonicalTypeNameToType(painlessType, canonicalClassNamesToClasses); } - public List> getSortedClassesByCanonicalClassName() { - return sortedClassesByCanonicalClassName; + public Set> getClasses() { + return classesToPainlessClasses.keySet(); } public PainlessClass lookupPainlessClass(Class targetClass) { 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 02c28b955fc35..1460d5f2359b6 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 @@ -45,6 +45,7 @@ import java.util.Map; import java.util.TreeMap; import java.util.function.Consumer; +import java.util.stream.Collectors; import static java.util.Comparator.comparing; @@ -73,7 +74,8 @@ public static void main(String[] args) throws IOException { Files.newOutputStream(indexPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE), false, StandardCharsets.UTF_8.name())) { emitGeneratedWarning(indexStream); - List> classes = PAINLESS_LOOKUP.getSortedClassesByCanonicalClassName(); + List> classes = PAINLESS_LOOKUP.getClasses().stream().sorted( + Comparator.comparing(Class::getCanonicalName)).collect(Collectors.toList()); for (Class clazz : classes) { PainlessClass struct = PAINLESS_LOOKUP.lookupPainlessClass(clazz); String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(clazz); From d7d029236227438c552b7a1e6735e1e37bd1d579 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Thu, 2 Aug 2018 12:38:11 -0700 Subject: [PATCH 27/33] Remove extraneous line. --- .../org/elasticsearch/painless/lookup/PainlessLookupBuilder.java | 1 - 1 file changed, 1 deletion(-) 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 a0cab7f1a5bda..799650c2c5da7 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 @@ -570,7 +570,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; if (augmentedClass == null) { From aac6d88b64a27aa4d598538e757b69585fc7cf85 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Thu, 2 Aug 2018 12:51:32 -0700 Subject: [PATCH 28/33] Change Object to Map. --- .../java/org/elasticsearch/painless/Compiler.java | 4 +++- .../java/org/elasticsearch/painless/DefBootstrap.java | 11 ++++------- .../org/elasticsearch/painless/WriterConstants.java | 2 +- .../java/org/elasticsearch/painless/node/SSource.java | 5 +++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index 857f2f3f4bea8..97dddbdfe52c5 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -20,6 +20,7 @@ package org.elasticsearch.painless; import org.elasticsearch.bootstrap.BootstrapInfo; +import org.elasticsearch.painless.Locals.LocalMethod; import org.elasticsearch.painless.antlr.Walker; import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.node.SSource; @@ -32,6 +33,7 @@ import java.security.CodeSource; import java.security.SecureClassLoader; import java.security.cert.Certificate; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import static org.elasticsearch.painless.WriterConstants.CLASS_NAME; @@ -200,7 +202,7 @@ Constructor compile(Loader loader, MainMethodReserved reserved, String name, ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, scriptClass); SSource root = Walker.buildPainlessTree(scriptClassInfo, reserved, name, source, settings, painlessLookup, null); - Object localMethods = root.analyze(painlessLookup); + Map localMethods = root.analyze(painlessLookup); root.write(); try { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java index 96ea64b8f5fa1..2488b6f218a7f 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java @@ -437,7 +437,7 @@ static boolean checkBoth(Class left, Class right, Object leftObject, Objec * see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic */ @SuppressWarnings("unchecked") - public static CallSite bootstrap(PainlessLookup painlessLookup, Object localMethods, + public static CallSite bootstrap(PainlessLookup painlessLookup, Map localMethods, MethodHandles.Lookup methodHandlesLookup, String name, MethodType type, int initialDepth, int flavor, Object... args) { // validate arguments switch(flavor) { @@ -457,8 +457,7 @@ public static CallSite bootstrap(PainlessLookup painlessLookup, Object localMeth if (args.length != numLambdas + 1) { throw new BootstrapMethodError("Illegal number of parameters: expected " + numLambdas + " references"); } - return new PIC(painlessLookup, (Map)localMethods, - methodHandlesLookup, name, type, initialDepth, flavor, args); + return new PIC(painlessLookup, localMethods, methodHandlesLookup, name, type, initialDepth, flavor, args); case LOAD: case STORE: case ARRAY_LOAD: @@ -468,8 +467,7 @@ public static CallSite bootstrap(PainlessLookup painlessLookup, Object localMeth if (args.length > 0) { throw new BootstrapMethodError("Illegal static bootstrap parameters for flavor: " + flavor); } - return new PIC(painlessLookup, (Map)localMethods, - methodHandlesLookup, name, type, initialDepth, flavor, args); + return new PIC(painlessLookup, localMethods, methodHandlesLookup, name, type, initialDepth, flavor, args); case REFERENCE: if (args.length != 1) { throw new BootstrapMethodError("Invalid number of parameters for reference call"); @@ -477,8 +475,7 @@ public static CallSite bootstrap(PainlessLookup painlessLookup, Object localMeth if (args[0] instanceof String == false) { throw new BootstrapMethodError("Illegal parameter for reference call: " + args[0]); } - return new PIC(painlessLookup, (Map)localMethods, - methodHandlesLookup, name, type, initialDepth, flavor, args); + return new PIC(painlessLookup, localMethods, methodHandlesLookup, name, type, initialDepth, flavor, args); // operators get monomorphic cache, with a generic impl for a fallback case UNARY_OPERATOR: diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java index 72ec57de8013c..9c3d991080d26 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java @@ -120,7 +120,7 @@ public final class WriterConstants { DEF_BOOTSTRAP_METHOD.getDescriptor(), false); public static final Type DEF_BOOTSTRAP_DELEGATE_TYPE = Type.getType(DefBootstrap.class); public static final Method DEF_BOOTSTRAP_DELEGATE_METHOD = getAsmMethod(CallSite.class, "bootstrap", PainlessLookup.class, - Object.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class); + Map.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class); public static final Type DEF_UTIL_TYPE = Type.getType(Def.class); public static final Method DEF_TO_BOOLEAN = getAsmMethod(boolean.class, "DefToboolean" , Object.class); 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 6b18609bfc996..d194fb236767e 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 @@ -69,6 +69,7 @@ import static org.elasticsearch.painless.WriterConstants.GET_NAME_METHOD; import static org.elasticsearch.painless.WriterConstants.GET_SOURCE_METHOD; import static org.elasticsearch.painless.WriterConstants.GET_STATEMENTS_METHOD; +import static org.elasticsearch.painless.WriterConstants.MAP_TYPE; import static org.elasticsearch.painless.WriterConstants.OBJECT_TYPE; import static org.elasticsearch.painless.WriterConstants.OUT_OF_MEMORY_ERROR_TYPE; import static org.elasticsearch.painless.WriterConstants.PAINLESS_ERROR_TYPE; @@ -257,7 +258,7 @@ public void write() { globals.getStatements(), settings); bootstrapDef.visitCode(); bootstrapDef.getStatic(CLASS_TYPE, "$DEFINITION", DEFINITION_TYPE); - bootstrapDef.getStatic(CLASS_TYPE, "$LOCALS", OBJECT_TYPE); + bootstrapDef.getStatic(CLASS_TYPE, "$LOCALS", MAP_TYPE); bootstrapDef.loadArgs(); bootstrapDef.invokeStatic(DEF_BOOTSTRAP_DELEGATE_TYPE, DEF_BOOTSTRAP_DELEGATE_METHOD); bootstrapDef.returnValue(); @@ -270,7 +271,7 @@ public void write() { // Write the static variables used by the method to bootstrap def calls visitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$DEFINITION", DEFINITION_TYPE.getDescriptor(), null, null).visitEnd(); - visitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$LOCALS", OBJECT_TYPE.getDescriptor(), null, null).visitEnd(); + visitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$LOCALS", MAP_TYPE.getDescriptor(), null, null).visitEnd(); org.objectweb.asm.commons.Method init; From 4051638cb27e851f38d93ca23dccdf372916d7fd Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Thu, 2 Aug 2018 13:01:31 -0700 Subject: [PATCH 29/33] Fix import. --- .../src/main/java/org/elasticsearch/painless/node/SSource.java | 1 - 1 file changed, 1 deletion(-) 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 d194fb236767e..0f7445a38c44c 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 @@ -70,7 +70,6 @@ import static org.elasticsearch.painless.WriterConstants.GET_SOURCE_METHOD; import static org.elasticsearch.painless.WriterConstants.GET_STATEMENTS_METHOD; import static org.elasticsearch.painless.WriterConstants.MAP_TYPE; -import static org.elasticsearch.painless.WriterConstants.OBJECT_TYPE; import static org.elasticsearch.painless.WriterConstants.OUT_OF_MEMORY_ERROR_TYPE; import static org.elasticsearch.painless.WriterConstants.PAINLESS_ERROR_TYPE; import static org.elasticsearch.painless.WriterConstants.PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD; From 0b8ebdf93a378267c7a228bb317e71ec7d5e28dc Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 7 Aug 2018 08:45:37 -0700 Subject: [PATCH 30/33] Fix formatting. --- .../java/org/elasticsearch/painless/Def.java | 18 +- .../painless/FunctionReference.java | 243 ------------------ .../elasticsearch/painless/MethodWriter.java | 18 +- .../painless/node/ECapturingFunctionRef.java | 6 +- .../painless/node/EFunctionRef.java | 6 +- .../elasticsearch/painless/node/ELambda.java | 6 +- 6 files changed, 27 insertions(+), 270 deletions(-) delete mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java 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 587106e8628f1..4a819ccc0e58d 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 @@ -331,14 +331,14 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, Map localMethods, MethodHandles.Lookup methodHandlesLookup, String interfaceClass, Class receiverClass, String name) throws Throwable { - Class interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass); - PainlessMethod interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(interfaceType); - if (interfaceMethod == null) { - throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface"); - } - int arity = interfaceMethod.typeParameters.size(); - PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity); - return lookupReferenceInternal(painlessLookup, localMethods, methodHandlesLookup, + Class interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass); + PainlessMethod interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(interfaceType); + if (interfaceMethod == null) { + throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface"); \ + } + int arity = interfaceMethod.typeParameters.size(); + PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity); + return lookupReferenceInternal(painlessLookup, localMethods, methodHandlesLookup, interfaceType, PainlessLookupUtility.typeToCanonicalTypeName(implMethod.targetClass), implMethod.javaMethod.getName(), 1); } @@ -346,7 +346,7 @@ static MethodHandle lookupReference(PainlessLookup painlessLookup, Map localMethods, MethodHandles.Lookup methodHandlesLookup, Class clazz, String type, String call, int captures) throws Throwable { - final FunctionReference ref = FunctionReference.create(painlessLookup, localMethods, null, clazz, type, call, captures); + final FunctionRef ref = FunctionRef.create(painlessLookup, localMethods, null, clazz, type, call, captures); final CallSite callSite = LambdaBootstrap.lambdaBootstrap( methodHandlesLookup, ref.interfaceMethodName, diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java deleted file mode 100644 index 64d08a9763f4d..0000000000000 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionReference.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.elasticsearch.painless; - -import org.elasticsearch.painless.Locals.LocalMethod; -import org.elasticsearch.painless.lookup.PainlessConstructor; -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.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import static org.elasticsearch.painless.WriterConstants.CLASS_NAME; -import static org.objectweb.asm.Opcodes.H_INVOKEINTERFACE; -import static org.objectweb.asm.Opcodes.H_INVOKESTATIC; -import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL; -import static org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL; - -public class FunctionReference { - - /** - * Creates a new FunctionReference which will resolve {@code type::call} from the whitelist. - * @param painlessLookup the whitelist against which this script is being compiled - * @param localMethods user-defined and synthetic methods generated directly on the script class - * @param location the character number within the script at compile-time - * @param targetClass functional interface type to implement. - * @param typeName the left hand side of a method reference expression - * @param methodName the right hand side of a method reference expression - * @param numberOfCaptures number of captured arguments - */ - public static FunctionReference create(PainlessLookup painlessLookup, Map localMethods, Location location, - Class targetClass, String typeName, String methodName, int numberOfCaptures) { - - Objects.requireNonNull(painlessLookup); - Objects.requireNonNull(targetClass); - Objects.requireNonNull(typeName); - Objects.requireNonNull(methodName); - - String targetClassName = PainlessLookupUtility.typeToCanonicalTypeName(targetClass); - PainlessMethod interfaceMethod; - - try { - try { - interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(targetClass); - } catch (IllegalArgumentException iae) { - throw new IllegalArgumentException("cannot convert function reference [" + typeName + "::" + methodName + "] " + - "to a non-functional interface [" + targetClassName + "]", iae); - } - - String interfaceMethodName = interfaceMethod.javaMethod.getName(); - MethodType interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1); - String delegateClassName; - boolean isDelegateInterface; - int delegateInvokeType; - String delegateMethodName; - MethodType delegateMethodType; - - Class delegateMethodReturnType; - List> delegateMethodParameters; - int interfaceTypeParametersSize = interfaceMethod.typeParameters.size(); - - if ("this".equals(typeName)) { - Objects.requireNonNull(localMethods); - - if (numberOfCaptures < 0) { - throw new IllegalStateException("internal error"); - } - - String localMethodKey = Locals.buildLocalMethodKey(methodName, numberOfCaptures + interfaceTypeParametersSize); - LocalMethod localMethod = localMethods.get(localMethodKey); - - if (localMethod == null) { - throw new IllegalArgumentException("function reference [this::" + localMethodKey + "] " + - "matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " + - "not found" + (localMethodKey.contains("$") ? " due to an incorrect number of arguments" : "") - ); - } - - delegateClassName = CLASS_NAME; - isDelegateInterface = false; - delegateInvokeType = H_INVOKESTATIC; - delegateMethodName = localMethod.name; - delegateMethodType = localMethod.methodType; - - delegateMethodReturnType = localMethod.returnType; - delegateMethodParameters = localMethod.typeParameters; - } else if ("new".equals(methodName)) { - if (numberOfCaptures != 0) { - throw new IllegalStateException("internal error"); - } - - PainlessConstructor painlessConstructor; - - try { - painlessConstructor = painlessLookup.lookupPainlessConstructor(typeName, interfaceTypeParametersSize); - } catch (IllegalArgumentException iae) { - throw new IllegalArgumentException("function reference [" + typeName + "::new/" + interfaceTypeParametersSize + "] " + - "matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " + - "not found", iae); - } - - delegateClassName = painlessConstructor.javaConstructor.getDeclaringClass().getName(); - isDelegateInterface = false; - delegateInvokeType = H_NEWINVOKESPECIAL; - delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME; - delegateMethodType = painlessConstructor.methodType; - - delegateMethodReturnType = painlessConstructor.javaConstructor.getDeclaringClass(); - delegateMethodParameters = painlessConstructor.typeParameters; - } else { - if (numberOfCaptures != 0 && numberOfCaptures != 1) { - throw new IllegalStateException("internal error"); - } - - boolean captured = numberOfCaptures == 1; - PainlessMethod painlessMethod; - - try { - painlessMethod = painlessLookup.lookupPainlessMethod(typeName, true, methodName, interfaceTypeParametersSize); - - if (captured) { - throw new IllegalStateException("internal error"); - } - } catch (IllegalArgumentException staticIAE) { - try { - painlessMethod = painlessLookup.lookupPainlessMethod(typeName, false, methodName, - captured ? interfaceTypeParametersSize : interfaceTypeParametersSize - 1); - } catch (IllegalArgumentException iae) { - throw new IllegalArgumentException( - "function reference " + "[" + typeName + "::" + methodName + "/" + interfaceTypeParametersSize + "] " + - "matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " + - "not found", iae); - } - } - - delegateClassName = painlessMethod.javaMethod.getDeclaringClass().getName(); - isDelegateInterface = painlessMethod.javaMethod.getDeclaringClass().isInterface(); - - if (Modifier.isStatic(painlessMethod.javaMethod.getModifiers())) { - delegateInvokeType = H_INVOKESTATIC; - } else if (isDelegateInterface) { - delegateInvokeType = H_INVOKEINTERFACE; - } else { - delegateInvokeType = H_INVOKEVIRTUAL; - } - - delegateMethodName = painlessMethod.javaMethod.getName(); - delegateMethodType = painlessMethod.methodType; - - delegateMethodReturnType = painlessMethod.returnType; - - if (delegateMethodType.parameterList().size() > painlessMethod.typeParameters.size()) { - delegateMethodParameters = new ArrayList<>(painlessMethod.typeParameters); - delegateMethodParameters.add(0, delegateMethodType.parameterType(0)); - } else { - delegateMethodParameters = painlessMethod.typeParameters; - } - } - - if (location != null) { - for (int typeParameter = 0; typeParameter < interfaceTypeParametersSize; ++typeParameter) { - Class from = interfaceMethod.typeParameters.get(typeParameter); - Class to = delegateMethodParameters.get(numberOfCaptures + typeParameter); - AnalyzerCaster.getLegalCast(location, from, to, false, true); - } - - if (interfaceMethod.returnType != void.class) { - AnalyzerCaster.getLegalCast(location, delegateMethodReturnType, interfaceMethod.returnType, false, true); - } - } - - MethodType factoryMethodType = MethodType.methodType(targetClass, - delegateMethodType.dropParameterTypes(numberOfCaptures, delegateMethodType.parameterCount())); - delegateMethodType = delegateMethodType.dropParameterTypes(0, numberOfCaptures); - - return new FunctionReference(interfaceMethodName, interfaceMethodType, - delegateClassName, isDelegateInterface, delegateInvokeType, delegateMethodName, delegateMethodType, - factoryMethodType - ); - } catch (IllegalArgumentException iae) { - if (location != null) { - throw location.createError(iae); - } - - throw iae; - } - } - - /** functional interface method name */ - public final String interfaceMethodName; - /** functional interface method signature */ - public final MethodType interfaceMethodType; - /** class of the delegate method to be called */ - public final String delegateClassName; - /** whether a call is made on a delegate interface */ - public final boolean isDelegateInterface; - /** the invocation type of the delegate method */ - public final int delegateInvokeType; - /** the name of the delegate method */ - public final String delegateMethodName; - /** delegate method signature */ - public final MethodType delegateMethodType; - /** factory (CallSite) method signature */ - public final MethodType factoryMethodType; - - public FunctionReference( - String interfaceMethodName, MethodType interfaceMethodType, - String delegateClassName, boolean isDelegateInterface, - int delegateInvokeType, String delegateMethodName, MethodType delegateMethodType, - MethodType factoryMethodType) { - - this.interfaceMethodName = interfaceMethodName; - this.interfaceMethodType = interfaceMethodType; - this.delegateClassName = delegateClassName; - this.isDelegateInterface = isDelegateInterface; - this.delegateInvokeType = delegateInvokeType; - this.delegateMethodName = delegateMethodName; - this.delegateMethodType = delegateMethodType; - this.factoryMethodType = factoryMethodType; - } -} 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 c8e38467801bd..df5f7966c3583 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 @@ -441,17 +441,17 @@ public void invokeMethodCall(PainlessMethod painlessMethod) { } } - public void invokeLambdaCall(FunctionReference functionReference) { + public void invokeLambdaCall(FunctionRef functionRef) { invokeDynamic( - functionReference.interfaceMethodName, - functionReference.factoryMethodType.toMethodDescriptorString(), + functionRef.interfaceMethodName, + functionRef.factoryMethodType.toMethodDescriptorString(), LAMBDA_BOOTSTRAP_HANDLE, - Type.getMethodType(functionReference.interfaceMethodType.toMethodDescriptorString()), - functionReference.delegateClassName, - functionReference.delegateInvokeType, - functionReference.delegateMethodName, - Type.getMethodType(functionReference.delegateMethodType.toMethodDescriptorString()), - functionReference.isDelegateInterface ? 1 : 0 + Type.getMethodType(functionRef.interfaceMethodType.toMethodDescriptorString()), + functionRef.delegateClassName, + functionRef.delegateInvokeType, + functionRef.delegateMethodName, + Type.getMethodType(functionRef.delegateMethodType.toMethodDescriptorString()), + functionRef.isDelegateInterface ? 1 : 0 ); } } 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 5c1a9380ce109..a649fa7611c65 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 @@ -20,7 +20,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.FunctionReference; +import org.elasticsearch.painless.FunctionRef; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Variable; @@ -41,7 +41,7 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda private final String variable; private final String call; - private FunctionReference ref; + private FunctionRef ref; private Variable captured; private String defPointer; @@ -73,7 +73,7 @@ void analyze(Locals locals) { defPointer = null; // static case if (captured.clazz != def.class) { - ref = FunctionReference.create(locals.getPainlessLookup(), locals.getMethods(), location, + ref = FunctionRef.create(locals.getPainlessLookup(), locals.getMethods(), location, expected, PainlessLookupUtility.typeToCanonicalTypeName(captured.clazz), call, 1); } actual = expected; 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 8710707ac2a81..c97cc66c7c7ca 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 @@ -19,7 +19,7 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.FunctionReference; +import org.elasticsearch.painless.FunctionRef; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -36,7 +36,7 @@ public final class EFunctionRef extends AExpression implements ILambda { private final String type; private final String call; - private FunctionReference ref; + private FunctionRef ref; private String defPointer; public EFunctionRef(Location location, String type, String call) { @@ -57,7 +57,7 @@ void analyze(Locals locals) { defPointer = "S" + type + "." + call + ",0"; } else { defPointer = null; - ref = FunctionReference.create(locals.getPainlessLookup(), locals.getMethods(), location, expected, type, call, 0); + ref = FunctionRef.create(locals.getPainlessLookup(), locals.getMethods(), location, expected, type, call, 0); actual = expected; } } 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 ead1d8d08fdb0..af906416ca7bc 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 @@ -19,7 +19,7 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.FunctionReference; +import org.elasticsearch.painless.FunctionRef; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Variable; @@ -74,7 +74,7 @@ public final class ELambda extends AExpression implements ILambda { // captured variables private List captures; // static parent, static lambda - private FunctionReference ref; + private FunctionRef ref; // dynamic parent, deferred until link time private String defPointer; @@ -180,7 +180,7 @@ void analyze(Locals locals) { defPointer = "Sthis." + name + "," + captures.size(); } else { defPointer = null; - ref = FunctionReference.create( + ref = FunctionRef.create( locals.getPainlessLookup(), locals.getMethods(), location, expected, "this", desugared.name, captures.size()); actual = expected; } From b7812d6b881c55848982e96c676aa990cc7a86cd Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 7 Aug 2018 08:47:31 -0700 Subject: [PATCH 31/33] Add FunctionRef back. --- .../elasticsearch/painless/FunctionRef.java | 248 ++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java 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 new file mode 100644 index 0000000000000..065f63dc3f5a4 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java @@ -0,0 +1,248 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.painless; + +import org.elasticsearch.painless.Locals.LocalMethod; +import org.elasticsearch.painless.lookup.PainlessConstructor; +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.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.elasticsearch.painless.WriterConstants.CLASS_NAME; +import static org.objectweb.asm.Opcodes.H_INVOKEINTERFACE; +import static org.objectweb.asm.Opcodes.H_INVOKESTATIC; +import static org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL; + +/** + * Contains all the values necessary to write the instruction to initiate a + * {@link LambdaBootstrap} for either a function reference or a user-defined + * lambda function. + */ +public class FunctionRef { + + /** + * Creates a new FunctionRef which will resolve {@code type::call} from the whitelist. + * @param painlessLookup the whitelist against which this script is being compiled + * @param localMethods user-defined and synthetic methods generated directly on the script class + * @param location the character number within the script at compile-time + * @param targetClass functional interface type to implement. + * @param typeName the left hand side of a method reference expression + * @param methodName the right hand side of a method reference expression + * @param numberOfCaptures number of captured arguments + */ + public static FunctionRef create(PainlessLookup painlessLookup, Map localMethods, Location location, + Class targetClass, String typeName, String methodName, int numberOfCaptures) { + + Objects.requireNonNull(painlessLookup); + Objects.requireNonNull(targetClass); + Objects.requireNonNull(typeName); + Objects.requireNonNull(methodName); + + String targetClassName = PainlessLookupUtility.typeToCanonicalTypeName(targetClass); + PainlessMethod interfaceMethod; + + try { + try { + interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(targetClass); + } catch (IllegalArgumentException iae) { + throw new IllegalArgumentException("cannot convert function reference [" + typeName + "::" + methodName + "] " + + "to a non-functional interface [" + targetClassName + "]", iae); + } + + String interfaceMethodName = interfaceMethod.javaMethod.getName(); + MethodType interfaceMethodType = interfaceMethod.methodType.dropParameterTypes(0, 1); + String delegateClassName; + boolean isDelegateInterface; + int delegateInvokeType; + String delegateMethodName; + MethodType delegateMethodType; + + Class delegateMethodReturnType; + List> delegateMethodParameters; + int interfaceTypeParametersSize = interfaceMethod.typeParameters.size(); + + if ("this".equals(typeName)) { + Objects.requireNonNull(localMethods); + + if (numberOfCaptures < 0) { + throw new IllegalStateException("internal error"); + } + + String localMethodKey = Locals.buildLocalMethodKey(methodName, numberOfCaptures + interfaceTypeParametersSize); + LocalMethod localMethod = localMethods.get(localMethodKey); + + if (localMethod == null) { + throw new IllegalArgumentException("function reference [this::" + localMethodKey + "] " + + "matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " + + "not found" + (localMethodKey.contains("$") ? " due to an incorrect number of arguments" : "") + ); + } + + delegateClassName = CLASS_NAME; + isDelegateInterface = false; + delegateInvokeType = H_INVOKESTATIC; + delegateMethodName = localMethod.name; + delegateMethodType = localMethod.methodType; + + delegateMethodReturnType = localMethod.returnType; + delegateMethodParameters = localMethod.typeParameters; + } else if ("new".equals(methodName)) { + if (numberOfCaptures != 0) { + throw new IllegalStateException("internal error"); + } + + PainlessConstructor painlessConstructor; + + try { + painlessConstructor = painlessLookup.lookupPainlessConstructor(typeName, interfaceTypeParametersSize); + } catch (IllegalArgumentException iae) { + throw new IllegalArgumentException("function reference [" + typeName + "::new/" + interfaceTypeParametersSize + "] " + + "matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " + + "not found", iae); + } + + delegateClassName = painlessConstructor.javaConstructor.getDeclaringClass().getName(); + isDelegateInterface = false; + delegateInvokeType = H_NEWINVOKESPECIAL; + delegateMethodName = PainlessLookupUtility.CONSTRUCTOR_NAME; + delegateMethodType = painlessConstructor.methodType; + + delegateMethodReturnType = painlessConstructor.javaConstructor.getDeclaringClass(); + delegateMethodParameters = painlessConstructor.typeParameters; + } else { + if (numberOfCaptures != 0 && numberOfCaptures != 1) { + throw new IllegalStateException("internal error"); + } + + boolean captured = numberOfCaptures == 1; + PainlessMethod painlessMethod; + + try { + painlessMethod = painlessLookup.lookupPainlessMethod(typeName, true, methodName, interfaceTypeParametersSize); + + if (captured) { + throw new IllegalStateException("internal error"); + } + } catch (IllegalArgumentException staticIAE) { + try { + painlessMethod = painlessLookup.lookupPainlessMethod(typeName, false, methodName, + captured ? interfaceTypeParametersSize : interfaceTypeParametersSize - 1); + } catch (IllegalArgumentException iae) { + throw new IllegalArgumentException( + "function reference " + "[" + typeName + "::" + methodName + "/" + interfaceTypeParametersSize + "] " + + "matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " + + "not found", iae); + } + } + + delegateClassName = painlessMethod.javaMethod.getDeclaringClass().getName(); + isDelegateInterface = painlessMethod.javaMethod.getDeclaringClass().isInterface(); + + if (Modifier.isStatic(painlessMethod.javaMethod.getModifiers())) { + delegateInvokeType = H_INVOKESTATIC; + } else if (isDelegateInterface) { + delegateInvokeType = H_INVOKEINTERFACE; + } else { + delegateInvokeType = H_INVOKEVIRTUAL; + } + + delegateMethodName = painlessMethod.javaMethod.getName(); + delegateMethodType = painlessMethod.methodType; + + delegateMethodReturnType = painlessMethod.returnType; + + if (delegateMethodType.parameterList().size() > painlessMethod.typeParameters.size()) { + delegateMethodParameters = new ArrayList<>(painlessMethod.typeParameters); + delegateMethodParameters.add(0, delegateMethodType.parameterType(0)); + } else { + delegateMethodParameters = painlessMethod.typeParameters; + } + } + + if (location != null) { + for (int typeParameter = 0; typeParameter < interfaceTypeParametersSize; ++typeParameter) { + Class from = interfaceMethod.typeParameters.get(typeParameter); + Class to = delegateMethodParameters.get(numberOfCaptures + typeParameter); + AnalyzerCaster.getLegalCast(location, from, to, false, true); + } + + if (interfaceMethod.returnType != void.class) { + AnalyzerCaster.getLegalCast(location, delegateMethodReturnType, interfaceMethod.returnType, false, true); + } + } + + MethodType factoryMethodType = MethodType.methodType(targetClass, + delegateMethodType.dropParameterTypes(numberOfCaptures, delegateMethodType.parameterCount())); + delegateMethodType = delegateMethodType.dropParameterTypes(0, numberOfCaptures); + + return new FunctionRef(interfaceMethodName, interfaceMethodType, + delegateClassName, isDelegateInterface, delegateInvokeType, delegateMethodName, delegateMethodType, + factoryMethodType + ); + } catch (IllegalArgumentException iae) { + if (location != null) { + throw location.createError(iae); + } + + throw iae; + } + } + + /** functional interface method name */ + public final String interfaceMethodName; + /** functional interface method signature */ + public final MethodType interfaceMethodType; + /** class of the delegate method to be called */ + public final String delegateClassName; + /** whether a call is made on a delegate interface */ + public final boolean isDelegateInterface; + /** the invocation type of the delegate method */ + public final int delegateInvokeType; + /** the name of the delegate method */ + public final String delegateMethodName; + /** delegate method signature */ + public final MethodType delegateMethodType; + /** factory (CallSite) method signature */ + public final MethodType factoryMethodType; + + private FunctionRef( + String interfaceMethodName, MethodType interfaceMethodType, + String delegateClassName, boolean isDelegateInterface, + int delegateInvokeType, String delegateMethodName, MethodType delegateMethodType, + MethodType factoryMethodType) { + + this.interfaceMethodName = interfaceMethodName; + this.interfaceMethodType = interfaceMethodType; + this.delegateClassName = delegateClassName; + this.isDelegateInterface = isDelegateInterface; + this.delegateInvokeType = delegateInvokeType; + this.delegateMethodName = delegateMethodName; + this.delegateMethodType = delegateMethodType; + this.factoryMethodType = factoryMethodType; + } +} From c7ebe54920277e8503381e84d65a0b1d16c21354 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Tue, 7 Aug 2018 09:08:13 -0700 Subject: [PATCH 32/33] Remove extraneous character. --- .../src/main/java/org/elasticsearch/painless/Def.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4a819ccc0e58d..4752c2b268573 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 @@ -334,7 +334,7 @@ static MethodHandle lookupReference(PainlessLookup painlessLookup, Map interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass); PainlessMethod interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(interfaceType); if (interfaceMethod == null) { - throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface"); \ + throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface"); } int arity = interfaceMethod.typeParameters.size(); PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity); From 804f52982a2201c8e4ba1f2cb2639a6ec12b2a1e Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Wed, 8 Aug 2018 13:31:28 -0700 Subject: [PATCH 33/33] Return null instead of throwing exceptions for all methods in PainlessLookup. --- .../java/org/elasticsearch/painless/Def.java | 119 +++++++++++------- .../elasticsearch/painless/FunctionRef.java | 37 +++--- .../painless/lookup/PainlessLookup.java | 63 ++++------ .../lookup/PainlessLookupBuilder.java | 68 +++++----- .../lookup/PainlessLookupUtility.java | 77 +++++------- .../painless/node/EExplicit.java | 6 +- .../painless/node/EInstanceof.java | 7 +- .../painless/node/EListInit.java | 19 +-- .../elasticsearch/painless/node/EMapInit.java | 19 +-- .../painless/node/ENewArray.java | 10 +- .../elasticsearch/painless/node/ENewObj.java | 17 +-- .../elasticsearch/painless/node/EStatic.java | 6 +- .../painless/node/PCallInvoke.java | 16 ++- .../elasticsearch/painless/node/PField.java | 37 +++--- .../painless/node/PSubListShortcut.java | 8 +- .../painless/node/PSubMapShortcut.java | 8 +- .../elasticsearch/painless/node/SCatch.java | 6 +- .../painless/node/SDeclaration.java | 6 +- .../elasticsearch/painless/node/SEach.java | 6 +- .../painless/node/SFunction.java | 16 +-- .../painless/node/SSubEachIterable.java | 10 +- 21 files changed, 274 insertions(+), 287 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index 6ae814fa76ec4..1e17d6024d4d1 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 @@ -37,6 +37,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName; + /** * Support for dynamic type (def). *

@@ -194,7 +196,14 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, Map localMethods, MethodHandles.Lookup methodHandlesLookup, String interfaceClass, Class receiverClass, String name) throws Throwable { Class interfaceType = painlessLookup.canonicalTypeNameToType(interfaceClass); + if (interfaceType == null) { + throw new IllegalArgumentException("type [" + interfaceClass + "] not found"); + } PainlessMethod interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(interfaceType); if (interfaceMethod == null) { - throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface"); + throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface"); } int arity = interfaceMethod.typeParameters.size(); PainlessMethod implMethod = painlessLookup.lookupRuntimePainlessMethod(receiverClass, name, arity); + if (implMethod == null) { + throw new IllegalArgumentException( + "dynamic method [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "/" + arity + "] not found"); + } + return lookupReferenceInternal(painlessLookup, localMethods, methodHandlesLookup, interfaceType, PainlessLookupUtility.typeToCanonicalTypeName(implMethod.targetClass), implMethod.javaMethod.getName(), 1); @@ -342,31 +365,34 @@ private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLooku */ static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class receiverClass, String name) { // first try whitelist - try { - return painlessLookup.lookupRuntimeGetterMethodHandle(receiverClass, name); - } catch (IllegalArgumentException iae) { - // special case: arrays, maps, and lists - if (receiverClass.isArray() && "length".equals(name)) { - // arrays expose .length as a read-only getter - return arrayLengthGetter(receiverClass); - } else if (Map.class.isAssignableFrom(receiverClass)) { - // maps allow access like mymap.key - // wire 'key' as a parameter, its a constant in painless - return MethodHandles.insertArguments(MAP_GET, 1, name); - } else if (List.class.isAssignableFrom(receiverClass)) { - // lists allow access like mylist.0 - // wire '0' (index) as a parameter, its a constant. this also avoids - // parsing the same integer millions of times! - try { - int index = Integer.parseInt(name); - return MethodHandles.insertArguments(LIST_GET, 1, index); - } catch (NumberFormatException exception) { - throw new IllegalArgumentException("Illegal list shortcut value [" + name + "]."); - } - } + MethodHandle getter = painlessLookup.lookupRuntimeGetterMethodHandle(receiverClass, name); - throw iae; + if (getter != null) { + return getter; } + + // special case: arrays, maps, and lists + if (receiverClass.isArray() && "length".equals(name)) { + // arrays expose .length as a read-only getter + return arrayLengthGetter(receiverClass); + } else if (Map.class.isAssignableFrom(receiverClass)) { + // maps allow access like mymap.key + // wire 'key' as a parameter, its a constant in painless + return MethodHandles.insertArguments(MAP_GET, 1, name); + } else if (List.class.isAssignableFrom(receiverClass)) { + // lists allow access like mylist.0 + // wire '0' (index) as a parameter, its a constant. this also avoids + // parsing the same integer millions of times! + try { + int index = Integer.parseInt(name); + return MethodHandles.insertArguments(LIST_GET, 1, index); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException("Illegal list shortcut value [" + name + "]."); + } + } + + throw new IllegalArgumentException( + "dynamic getter [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "] not found"); } /** @@ -395,28 +421,31 @@ static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class receive */ static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class receiverClass, String name) { // first try whitelist - try { - return painlessLookup.lookupRuntimeSetterMethodHandle(receiverClass, name); - } catch (IllegalArgumentException iae) { - // special case: maps, and lists - if (Map.class.isAssignableFrom(receiverClass)) { - // maps allow access like mymap.key - // wire 'key' as a parameter, its a constant in painless - return MethodHandles.insertArguments(MAP_PUT, 1, name); - } else if (List.class.isAssignableFrom(receiverClass)) { - // lists allow access like mylist.0 - // wire '0' (index) as a parameter, its a constant. this also avoids - // parsing the same integer millions of times! - try { - int index = Integer.parseInt(name); - return MethodHandles.insertArguments(LIST_SET, 1, index); - } catch (final NumberFormatException exception) { - throw new IllegalArgumentException("Illegal list shortcut value [" + name + "]."); - } - } + MethodHandle setter = painlessLookup.lookupRuntimeSetterMethodHandle(receiverClass, name); - throw iae; + if (setter != null) { + return setter; } + + // special case: maps, and lists + if (Map.class.isAssignableFrom(receiverClass)) { + // maps allow access like mymap.key + // wire 'key' as a parameter, its a constant in painless + return MethodHandles.insertArguments(MAP_PUT, 1, name); + } else if (List.class.isAssignableFrom(receiverClass)) { + // lists allow access like mylist.0 + // wire '0' (index) as a parameter, its a constant. this also avoids + // parsing the same integer millions of times! + try { + int index = Integer.parseInt(name); + return MethodHandles.insertArguments(LIST_SET, 1, index); + } catch (final NumberFormatException exception) { + throw new IllegalArgumentException("Illegal list shortcut value [" + name + "]."); + } + } + + throw new IllegalArgumentException( + "dynamic getter [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "] not found"); } /** 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 065f63dc3f5a4..2580d7da3e8e7 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 @@ -67,11 +67,11 @@ public static FunctionRef create(PainlessLookup painlessLookup, Map targetClass = canonicalTypeNameToType(targetClassName); + if (targetClass == null) { + return null; + } + return lookupPainlessConstructor(targetClass, constructorArity); } @@ -80,15 +83,13 @@ public PainlessConstructor lookupPainlessConstructor(Class targetClass, int c String painlessConstructorKey = buildPainlessConstructorKey(constructorArity); if (targetPainlessClass == null) { - throw new IllegalArgumentException("target class [" + typeToCanonicalTypeName(targetClass) + "] " + - "not found for constructor [" + painlessConstructorKey + "]"); + return null; } PainlessConstructor painlessConstructor = targetPainlessClass.constructors.get(painlessConstructorKey); if (painlessConstructor == null) { - throw new IllegalArgumentException( - "constructor [" + typeToCanonicalTypeName(targetClass) + ", " + painlessConstructorKey + "] not found"); + return null; } return painlessConstructor; @@ -99,6 +100,10 @@ public PainlessMethod lookupPainlessMethod(String targetClassName, boolean isSta Class targetClass = canonicalTypeNameToType(targetClassName); + if (targetClass == null) { + return null; + } + return lookupPainlessMethod(targetClass, isStatic, methodName, methodArity); } @@ -114,20 +119,12 @@ public PainlessMethod lookupPainlessMethod(Class targetClass, boolean isStati String painlessMethodKey = buildPainlessMethodKey(methodName, methodArity); if (targetPainlessClass == null) { - throw new IllegalArgumentException( - "target class [" + typeToCanonicalTypeName(targetClass) + "] not found for method [" + painlessMethodKey + "]"); + return null; } - PainlessMethod painlessMethod = isStatic ? + return isStatic ? targetPainlessClass.staticMethods.get(painlessMethodKey) : targetPainlessClass.methods.get(painlessMethodKey); - - if (painlessMethod == null) { - throw new IllegalArgumentException( - "method [" + typeToCanonicalTypeName(targetClass) + ", " + painlessMethodKey + "] not found"); - } - - return painlessMethod; } public PainlessField lookupPainlessField(String targetClassName, boolean isStatic, String fieldName) { @@ -135,6 +132,10 @@ public PainlessField lookupPainlessField(String targetClassName, boolean isStati Class targetClass = canonicalTypeNameToType(targetClassName); + if (targetClass == null) { + return null; + } + return lookupPainlessField(targetClass, isStatic, fieldName); } @@ -146,8 +147,7 @@ public PainlessField lookupPainlessField(Class targetClass, boolean isStatic, String painlessFieldKey = buildPainlessFieldKey(fieldName); if (targetPainlessClass == null) { - throw new IllegalArgumentException( - "target class [" + typeToCanonicalTypeName(targetClass) + "] not found for field [" + painlessFieldKey + "]"); + return null; } PainlessField painlessField = isStatic ? @@ -155,8 +155,7 @@ public PainlessField lookupPainlessField(Class targetClass, boolean isStatic, targetPainlessClass.fields.get(painlessFieldKey); if (painlessField == null) { - throw new IllegalArgumentException( - "field [" + typeToCanonicalTypeName(targetClass) + ", " + painlessFieldKey + "] not found"); + return null; } return painlessField; @@ -166,16 +165,10 @@ public PainlessMethod lookupFunctionalInterfacePainlessMethod(Class targetCla PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass); if (targetPainlessClass == null) { - throw new IllegalArgumentException("target class [" + typeToCanonicalTypeName(targetClass) + "] not found"); - } - - PainlessMethod functionalInterfacePainlessMethod = targetPainlessClass.functionalInterfaceMethod; - - if (functionalInterfacePainlessMethod == null) { - throw new IllegalArgumentException("target class [" + typeToCanonicalTypeName(targetClass) + "] is not a functional interface"); + return null; } - return functionalInterfacePainlessMethod; + return targetPainlessClass.functionalInterfaceMethod; } public PainlessMethod lookupRuntimePainlessMethod(Class originalTargetClass, String methodName, int methodArity) { @@ -184,10 +177,8 @@ public PainlessMethod lookupRuntimePainlessMethod(Class originalTargetClass, String painlessMethodKey = buildPainlessMethodKey(methodName, methodArity); Function objectLookup = targetPainlessClass -> targetPainlessClass.methods.get(painlessMethodKey); - String notFoundErrorMessage = - "dynamic method [" + typeToCanonicalTypeName(originalTargetClass) + ", " + painlessMethodKey + "] not found"; - return lookupRuntimePainlessObject(originalTargetClass, objectLookup, notFoundErrorMessage); + return lookupRuntimePainlessObject(originalTargetClass, objectLookup); } public MethodHandle lookupRuntimeGetterMethodHandle(Class originalTargetClass, String getterName) { @@ -195,9 +186,8 @@ public MethodHandle lookupRuntimeGetterMethodHandle(Class originalTargetClass Objects.requireNonNull(getterName); Function objectLookup = targetPainlessClass -> targetPainlessClass.getterMethodHandles.get(getterName); - String notFoundErrorMessage = "dynamic getter [" + typeToCanonicalTypeName(originalTargetClass) + ", " + getterName + "] not found"; - return lookupRuntimePainlessObject(originalTargetClass, objectLookup, notFoundErrorMessage); + return lookupRuntimePainlessObject(originalTargetClass, objectLookup); } public MethodHandle lookupRuntimeSetterMethodHandle(Class originalTargetClass, String setterName) { @@ -205,13 +195,12 @@ public MethodHandle lookupRuntimeSetterMethodHandle(Class originalTargetClass Objects.requireNonNull(setterName); Function objectLookup = targetPainlessClass -> targetPainlessClass.setterMethodHandles.get(setterName); - String notFoundErrorMessage = "dynamic setter [" + typeToCanonicalTypeName(originalTargetClass) + ", " + setterName + "] not found"; - return lookupRuntimePainlessObject(originalTargetClass, objectLookup, notFoundErrorMessage); + return lookupRuntimePainlessObject(originalTargetClass, objectLookup); } private T lookupRuntimePainlessObject( - Class originalTargetClass, Function objectLookup, String notFoundErrorMessage) { + Class originalTargetClass, Function objectLookup) { Class currentTargetClass = originalTargetClass; @@ -247,6 +236,6 @@ private T lookupRuntimePainlessObject( currentTargetClass = currentTargetClass.getSuperclass(); } - throw new IllegalArgumentException(notFoundErrorMessage); + return null; } } 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 45a5e188db331..e17a01941bc97 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 @@ -220,8 +220,12 @@ private Class canonicalTypeNameToType(String canonicalTypeName) { return PainlessLookupUtility.canonicalTypeNameToType(canonicalTypeName, canonicalClassNamesToClasses); } - private void validateType(Class type) { - PainlessLookupUtility.validateType(type, classesToPainlessClassBuilders.keySet()); + private boolean isValidType(Class type) { + while (type.getComponentType() != null) { + type = type.getComponentType(); + } + + return classesToPainlessClassBuilders.containsKey(type); } public void addPainlessClass(ClassLoader classLoader, String javaClassName, boolean importClassName) { @@ -325,13 +329,14 @@ public void addPainlessConstructor(String targetCanonicalClassName, List List> typeParameters = new ArrayList<>(typeNameParameters.size()); for (String typeNameParameter : typeNameParameters) { - try { - Class typeParameter = canonicalTypeNameToType(typeNameParameter); - typeParameters.add(typeParameter); - } catch (IllegalArgumentException iae) { + Class typeParameter = canonicalTypeNameToType(typeNameParameter); + + if (typeParameter == null) { throw new IllegalArgumentException("type parameter [" + typeNameParameter + "] not found " + - "for constructor [[" + targetCanonicalClassName + "], " + typeNameParameters + "]", iae); + "for constructor [[" + targetCanonicalClassName + "], " + typeNameParameters + "]"); } + + typeParameters.add(typeParameter); } addPainlessConstructor(targetClass, typeParameters); @@ -357,11 +362,9 @@ public void addPainlessConstructor(Class targetClass, List> typePara List> javaTypeParameters = new ArrayList<>(typeParametersSize); for (Class typeParameter : typeParameters) { - try { - validateType(typeParameter); - } catch (IllegalArgumentException iae) { + if (isValidType(typeParameter) == false) { throw new IllegalArgumentException("type parameter [" + typeToCanonicalTypeName(typeParameter) + "] not found " + - "for constructor [[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(typeParameters) + "]", iae); + "for constructor [[" + targetCanonicalClassName + "], " + typesToCanonicalTypeNames(typeParameters) + "]"); } javaTypeParameters.add(typeToJavaType(typeParameter)); @@ -435,22 +438,21 @@ public void addPainlessMethod(ClassLoader classLoader, String targetCanonicalCla List> typeParameters = new ArrayList<>(typeNameParameters.size()); for (String typeNameParameter : typeNameParameters) { - try { - Class typeParameter = canonicalTypeNameToType(typeNameParameter); - typeParameters.add(typeParameter); - } catch (IllegalArgumentException iae) { + Class typeParameter = canonicalTypeNameToType(typeNameParameter); + + if (typeParameter == null) { throw new IllegalArgumentException("parameter type [" + typeNameParameter + "] not found for method " + - "[[" + targetCanonicalClassName + "], [" + methodName + "], " + typeNameParameters + "]", iae); + "[[" + targetCanonicalClassName + "], [" + methodName + "], " + typeNameParameters + "]"); } + + typeParameters.add(typeParameter); } - Class returnType; + Class returnType = canonicalTypeNameToType(returnCanonicalTypeName); - try { - returnType = canonicalTypeNameToType(returnCanonicalTypeName); - } catch (IllegalArgumentException iae) { + if (returnType == null) { throw new IllegalArgumentException("parameter type [" + returnCanonicalTypeName + "] not found for method " + - "[[" + targetCanonicalClassName + "], [" + methodName + "], " + typeNameParameters + "]", iae); + "[[" + targetCanonicalClassName + "], [" + methodName + "], " + typeNameParameters + "]"); } addPainlessMethod(targetClass, augmentedClass, methodName, returnType, typeParameters); @@ -490,22 +492,18 @@ public void addPainlessMethod(Class targetClass, Class augmentedClass, Str } for (Class typeParameter : typeParameters) { - try { - validateType(typeParameter); - } catch (IllegalArgumentException iae) { + if (isValidType(typeParameter) == false) { throw new IllegalArgumentException("type parameter [" + typeToCanonicalTypeName(typeParameter) + "] " + "not found for method [[" + targetCanonicalClassName + "], [" + methodName + "], " + - typesToCanonicalTypeNames(typeParameters) + "]", iae); + typesToCanonicalTypeNames(typeParameters) + "]"); } javaTypeParameters.add(typeToJavaType(typeParameter)); } - try { - validateType(returnType); - } catch (IllegalArgumentException iae) { + if (isValidType(returnType) == false) { throw new IllegalArgumentException("return type [" + typeToCanonicalTypeName(returnType) + "] not found for method " + - "[[" + targetCanonicalClassName + "], [" + methodName + "], " + typesToCanonicalTypeNames(typeParameters) + "]", iae); + "[[" + targetCanonicalClassName + "], [" + methodName + "], " + typesToCanonicalTypeNames(typeParameters) + "]"); } Method javaMethod; @@ -620,11 +618,9 @@ public void addPainlessField(String targetCanonicalClassName, String fieldName, throw new IllegalArgumentException("class [" + targetCanonicalClassName + "] not found"); } - Class typeParameter; + Class typeParameter = canonicalTypeNameToType(typeNameParameter); - try { - typeParameter = canonicalTypeNameToType(typeNameParameter); - } catch (IllegalArgumentException iae) { + if (typeParameter == null) { throw new IllegalArgumentException("type parameter [" + typeNameParameter + "] not found " + "for field [[" + targetCanonicalClassName + "], [" + fieldName + "]"); } @@ -656,11 +652,9 @@ public void addPainlessField(Class targetClass, String fieldName, Class ty throw new IllegalArgumentException("class [" + targetCanonicalClassName + "] not found"); } - try { - validateType(typeParameter); - } catch (IllegalArgumentException iae) { + if (isValidType(typeParameter) == false) { throw new IllegalArgumentException("type parameter [" + typeToCanonicalTypeName(typeParameter) + "] not found " + - "for field [[" + targetCanonicalClassName + "], [" + fieldName + "]", iae); + "for field [[" + targetCanonicalClassName + "], [" + fieldName + "]"); } Field javaField; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java index 3aefe8d9694cd..f2eb434516961 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java @@ -20,7 +20,6 @@ package org.elasticsearch.painless.lookup; import java.util.Arrays; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; @@ -101,45 +100,47 @@ public static Class canonicalTypeNameToType(String canonicalTypeName, Map typeToJavaType(Class type) { return type; } - /** - * Ensures a type exists based on the terminology specified as part of {@link PainlessLookupUtility}. Throws an - * {@link IllegalArgumentException} if the type does not exist. - */ - public static void validateType(Class type, Collection> classes) { - String canonicalTypeName = typeToCanonicalTypeName(type); - - while (type.getComponentType() != null) { - type = type.getComponentType(); - } - - if (classes.contains(type) == false) { - throw new IllegalArgumentException("type [" + canonicalTypeName + "] not found"); - } - } - /** * Converts a type to its boxed type equivalent if one exists based on the terminology specified as part of * {@link PainlessLookupUtility}. Otherwise, this behaves as an identity function. diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java index c58d51e45cb4d..3ad3018c61e34 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java @@ -49,9 +49,9 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - try { - actual = locals.getPainlessLookup().canonicalTypeNameToType(type); - } catch (IllegalArgumentException exception) { + actual = locals.getPainlessLookup().canonicalTypeNameToType(type); + + if (actual == null) { throw createError(new IllegalArgumentException("Not a type [" + type + "].")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java index 8585b7fc0bb54..73e4f176ea1ba 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java @@ -54,12 +54,11 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - Class clazz; // ensure the specified type is part of the definition - try { - clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); - } catch (IllegalArgumentException exception) { + Class clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); + + if (clazz == null) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } 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 bd931558b620d..8c9154aaaf304 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 @@ -33,6 +33,8 @@ import java.util.List; import java.util.Set; +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName; + /** * Represents a list initialization shortcut. */ @@ -63,16 +65,17 @@ void analyze(Locals locals) { actual = ArrayList.class; - try { - constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, 0); - } catch (IllegalArgumentException iae) { - throw createError(iae); + constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, 0); + + if (constructor == null) { + throw createError(new IllegalArgumentException( + "constructor [" + typeToCanonicalTypeName(actual) + ", /0] not found")); } - try { - method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "add", 1); - } catch (IllegalArgumentException iae) { - throw createError(iae); + method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "add", 1); + + if (method == null) { + throw createError(new IllegalArgumentException("method [" + typeToCanonicalTypeName(actual) + ", add/1] not found")); } for (int index = 0; index < values.size(); ++index) { 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 91332672c0510..11c12b2cd0a96 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 @@ -33,6 +33,8 @@ import java.util.List; import java.util.Set; +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName; + /** * Represents a map initialization shortcut. */ @@ -69,16 +71,17 @@ void analyze(Locals locals) { actual = HashMap.class; - try { - constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, 0); - } catch (IllegalArgumentException iae) { - throw createError(iae); + constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, 0); + + if (constructor == null) { + throw createError(new IllegalArgumentException( + "constructor [" + typeToCanonicalTypeName(actual) + ", /0] not found")); } - try { - method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "put", 2); - } catch (IllegalArgumentException iae) { - throw createError(iae); + method = locals.getPainlessLookup().lookupPainlessMethod(actual, false, "put", 2); + + if (method == null) { + throw createError(new IllegalArgumentException("method [" + typeToCanonicalTypeName(actual) + ", put/2] not found")); } if (keys.size() != values.size()) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java index e0a49ebd6158e..cef005de9c3bc 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java @@ -54,15 +54,13 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - if (!read) { - throw createError(new IllegalArgumentException("A newly created array must be read from.")); + if (!read) { + throw createError(new IllegalArgumentException("A newly created array must be read from.")); } - Class clazz; + Class clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); - try { - clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); - } catch (IllegalArgumentException exception) { + if (clazz == null) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java index 55ba60feb3e77..9423ed5d109de 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java @@ -32,6 +32,8 @@ import java.util.Objects; import java.util.Set; +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName; + /** * Represents and object instantiation. */ @@ -58,16 +60,17 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - try { - actual = locals.getPainlessLookup().canonicalTypeNameToType(this.type); - } catch (IllegalArgumentException exception) { + actual = locals.getPainlessLookup().canonicalTypeNameToType(this.type); + + if (actual == null) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } - try { - constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, arguments.size()); - } catch (IllegalArgumentException iae) { - throw createError(iae); + constructor = locals.getPainlessLookup().lookupPainlessConstructor(actual, arguments.size()); + + if (constructor == null) { + throw createError(new IllegalArgumentException( + "constructor [" + typeToCanonicalTypeName(actual) + ", /" + arguments.size() + "] not found")); } Class[] types = new Class[constructor.typeParameters.size()]; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java index e5909d93e9dc2..0d8c94db0f1fc 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java @@ -47,9 +47,9 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - try { - actual = locals.getPainlessLookup().canonicalTypeNameToType(type); - } catch (IllegalArgumentException exception) { + actual = locals.getPainlessLookup().canonicalTypeNameToType(type); + + if (actual == null) { throw createError(new IllegalArgumentException("Not a type [" + type + "].")); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java index 9406b4ca41127..25ae1ed97742a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java @@ -30,6 +30,8 @@ import java.util.Objects; import java.util.Set; +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName; + /** * Represents a method call and defers to a child subnode. */ @@ -67,13 +69,15 @@ void analyze(Locals locals) { if (prefix.actual == def.class) { sub = new PSubDefCall(location, name, arguments); } else { - try { - PainlessMethod method = - locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, prefix instanceof EStatic, name, arguments.size()); - sub = new PSubCallInvoke(location, method, prefix.actual, arguments); - } catch (IllegalArgumentException iae) { - throw createError(iae); + PainlessMethod method = + locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, prefix instanceof EStatic, name, arguments.size()); + + if (method == null) { + throw createError(new IllegalArgumentException( + "method [" + typeToCanonicalTypeName(prefix.actual) + ", " + name + "/" + arguments.size() + "] not found")); } + + sub = new PSubCallInvoke(location, method, prefix.actual, arguments); } if (nullSafe) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java index 59cbfd405b7fd..7efd6a29899c4 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java @@ -23,6 +23,7 @@ import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; +import org.elasticsearch.painless.lookup.PainlessField; import org.elasticsearch.painless.lookup.PainlessLookupUtility; import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.lookup.def; @@ -32,6 +33,8 @@ import java.util.Objects; import java.util.Set; +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName; + /** * Represents a field load/store and defers to a child subnode. */ @@ -65,31 +68,22 @@ void analyze(Locals locals) { } else if (prefix.actual == def.class) { sub = new PSubDefField(location, value); } else { - try { - sub = new PSubField(location, - locals.getPainlessLookup().lookupPainlessField(prefix.actual, prefix instanceof EStatic, value)); - } catch (IllegalArgumentException fieldIAE) { + PainlessField field = locals.getPainlessLookup().lookupPainlessField(prefix.actual, prefix instanceof EStatic, value); + + if (field == null) { PainlessMethod getter; PainlessMethod setter; - try { + getter = locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, false, + "get" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0); + + if (getter == null) { getter = locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, false, - "get" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0); - } catch (IllegalArgumentException getIAE) { - try { - getter = locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, false, - "is" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0); - } catch (IllegalArgumentException isIAE) { - getter = null; - } + "is" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0); } - try { - setter = locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, false, - "set" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0); - } catch (IllegalArgumentException setIAE) { - setter = null; - } + setter = locals.getPainlessLookup().lookupPainlessMethod(prefix.actual, false, + "set" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0); if (getter != null || setter != null) { sub = new PSubShortcut(location, value, PainlessLookupUtility.typeToCanonicalTypeName(prefix.actual), getter, setter); @@ -107,8 +101,11 @@ void analyze(Locals locals) { } if (sub == null) { - throw createError(fieldIAE); + throw createError(new IllegalArgumentException( + "field [" + typeToCanonicalTypeName(prefix.actual) + ", " + value + "] not found")); } + } else { + sub = new PSubField(location, field); } } 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 838756fcc67b4..3bc4913fde940 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 @@ -57,12 +57,8 @@ void extractVariables(Set variables) { void analyze(Locals locals) { String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(targetClass); - try { - getter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "get", 1); - setter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "set", 2); - } catch (IllegalArgumentException iae) { - throw createError(iae); - } + getter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "get", 1); + setter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "set", 2); if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1 || getter.typeParameters.get(0) != int.class)) { 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 27a3f69775aa9..0a0f099bd6841 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 @@ -56,12 +56,8 @@ void extractVariables(Set variables) { void analyze(Locals locals) { String canonicalClassName = PainlessLookupUtility.typeToCanonicalTypeName(targetClass); - try { - getter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "get", 1); - setter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "put", 2); - } catch (IllegalArgumentException iae) { - throw createError(iae); - } + getter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "get", 1); + setter = locals.getPainlessLookup().lookupPainlessMethod(targetClass, false, "put", 2); if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1)) { throw createError(new IllegalArgumentException("Illegal map get shortcut for type [" + canonicalClassName + "].")); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java index 04b0462b53383..0c8ba5de6b2cf 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java @@ -64,11 +64,9 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - Class clazz; + Class clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); - try { - clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); - } catch (IllegalArgumentException exception) { + if (clazz == null) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java index f3774885cfd58..7ead673c70b7a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java @@ -59,11 +59,9 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - Class clazz; + Class clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); - try { - clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); - } catch (IllegalArgumentException exception) { + if (clazz == null) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java index a83f501df3292..cf41105c4fe36 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java @@ -68,11 +68,9 @@ void analyze(Locals locals) { expression.expected = expression.actual; expression = expression.cast(locals); - Class clazz; + Class clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); - try { - clazz = locals.getPainlessLookup().canonicalTypeNameToType(this.type); - } catch (IllegalArgumentException exception) { + if (clazz == null) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } 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 4a844c7bc30ef..6fe09627f9dfd 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 @@ -115,9 +115,9 @@ void extractVariables(Set variables) { } void generateSignature(PainlessLookup painlessLookup) { - try { - returnType = painlessLookup.canonicalTypeNameToType(rtnTypeStr); - } catch (IllegalArgumentException exception) { + returnType = painlessLookup.canonicalTypeNameToType(rtnTypeStr); + + if (returnType == null) { throw createError(new IllegalArgumentException("Illegal return type [" + rtnTypeStr + "] for function [" + name + "].")); } @@ -129,16 +129,16 @@ void generateSignature(PainlessLookup painlessLookup) { List> paramTypes = new ArrayList<>(); for (int param = 0; param < this.paramTypeStrs.size(); ++param) { - try { Class paramType = painlessLookup.canonicalTypeNameToType(this.paramTypeStrs.get(param)); - paramClasses[param] = PainlessLookupUtility.typeToJavaType(paramType); - paramTypes.add(paramType); - parameters.add(new Parameter(location, paramNameStrs.get(param), paramType)); - } catch (IllegalArgumentException exception) { + if (paramType == null) { throw createError(new IllegalArgumentException( "Illegal parameter type [" + this.paramTypeStrs.get(param) + "] for function [" + name + "].")); } + + paramClasses[param] = PainlessLookupUtility.typeToJavaType(paramType); + paramTypes.add(paramType); + parameters.add(new Parameter(location, paramNameStrs.get(param), paramType)); } typeParameters = paramTypes; 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 577d1d51d09b0..46dfa056874f2 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 @@ -40,6 +40,7 @@ import static org.elasticsearch.painless.WriterConstants.ITERATOR_HASNEXT; import static org.elasticsearch.painless.WriterConstants.ITERATOR_NEXT; import static org.elasticsearch.painless.WriterConstants.ITERATOR_TYPE; +import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName; /** * Represents a for-each loop for iterables. @@ -76,10 +77,11 @@ void analyze(Locals locals) { if (expression.actual == def.class) { method = null; } else { - try { - method = locals.getPainlessLookup().lookupPainlessMethod(expression.actual, false, "iterator", 0); - } catch (IllegalArgumentException iae) { - throw createError(iae); + method = locals.getPainlessLookup().lookupPainlessMethod(expression.actual, false, "iterator", 0); + + if (method == null) { + throw createError(new IllegalArgumentException( + "method [" + typeToCanonicalTypeName(expression.actual) + ", iterator/0] not found")); } }