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 4752c2b268573..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 @@ -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; @@ -38,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). *

@@ -167,52 +168,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 +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"); } int arity = interfaceMethod.typeParameters.size(); - PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity); + 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); + interfaceType, PainlessLookupUtility.typeToCanonicalTypeName(implMethod.targetClass), + implMethod.javaMethod.getName(), 1); } /** Returns a method handle to an implementation of clazz, given method reference signature. */ @@ -389,27 +365,12 @@ 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; - } - } - - for (final Class iface : clazz.getInterfaces()) { - struct = painlessLookup.lookupPainlessClass(iface); + MethodHandle getter = painlessLookup.lookupRuntimeGetterMethodHandle(receiverClass, name); - if (struct != null) { - MethodHandle handle = struct.getterMethodHandles.get(name); - if (handle != null) { - return handle; - } - } - } + 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 @@ -426,12 +387,12 @@ static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class receive 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("Illegal list shortcut value [" + name + "]."); } } - throw new IllegalArgumentException("Unable to find dynamic field [" + name + "] " + - "for class [" + receiverClass.getCanonicalName() + "]."); + throw new IllegalArgumentException( + "dynamic getter [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "] not found"); } /** @@ -460,27 +421,12 @@ 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; - } - } - - for (final Class iface : clazz.getInterfaces()) { - struct = painlessLookup.lookupPainlessClass(iface); + MethodHandle setter = painlessLookup.lookupRuntimeSetterMethodHandle(receiverClass, name); - if (struct != null) { - MethodHandle handle = struct.setterMethodHandles.get(name); - if (handle != null) { - return handle; - } - } - } + if (setter != null) { + return setter; } + // special case: maps, and lists if (Map.class.isAssignableFrom(receiverClass)) { // maps allow access like mymap.key @@ -494,12 +440,12 @@ static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class receive 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("Illegal list shortcut value [" + name + "]."); } } - throw new IllegalArgumentException("Unable to find dynamic field [" + name + "] " + - "for class [" + receiverClass.getCanonicalName() + "]."); + 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); } @@ -77,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; @@ -96,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); } @@ -104,27 +112,19 @@ public PainlessMethod lookupPainlessMethod(Class targetClass, boolean isStati Objects.requireNonNull(methodName); if (targetClass.isPrimitive()) { - targetClass = PainlessLookupUtility.typeToBoxedType(targetClass); + targetClass = 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 + "]"); + 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) { @@ -132,6 +132,10 @@ public PainlessField lookupPainlessField(String targetClassName, boolean isStati Class targetClass = canonicalTypeNameToType(targetClassName); + if (targetClass == null) { + return null; + } + return lookupPainlessField(targetClass, isStatic, fieldName); } @@ -143,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 ? @@ -152,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; @@ -163,15 +165,77 @@ public PainlessMethod lookupFunctionalInterfacePainlessMethod(Class targetCla PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass); if (targetPainlessClass == null) { - throw new IllegalArgumentException("target class [" + typeToCanonicalTypeName(targetClass) + "] not found"); + return null; } - PainlessMethod functionalInterfacePainlessMethod = targetPainlessClass.functionalInterfaceMethod; + return targetPainlessClass.functionalInterfaceMethod; + } + + public PainlessMethod lookupRuntimePainlessMethod(Class originalTargetClass, String methodName, int methodArity) { + Objects.requireNonNull(originalTargetClass); + Objects.requireNonNull(methodName); + + String painlessMethodKey = buildPainlessMethodKey(methodName, methodArity); + Function objectLookup = targetPainlessClass -> targetPainlessClass.methods.get(painlessMethodKey); + + return lookupRuntimePainlessObject(originalTargetClass, objectLookup); + } + + public MethodHandle lookupRuntimeGetterMethodHandle(Class originalTargetClass, String getterName) { + Objects.requireNonNull(originalTargetClass); + Objects.requireNonNull(getterName); + + Function objectLookup = targetPainlessClass -> targetPainlessClass.getterMethodHandles.get(getterName); + + return lookupRuntimePainlessObject(originalTargetClass, objectLookup); + } + + public MethodHandle lookupRuntimeSetterMethodHandle(Class originalTargetClass, String setterName) { + Objects.requireNonNull(originalTargetClass); + Objects.requireNonNull(setterName); + + Function objectLookup = targetPainlessClass -> targetPainlessClass.setterMethodHandles.get(setterName); + + return lookupRuntimePainlessObject(originalTargetClass, objectLookup); + } + + private T lookupRuntimePainlessObject( + Class originalTargetClass, Function objectLookup) { + + Class currentTargetClass = originalTargetClass; + + while (currentTargetClass != null) { + PainlessClass targetPainlessClass = classesToPainlessClasses.get(currentTargetClass); + + if (targetPainlessClass != null) { + T painlessObject = objectLookup.apply(targetPainlessClass); + + if (painlessObject != null) { + return painlessObject; + } + } + + currentTargetClass = currentTargetClass.getSuperclass(); + } + + currentTargetClass = originalTargetClass; + + while (currentTargetClass != null) { + for (Class targetInterface : currentTargetClass.getInterfaces()) { + PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetInterface); + + if (targetPainlessClass != null) { + T painlessObject = objectLookup.apply(targetPainlessClass); + + if (painlessObject != null) { + return painlessObject; + } + } + } - if (functionalInterfacePainlessMethod == null) { - throw new IllegalArgumentException("target class [" + typeToCanonicalTypeName(targetClass) + "] is not a functional interface"); + currentTargetClass = currentTargetClass.getSuperclass(); } - return functionalInterfacePainlessMethod; + 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 0a181c5f1b02d..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 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); } @@ -252,22 +255,6 @@ public static Class 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. @@ -357,6 +344,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. */ 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")); } } 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 8a6aa2bbdc593..c4b85521e098f 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() {