Przeglądaj źródła

Painless: Move More Logic to PainlessLookup (#32689)

This moves some run-time lookups for methods and fields to the PainlessLookup.
Jack Conradson 7 lat temu
rodzic
commit
9b00f095b9
24 zmienionych plików z 316 dodań i 330 usunięć
  1. 42 96
      modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java
  2. 16 21
      modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java
  3. 89 25
      modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java
  4. 31 37
      modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupBuilder.java
  5. 39 47
      modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupUtility.java
  6. 3 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java
  7. 3 4
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java
  8. 11 8
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java
  9. 11 8
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java
  10. 4 6
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java
  11. 10 7
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java
  12. 3 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java
  13. 10 6
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java
  14. 17 20
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java
  15. 2 6
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubListShortcut.java
  16. 2 6
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubMapShortcut.java
  17. 2 4
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java
  18. 2 4
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java
  19. 2 4
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java
  20. 8 8
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java
  21. 6 4
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java
  22. 1 1
      modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java
  23. 1 1
      modules/lang-painless/src/test/java/org/elasticsearch/painless/OverloadTests.java
  24. 1 1
      modules/lang-painless/src/test/java/org/elasticsearch/painless/WhenThingsGoWrongTests.java

+ 42 - 96
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.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName;
+
 /**
  * Support for dynamic type (def).
  * <p>
@@ -167,52 +168,6 @@ public final class Def {
         }
     }
 
-    /**
-     * Looks up method entry for a dynamic method call.
-     * <p>
-     * A dynamic method call for variable {@code x} of type {@code def} looks like:
-     * {@code x.method(args...)}
-     * <p>
-     * 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.
-     * <p>
-     * @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
      * <p>
@@ -241,7 +196,14 @@ public final class Def {
          int numArguments = callSiteType.parameterCount();
          // simple case: no lambdas
          if (recipeString.isEmpty()) {
-             return lookupMethodInternal(painlessLookup, receiverClass, name, numArguments - 1).methodHandle;
+             PainlessMethod painlessMethod =  painlessLookup.lookupRuntimePainlessMethod(receiverClass, name, numArguments - 1);
+
+             if (painlessMethod == null) {
+                 throw new IllegalArgumentException("dynamic method " +
+                         "[" + typeToCanonicalTypeName(receiverClass) + ", " + name + "/" + (numArguments - 1) + "] not found");
+             }
+
+             return painlessMethod.methodHandle;
          }
 
          // convert recipe string to a bitset for convenience (the code below should be refactored...)
@@ -264,7 +226,13 @@ public final class Def {
 
          // lookup the method with the proper arity, then we know everything (e.g. interface types of parameters).
          // based on these we can finally link any remaining lambdas that were deferred.
-         PainlessMethod method = lookupMethodInternal(painlessLookup, receiverClass, name, arity);
+         PainlessMethod method = painlessLookup.lookupRuntimePainlessMethod(receiverClass, name, arity);
+
+        if (method == null) {
+            throw new IllegalArgumentException(
+                    "dynamic method [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "/" + arity + "] not found");
+        }
+
          MethodHandle handle = method.methodHandle;
 
          int replaced = 0;
@@ -332,15 +300,23 @@ public final class Def {
     static MethodHandle lookupReference(PainlessLookup painlessLookup, Map<String, LocalMethod> 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 @@ public final class Def {
      */
     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 @@ public final class Def {
                 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 @@ public final class Def {
      */
     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 @@ public final class Def {
                 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");
     }
 
     /**

+ 16 - 21
modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java

@@ -67,11 +67,11 @@ public class FunctionRef {
         PainlessMethod interfaceMethod;
 
         try {
-            try {
-                interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(targetClass);
-            } catch (IllegalArgumentException iae) {
+            interfaceMethod = painlessLookup.lookupFunctionalInterfacePainlessMethod(targetClass);
+
+            if (interfaceMethod == null) {
                 throw new IllegalArgumentException("cannot convert function reference [" + typeName + "::" + methodName + "] " +
-                        "to a non-functional interface [" + targetClassName + "]", iae);
+                        "to a non-functional interface [" + targetClassName + "]");
             }
 
             String interfaceMethodName = interfaceMethod.javaMethod.getName();
@@ -116,14 +116,12 @@ public class FunctionRef {
                     throw new IllegalStateException("internal error");
                 }
 
-                PainlessConstructor painlessConstructor;
+                PainlessConstructor painlessConstructor = painlessLookup.lookupPainlessConstructor(typeName, interfaceTypeParametersSize);
 
-                try {
-                    painlessConstructor = painlessLookup.lookupPainlessConstructor(typeName, interfaceTypeParametersSize);
-                } catch (IllegalArgumentException iae) {
+                if (painlessConstructor == null) {
                     throw new IllegalArgumentException("function reference [" + typeName + "::new/" + interfaceTypeParametersSize + "] " +
                             "matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " +
-                            "not found", iae);
+                            "not found");
                 }
 
                 delegateClassName = painlessConstructor.javaConstructor.getDeclaringClass().getName();
@@ -140,24 +138,21 @@ public class FunctionRef {
                 }
 
                 boolean captured = numberOfCaptures == 1;
-                PainlessMethod painlessMethod;
+                PainlessMethod painlessMethod =
+                        painlessLookup.lookupPainlessMethod(typeName, true, methodName, interfaceTypeParametersSize);
 
-                try {
-                    painlessMethod = painlessLookup.lookupPainlessMethod(typeName, true, methodName, interfaceTypeParametersSize);
+                if (painlessMethod == null) {
+                    painlessMethod = painlessLookup.lookupPainlessMethod(typeName, false, methodName,
+                            captured ? interfaceTypeParametersSize : interfaceTypeParametersSize - 1);
 
-                    if (captured) {
-                        throw new IllegalStateException("internal error");
-                    }
-                } catch (IllegalArgumentException staticIAE) {
-                    try {
-                        painlessMethod = painlessLookup.lookupPainlessMethod(typeName, false, methodName,
-                                captured ? interfaceTypeParametersSize : interfaceTypeParametersSize - 1);
-                    } catch (IllegalArgumentException iae) {
+                    if (painlessMethod == null) {
                         throw new IllegalArgumentException(
                                 "function reference " + "[" + typeName + "::" + methodName + "/" + interfaceTypeParametersSize + "] " +
                                 "matching [" + targetClassName + ", " + interfaceMethodName + "/" + interfaceTypeParametersSize + "] " +
-                                "not found", iae);
+                                "not found");
                     }
+                } else if (captured) {
+                    throw new IllegalStateException("internal error");
                 }
 
                 delegateClassName = painlessMethod.javaMethod.getDeclaringClass().getName();

+ 89 - 25
modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java

@@ -19,15 +19,17 @@
 
 package org.elasticsearch.painless.lookup;
 
+import java.lang.invoke.MethodHandle;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.Function;
 
 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;
+import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToBoxedType;
 
 public final class PainlessLookup {
 
@@ -67,6 +69,10 @@ public final class PainlessLookup {
 
         Class<?> targetClass = canonicalTypeNameToType(targetClassName);
 
+        if (targetClass == null) {
+            return null;
+        }
+
         return lookupPainlessConstructor(targetClass, constructorArity);
     }
 
@@ -77,15 +83,13 @@ public final class PainlessLookup {
         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 final class PainlessLookup {
 
         Class<?> targetClass = canonicalTypeNameToType(targetClassName);
 
+        if (targetClass == null) {
+            return null;
+        }
+
         return lookupPainlessMethod(targetClass, isStatic, methodName, methodArity);
     }
 
@@ -104,27 +112,19 @@ public final class PainlessLookup {
         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 final class PainlessLookup {
 
         Class<?> targetClass = canonicalTypeNameToType(targetClassName);
 
+        if (targetClass == null) {
+            return null;
+        }
+
         return lookupPainlessField(targetClass, isStatic, fieldName);
     }
 
@@ -143,8 +147,7 @@ public final class PainlessLookup {
         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 final class PainlessLookup {
                 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 final class PainlessLookup {
         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<PainlessClass, PainlessMethod> objectLookup = targetPainlessClass -> targetPainlessClass.methods.get(painlessMethodKey);
+
+        return lookupRuntimePainlessObject(originalTargetClass, objectLookup);
+    }
+
+    public MethodHandle lookupRuntimeGetterMethodHandle(Class<?> originalTargetClass, String getterName) {
+        Objects.requireNonNull(originalTargetClass);
+        Objects.requireNonNull(getterName);
+
+        Function<PainlessClass, MethodHandle> objectLookup = targetPainlessClass -> targetPainlessClass.getterMethodHandles.get(getterName);
+
+        return lookupRuntimePainlessObject(originalTargetClass, objectLookup);
+    }
+
+    public MethodHandle lookupRuntimeSetterMethodHandle(Class<?> originalTargetClass, String setterName) {
+        Objects.requireNonNull(originalTargetClass);
+        Objects.requireNonNull(setterName);
+
+        Function<PainlessClass, MethodHandle> objectLookup = targetPainlessClass -> targetPainlessClass.setterMethodHandles.get(setterName);
+
+        return lookupRuntimePainlessObject(originalTargetClass, objectLookup);
+    }
+
+    private <T> T lookupRuntimePainlessObject(
+            Class<?> originalTargetClass, Function<PainlessClass, T> 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;
     }
 }

+ 31 - 37
modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupBuilder.java

@@ -220,8 +220,12 @@ public final class PainlessLookupBuilder {
         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 final class PainlessLookupBuilder {
         List<Class<?>> 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 final class PainlessLookupBuilder {
         List<Class<?>> 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 final class PainlessLookupBuilder {
         List<Class<?>> 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 final class PainlessLookupBuilder {
         }
 
         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 final class PainlessLookupBuilder {
             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 final class PainlessLookupBuilder {
             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;

+ 39 - 47
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 final class PainlessLookupUtility {
                     canonicalTypeName.charAt(arrayIndex++) == ']') {
                     ++arrayDimensions;
                 } else {
-                    throw new IllegalArgumentException("type [" + canonicalTypeName + "] not found");
+                    return null;
                 }
             }
 
             canonicalTypeName = canonicalTypeName.substring(0, canonicalTypeName.indexOf('['));
             type = canonicalClassNamesToClasses.get(canonicalTypeName);
 
-            char arrayBraces[] = new char[arrayDimensions];
-            Arrays.fill(arrayBraces, '[');
-            String javaTypeName = new String(arrayBraces);
-
-            if (type == boolean.class) {
-                javaTypeName += "Z";
-            } else if (type == byte.class) {
-                javaTypeName += "B";
-            } else if (type == short.class) {
-                javaTypeName += "S";
-            } else if (type == char.class) {
-                javaTypeName += "C";
-            } else if (type == int.class) {
-                javaTypeName += "I";
-            } else if (type == long.class) {
-                javaTypeName += "J";
-            } else if (type == float.class) {
-                javaTypeName += "F";
-            } else if (type == double.class) {
-                javaTypeName += "D";
-            } else {
-                javaTypeName += "L" + type.getName() + ";";
-            }
+            if (type != null) {
+                char arrayBraces[] = new char[arrayDimensions];
+                Arrays.fill(arrayBraces, '[');
+                String javaTypeName = new String(arrayBraces);
+
+                if (type == boolean.class) {
+                    javaTypeName += "Z";
+                } else if (type == byte.class) {
+                    javaTypeName += "B";
+                } else if (type == short.class) {
+                    javaTypeName += "S";
+                } else if (type == char.class) {
+                    javaTypeName += "C";
+                } else if (type == int.class) {
+                    javaTypeName += "I";
+                } else if (type == long.class) {
+                    javaTypeName += "J";
+                } else if (type == float.class) {
+                    javaTypeName += "F";
+                } else if (type == double.class) {
+                    javaTypeName += "D";
+                } else {
+                    javaTypeName += "L" + type.getName() + ";";
+                }
 
-            try {
-                return Class.forName(javaTypeName);
-            } catch (ClassNotFoundException cnfe) {
-                throw new IllegalArgumentException("type [" + canonicalTypeName + "] not found", cnfe);
+                try {
+                    return Class.forName(javaTypeName);
+                } catch (ClassNotFoundException cnfe) {
+                    throw new IllegalStateException("internal error", cnfe);
+                }
             }
         }
 
-        throw new IllegalArgumentException("type [" + canonicalTypeName + "] not found");
+        return null;
     }
 
     /**
@@ -152,7 +153,9 @@ public final class PainlessLookupUtility {
 
         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 final class PainlessLookupUtility {
         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<Class<?>> 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 final class PainlessLookupUtility {
         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.
      */

+ 3 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java

@@ -49,9 +49,9 @@ public final class EExplicit extends AExpression {
 
     @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 + "]."));
         }
 

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

@@ -54,12 +54,11 @@ public final class EInstanceof extends AExpression {
 
     @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 + "]."));
         }
 

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

@@ -33,6 +33,8 @@ import java.util.ArrayList;
 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 @@ public final class EListInit extends AExpression {
 
         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) + ", <init>/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) {

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

@@ -33,6 +33,8 @@ import java.util.HashMap;
 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 @@ public final class EMapInit extends AExpression {
 
         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) + ", <init>/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()) {

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

@@ -54,15 +54,13 @@ public final class ENewArray extends AExpression {
 
     @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 + "]."));
         }
 

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

@@ -32,6 +32,8 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
+import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName;
+
 /**
  * Represents and object instantiation.
  */
@@ -58,16 +60,17 @@ public final class ENewObj extends AExpression {
 
     @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) + ", <init>/" + arguments.size() + "] not found"));
         }
 
         Class<?>[] types = new Class<?>[constructor.typeParameters.size()];

+ 3 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java

@@ -47,9 +47,9 @@ public final class EStatic extends AExpression {
 
     @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 + "]."));
         }
     }

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

@@ -30,6 +30,8 @@ import java.util.List;
 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 @@ public final class PCallInvoke extends AExpression {
         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) {

+ 17 - 20
modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java

@@ -23,6 +23,7 @@ import org.elasticsearch.painless.Globals;
 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.Map;
 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 @@ public final class PField extends AStoreable {
         } 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 @@ public final class PField extends AStoreable {
                 }
 
                 if (sub == null) {
-                    throw createError(fieldIAE);
+                    throw createError(new IllegalArgumentException(
+                            "field [" + typeToCanonicalTypeName(prefix.actual) + ", " + value + "] not found"));
                 }
+            } else {
+                sub = new PSubField(location, field);
             }
         }
 

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

@@ -57,12 +57,8 @@ final class PSubListShortcut extends AStoreable {
     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)) {

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

@@ -56,12 +56,8 @@ final class PSubMapShortcut extends AStoreable {
     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 + "]."));

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

@@ -64,11 +64,9 @@ public final class SCatch extends AStatement {
 
     @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 + "]."));
         }
 

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

@@ -59,11 +59,9 @@ public final class SDeclaration extends AStatement {
 
     @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 + "]."));
         }
 

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

@@ -68,11 +68,9 @@ public class SEach extends AStatement {
         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 + "]."));
         }
 

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

@@ -115,9 +115,9 @@ public final class SFunction extends AStatement {
     }
 
     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 @@ public final class SFunction extends AStatement {
         List<Class<?>> 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;

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

@@ -40,6 +40,7 @@ import java.util.Set;
 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 @@ final class SSubEachIterable extends AStatement {
         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"));
             }
         }
 

+ 1 - 1
modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java

@@ -134,7 +134,7 @@ public class DefBootstrapTests extends ESTestCase {
         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$");

+ 1 - 1
modules/lang-painless/src/test/java/org/elasticsearch/painless/OverloadTests.java

@@ -37,7 +37,7 @@ public class OverloadTests extends ScriptTestCase {
         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() {

+ 1 - 1
modules/lang-painless/src/test/java/org/elasticsearch/painless/WhenThingsGoWrongTests.java

@@ -219,7 +219,7 @@ public class WhenThingsGoWrongTests extends ScriptTestCase {
         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() {