瀏覽代碼

Painless: Complete Removal of Painless Type (#31699)

This completes the removal of Painless Type. The new data structures in the definition are a map of names (String) to Java Classes and a map of Java Classes to Painless Structs. The names to Java Classes map can contain a 2 to 1 ratio of names to classes depending on whether or not a short (imported) name is used. The Java Classes to Painless Structs is 1 to 1 always where the Java Class name must match the Painless Struct name. This should lead a significantly simpler type system in Painless moving forward since the Painless Type only held redundant information since Painless does not support generics.
Jack Conradson 7 年之前
父節點
當前提交
a02e5ee740
共有 35 個文件被更改,包括 541 次插入705 次删除
  1. 3 3
      modules/lang-painless/src/main/antlr/PainlessLexer.g4
  2. 18 18
      modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java
  3. 30 30
      modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java
  4. 164 164
      modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java
  5. 106 275
      modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java
  6. 3 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java
  7. 1 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java
  8. 1 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessPlugin.java
  9. 7 11
      modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java
  10. 2 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java
  11. 48 41
      modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessLexer.java
  12. 79 64
      modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java
  13. 4 3
      modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java
  14. 1 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java
  15. 1 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java
  16. 1 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java
  17. 1 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java
  18. 2 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java
  19. 2 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java
  20. 7 12
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java
  21. 2 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java
  22. 1 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ERegex.java
  23. 1 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java
  24. 2 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PBrace.java
  25. 2 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java
  26. 1 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java
  27. 0 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefCall.java
  28. 1 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java
  29. 1 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java
  30. 1 1
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java
  31. 2 2
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java
  32. 2 4
      modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java
  33. 2 2
      modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java
  34. 30 32
      modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java
  35. 12 12
      modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java

+ 3 - 3
modules/lang-painless/src/main/antlr/PainlessLexer.g4

@@ -19,14 +19,14 @@
 
 lexer grammar PainlessLexer;
 
-@members{
+@members {
 /**
  * Check against the current whitelist to determine whether a token is a type
  * or not. Called by the {@code TYPE} token defined in {@code PainlessLexer.g4}.
  * See also
  * <a href="https://en.wikipedia.org/wiki/The_lexer_hack">The lexer hack</a>.
  */
-protected abstract boolean isSimpleType(String name);
+protected abstract boolean isType(String name);
 
 /**
  * Is the preceding {@code /} a the beginning of a regex (true) or a division
@@ -133,7 +133,7 @@ NULL: 'null';
 // or not.  Note this works by processing one character at a time
 // and the rule is added or removed as this happens.  This is also known
 // as "the lexer hack."  See (https://en.wikipedia.org/wiki/The_lexer_hack).
-TYPE: ID ( DOT ID )* { isSimpleType(getText()) }?;
+TYPE: ID ( DOT ID )* { isType(getText()) }?;
 ID: [_a-zA-Z] [_a-zA-Z0-9]*;
 
 mode AFTER_DOT;

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

@@ -185,7 +185,7 @@ public final class Def {
         Definition.MethodKey key = new Definition.MethodKey(name, arity);
         // check whitelist for matching method
         for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
-            Struct struct = definition.RuntimeClassToStruct(clazz);
+            Struct struct = definition.getPainlessStructFromJavaClass(clazz);
 
             if (struct != null) {
                 Method method = struct.methods.get(key);
@@ -195,7 +195,7 @@ public final class Def {
             }
 
             for (Class<?> iface : clazz.getInterfaces()) {
-                struct = definition.RuntimeClassToStruct(iface);
+                struct = definition.getPainlessStructFromJavaClass(iface);
 
                 if (struct != null) {
                     Method method = struct.methods.get(key);
@@ -279,7 +279,7 @@ public final class Def {
                      captures[capture] = callSiteType.parameterType(i + 1 + capture);
                  }
                  MethodHandle filter;
-                 Definition.Type interfaceType = definition.ClassToType(method.arguments.get(i - 1 - replaced));
+                 Class<?> interfaceType = method.arguments.get(i - 1 - replaced);
                  if (signature.charAt(0) == 'S') {
                      // the implementation is strongly typed, now that we know the interface type,
                      // we have everything.
@@ -293,14 +293,14 @@ public final class Def {
                      // the interface type is now known, but we need to get the implementation.
                      // this is dynamically based on the receiver type (and cached separately, underneath
                      // this cache). It won't blow up since we never nest here (just references)
-                     MethodType nestedType = MethodType.methodType(interfaceType.clazz, captures);
+                     MethodType nestedType = MethodType.methodType(interfaceType, captures);
                      CallSite nested = DefBootstrap.bootstrap(definition,
                                                               lookup,
                                                               call,
                                                               nestedType,
                                                               0,
                                                               DefBootstrap.REFERENCE,
-                                                              interfaceType.name);
+                                                              Definition.ClassToName(interfaceType));
                      filter = nested.dynamicInvoker();
                  } else {
                      throw new AssertionError();
@@ -324,8 +324,8 @@ public final class Def {
       */
     static MethodHandle lookupReference(Definition definition, Lookup lookup, String interfaceClass,
             Class<?> receiverClass, String name) throws Throwable {
-         Definition.Type interfaceType = definition.getType(interfaceClass);
-         Method interfaceMethod = interfaceType.struct.functionalMethod;
+         Class<?> interfaceType = definition.getJavaClassFromPainlessType(interfaceClass);
+         Method interfaceMethod = definition.getPainlessStructFromJavaClass(interfaceType).functionalMethod;
          if (interfaceMethod == null) {
              throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
          }
@@ -337,15 +337,15 @@ public final class Def {
 
      /** Returns a method handle to an implementation of clazz, given method reference signature. */
     private static MethodHandle lookupReferenceInternal(Definition definition, Lookup lookup,
-            Definition.Type clazz, String type, String call, Class<?>... captures)
+            Class<?> clazz, String type, String call, Class<?>... captures)
             throws Throwable {
          final FunctionRef ref;
          if ("this".equals(type)) {
              // user written method
-             Method interfaceMethod = clazz.struct.functionalMethod;
+             Method interfaceMethod = definition.getPainlessStructFromJavaClass(clazz).functionalMethod;
              if (interfaceMethod == null) {
                  throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
-                                                    "to [" + clazz.name + "], not a functional interface");
+                                                    "to [" + Definition.ClassToName(clazz) + "], not a functional interface");
              }
              int arity = interfaceMethod.arguments.size() + captures.length;
              final MethodHandle handle;
@@ -359,14 +359,14 @@ public final class Def {
                  // because the arity does not match the expected interface type.
                  if (call.contains("$")) {
                      throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name +
-                                                        "] in [" + clazz.clazz + "]");
+                                                        "] in [" + clazz + "]");
                  }
                  throw new IllegalArgumentException("Unknown call [" + call + "] with [" + arity + "] arguments.");
              }
-             ref = new FunctionRef(clazz.clazz, interfaceMethod, call, handle.type(), captures.length);
+             ref = new FunctionRef(clazz, interfaceMethod, call, handle.type(), captures.length);
          } else {
              // whitelist lookup
-             ref = new FunctionRef(definition, clazz.clazz, type, call, captures.length);
+             ref = new FunctionRef(definition, clazz, type, call, captures.length);
          }
          final CallSite callSite = LambdaBootstrap.lambdaBootstrap(
              lookup,
@@ -379,7 +379,7 @@ public final class Def {
              ref.delegateMethodType,
              ref.isDelegateInterface ? 1 : 0
          );
-         return callSite.dynamicInvoker().asType(MethodType.methodType(clazz.clazz, captures));
+         return callSite.dynamicInvoker().asType(MethodType.methodType(clazz, captures));
      }
 
      /** gets the field name used to lookup up the MethodHandle for a function. */
@@ -416,7 +416,7 @@ public final class Def {
     static MethodHandle lookupGetter(Definition definition, Class<?> receiverClass, String name) {
         // first try whitelist
         for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
-            Struct struct = definition.RuntimeClassToStruct(clazz);
+            Struct struct = definition.getPainlessStructFromJavaClass(clazz);
 
             if (struct != null) {
                 MethodHandle handle = struct.getters.get(name);
@@ -426,7 +426,7 @@ public final class Def {
             }
 
             for (final Class<?> iface : clazz.getInterfaces()) {
-                struct = definition.RuntimeClassToStruct(iface);
+                struct = definition.getPainlessStructFromJavaClass(iface);
 
                 if (struct != null) {
                     MethodHandle handle = struct.getters.get(name);
@@ -487,7 +487,7 @@ public final class Def {
     static MethodHandle lookupSetter(Definition definition, Class<?> receiverClass, String name) {
         // first try whitelist
         for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
-            Struct struct = definition.RuntimeClassToStruct(clazz);
+            Struct struct = definition.getPainlessStructFromJavaClass(clazz);
 
             if (struct != null) {
                 MethodHandle handle = struct.setters.get(name);
@@ -497,7 +497,7 @@ public final class Def {
             }
 
             for (final Class<?> iface : clazz.getInterfaces()) {
-                struct = definition.RuntimeClassToStruct(iface);
+                struct = definition.getPainlessStructFromJavaClass(iface);
 
                 if (struct != null) {
                     MethodHandle handle = struct.setters.get(name);

+ 30 - 30
modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java

@@ -1,7 +1,3 @@
-package org.elasticsearch.painless;
-
-import org.elasticsearch.common.SuppressForbidden;
-
 /*
  * Licensed to Elasticsearch under one or more contributor
  * license agreements. See the NOTICE file distributed with
@@ -21,6 +17,10 @@ import org.elasticsearch.common.SuppressForbidden;
  * under the License.
  */
 
+package org.elasticsearch.painless;
+
+import org.elasticsearch.common.SuppressForbidden;
+
 import java.lang.invoke.CallSite;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
@@ -72,16 +72,16 @@ public final class DefBootstrap {
     public static final int SHIFT_OPERATOR = 9;
     /** static bootstrap parameter indicating a request to normalize an index for array-like-access */
     public static final int INDEX_NORMALIZE = 10;
-    
+
     // constants for the flags parameter of operators
-    /** 
-     * static bootstrap parameter indicating the binary operator allows nulls (e.g. == and +) 
+    /**
+     * static bootstrap parameter indicating the binary operator allows nulls (e.g. == and +)
      * <p>
      * requires additional {@link MethodHandles#catchException} guard, which will invoke
      * the fallback if a null is encountered.
      */
     public static final int OPERATOR_ALLOWS_NULL = 1 << 0;
-    
+
     /**
      * static bootstrap parameter indicating the binary operator is part of compound assignment (e.g. +=).
      * <p>
@@ -89,7 +89,7 @@ public final class DefBootstrap {
      * to cast back to the receiver's type, depending on types seen.
      */
     public static final int OPERATOR_COMPOUND_ASSIGNMENT = 1 << 1;
-    
+
     /**
      * static bootstrap parameter indicating an explicit cast to the return type.
      * <p>
@@ -129,7 +129,7 @@ public final class DefBootstrap {
 
             setTarget(fallback);
         }
-        
+
         /**
          * guard method for inline caching: checks the receiver's class is the same
          * as the cached class
@@ -162,7 +162,7 @@ public final class DefBootstrap {
                 default: throw new AssertionError();
             }
         }
-        
+
         /**
          * Creates the {@link MethodHandle} for the megamorphic call site
          * using {@link ClassValue} and {@link MethodHandles#exactInvoker(MethodType)}:
@@ -182,7 +182,7 @@ public final class DefBootstrap {
                 }
             };
             return MethodHandles.foldArguments(MethodHandles.exactInvoker(type),
-                    MEGAMORPHIC_LOOKUP.bindTo(megamorphicCache));            
+                    MEGAMORPHIC_LOOKUP.bindTo(megamorphicCache));
         }
 
         /**
@@ -195,18 +195,18 @@ public final class DefBootstrap {
             if (depth >= MAX_DEPTH) {
                 // we revert the whole cache and build a new megamorphic one
                 final MethodHandle target = this.createMegamorphicHandle();
-                
+
                 setTarget(target);
-                return target.invokeWithArguments(callArgs);                    
+                return target.invokeWithArguments(callArgs);
             } else {
                 final Class<?> receiver = callArgs[0].getClass();
                 final MethodHandle target = lookup(flavor, name, receiver).asType(type());
-    
+
                 MethodHandle test = CHECK_CLASS.bindTo(receiver);
                 MethodHandle guard = MethodHandles.guardWithTest(test, target, getTarget());
-                
+
                 depth++;
-    
+
                 setTarget(guard);
                 return target.invokeWithArguments(callArgs);
             }
@@ -225,7 +225,7 @@ public final class DefBootstrap {
                         MethodType.methodType(Object.class, Object[].class));
                 MethodHandle mh = publicLookup.findVirtual(ClassValue.class, "get",
                         MethodType.methodType(Object.class, Class.class));
-                mh = MethodHandles.filterArguments(mh, 1, 
+                mh = MethodHandles.filterArguments(mh, 1,
                         publicLookup.findVirtual(Object.class, "getClass", MethodType.methodType(Class.class)));
                 MEGAMORPHIC_LOOKUP = mh.asType(mh.type().changeReturnType(MethodHandle.class));
             } catch (ReflectiveOperationException e) {
@@ -233,7 +233,7 @@ public final class DefBootstrap {
             }
         }
     }
-    
+
     /**
      * CallSite that implements the monomorphic inlining cache (for operators).
      */
@@ -252,14 +252,14 @@ public final class DefBootstrap {
             if (initialDepth > 0) {
                 initialized = true;
             }
-            
+
             MethodHandle fallback = FALLBACK.bindTo(this)
               .asCollector(Object[].class, type.parameterCount())
               .asType(type);
 
             setTarget(fallback);
         }
-        
+
         /**
          * Does a slow lookup for the operator
          */
@@ -290,7 +290,7 @@ public final class DefBootstrap {
                 default: throw new AssertionError();
             }
         }
-        
+
         private MethodHandle lookupGeneric() {
             MethodHandle target = DefMath.lookupGeneric(name);
             if ((flags & OPERATOR_EXPLICIT_CAST) != 0) {
@@ -302,7 +302,7 @@ public final class DefBootstrap {
             }
             return target;
         }
-        
+
         /**
          * Called when a new type is encountered or if cached type does not match.
          * In that case we revert to a generic, but slower operator handling.
@@ -315,7 +315,7 @@ public final class DefBootstrap {
                 setTarget(generic.asType(type()));
                 return generic.invokeWithArguments(args);
             }
-            
+
             final MethodType type = type();
             MethodHandle target = lookup(args);
             // for math operators: WrongMethodType can be confusing. convert into a ClassCastException if they screw up.
@@ -361,18 +361,18 @@ public final class DefBootstrap {
             // very special cases, where even the receiver can be null (see JLS rules for string concat)
             // we wrap + with an NPE catcher, and use our generic method in that case.
             if (flavor == BINARY_OPERATOR && (flags & OPERATOR_ALLOWS_NULL) != 0) {
-                MethodHandle handler = MethodHandles.dropArguments(lookupGeneric().asType(type()), 
-                                                                   0, 
+                MethodHandle handler = MethodHandles.dropArguments(lookupGeneric().asType(type()),
+                                                                   0,
                                                                    NullPointerException.class);
                 guard = MethodHandles.catchException(guard, NullPointerException.class, handler);
             }
-            
+
             initialized = true;
 
             setTarget(guard);
             return target.invokeWithArguments(args);
         }
-        
+
         /**
          * guard method for inline caching: checks the receiver's class is the same
          * as the cached class
@@ -388,7 +388,7 @@ public final class DefBootstrap {
         static boolean checkRHS(Class<?> left, Class<?> right, Object leftObject, Object rightObject) {
             return rightObject.getClass() == right;
         }
-        
+
         /**
          * guard method for inline caching: checks the receiver's class and the first argument
          * are the same as the cached receiver and first argument.
@@ -396,7 +396,7 @@ public final class DefBootstrap {
         static boolean checkBoth(Class<?> left, Class<?> right, Object leftObject, Object rightObject) {
             return leftObject.getClass() == left && rightObject.getClass() == right;
         }
-        
+
         private static final MethodHandle CHECK_LHS;
         private static final MethodHandle CHECK_RHS;
         private static final MethodHandle CHECK_BOTH;

+ 164 - 164
modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java

@@ -21,8 +21,8 @@ package org.elasticsearch.painless;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
 import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -31,38 +31,38 @@ import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 /**
- * Dynamic operators for painless. 
+ * Dynamic operators for painless.
  * <p>
  * Each operator must "support" the following types:
- * {@code int,long,float,double,boolean,Object}. Operators can throw exceptions if 
+ * {@code int,long,float,double,boolean,Object}. Operators can throw exceptions if
  * the type is illegal. The {@code Object} type must be a "generic" handler that
  * handles all legal types: it must be convertible to every possible legal signature.
  */
 @SuppressWarnings("unused")
 public class DefMath {
-    
+
     // Unary not: only applicable to integral types
 
     private static int not(int v) {
         return ~v;
     }
-    
+
     private static long not(long v) {
         return ~v;
     }
-    
+
     private static float not(float v) {
         throw new ClassCastException("Cannot apply not [~] to type [float]");
     }
-    
+
     private static double not(double v) {
         throw new ClassCastException("Cannot apply not [~] to type [double]");
     }
-    
+
     private static boolean not(boolean v) {
         throw new ClassCastException("Cannot apply not [~] to type [boolean]");
     }
-    
+
     private static Object not(Object unary) {
         if (unary instanceof Long) {
             return ~(Long)unary;
@@ -79,29 +79,29 @@ public class DefMath {
         throw new ClassCastException("Cannot apply [~] operation to type " +
                 "[" + unary.getClass().getCanonicalName() + "].");
     }
-    
+
     // unary negation and plus: applicable to all numeric types
 
     private static int neg(int v) {
         return -v;
     }
-    
+
     private static long neg(long v) {
         return -v;
     }
-    
+
     private static float neg(float v) {
         return -v;
     }
-    
+
     private static double neg(double v) {
         return -v;
     }
-    
+
     private static boolean neg(boolean v) {
         throw new ClassCastException("Cannot apply [-] operation to type [boolean]");
     }
-    
+
     private static Object neg(final Object unary) {
         if (unary instanceof Double) {
             return -(double)unary;
@@ -122,27 +122,27 @@ public class DefMath {
         throw new ClassCastException("Cannot apply [-] operation to type " +
                 "[" + unary.getClass().getCanonicalName() + "].");
     }
-    
+
     private static int plus(int v) {
         return +v;
     }
-    
+
     private static long plus(long v) {
         return +v;
     }
-    
+
     private static float plus(float v) {
         return +v;
     }
-    
+
     private static double plus(double v) {
         return +v;
     }
-    
+
     private static boolean plus(boolean v) {
         throw new ClassCastException("Cannot apply [+] operation to type [boolean]");
     }
-    
+
     private static Object plus(final Object unary) {
         if (unary instanceof Double) {
             return +(double)unary;
@@ -163,29 +163,29 @@ public class DefMath {
         throw new ClassCastException("Cannot apply [+] operation to type " +
                 "[" + unary.getClass().getCanonicalName() + "].");
     }
-    
+
     // multiplication/division/remainder/subtraction: applicable to all integer types
-    
+
     private static int mul(int a, int b) {
         return a * b;
     }
-    
+
     private static long mul(long a, long b) {
         return a * b;
     }
-    
+
     private static float mul(float a, float b) {
         return a * b;
     }
-    
+
     private static double mul(double a, double b) {
         return a * b;
     }
-    
+
     private static boolean mul(boolean a, boolean b) {
         throw new ClassCastException("Cannot apply [*] operation to type [boolean]");
     }
-    
+
     private static Object mul(Object left, Object right) {
         if (left instanceof Number) {
             if (right instanceof Number) {
@@ -228,27 +228,27 @@ public class DefMath {
         throw new ClassCastException("Cannot apply [*] operation to types " +
                 "[" + left.getClass().getCanonicalName() + "] and [" + right.getClass().getCanonicalName() + "].");
     }
-    
+
     private static int div(int a, int b) {
         return a / b;
     }
-    
+
     private static long div(long a, long b) {
         return a / b;
     }
-    
+
     private static float div(float a, float b) {
         return a / b;
     }
-    
+
     private static double div(double a, double b) {
         return a / b;
     }
-    
+
     private static boolean div(boolean a, boolean b) {
         throw new ClassCastException("Cannot apply [/] operation to type [boolean]");
     }
-    
+
     private static Object div(Object left, Object right) {
         if (left instanceof Number) {
             if (right instanceof Number) {
@@ -291,27 +291,27 @@ public class DefMath {
         throw new ClassCastException("Cannot apply [/] operation to types " +
                 "[" + left.getClass().getCanonicalName() + "] and [" + right.getClass().getCanonicalName() + "].");
     }
-    
+
     private static int rem(int a, int b) {
         return a % b;
     }
-    
+
     private static long rem(long a, long b) {
         return a % b;
     }
-    
+
     private static float rem(float a, float b) {
         return a % b;
     }
-    
+
     private static double rem(double a, double b) {
         return a % b;
     }
-    
+
     private static boolean rem(boolean a, boolean b) {
         throw new ClassCastException("Cannot apply [%] operation to type [boolean]");
     }
-    
+
     private static Object rem(Object left, Object right) {
         if (left instanceof Number) {
             if (right instanceof Number) {
@@ -354,30 +354,30 @@ public class DefMath {
         throw new ClassCastException("Cannot apply [%] operation to types " +
                 "[" + left.getClass().getCanonicalName() + "] and [" + right.getClass().getCanonicalName() + "].");
     }
-    
+
     // addition: applicable to all numeric types.
     // additionally, if either type is a string, the other type can be any arbitrary type (including null)
-    
+
     private static int add(int a, int b) {
         return a + b;
     }
-    
+
     private static long add(long a, long b) {
         return a + b;
     }
-    
+
     private static float add(float a, float b) {
         return a + b;
     }
-    
+
     private static double add(double a, double b) {
         return a + b;
     }
-    
+
     private static boolean add(boolean a, boolean b) {
         throw new ClassCastException("Cannot apply [+] operation to type [boolean]");
     }
-    
+
     private static Object add(Object left, Object right) {
         if (left instanceof String) {
             return (String) left + right;
@@ -424,27 +424,27 @@ public class DefMath {
         throw new ClassCastException("Cannot apply [+] operation to types " +
                 "[" + left.getClass().getCanonicalName() + "] and [" + right.getClass().getCanonicalName() + "].");
     }
-    
+
     private static int sub(int a, int b) {
         return a - b;
     }
-    
+
     private static long sub(long a, long b) {
         return a - b;
     }
-    
+
     private static float sub(float a, float b) {
         return a - b;
     }
-    
+
     private static double sub(double a, double b) {
         return a - b;
     }
-    
+
     private static boolean sub(boolean a, boolean b) {
         throw new ClassCastException("Cannot apply [-] operation to type [boolean]");
     }
-    
+
     private static Object sub(Object left, Object right) {
         if (left instanceof Number) {
             if (right instanceof Number) {
@@ -487,29 +487,29 @@ public class DefMath {
         throw new ClassCastException("Cannot apply [-] operation to types " +
                 "[" + left.getClass().getCanonicalName() + "] and [" + right.getClass().getCanonicalName() + "].");
     }
-    
+
     // eq: applicable to any arbitrary type, including nulls for both arguments!!!
 
     private static boolean eq(int a, int b) {
         return a == b;
     }
-    
+
     private static boolean eq(long a, long b) {
         return a == b;
     }
-    
+
     private static boolean eq(float a, float b) {
         return a == b;
     }
-    
+
     private static boolean eq(double a, double b) {
         return a == b;
     }
-    
+
     private static boolean eq(boolean a, boolean b) {
         return a == b;
     }
-    
+
     private static boolean eq(Object left, Object right) {
         if (left != null && right != null) {
             if (left instanceof Double) {
@@ -565,29 +565,29 @@ public class DefMath {
 
         return left == null && right == null;
     }
-    
+
     // comparison operators: applicable for any numeric type
 
     private static boolean lt(int a, int b) {
         return a < b;
     }
-    
+
     private static boolean lt(long a, long b) {
         return a < b;
     }
-    
+
     private static boolean lt(float a, float b) {
         return a < b;
     }
-    
+
     private static boolean lt(double a, double b) {
         return a < b;
     }
-    
+
     private static boolean lt(boolean a, boolean b) {
-        throw new ClassCastException("Cannot apply [<] operation to type [boolean]");    
+        throw new ClassCastException("Cannot apply [<] operation to type [boolean]");
     }
-    
+
     private static boolean lt(Object left, Object right) {
         if (left instanceof Number) {
             if (right instanceof Number) {
@@ -634,23 +634,23 @@ public class DefMath {
     private static boolean lte(int a, int b) {
         return a <= b;
     }
-    
+
     private static boolean lte(long a, long b) {
         return a <= b;
     }
-    
+
     private static boolean lte(float a, float b) {
         return a <= b;
     }
-    
+
     private static boolean lte(double a, double b) {
         return a <= b;
     }
-    
+
     private static boolean lte(boolean a, boolean b) {
-        throw new ClassCastException("Cannot apply [<=] operation to type [boolean]");    
+        throw new ClassCastException("Cannot apply [<=] operation to type [boolean]");
     }
-    
+
     private static boolean lte(Object left, Object right) {
         if (left instanceof Number) {
             if (right instanceof Number) {
@@ -697,23 +697,23 @@ public class DefMath {
     private static boolean gt(int a, int b) {
         return a > b;
     }
-    
+
     private static boolean gt(long a, long b) {
         return a > b;
     }
-    
+
     private static boolean gt(float a, float b) {
         return a > b;
     }
-    
+
     private static boolean gt(double a, double b) {
         return a > b;
     }
-    
+
     private static boolean gt(boolean a, boolean b) {
-        throw new ClassCastException("Cannot apply [>] operation to type [boolean]");    
+        throw new ClassCastException("Cannot apply [>] operation to type [boolean]");
     }
-    
+
     private static boolean gt(Object left, Object right) {
         if (left instanceof Number) {
             if (right instanceof Number) {
@@ -756,25 +756,25 @@ public class DefMath {
         throw new ClassCastException("Cannot apply [>] operation to types " +
                 "[" + left.getClass().getCanonicalName() + "] and [" + right.getClass().getCanonicalName() + "].");
     }
-    
+
     private static boolean gte(int a, int b) {
         return a >= b;
     }
-    
+
     private static boolean gte(long a, long b) {
         return a >= b;
     }
-    
+
     private static boolean gte(float a, float b) {
         return a >= b;
     }
-    
+
     private static boolean gte(double a, double b) {
         return a >= b;
     }
-    
+
     private static boolean gte(boolean a, boolean b) {
-        throw new ClassCastException("Cannot apply [>=] operation to type [boolean]");    
+        throw new ClassCastException("Cannot apply [>=] operation to type [boolean]");
     }
 
     private static boolean gte(Object left, Object right) {
@@ -819,10 +819,10 @@ public class DefMath {
         throw new ClassCastException("Cannot apply [>] operation to types " +
                 "[" + left.getClass().getCanonicalName() + "] and [" + right.getClass().getCanonicalName() + "].");
     }
-    
+
     // helper methods to convert an integral according to numeric promotion
     // this is used by the generic code for bitwise and shift operators
-    
+
     private static long longIntegralValue(Object o) {
         if (o instanceof Long) {
             return (long)o;
@@ -834,7 +834,7 @@ public class DefMath {
             throw new ClassCastException("Cannot convert [" + o.getClass().getCanonicalName() + "] to an integral value.");
         }
     }
-    
+
     private static int intIntegralValue(Object o) {
         if (o instanceof Integer || o instanceof Short || o instanceof Byte) {
             return ((Number)o).intValue();
@@ -844,29 +844,29 @@ public class DefMath {
             throw new ClassCastException("Cannot convert [" + o.getClass().getCanonicalName() + "] to an integral value.");
         }
     }
-    
+
     // bitwise operators: valid only for integral types
 
     private static int and(int a, int b) {
         return a & b;
     }
-    
+
     private static long and(long a, long b) {
         return a & b;
     }
-    
+
     private static float and(float a, float b) {
-        throw new ClassCastException("Cannot apply [&] operation to type [float]");    
+        throw new ClassCastException("Cannot apply [&] operation to type [float]");
     }
-    
+
     private static double and(double a, double b) {
-        throw new ClassCastException("Cannot apply [&] operation to type [float]");    
+        throw new ClassCastException("Cannot apply [&] operation to type [float]");
     }
-    
+
     private static boolean and(boolean a, boolean b) {
         return a & b;
     }
-    
+
     private static Object and(Object left, Object right) {
         if (left instanceof Boolean && right instanceof Boolean) {
             return (boolean)left & (boolean)right;
@@ -876,23 +876,23 @@ public class DefMath {
             return intIntegralValue(left) & intIntegralValue(right);
         }
     }
-    
+
     private static int xor(int a, int b) {
         return a ^ b;
     }
-    
+
     private static long xor(long a, long b) {
         return a ^ b;
     }
-    
+
     private static float xor(float a, float b) {
-        throw new ClassCastException("Cannot apply [^] operation to type [float]");    
+        throw new ClassCastException("Cannot apply [^] operation to type [float]");
     }
-    
+
     private static double xor(double a, double b) {
-        throw new ClassCastException("Cannot apply [^] operation to type [float]");    
+        throw new ClassCastException("Cannot apply [^] operation to type [float]");
     }
-    
+
     private static boolean xor(boolean a, boolean b) {
         return a ^ b;
     }
@@ -910,23 +910,23 @@ public class DefMath {
     private static int or(int a, int b) {
         return a | b;
     }
-    
+
     private static long or(long a, long b) {
         return a | b;
     }
-    
+
     private static float or(float a, float b) {
-        throw new ClassCastException("Cannot apply [|] operation to type [float]");    
+        throw new ClassCastException("Cannot apply [|] operation to type [float]");
     }
-    
+
     private static double or(double a, double b) {
-        throw new ClassCastException("Cannot apply [|] operation to type [float]");    
+        throw new ClassCastException("Cannot apply [|] operation to type [float]");
     }
-    
+
     private static boolean or(boolean a, boolean b) {
         return a | b;
     }
-    
+
     private static Object or(Object left, Object right) {
         if (left instanceof Boolean && right instanceof Boolean) {
             return (boolean)left | (boolean)right;
@@ -936,30 +936,30 @@ public class DefMath {
             return intIntegralValue(left) | intIntegralValue(right);
         }
     }
-    
+
     // shift operators, valid for any integral types, but does not promote.
     // we implement all shifts as long shifts, because the extra bits are ignored anyway.
-    
+
     private static int lsh(int a, long b) {
         return a << b;
     }
-    
+
     private static long lsh(long a, long b) {
         return a << b;
     }
-    
+
     private static float lsh(float a, long b) {
-        throw new ClassCastException("Cannot apply [<<] operation to type [float]");    
+        throw new ClassCastException("Cannot apply [<<] operation to type [float]");
     }
-    
+
     private static double lsh(double a, long b) {
-        throw new ClassCastException("Cannot apply [<<] operation to type [double]");    
+        throw new ClassCastException("Cannot apply [<<] operation to type [double]");
     }
-    
+
     private static boolean lsh(boolean a, long b) {
-        throw new ClassCastException("Cannot apply [<<] operation to type [boolean]");    
+        throw new ClassCastException("Cannot apply [<<] operation to type [boolean]");
     }
-    
+
     public static Object lsh(Object left, long right) {
         if (left instanceof Long) {
             return (long)(left) << right;
@@ -967,25 +967,25 @@ public class DefMath {
             return intIntegralValue(left) << right;
         }
     }
-    
+
     private static int rsh(int a, long b) {
         return a >> b;
     }
-    
+
     private static long rsh(long a, long b) {
         return a >> b;
     }
-    
+
     private static float rsh(float a, long b) {
-        throw new ClassCastException("Cannot apply [>>] operation to type [float]");    
+        throw new ClassCastException("Cannot apply [>>] operation to type [float]");
     }
-    
+
     private static double rsh(double a, long b) {
-        throw new ClassCastException("Cannot apply [>>] operation to type [double]");    
+        throw new ClassCastException("Cannot apply [>>] operation to type [double]");
     }
-    
+
     private static boolean rsh(boolean a, long b) {
-        throw new ClassCastException("Cannot apply [>>] operation to type [boolean]");    
+        throw new ClassCastException("Cannot apply [>>] operation to type [boolean]");
     }
 
     public static Object rsh(Object left, long right) {
@@ -995,25 +995,25 @@ public class DefMath {
             return intIntegralValue(left) >> right;
         }
     }
-    
+
     private static int ush(int a, long b) {
         return a >>> b;
     }
-    
+
     private static long ush(long a, long b) {
         return a >>> b;
     }
-    
+
     private static float ush(float a, long b) {
-        throw new ClassCastException("Cannot apply [>>>] operation to type [float]");    
+        throw new ClassCastException("Cannot apply [>>>] operation to type [float]");
     }
-    
+
     private static double ush(double a, long b) {
-        throw new ClassCastException("Cannot apply [>>>] operation to type [double]");    
+        throw new ClassCastException("Cannot apply [>>>] operation to type [double]");
     }
-    
+
     private static boolean ush(boolean a, long b) {
-        throw new ClassCastException("Cannot apply [>>>] operation to type [boolean]");    
+        throw new ClassCastException("Cannot apply [>>>] operation to type [boolean]");
     }
 
     public static Object ush(Object left, long right) {
@@ -1023,15 +1023,15 @@ public class DefMath {
             return intIntegralValue(left) >>> right;
         }
     }
-    
-    /** 
-     * unboxes a class to its primitive type, or returns the original 
+
+    /**
+     * unboxes a class to its primitive type, or returns the original
      * class if its not a boxed type.
      */
     private static Class<?> unbox(Class<?> clazz) {
         return MethodType.methodType(clazz).unwrap().returnType();
     }
-    
+
     /** Unary promotion. All Objects are promoted to Object. */
     private static Class<?> promote(Class<?> clazz) {
         // if either is a non-primitive type -> Object.
@@ -1039,25 +1039,25 @@ public class DefMath {
             return Object.class;
         }
         // always promoted to integer
-        if (clazz == byte.class || clazz == short.class || clazz == char.class || clazz == int.class) { 
-            return int.class; 
-        } else { 
-            return clazz; 
-        } 
+        if (clazz == byte.class || clazz == short.class || clazz == char.class || clazz == int.class) {
+            return int.class;
+        } else {
+            return clazz;
+        }
     }
-    
+
     /** Binary promotion. */
     private static Class<?> promote(Class<?> a, Class<?> b) {
         // if either is a non-primitive type -> Object.
         if (a.isPrimitive() == false || b.isPrimitive() == false) {
             return Object.class;
         }
-        
+
         // boolean -> boolean
         if (a == boolean.class && b == boolean.class) {
             return boolean.class;
         }
-        
+
         // ordinary numeric promotion
         if (a == double.class || b == double.class) {
             return double.class;
@@ -1069,7 +1069,7 @@ public class DefMath {
             return int.class;
         }
     }
-    
+
     private static final Lookup PRIV_LOOKUP = MethodHandles.lookup();
 
     private static final Map<Class<?>,Map<String,MethodHandle>> TYPE_OP_MAPPING = Collections.unmodifiableMap(
@@ -1107,7 +1107,7 @@ public class DefMath {
                 }
             }))
     );
-    
+
     /** Returns an appropriate method handle for a unary or shift operator, based only on the receiver (LHS) */
     public static MethodHandle lookupUnary(Class<?> receiverClass, String name) {
         MethodHandle handle = TYPE_OP_MAPPING.get(promote(unbox(receiverClass))).get(name);
@@ -1116,7 +1116,7 @@ public class DefMath {
         }
         return handle;
     }
-    
+
     /** Returns an appropriate method handle for a binary operator, based on promotion of the LHS and RHS arguments */
     public static MethodHandle lookupBinary(Class<?> classA, Class<?> classB, String name) {
         MethodHandle handle = TYPE_OP_MAPPING.get(promote(promote(unbox(classA)), promote(unbox(classB)))).get(name);
@@ -1125,7 +1125,7 @@ public class DefMath {
         }
         return handle;
     }
-    
+
     /** Returns a generic method handle for any operator, that can handle all valid signatures, nulls, corner cases */
     public static MethodHandle lookupGeneric(String name) {
         return TYPE_OP_MAPPING.get(Object.class).get(name);
@@ -1143,7 +1143,7 @@ public class DefMath {
             return returnValue;
         }
     }
-    
+
     /**
      * Slow dynamic cast: casts {@code value} to an instance of {@code clazz}
      * based upon inspection. If {@code lhs} is null, no cast takes place.
@@ -1173,7 +1173,7 @@ public class DefMath {
             return value;
         }
     }
-    
+
     /** Slowly returns a Number for o. Just for supporting dynamicCast */
     static Number getNumber(Object o) {
         if (o instanceof Number) {
@@ -1184,17 +1184,17 @@ public class DefMath {
             throw new ClassCastException("Cannot convert [" + o.getClass() + "] to a Number");
         }
     }
-    
+
     private static final MethodHandle DYNAMIC_CAST;
     private static final MethodHandle DYNAMIC_RECEIVER_CAST;
     static {
         final Lookup lookup = MethodHandles.lookup();
         try {
-            DYNAMIC_CAST = lookup.findStatic(lookup.lookupClass(), 
-                                            "dynamicCast", 
+            DYNAMIC_CAST = lookup.findStatic(lookup.lookupClass(),
+                                            "dynamicCast",
                                             MethodType.methodType(Object.class, Class.class, Object.class));
-            DYNAMIC_RECEIVER_CAST = lookup.findStatic(lookup.lookupClass(), 
-                                                     "dynamicReceiverCast", 
+            DYNAMIC_RECEIVER_CAST = lookup.findStatic(lookup.lookupClass(),
+                                                     "dynamicReceiverCast",
                                                      MethodType.methodType(Object.class, Object.class, Object.class));
         } catch (ReflectiveOperationException e) {
             throw new AssertionError(e);
@@ -1204,7 +1204,7 @@ public class DefMath {
     /** Looks up generic method, with a dynamic cast to the receiver's type. (compound assignment) */
     public static MethodHandle dynamicCast(MethodHandle target) {
         // adapt dynamic receiver cast to the generic method
-        MethodHandle cast = DYNAMIC_RECEIVER_CAST.asType(MethodType.methodType(target.type().returnType(), 
+        MethodHandle cast = DYNAMIC_RECEIVER_CAST.asType(MethodType.methodType(target.type().returnType(),
                                                                       target.type().returnType(),
                                                                       target.type().parameterType(0)));
         // drop the RHS parameter
@@ -1212,7 +1212,7 @@ public class DefMath {
         // combine: f(x,y) -> g(f(x,y), x, y);
         return MethodHandles.foldArguments(cast, target);
     }
-    
+
     /** Looks up generic method, with a dynamic cast to the specified type. (explicit assignment) */
     public static MethodHandle dynamicCast(MethodHandle target, Class<?> desired) {
         // adapt dynamic cast to the generic method
@@ -1221,23 +1221,23 @@ public class DefMath {
         MethodHandle cast = DYNAMIC_CAST.bindTo(desired);
         return MethodHandles.filterReturnValue(target, cast);
     }
-    
+
     /** Forces a cast to class A for target (only if types differ) */
     public static MethodHandle cast(Class<?> classA, MethodHandle target) {
         MethodType newType = MethodType.methodType(classA).unwrap();
         MethodType targetType = MethodType.methodType(target.type().returnType()).unwrap();
-        
+
         // don't do a conversion if types are the same. explicitCastArguments has this opto,
         // but we do it explicitly, to make the boolean check simpler
         if (newType.returnType() == targetType.returnType()) {
             return target;
         }
-        
+
         // we don't allow the to/from boolean conversions of explicitCastArguments
         if (newType.returnType() == boolean.class || targetType.returnType() == boolean.class) {
             throw new ClassCastException("Cannot cast " + targetType.returnType() + " to " + newType.returnType());
         }
-        
+
         // null return values are not possible for our arguments.
         return MethodHandles.explicitCastArguments(target, target.type().changeReturnType(newType.returnType()));
     }

+ 106 - 275
modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java

@@ -21,6 +21,7 @@ package org.elasticsearch.painless;
 
 import org.elasticsearch.painless.spi.Whitelist;
 import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
@@ -48,35 +49,6 @@ public final class Definition {
 
     private static final Pattern TYPE_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][._a-zA-Z0-9]*$");
 
-    /** Some native types as constants: */
-    public final Type voidType;
-    public final Type booleanType;
-    public final Type BooleanType;
-    public final Type byteType;
-    public final Type ByteType;
-    public final Type shortType;
-    public final Type ShortType;
-    public final Type intType;
-    public final Type IntegerType;
-    public final Type longType;
-    public final Type LongType;
-    public final Type floatType;
-    public final Type FloatType;
-    public final Type doubleType;
-    public final Type DoubleType;
-    public final Type charType;
-    public final Type CharacterType;
-    public final Type ObjectType;
-    public final Type DefType;
-    public final Type NumberType;
-    public final Type StringType;
-    public final Type ExceptionType;
-    public final Type PatternType;
-    public final Type MatcherType;
-    public final Type IteratorType;
-    public final Type ArrayListType;
-    public final Type HashMapType;
-
     /** Marker class for def type to be used during type analysis. */
     public static final class def {
         private def() {
@@ -84,53 +56,6 @@ public final class Definition {
         }
     }
 
-    public static final class Type {
-        public final String name;
-        public final int dimensions;
-        public final boolean dynamic;
-        public final Struct struct;
-        public final Class<?> clazz;
-        public final org.objectweb.asm.Type type;
-
-        private Type(final String name, final int dimensions, final boolean dynamic,
-                     final Struct struct, final Class<?> clazz, final org.objectweb.asm.Type type) {
-            this.name = name;
-            this.dimensions = dimensions;
-            this.dynamic = dynamic;
-            this.struct = struct;
-            this.clazz = clazz;
-            this.type = type;
-        }
-
-        @Override
-        public boolean equals(final Object object) {
-            if (this == object) {
-                return true;
-            }
-
-            if (object == null || getClass() != object.getClass()) {
-                return false;
-            }
-
-            final Type type = (Type)object;
-
-            return this.type.equals(type.type) && struct.equals(type.struct);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = struct.hashCode();
-            result = 31 * result + type.hashCode();
-
-            return result;
-        }
-
-        @Override
-        public String toString() {
-            return name;
-        }
-    }
-
     public static class Method {
         public final String name;
         public final Struct owner;
@@ -431,21 +356,6 @@ public final class Definition {
         }
     }
 
-    /** Returns whether or not a non-array type exists. */
-    public boolean isSimpleType(final String name) {
-        return structsMap.containsKey(name);
-    }
-
-    /** Gets the type given by its name */
-    public Type getType(final String name) {
-        return getTypeInternal(name);
-    }
-
-    /** Creates an array type from the given Struct. */
-    public Type getType(final Struct struct, final int dimensions) {
-        return getTypeInternal(struct, dimensions);
-    }
-
     public static Class<?> getBoxedType(Class<?> clazz) {
         if (clazz == boolean.class) {
             return Boolean.class;
@@ -502,6 +412,10 @@ public final class Definition {
                clazz == String.class;
     }
 
+    public Class<?> getClassFromBinaryName(String painlessType) {
+        return painlessTypesToJavaClasses.get(painlessType.replace('$', '.'));
+    }
+
     public static Class<?> ObjectClassTodefClass(Class<?> clazz) {
         if (clazz.isArray()) {
             Class<?> component = clazz.getComponentType();
@@ -590,53 +504,6 @@ public final class Definition {
         return clazz.getCanonicalName().replace('$', '.');
     }
 
-    public Type ClassToType(Class<?> clazz) {
-        if (clazz == null) {
-            return null;
-        } else if (clazz.isArray()) {
-            Class<?> component = clazz.getComponentType();
-            int dimensions = 1;
-
-            while (component.isArray()) {
-                component = component.getComponentType();
-                ++dimensions;
-            }
-
-            if (component == def.class) {
-                return getType(structsMap.get(def.class.getSimpleName()), dimensions);
-            } else {
-                return getType(structsMap.get(ClassToName(component)), dimensions);
-            }
-        } else if (clazz == def.class) {
-            return getType(structsMap.get(def.class.getSimpleName()), 0);
-        }
-
-        return getType(structsMap.get(ClassToName(clazz)), 0);
-    }
-
-    public Struct RuntimeClassToStruct(Class<?> clazz) {
-        return structsMap.get(ClassToName(clazz));
-    }
-
-    public static Class<?> TypeToClass(Type type) {
-        if (def.class.getSimpleName().equals(type.struct.name)) {
-            return ObjectClassTodefClass(type.clazz);
-        }
-
-        return type.clazz;
-    }
-
-    public Class<?> getClassFromBinaryName(String name) {
-        Struct struct = structsMap.get(name.replace('$', '.'));
-
-        return struct == null ? null : struct.clazz;
-    }
-
-    /** Collection of all simple types. Used by {@code PainlessDocGenerator} to generate an API reference. */
-    Collection<Type> allSimpleTypes() {
-        return simpleTypesMap.values();
-    }
-
     private static String buildMethodCacheKey(String structName, String methodName, List<Class<?>> arguments) {
         StringBuilder key = new StringBuilder();
         key.append(structName);
@@ -653,21 +520,21 @@ public final class Definition {
         return structName + fieldName + typeName;
     }
 
-    // INTERNAL IMPLEMENTATION:
+    public Collection<Struct> getStructs() {
+        return javaClassesToPainlessStructs.values();
+    }
 
-    private final Map<String, Struct> structsMap;
-    private final Map<String, Type> simpleTypesMap;
+    private final Map<String, Class<?>> painlessTypesToJavaClasses;
+    private final Map<Class<?>, Struct> javaClassesToPainlessStructs;
 
     public Definition(List<Whitelist> whitelists) {
-        structsMap = new HashMap<>();
-        simpleTypesMap = new HashMap<>();
+        painlessTypesToJavaClasses = new HashMap<>();
+        javaClassesToPainlessStructs = new HashMap<>();
 
-        Map<Class<?>, Struct> javaClassesToPainlessStructs = new HashMap<>();
         String origin = null;
 
-        // add the universal def type
-        structsMap.put(def.class.getSimpleName(),
-                new Struct(def.class.getSimpleName(), Object.class, org.objectweb.asm.Type.getType(Object.class)));
+        painlessTypesToJavaClasses.put("def", def.class);
+        javaClassesToPainlessStructs.put(def.class, new Struct("def", Object.class, Type.getType(Object.class)));
 
         try {
             // first iteration collects all the Painless type names that
@@ -675,7 +542,7 @@ public final class Definition {
             for (Whitelist whitelist : whitelists) {
                 for (Whitelist.Struct whitelistStruct : whitelist.whitelistStructs) {
                     String painlessTypeName = whitelistStruct.javaClassName.replace('$', '.');
-                    Struct painlessStruct = structsMap.get(painlessTypeName);
+                    Struct painlessStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(painlessTypeName));
 
                     if (painlessStruct != null && painlessStruct.clazz.getName().equals(whitelistStruct.javaClassName) == false) {
                         throw new IllegalArgumentException("struct [" + painlessStruct.name + "] cannot represent multiple classes " +
@@ -685,7 +552,7 @@ public final class Definition {
                     origin = whitelistStruct.origin;
                     addStruct(whitelist.javaClassLoader, whitelistStruct);
 
-                    painlessStruct = structsMap.get(painlessTypeName);
+                    painlessStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(painlessTypeName));
                     javaClassesToPainlessStructs.put(painlessStruct.clazz, painlessStruct);
                 }
             }
@@ -719,13 +586,8 @@ public final class Definition {
 
         // goes through each Painless struct and determines the inheritance list,
         // and then adds all inherited types to the Painless struct's whitelist
-        for (Map.Entry<String, Struct> painlessNameStructEntry : structsMap.entrySet()) {
-            String painlessStructName = painlessNameStructEntry.getKey();
-            Struct painlessStruct = painlessNameStructEntry.getValue();
-
-            if (painlessStruct.name.equals(painlessStructName) == false) {
-                continue;
-            }
+        for (Class<?> javaClass : javaClassesToPainlessStructs.keySet()) {
+            Struct painlessStruct = javaClassesToPainlessStructs.get(javaClass);
 
             List<String> painlessSuperStructs = new ArrayList<>();
             Class<?> javaSuperClass = painlessStruct.clazz.getSuperclass();
@@ -782,52 +644,14 @@ public final class Definition {
         }
 
         // precompute runtime classes
-        for (String painlessStructName : structsMap.keySet()) {
-            Struct painlessStruct = structsMap.get(painlessStructName);
-
-            if (painlessStruct.name.equals(painlessStructName) == false) {
-                continue;
-            }
-
+        for (Struct painlessStruct : javaClassesToPainlessStructs.values()) {
             addRuntimeClass(painlessStruct);
         }
 
         // copy all structs to make them unmodifiable for outside users:
-        for (Map.Entry<String,Struct> entry : structsMap.entrySet()) {
-            if (entry.getKey().equals(entry.getValue().name) == false) {
-                continue;
-            }
-
+        for (Map.Entry<Class<?>,Struct> entry : javaClassesToPainlessStructs.entrySet()) {
             entry.setValue(entry.getValue().freeze(computeFunctionalInterfaceMethod(entry.getValue())));
         }
-
-        voidType = getType("void");
-        booleanType = getType("boolean");
-        BooleanType = getType("Boolean");
-        byteType = getType("byte");
-        ByteType = getType("Byte");
-        shortType = getType("short");
-        ShortType = getType("Short");
-        intType = getType("int");
-        IntegerType = getType("Integer");
-        longType = getType("long");
-        LongType = getType("Long");
-        floatType = getType("float");
-        FloatType = getType("Float");
-        doubleType = getType("double");
-        DoubleType = getType("Double");
-        charType = getType("char");
-        CharacterType = getType("Character");
-        ObjectType = getType("Object");
-        DefType = getType(def.class.getSimpleName());
-        NumberType = getType("Number");
-        StringType = getType("String");
-        ExceptionType = getType("Exception");
-        PatternType = getType("Pattern");
-        MatcherType = getType("Matcher");
-        IteratorType = getType("Iterator");
-        ArrayListType = getType("ArrayList");
-        HashMapType = getType("HashMap");
     }
 
     private void addStruct(ClassLoader whitelistClassLoader, Whitelist.Struct whitelistStruct) {
@@ -864,35 +688,45 @@ public final class Definition {
             }
         }
 
-        Struct existingStruct = structsMap.get(painlessTypeName);
+        Struct existingStruct = javaClassesToPainlessStructs.get(javaClass);
 
         if (existingStruct == null) {
             Struct struct = new Struct(painlessTypeName, javaClass, org.objectweb.asm.Type.getType(javaClass));
-            structsMap.put(painlessTypeName, struct);
-
-            if (whitelistStruct.onlyFQNJavaClassName) {
-                simpleTypesMap.put(painlessTypeName, getType(painlessTypeName));
-            } else if (simpleTypesMap.containsKey(importedPainlessTypeName) == false) {
-                simpleTypesMap.put(importedPainlessTypeName, getType(painlessTypeName));
-                structsMap.put(importedPainlessTypeName, struct);
-            } else {
-                throw new IllegalArgumentException("duplicate short name [" + importedPainlessTypeName + "] " +
-                        "found for struct [" + painlessTypeName + "]");
-            }
+            painlessTypesToJavaClasses.put(painlessTypeName, javaClass);
+            javaClassesToPainlessStructs.put(javaClass, struct);
         } else if (existingStruct.clazz.equals(javaClass) == false) {
             throw new IllegalArgumentException("struct [" + painlessTypeName + "] is used to " +
                     "illegally represent multiple java classes [" + whitelistStruct.javaClassName + "] and " +
                     "[" + existingStruct.clazz.getName() + "]");
-        } else if (whitelistStruct.onlyFQNJavaClassName && simpleTypesMap.containsKey(importedPainlessTypeName) &&
-                simpleTypesMap.get(importedPainlessTypeName).clazz == javaClass ||
-                whitelistStruct.onlyFQNJavaClassName == false && (simpleTypesMap.containsKey(importedPainlessTypeName) == false ||
-                simpleTypesMap.get(importedPainlessTypeName).clazz != javaClass)) {
-            throw new IllegalArgumentException("inconsistent only_fqn parameters found for type [" + painlessTypeName + "]");
+        }
+
+        if (painlessTypeName.equals(importedPainlessTypeName)) {
+            if (whitelistStruct.onlyFQNJavaClassName == false) {
+                throw new IllegalArgumentException("must use only_fqn parameter on type [" + painlessTypeName + "] with no package");
+            }
+        } else {
+            Class<?> importedJavaClass = painlessTypesToJavaClasses.get(importedPainlessTypeName);
+
+            if (importedJavaClass == null) {
+                if (whitelistStruct.onlyFQNJavaClassName == false) {
+                    if (existingStruct != null) {
+                        throw new IllegalArgumentException("inconsistent only_fqn parameters found for type [" + painlessTypeName + "]");
+                    }
+
+                    painlessTypesToJavaClasses.put(importedPainlessTypeName, javaClass);
+                }
+            } else if (importedJavaClass.equals(javaClass) == false) {
+                throw new IllegalArgumentException("imported name [" + painlessTypeName + "] is used to " +
+                    "illegally represent multiple java classes [" + whitelistStruct.javaClassName + "] " +
+                    "and [" + importedJavaClass.getName() + "]");
+            } else if (whitelistStruct.onlyFQNJavaClassName) {
+                throw new IllegalArgumentException("inconsistent only_fqn parameters found for type [" + painlessTypeName + "]");
+            }
         }
     }
 
     private void addConstructor(String ownerStructName, Whitelist.Constructor whitelistConstructor) {
-        Struct ownerStruct = structsMap.get(ownerStructName);
+        Struct ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
 
         if (ownerStruct == null) {
             throw new IllegalArgumentException("owner struct [" + ownerStructName + "] not defined for constructor with " +
@@ -906,7 +740,7 @@ public final class Definition {
             String painlessParameterTypeName = whitelistConstructor.painlessParameterTypeNames.get(parameterCount);
 
             try {
-                Class<?> painlessParameterClass = TypeToClass(getTypeInternal(painlessParameterTypeName));
+                Class<?> painlessParameterClass = getJavaClassFromPainlessType(painlessParameterTypeName);
 
                 painlessParametersTypes.add(painlessParameterClass);
                 javaClassParameters[parameterCount] = defClassToObjectClass(painlessParameterClass);
@@ -952,7 +786,7 @@ public final class Definition {
     }
 
     private void addMethod(ClassLoader whitelistClassLoader, String ownerStructName, Whitelist.Method whitelistMethod) {
-        Struct ownerStruct = structsMap.get(ownerStructName);
+        Struct ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
 
         if (ownerStruct == null) {
             throw new IllegalArgumentException("owner struct [" + ownerStructName + "] not defined for method with " +
@@ -991,7 +825,7 @@ public final class Definition {
             String painlessParameterTypeName = whitelistMethod.painlessParameterTypeNames.get(parameterCount);
 
             try {
-                Class<?> painlessParameterClass = TypeToClass(getTypeInternal(painlessParameterTypeName));
+                Class<?> painlessParameterClass = getJavaClassFromPainlessType(painlessParameterTypeName);
 
                 painlessParametersTypes.add(painlessParameterClass);
                 javaClassParameters[parameterCount + augmentedOffset] = defClassToObjectClass(painlessParameterClass);
@@ -1016,7 +850,7 @@ public final class Definition {
         Class<?> painlessReturnClass;
 
         try {
-            painlessReturnClass = TypeToClass(getTypeInternal(whitelistMethod.painlessReturnTypeName));
+            painlessReturnClass = getJavaClassFromPainlessType(whitelistMethod.painlessReturnTypeName);
         } catch (IllegalArgumentException iae) {
             throw new IllegalArgumentException("struct not defined for return type [" + whitelistMethod.painlessReturnTypeName + "] " +
                     "with owner struct [" + ownerStructName + "] and method with name [" + whitelistMethod.javaMethodName + "] " +
@@ -1088,7 +922,7 @@ public final class Definition {
     }
 
     private void addField(String ownerStructName, Whitelist.Field whitelistField) {
-        Struct ownerStruct = structsMap.get(ownerStructName);
+        Struct ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName));
 
         if (ownerStruct == null) {
             throw new IllegalArgumentException("owner struct [" + ownerStructName + "] not defined for method with " +
@@ -1112,7 +946,7 @@ public final class Definition {
         Class<?> painlessFieldClass;
 
         try {
-            painlessFieldClass = TypeToClass(getTypeInternal(whitelistField.painlessFieldTypeName));
+            painlessFieldClass = getJavaClassFromPainlessType(whitelistField.painlessFieldTypeName);
         } catch (IllegalArgumentException iae) {
             throw new IllegalArgumentException("struct not defined for return type [" + whitelistField.painlessFieldTypeName + "] " +
                 "with owner struct [" + ownerStructName + "] and field with name [" + whitelistField.javaFieldName + "]", iae);
@@ -1169,14 +1003,14 @@ public final class Definition {
     }
 
     private void copyStruct(String struct, List<String> children) {
-        final Struct owner = structsMap.get(struct);
+        final Struct owner = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(struct));
 
         if (owner == null) {
             throw new IllegalArgumentException("Owner struct [" + struct + "] not defined for copy.");
         }
 
         for (int count = 0; count < children.size(); ++count) {
-            final Struct child = structsMap.get(children.get(count));
+            final Struct child = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(children.get(count)));
 
             if (child == null) {
                 throw new IllegalArgumentException("Child struct [" + children.get(count) + "]" +
@@ -1340,71 +1174,68 @@ public final class Definition {
         return painless;
     }
 
-    private Type getTypeInternal(String name) {
-        // simple types (e.g. 0 array dimensions) are a simple hash lookup for speed
-        Type simple = simpleTypesMap.get(name);
+    public boolean isSimplePainlessType(String painlessType) {
+        return painlessTypesToJavaClasses.containsKey(painlessType);
+    }
 
-        if (simple != null) {
-            return simple;
-        }
+    public Struct getPainlessStructFromJavaClass(Class<?> clazz) {
+        return javaClassesToPainlessStructs.get(clazz);
+    }
 
-        int dimensions = getDimensions(name);
-        String structstr = dimensions == 0 ? name : name.substring(0, name.indexOf('['));
-        Struct struct = structsMap.get(structstr);
+    public Class<?> getJavaClassFromPainlessType(String painlessType) {
+        Class<?> javaClass = painlessTypesToJavaClasses.get(painlessType);
 
-        if (struct == null) {
-            throw new IllegalArgumentException("The struct with name [" + name + "] has not been defined.");
+        if (javaClass != null) {
+            return javaClass;
         }
+        int arrayDimensions = 0;
+        int arrayIndex = painlessType.indexOf('[');
 
-        return getTypeInternal(struct, dimensions);
-    }
-
-    private Type getTypeInternal(Struct struct, int dimensions) {
-        String name = struct.name;
-        org.objectweb.asm.Type type = struct.type;
-        Class<?> clazz = struct.clazz;
+        if (arrayIndex != -1) {
+            int length = painlessType.length();
 
-        if (dimensions > 0) {
-            StringBuilder builder = new StringBuilder(name);
-            char[] brackets = new char[dimensions];
-
-            for (int count = 0; count < dimensions; ++count) {
-                builder.append("[]");
-                brackets[count] = '[';
+            while (arrayIndex < length) {
+                if (painlessType.charAt(arrayIndex) == '[' && ++arrayIndex < length && painlessType.charAt(arrayIndex++) == ']') {
+                    ++arrayDimensions;
+                } else {
+                    throw new IllegalArgumentException("invalid painless type [" + painlessType + "].");
+                }
             }
 
-            String descriptor = new String(brackets) + struct.type.getDescriptor();
-
-            name = builder.toString();
-            type = org.objectweb.asm.Type.getType(descriptor);
-
-            try {
-                clazz = Class.forName(type.getInternalName().replace('/', '.'));
-            } catch (ClassNotFoundException exception) {
-                throw new IllegalArgumentException("The class [" + type.getInternalName() + "]" +
-                    " could not be found to create type [" + name + "].");
+            painlessType = painlessType.substring(0, painlessType.indexOf('['));
+            javaClass = painlessTypesToJavaClasses.get(painlessType);
+
+            char braces[] = new char[arrayDimensions];
+            Arrays.fill(braces, '[');
+            String descriptor = new String(braces);
+
+            if (javaClass == boolean.class) {
+                descriptor += "Z";
+            } else if (javaClass == byte.class) {
+                descriptor += "B";
+            } else if (javaClass == short.class) {
+                descriptor += "S";
+            } else if (javaClass == char.class) {
+                descriptor += "C";
+            } else if (javaClass == int.class) {
+                descriptor += "I";
+            } else if (javaClass == long.class) {
+                descriptor += "J";
+            } else if (javaClass == float.class) {
+                descriptor += "F";
+            } else if (javaClass == double.class) {
+                descriptor += "D";
+            } else {
+                descriptor += "L" + javaClass.getName() + ";";
             }
-        }
-
-        return new Type(name, dimensions, def.class.getSimpleName().equals(name), struct, clazz, type);
-    }
 
-    private int getDimensions(String name) {
-        int dimensions = 0;
-        int index = name.indexOf('[');
-
-        if (index != -1) {
-            int length = name.length();
-
-            while (index < length) {
-                if (name.charAt(index) == '[' && ++index < length && name.charAt(index++) == ']') {
-                    ++dimensions;
-                } else {
-                    throw new IllegalArgumentException("Invalid array braces in canonical name [" + name + "].");
-                }
+            try {
+                return Class.forName(descriptor);
+            } catch (ClassNotFoundException cnfe) {
+                throw new IllegalStateException("invalid painless type [" + painlessType + "]", cnfe);
             }
         }
 
-        return dimensions;
+        throw new IllegalArgumentException("invalid painless type [" + painlessType + "]");
     }
 }

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

@@ -78,7 +78,7 @@ public class FunctionRef {
      * @param numCaptures number of captured arguments
      */
     public FunctionRef(Definition definition, Class<?> expected, String type, String call, int numCaptures) {
-        this(expected, definition.ClassToType(expected).struct.functionalMethod,
+        this(expected, definition.getPainlessStructFromJavaClass(expected).functionalMethod,
                 lookup(definition, expected, type, call, numCaptures > 0), numCaptures);
     }
 
@@ -162,14 +162,14 @@ public class FunctionRef {
                                             String type, String call, boolean receiverCaptured) {
         // check its really a functional interface
         // for e.g. Comparable
-        Method method = definition.ClassToType(expected).struct.functionalMethod;
+        Method method = definition.getPainlessStructFromJavaClass(expected).functionalMethod;
         if (method == null) {
             throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
                                                "to [" + Definition.ClassToName(expected) + "], not a functional interface");
         }
 
         // lookup requested method
-        Definition.Struct struct = definition.getType(type).struct;
+        Definition.Struct struct = definition.getPainlessStructFromJavaClass(definition.getJavaClassFromPainlessType(type));
         final Definition.Method impl;
         // ctor ref
         if ("new".equals(call)) {

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

@@ -54,7 +54,7 @@ public class PainlessExplainError extends Error {
         if (objectToExplain != null) {
             toString = objectToExplain.toString();
             javaClassName = objectToExplain.getClass().getName();
-            Definition.Struct struct = definition.ClassToType(objectToExplain.getClass()).struct;
+            Definition.Struct struct = definition.getPainlessStructFromJavaClass(objectToExplain.getClass());
             if (struct != null) {
                 painlessClassName = struct.name;
             }

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

@@ -31,8 +31,8 @@ import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.settings.SettingsFilter;
 import org.elasticsearch.painless.spi.PainlessExtension;
 import org.elasticsearch.painless.spi.Whitelist;
-import org.elasticsearch.plugins.ActionPlugin;
 import org.elasticsearch.painless.spi.WhitelistLoader;
+import org.elasticsearch.plugins.ActionPlugin;
 import org.elasticsearch.plugins.ExtensiblePlugin;
 import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.plugins.ScriptPlugin;

+ 7 - 11
modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java

@@ -179,22 +179,18 @@ public class ScriptClassInfo {
 
     private static Class<?> definitionTypeForClass(Definition definition, Class<?> type,
             Function<Class<?>, String> unknownErrorMessageSource) {
-        int dimensions = 0;
+        type = Definition.ObjectClassTodefClass(type);
         Class<?> componentType = type;
+
         while (componentType.isArray()) {
-            dimensions++;
             componentType = componentType.getComponentType();
         }
-        Definition.Struct struct;
-        if (componentType == Object.class) {
-            struct = definition.getType("def").struct;
-        } else {
-            if (definition.RuntimeClassToStruct(componentType) == null) {
-                throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType));
-            }
-            struct = definition.RuntimeClassToStruct(componentType);
+
+        if (definition.getPainlessStructFromJavaClass(componentType) == null) {
+            throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType));
         }
-        return Definition.TypeToClass(definition.getType(struct, dimensions));
+
+        return type;
     }
 
     private static String[] readArgumentNamesConstant(Class<?> iface) {

+ 2 - 2
modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java

@@ -74,8 +74,8 @@ final class EnhancedPainlessLexer extends PainlessLexer {
     }
 
     @Override
-    protected boolean isSimpleType(String name) {
-        return definition.isSimpleType(name);
+    protected boolean isType(String name) {
+        return definition.isSimplePainlessType(name);
     }
 
     @Override

+ 48 - 41
modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessLexer.java

@@ -1,9 +1,16 @@
 // ANTLR GENERATED CODE: DO NOT EDIT
 package org.elasticsearch.painless.antlr;
-import org.antlr.v4.runtime.Lexer;
+
 import org.antlr.v4.runtime.CharStream;
-import org.antlr.v4.runtime.*;
-import org.antlr.v4.runtime.atn.*;
+import org.antlr.v4.runtime.Lexer;
+import org.antlr.v4.runtime.RuleContext;
+import org.antlr.v4.runtime.RuntimeMetaData;
+import org.antlr.v4.runtime.Vocabulary;
+import org.antlr.v4.runtime.VocabularyImpl;
+import org.antlr.v4.runtime.atn.ATN;
+import org.antlr.v4.runtime.atn.ATNDeserializer;
+import org.antlr.v4.runtime.atn.LexerATNSimulator;
+import org.antlr.v4.runtime.atn.PredictionContextCache;
 import org.antlr.v4.runtime.dfa.DFA;
 
 @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
@@ -14,16 +21,16 @@ abstract class PainlessLexer extends Lexer {
   protected static final PredictionContextCache _sharedContextCache =
     new PredictionContextCache();
   public static final int
-    WS=1, COMMENT=2, LBRACK=3, RBRACK=4, LBRACE=5, RBRACE=6, LP=7, RP=8, DOT=9, 
-    NSDOT=10, COMMA=11, SEMICOLON=12, IF=13, IN=14, ELSE=15, WHILE=16, DO=17, 
-    FOR=18, CONTINUE=19, BREAK=20, RETURN=21, NEW=22, TRY=23, CATCH=24, THROW=25, 
-    THIS=26, INSTANCEOF=27, BOOLNOT=28, BWNOT=29, MUL=30, DIV=31, REM=32, 
-    ADD=33, SUB=34, LSH=35, RSH=36, USH=37, LT=38, LTE=39, GT=40, GTE=41, 
-    EQ=42, EQR=43, NE=44, NER=45, BWAND=46, XOR=47, BWOR=48, BOOLAND=49, BOOLOR=50, 
-    COND=51, COLON=52, ELVIS=53, REF=54, ARROW=55, FIND=56, MATCH=57, INCR=58, 
-    DECR=59, ASSIGN=60, AADD=61, ASUB=62, AMUL=63, ADIV=64, AREM=65, AAND=66, 
-    AXOR=67, AOR=68, ALSH=69, ARSH=70, AUSH=71, OCTAL=72, HEX=73, INTEGER=74, 
-    DECIMAL=75, STRING=76, REGEX=77, TRUE=78, FALSE=79, NULL=80, TYPE=81, 
+    WS=1, COMMENT=2, LBRACK=3, RBRACK=4, LBRACE=5, RBRACE=6, LP=7, RP=8, DOT=9,
+    NSDOT=10, COMMA=11, SEMICOLON=12, IF=13, IN=14, ELSE=15, WHILE=16, DO=17,
+    FOR=18, CONTINUE=19, BREAK=20, RETURN=21, NEW=22, TRY=23, CATCH=24, THROW=25,
+    THIS=26, INSTANCEOF=27, BOOLNOT=28, BWNOT=29, MUL=30, DIV=31, REM=32,
+    ADD=33, SUB=34, LSH=35, RSH=36, USH=37, LT=38, LTE=39, GT=40, GTE=41,
+    EQ=42, EQR=43, NE=44, NER=45, BWAND=46, XOR=47, BWOR=48, BOOLAND=49, BOOLOR=50,
+    COND=51, COLON=52, ELVIS=53, REF=54, ARROW=55, FIND=56, MATCH=57, INCR=58,
+    DECR=59, ASSIGN=60, AADD=61, ASUB=62, AMUL=63, ADIV=64, AREM=65, AAND=66,
+    AXOR=67, AOR=68, ALSH=69, ARSH=70, AUSH=71, OCTAL=72, HEX=73, INTEGER=74,
+    DECIMAL=75, STRING=76, REGEX=77, TRUE=78, FALSE=79, NULL=80, TYPE=81,
     ID=82, DOTINTEGER=83, DOTID=84;
   public static final int AFTER_DOT = 1;
   public static String[] modeNames = {
@@ -31,39 +38,39 @@ abstract class PainlessLexer extends Lexer {
   };
 
   public static final String[] ruleNames = {
-    "WS", "COMMENT", "LBRACK", "RBRACK", "LBRACE", "RBRACE", "LP", "RP", "DOT", 
-    "NSDOT", "COMMA", "SEMICOLON", "IF", "IN", "ELSE", "WHILE", "DO", "FOR", 
-    "CONTINUE", "BREAK", "RETURN", "NEW", "TRY", "CATCH", "THROW", "THIS", 
-    "INSTANCEOF", "BOOLNOT", "BWNOT", "MUL", "DIV", "REM", "ADD", "SUB", "LSH", 
-    "RSH", "USH", "LT", "LTE", "GT", "GTE", "EQ", "EQR", "NE", "NER", "BWAND", 
-    "XOR", "BWOR", "BOOLAND", "BOOLOR", "COND", "COLON", "ELVIS", "REF", "ARROW", 
-    "FIND", "MATCH", "INCR", "DECR", "ASSIGN", "AADD", "ASUB", "AMUL", "ADIV", 
-    "AREM", "AAND", "AXOR", "AOR", "ALSH", "ARSH", "AUSH", "OCTAL", "HEX", 
-    "INTEGER", "DECIMAL", "STRING", "REGEX", "TRUE", "FALSE", "NULL", "TYPE", 
+    "WS", "COMMENT", "LBRACK", "RBRACK", "LBRACE", "RBRACE", "LP", "RP", "DOT",
+    "NSDOT", "COMMA", "SEMICOLON", "IF", "IN", "ELSE", "WHILE", "DO", "FOR",
+    "CONTINUE", "BREAK", "RETURN", "NEW", "TRY", "CATCH", "THROW", "THIS",
+    "INSTANCEOF", "BOOLNOT", "BWNOT", "MUL", "DIV", "REM", "ADD", "SUB", "LSH",
+    "RSH", "USH", "LT", "LTE", "GT", "GTE", "EQ", "EQR", "NE", "NER", "BWAND",
+    "XOR", "BWOR", "BOOLAND", "BOOLOR", "COND", "COLON", "ELVIS", "REF", "ARROW",
+    "FIND", "MATCH", "INCR", "DECR", "ASSIGN", "AADD", "ASUB", "AMUL", "ADIV",
+    "AREM", "AAND", "AXOR", "AOR", "ALSH", "ARSH", "AUSH", "OCTAL", "HEX",
+    "INTEGER", "DECIMAL", "STRING", "REGEX", "TRUE", "FALSE", "NULL", "TYPE",
     "ID", "DOTINTEGER", "DOTID"
   };
 
   private static final String[] _LITERAL_NAMES = {
-    null, null, null, "'{'", "'}'", "'['", "']'", "'('", "')'", "'.'", "'?.'", 
-    "','", "';'", "'if'", "'in'", "'else'", "'while'", "'do'", "'for'", "'continue'", 
-    "'break'", "'return'", "'new'", "'try'", "'catch'", "'throw'", "'this'", 
-    "'instanceof'", "'!'", "'~'", "'*'", "'/'", "'%'", "'+'", "'-'", "'<<'", 
-    "'>>'", "'>>>'", "'<'", "'<='", "'>'", "'>='", "'=='", "'==='", "'!='", 
-    "'!=='", "'&'", "'^'", "'|'", "'&&'", "'||'", "'?'", "':'", "'?:'", "'::'", 
-    "'->'", "'=~'", "'==~'", "'++'", "'--'", "'='", "'+='", "'-='", "'*='", 
-    "'/='", "'%='", "'&='", "'^='", "'|='", "'<<='", "'>>='", "'>>>='", null, 
+    null, null, null, "'{'", "'}'", "'['", "']'", "'('", "')'", "'.'", "'?.'",
+    "','", "';'", "'if'", "'in'", "'else'", "'while'", "'do'", "'for'", "'continue'",
+    "'break'", "'return'", "'new'", "'try'", "'catch'", "'throw'", "'this'",
+    "'instanceof'", "'!'", "'~'", "'*'", "'/'", "'%'", "'+'", "'-'", "'<<'",
+    "'>>'", "'>>>'", "'<'", "'<='", "'>'", "'>='", "'=='", "'==='", "'!='",
+    "'!=='", "'&'", "'^'", "'|'", "'&&'", "'||'", "'?'", "':'", "'?:'", "'::'",
+    "'->'", "'=~'", "'==~'", "'++'", "'--'", "'='", "'+='", "'-='", "'*='",
+    "'/='", "'%='", "'&='", "'^='", "'|='", "'<<='", "'>>='", "'>>>='", null,
     null, null, null, null, null, "'true'", "'false'", "'null'"
   };
   private static final String[] _SYMBOLIC_NAMES = {
-    null, "WS", "COMMENT", "LBRACK", "RBRACK", "LBRACE", "RBRACE", "LP", "RP", 
-    "DOT", "NSDOT", "COMMA", "SEMICOLON", "IF", "IN", "ELSE", "WHILE", "DO", 
-    "FOR", "CONTINUE", "BREAK", "RETURN", "NEW", "TRY", "CATCH", "THROW", 
-    "THIS", "INSTANCEOF", "BOOLNOT", "BWNOT", "MUL", "DIV", "REM", "ADD", 
-    "SUB", "LSH", "RSH", "USH", "LT", "LTE", "GT", "GTE", "EQ", "EQR", "NE", 
-    "NER", "BWAND", "XOR", "BWOR", "BOOLAND", "BOOLOR", "COND", "COLON", "ELVIS", 
-    "REF", "ARROW", "FIND", "MATCH", "INCR", "DECR", "ASSIGN", "AADD", "ASUB", 
-    "AMUL", "ADIV", "AREM", "AAND", "AXOR", "AOR", "ALSH", "ARSH", "AUSH", 
-    "OCTAL", "HEX", "INTEGER", "DECIMAL", "STRING", "REGEX", "TRUE", "FALSE", 
+    null, "WS", "COMMENT", "LBRACK", "RBRACK", "LBRACE", "RBRACE", "LP", "RP",
+    "DOT", "NSDOT", "COMMA", "SEMICOLON", "IF", "IN", "ELSE", "WHILE", "DO",
+    "FOR", "CONTINUE", "BREAK", "RETURN", "NEW", "TRY", "CATCH", "THROW",
+    "THIS", "INSTANCEOF", "BOOLNOT", "BWNOT", "MUL", "DIV", "REM", "ADD",
+    "SUB", "LSH", "RSH", "USH", "LT", "LTE", "GT", "GTE", "EQ", "EQR", "NE",
+    "NER", "BWAND", "XOR", "BWOR", "BOOLAND", "BOOLOR", "COND", "COLON", "ELVIS",
+    "REF", "ARROW", "FIND", "MATCH", "INCR", "DECR", "ASSIGN", "AADD", "ASUB",
+    "AMUL", "ADIV", "AREM", "AAND", "AXOR", "AOR", "ALSH", "ARSH", "AUSH",
+    "OCTAL", "HEX", "INTEGER", "DECIMAL", "STRING", "REGEX", "TRUE", "FALSE",
     "NULL", "TYPE", "ID", "DOTINTEGER", "DOTID"
   };
   public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
@@ -106,7 +113,7 @@ abstract class PainlessLexer extends Lexer {
    * See also
    * <a href="https://en.wikipedia.org/wiki/The_lexer_hack">The lexer hack</a>.
    */
-  protected abstract boolean isSimpleType(String name);
+  protected abstract boolean isType(String name);
 
   /**
    * Is the preceding {@code /} a the beginning of a regex (true) or a division
@@ -164,7 +171,7 @@ abstract class PainlessLexer extends Lexer {
   private boolean TYPE_sempred(RuleContext _localctx, int predIndex) {
     switch (predIndex) {
     case 2:
-      return  isSimpleType(getText()) ;
+      return  isType(getText()) ;
     }
     return true;
   }

+ 79 - 64
modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java

@@ -1,9 +1,24 @@
 // ANTLR GENERATED CODE: DO NOT EDIT
 package org.elasticsearch.painless.antlr;
-import org.antlr.v4.runtime.atn.*;
+
+import org.antlr.v4.runtime.FailedPredicateException;
+import org.antlr.v4.runtime.NoViableAltException;
+import org.antlr.v4.runtime.Parser;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.RuleContext;
+import org.antlr.v4.runtime.RuntimeMetaData;
+import org.antlr.v4.runtime.TokenStream;
+import org.antlr.v4.runtime.Vocabulary;
+import org.antlr.v4.runtime.VocabularyImpl;
+import org.antlr.v4.runtime.atn.ATN;
+import org.antlr.v4.runtime.atn.ATNDeserializer;
+import org.antlr.v4.runtime.atn.ParserATNSimulator;
+import org.antlr.v4.runtime.atn.PredictionContextCache;
 import org.antlr.v4.runtime.dfa.DFA;
-import org.antlr.v4.runtime.*;
-import org.antlr.v4.runtime.tree.*;
+import org.antlr.v4.runtime.tree.ParseTreeVisitor;
+import org.antlr.v4.runtime.tree.TerminalNode;
+
 import java.util.List;
 
 @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
@@ -14,57 +29,57 @@ class PainlessParser extends Parser {
   protected static final PredictionContextCache _sharedContextCache =
     new PredictionContextCache();
   public static final int
-    WS=1, COMMENT=2, LBRACK=3, RBRACK=4, LBRACE=5, RBRACE=6, LP=7, RP=8, DOT=9, 
-    NSDOT=10, COMMA=11, SEMICOLON=12, IF=13, IN=14, ELSE=15, WHILE=16, DO=17, 
-    FOR=18, CONTINUE=19, BREAK=20, RETURN=21, NEW=22, TRY=23, CATCH=24, THROW=25, 
-    THIS=26, INSTANCEOF=27, BOOLNOT=28, BWNOT=29, MUL=30, DIV=31, REM=32, 
-    ADD=33, SUB=34, LSH=35, RSH=36, USH=37, LT=38, LTE=39, GT=40, GTE=41, 
-    EQ=42, EQR=43, NE=44, NER=45, BWAND=46, XOR=47, BWOR=48, BOOLAND=49, BOOLOR=50, 
-    COND=51, COLON=52, ELVIS=53, REF=54, ARROW=55, FIND=56, MATCH=57, INCR=58, 
-    DECR=59, ASSIGN=60, AADD=61, ASUB=62, AMUL=63, ADIV=64, AREM=65, AAND=66, 
-    AXOR=67, AOR=68, ALSH=69, ARSH=70, AUSH=71, OCTAL=72, HEX=73, INTEGER=74, 
-    DECIMAL=75, STRING=76, REGEX=77, TRUE=78, FALSE=79, NULL=80, TYPE=81, 
+    WS=1, COMMENT=2, LBRACK=3, RBRACK=4, LBRACE=5, RBRACE=6, LP=7, RP=8, DOT=9,
+    NSDOT=10, COMMA=11, SEMICOLON=12, IF=13, IN=14, ELSE=15, WHILE=16, DO=17,
+    FOR=18, CONTINUE=19, BREAK=20, RETURN=21, NEW=22, TRY=23, CATCH=24, THROW=25,
+    THIS=26, INSTANCEOF=27, BOOLNOT=28, BWNOT=29, MUL=30, DIV=31, REM=32,
+    ADD=33, SUB=34, LSH=35, RSH=36, USH=37, LT=38, LTE=39, GT=40, GTE=41,
+    EQ=42, EQR=43, NE=44, NER=45, BWAND=46, XOR=47, BWOR=48, BOOLAND=49, BOOLOR=50,
+    COND=51, COLON=52, ELVIS=53, REF=54, ARROW=55, FIND=56, MATCH=57, INCR=58,
+    DECR=59, ASSIGN=60, AADD=61, ASUB=62, AMUL=63, ADIV=64, AREM=65, AAND=66,
+    AXOR=67, AOR=68, ALSH=69, ARSH=70, AUSH=71, OCTAL=72, HEX=73, INTEGER=74,
+    DECIMAL=75, STRING=76, REGEX=77, TRUE=78, FALSE=79, NULL=80, TYPE=81,
     ID=82, DOTINTEGER=83, DOTID=84;
   public static final int
-    RULE_source = 0, RULE_function = 1, RULE_parameters = 2, RULE_statement = 3, 
-    RULE_rstatement = 4, RULE_dstatement = 5, RULE_trailer = 6, RULE_block = 7, 
-    RULE_empty = 8, RULE_initializer = 9, RULE_afterthought = 10, RULE_declaration = 11, 
-    RULE_decltype = 12, RULE_declvar = 13, RULE_trap = 14, RULE_expression = 15, 
-    RULE_unary = 16, RULE_chain = 17, RULE_primary = 18, RULE_postfix = 19, 
-    RULE_postdot = 20, RULE_callinvoke = 21, RULE_fieldaccess = 22, RULE_braceaccess = 23, 
-    RULE_arrayinitializer = 24, RULE_listinitializer = 25, RULE_mapinitializer = 26, 
-    RULE_maptoken = 27, RULE_arguments = 28, RULE_argument = 29, RULE_lambda = 30, 
+    RULE_source = 0, RULE_function = 1, RULE_parameters = 2, RULE_statement = 3,
+    RULE_rstatement = 4, RULE_dstatement = 5, RULE_trailer = 6, RULE_block = 7,
+    RULE_empty = 8, RULE_initializer = 9, RULE_afterthought = 10, RULE_declaration = 11,
+    RULE_decltype = 12, RULE_declvar = 13, RULE_trap = 14, RULE_expression = 15,
+    RULE_unary = 16, RULE_chain = 17, RULE_primary = 18, RULE_postfix = 19,
+    RULE_postdot = 20, RULE_callinvoke = 21, RULE_fieldaccess = 22, RULE_braceaccess = 23,
+    RULE_arrayinitializer = 24, RULE_listinitializer = 25, RULE_mapinitializer = 26,
+    RULE_maptoken = 27, RULE_arguments = 28, RULE_argument = 29, RULE_lambda = 30,
     RULE_lamtype = 31, RULE_funcref = 32;
   public static final String[] ruleNames = {
-    "source", "function", "parameters", "statement", "rstatement", "dstatement", 
-    "trailer", "block", "empty", "initializer", "afterthought", "declaration", 
-    "decltype", "declvar", "trap", "expression", "unary", "chain", "primary", 
-    "postfix", "postdot", "callinvoke", "fieldaccess", "braceaccess", "arrayinitializer", 
-    "listinitializer", "mapinitializer", "maptoken", "arguments", "argument", 
+    "source", "function", "parameters", "statement", "rstatement", "dstatement",
+    "trailer", "block", "empty", "initializer", "afterthought", "declaration",
+    "decltype", "declvar", "trap", "expression", "unary", "chain", "primary",
+    "postfix", "postdot", "callinvoke", "fieldaccess", "braceaccess", "arrayinitializer",
+    "listinitializer", "mapinitializer", "maptoken", "arguments", "argument",
     "lambda", "lamtype", "funcref"
   };
 
   private static final String[] _LITERAL_NAMES = {
-    null, null, null, "'{'", "'}'", "'['", "']'", "'('", "')'", "'.'", "'?.'", 
-    "','", "';'", "'if'", "'in'", "'else'", "'while'", "'do'", "'for'", "'continue'", 
-    "'break'", "'return'", "'new'", "'try'", "'catch'", "'throw'", "'this'", 
-    "'instanceof'", "'!'", "'~'", "'*'", "'/'", "'%'", "'+'", "'-'", "'<<'", 
-    "'>>'", "'>>>'", "'<'", "'<='", "'>'", "'>='", "'=='", "'==='", "'!='", 
-    "'!=='", "'&'", "'^'", "'|'", "'&&'", "'||'", "'?'", "':'", "'?:'", "'::'", 
-    "'->'", "'=~'", "'==~'", "'++'", "'--'", "'='", "'+='", "'-='", "'*='", 
-    "'/='", "'%='", "'&='", "'^='", "'|='", "'<<='", "'>>='", "'>>>='", null, 
+    null, null, null, "'{'", "'}'", "'['", "']'", "'('", "')'", "'.'", "'?.'",
+    "','", "';'", "'if'", "'in'", "'else'", "'while'", "'do'", "'for'", "'continue'",
+    "'break'", "'return'", "'new'", "'try'", "'catch'", "'throw'", "'this'",
+    "'instanceof'", "'!'", "'~'", "'*'", "'/'", "'%'", "'+'", "'-'", "'<<'",
+    "'>>'", "'>>>'", "'<'", "'<='", "'>'", "'>='", "'=='", "'==='", "'!='",
+    "'!=='", "'&'", "'^'", "'|'", "'&&'", "'||'", "'?'", "':'", "'?:'", "'::'",
+    "'->'", "'=~'", "'==~'", "'++'", "'--'", "'='", "'+='", "'-='", "'*='",
+    "'/='", "'%='", "'&='", "'^='", "'|='", "'<<='", "'>>='", "'>>>='", null,
     null, null, null, null, null, "'true'", "'false'", "'null'"
   };
   private static final String[] _SYMBOLIC_NAMES = {
-    null, "WS", "COMMENT", "LBRACK", "RBRACK", "LBRACE", "RBRACE", "LP", "RP", 
-    "DOT", "NSDOT", "COMMA", "SEMICOLON", "IF", "IN", "ELSE", "WHILE", "DO", 
-    "FOR", "CONTINUE", "BREAK", "RETURN", "NEW", "TRY", "CATCH", "THROW", 
-    "THIS", "INSTANCEOF", "BOOLNOT", "BWNOT", "MUL", "DIV", "REM", "ADD", 
-    "SUB", "LSH", "RSH", "USH", "LT", "LTE", "GT", "GTE", "EQ", "EQR", "NE", 
-    "NER", "BWAND", "XOR", "BWOR", "BOOLAND", "BOOLOR", "COND", "COLON", "ELVIS", 
-    "REF", "ARROW", "FIND", "MATCH", "INCR", "DECR", "ASSIGN", "AADD", "ASUB", 
-    "AMUL", "ADIV", "AREM", "AAND", "AXOR", "AOR", "ALSH", "ARSH", "AUSH", 
-    "OCTAL", "HEX", "INTEGER", "DECIMAL", "STRING", "REGEX", "TRUE", "FALSE", 
+    null, "WS", "COMMENT", "LBRACK", "RBRACK", "LBRACE", "RBRACE", "LP", "RP",
+    "DOT", "NSDOT", "COMMA", "SEMICOLON", "IF", "IN", "ELSE", "WHILE", "DO",
+    "FOR", "CONTINUE", "BREAK", "RETURN", "NEW", "TRY", "CATCH", "THROW",
+    "THIS", "INSTANCEOF", "BOOLNOT", "BWNOT", "MUL", "DIV", "REM", "ADD",
+    "SUB", "LSH", "RSH", "USH", "LT", "LTE", "GT", "GTE", "EQ", "EQR", "NE",
+    "NER", "BWAND", "XOR", "BWOR", "BOOLAND", "BOOLOR", "COND", "COLON", "ELVIS",
+    "REF", "ARROW", "FIND", "MATCH", "INCR", "DECR", "ASSIGN", "AADD", "ASUB",
+    "AMUL", "ADIV", "AREM", "AAND", "AXOR", "AOR", "ALSH", "ARSH", "AUSH",
+    "OCTAL", "HEX", "INTEGER", "DECIMAL", "STRING", "REGEX", "TRUE", "FALSE",
     "NULL", "TYPE", "ID", "DOTINTEGER", "DOTID"
   };
   public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
@@ -162,7 +177,7 @@ class PainlessParser extends Parser {
           setState(66);
           function();
           }
-          } 
+          }
         }
         setState(71);
         _errHandler.sync(this);
@@ -178,7 +193,7 @@ class PainlessParser extends Parser {
           setState(72);
           statement();
           }
-          } 
+          }
         }
         setState(77);
         _errHandler.sync(this);
@@ -426,7 +441,7 @@ class PainlessParser extends Parser {
       super(parent, invokingState);
     }
     @Override public int getRuleIndex() { return RULE_rstatement; }
-   
+
     public RstatementContext() { }
     public void copyFrom(RstatementContext ctx) {
       super.copyFrom(ctx);
@@ -805,7 +820,7 @@ class PainlessParser extends Parser {
         match(TRY);
         setState(164);
         block();
-        setState(166); 
+        setState(166);
         _errHandler.sync(this);
         _alt = 1;
         do {
@@ -821,7 +836,7 @@ class PainlessParser extends Parser {
           default:
             throw new NoViableAltException(this);
           }
-          setState(168); 
+          setState(168);
           _errHandler.sync(this);
           _alt = getInterpreter().adaptivePredict(_input,12,_ctx);
         } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER );
@@ -845,7 +860,7 @@ class PainlessParser extends Parser {
       super(parent, invokingState);
     }
     @Override public int getRuleIndex() { return RULE_dstatement; }
-   
+
     public DstatementContext() { }
     public void copyFrom(DstatementContext ctx) {
       super.copyFrom(ctx);
@@ -1148,7 +1163,7 @@ class PainlessParser extends Parser {
           setState(194);
           statement();
           }
-          } 
+          }
         }
         setState(199);
         _errHandler.sync(this);
@@ -1407,7 +1422,7 @@ class PainlessParser extends Parser {
           setState(224);
           match(RBRACE);
           }
-          } 
+          }
         }
         setState(229);
         _errHandler.sync(this);
@@ -1532,7 +1547,7 @@ class PainlessParser extends Parser {
       super(parent, invokingState);
     }
     @Override public int getRuleIndex() { return RULE_expression; }
-   
+
     public ExpressionContext() { }
     public void copyFrom(ExpressionContext ctx) {
       super.copyFrom(ctx);
@@ -1943,7 +1958,7 @@ class PainlessParser extends Parser {
             }
             break;
           }
-          } 
+          }
         }
         setState(297);
         _errHandler.sync(this);
@@ -1967,7 +1982,7 @@ class PainlessParser extends Parser {
       super(parent, invokingState);
     }
     @Override public int getRuleIndex() { return RULE_unary; }
-   
+
     public UnaryContext() { }
     public void copyFrom(UnaryContext ctx) {
       super.copyFrom(ctx);
@@ -2135,7 +2150,7 @@ class PainlessParser extends Parser {
       super(parent, invokingState);
     }
     @Override public int getRuleIndex() { return RULE_chain; }
-   
+
     public ChainContext() { }
     public void copyFrom(ChainContext ctx) {
       super.copyFrom(ctx);
@@ -2214,7 +2229,7 @@ class PainlessParser extends Parser {
             setState(314);
             postfix();
             }
-            } 
+            }
           }
           setState(319);
           _errHandler.sync(this);
@@ -2240,7 +2255,7 @@ class PainlessParser extends Parser {
             setState(322);
             postfix();
             }
-            } 
+            }
           }
           setState(327);
           _errHandler.sync(this);
@@ -2274,7 +2289,7 @@ class PainlessParser extends Parser {
       super(parent, invokingState);
     }
     @Override public int getRuleIndex() { return RULE_primary; }
-   
+
     public PrimaryContext() { }
     public void copyFrom(PrimaryContext ctx) {
       super.copyFrom(ctx);
@@ -2799,7 +2814,7 @@ class PainlessParser extends Parser {
       super(parent, invokingState);
     }
     @Override public int getRuleIndex() { return RULE_arrayinitializer; }
-   
+
     public ArrayinitializerContext() { }
     public void copyFrom(ArrayinitializerContext ctx) {
       super.copyFrom(ctx);
@@ -2886,7 +2901,7 @@ class PainlessParser extends Parser {
         match(NEW);
         setState(372);
         match(TYPE);
-        setState(377); 
+        setState(377);
         _errHandler.sync(this);
         _alt = 1;
         do {
@@ -2906,7 +2921,7 @@ class PainlessParser extends Parser {
           default:
             throw new NoViableAltException(this);
           }
-          setState(379); 
+          setState(379);
           _errHandler.sync(this);
           _alt = getInterpreter().adaptivePredict(_input,31,_ctx);
         } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER );
@@ -2927,7 +2942,7 @@ class PainlessParser extends Parser {
               setState(382);
               postfix();
               }
-              } 
+              }
             }
             setState(387);
             _errHandler.sync(this);
@@ -2989,7 +3004,7 @@ class PainlessParser extends Parser {
             setState(406);
             postfix();
             }
-            } 
+            }
           }
           setState(411);
           _errHandler.sync(this);
@@ -3542,7 +3557,7 @@ class PainlessParser extends Parser {
       super(parent, invokingState);
     }
     @Override public int getRuleIndex() { return RULE_funcref; }
-   
+
     public FuncrefContext() { }
     public void copyFrom(FuncrefContext ctx) {
       super.copyFrom(ctx);

+ 4 - 3
modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java

@@ -986,19 +986,20 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
 
     @Override
     public ANode visitNewstandardarray(NewstandardarrayContext ctx) {
-        String type = ctx.TYPE().getText();
+        StringBuilder type = new StringBuilder(ctx.TYPE().getText());
         List<AExpression> expressions = new ArrayList<>();
 
         for (ExpressionContext expression : ctx.expression()) {
+            type.append("[]");
             expressions.add((AExpression)visit(expression));
         }
 
-        return buildPostfixChain(new ENewArray(location(ctx), type, expressions, false), ctx.postdot(), ctx.postfix());
+        return buildPostfixChain(new ENewArray(location(ctx), type.toString(), expressions, false), ctx.postdot(), ctx.postfix());
     }
 
     @Override
     public ANode visitNewinitializedarray(NewinitializedarrayContext ctx) {
-        String type = ctx.TYPE().getText();
+        String type = ctx.TYPE().getText() + "[]";
         List<AExpression> expressions = new ArrayList<>();
 
         for (ExpressionContext expression : ctx.expression()) {

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

@@ -19,7 +19,6 @@
 
 package org.elasticsearch.painless.node;
 
-import org.elasticsearch.painless.Definition;
 import org.elasticsearch.painless.Globals;
 import org.elasticsearch.painless.Locals;
 import org.elasticsearch.painless.Location;
@@ -51,7 +50,7 @@ public final class EExplicit extends AExpression {
     @Override
     void analyze(Locals locals) {
         try {
-            actual = Definition.TypeToClass(locals.getDefinition().getType(type));
+            actual = locals.getDefinition().getJavaClassFromPainlessType(type);
         } catch (IllegalArgumentException exception) {
             throw createError(new IllegalArgumentException("Not a type [" + type + "]."));
         }

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

@@ -66,7 +66,7 @@ public final class EFunctionRef extends AExpression implements ILambda {
             try {
                 if ("this".equals(type)) {
                     // user's own function
-                    Method interfaceMethod = locals.getDefinition().ClassToType(expected).struct.functionalMethod;
+                    Method interfaceMethod = locals.getDefinition().getPainlessStructFromJavaClass(expected).functionalMethod;
                     if (interfaceMethod == null) {
                         throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
                                                            "to [" + Definition.ClassToName(expected) + "], not a functional interface");

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

@@ -58,7 +58,7 @@ public final class EInstanceof extends AExpression {
 
         // ensure the specified type is part of the definition
         try {
-            clazz = Definition.TypeToClass(locals.getDefinition().getType(this.type));
+            clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type);
         } catch (IllegalArgumentException exception) {
             throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
         }

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

@@ -120,7 +120,7 @@ public final class ELambda extends AExpression implements ILambda {
             }
         } else {
             // we know the method statically, infer return type and any unknown/def types
-            interfaceMethod = locals.getDefinition().ClassToType(expected).struct.functionalMethod;
+            interfaceMethod = locals.getDefinition().getPainlessStructFromJavaClass(expected).functionalMethod;
             if (interfaceMethod == null) {
                 throw createError(new IllegalArgumentException("Cannot pass lambda to [" + Definition.ClassToName(expected) +
                                                                "], not a functional interface"));

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

@@ -61,13 +61,13 @@ public final class EListInit extends AExpression {
 
         actual = ArrayList.class;
 
-        constructor = locals.getDefinition().ClassToType(actual).struct.constructors.get(new MethodKey("<init>", 0));
+        constructor = locals.getDefinition().getPainlessStructFromJavaClass(actual).constructors.get(new MethodKey("<init>", 0));
 
         if (constructor == null) {
             throw createError(new IllegalStateException("Illegal tree structure."));
         }
 
-        method = locals.getDefinition().ClassToType(actual).struct.methods.get(new MethodKey("add", 1));
+        method = locals.getDefinition().getPainlessStructFromJavaClass(actual).methods.get(new MethodKey("add", 1));
 
         if (method == null) {
             throw createError(new IllegalStateException("Illegal tree structure."));

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

@@ -67,13 +67,13 @@ public final class EMapInit extends AExpression {
 
         actual = HashMap.class;
 
-        constructor = locals.getDefinition().ClassToType(actual).struct.constructors.get(new MethodKey("<init>", 0));
+        constructor = locals.getDefinition().getPainlessStructFromJavaClass(actual).constructors.get(new MethodKey("<init>", 0));
 
         if (constructor == null) {
             throw createError(new IllegalStateException("Illegal tree structure."));
         }
 
-        method = locals.getDefinition().ClassToType(actual).struct.methods.get(new MethodKey("put", 2));
+        method = locals.getDefinition().getPainlessStructFromJavaClass(actual).methods.get(new MethodKey("put", 2));
 
         if (method == null) {
             throw createError(new IllegalStateException("Illegal tree structure."));

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

@@ -19,7 +19,6 @@
 
 package org.elasticsearch.painless.node;
 
-import org.elasticsearch.painless.Definition;
 import org.elasticsearch.painless.Globals;
 import org.elasticsearch.painless.Locals;
 import org.elasticsearch.painless.Location;
@@ -38,8 +37,6 @@ public final class ENewArray extends AExpression {
     private final List<AExpression> arguments;
     private final boolean initialize;
 
-    private Class<?> array;
-
     public ENewArray(Location location, String type, List<AExpression> arguments, boolean initialize) {
         super(location);
 
@@ -64,7 +61,7 @@ public final class ENewArray extends AExpression {
         Class<?> clazz;
 
         try {
-            clazz = Definition.TypeToClass(locals.getDefinition().getType(this.type));
+            clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type);
         } catch (IllegalArgumentException exception) {
             throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
         }
@@ -72,15 +69,13 @@ public final class ENewArray extends AExpression {
         for (int argument = 0; argument < arguments.size(); ++argument) {
             AExpression expression = arguments.get(argument);
 
-            expression.expected = initialize ? clazz : int.class;
+            expression.expected = initialize ? clazz.getComponentType() : int.class;
             expression.internal = true;
             expression.analyze(locals);
             arguments.set(argument, expression.cast(locals));
         }
 
-        actual = Definition.TypeToClass(locals.getDefinition().getType(
-            locals.getDefinition().ClassToType(clazz).struct, initialize ? 1 : arguments.size()));
-        array = Definition.defClassToObjectClass(actual);
+        actual = clazz;
     }
 
     @Override
@@ -89,7 +84,7 @@ public final class ENewArray extends AExpression {
 
         if (initialize) {
             writer.push(arguments.size());
-            writer.newArray(MethodWriter.getType(array.getComponentType()));
+            writer.newArray(MethodWriter.getType(actual.getComponentType()));
 
             for (int index = 0; index < arguments.size(); ++index) {
                 AExpression argument = arguments.get(index);
@@ -97,7 +92,7 @@ public final class ENewArray extends AExpression {
                 writer.dup();
                 writer.push(index);
                 argument.write(writer, globals);
-                writer.arrayStore(MethodWriter.getType(array.getComponentType()));
+                writer.arrayStore(MethodWriter.getType(actual.getComponentType()));
             }
         } else {
             for (AExpression argument : arguments) {
@@ -105,9 +100,9 @@ public final class ENewArray extends AExpression {
             }
 
             if (arguments.size() > 1) {
-                writer.visitMultiANewArrayInsn(MethodWriter.getType(array).getDescriptor(), arguments.size());
+                writer.visitMultiANewArrayInsn(MethodWriter.getType(actual).getDescriptor(), arguments.size());
             } else {
-                writer.newArray(MethodWriter.getType(array.getComponentType()));
+                writer.newArray(MethodWriter.getType(actual.getComponentType()));
             }
         }
     }

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

@@ -58,12 +58,12 @@ public final class ENewObj extends AExpression {
     @Override
     void analyze(Locals locals) {
         try {
-            actual = Definition.TypeToClass(locals.getDefinition().getType(this.type));
+            actual = locals.getDefinition().getJavaClassFromPainlessType(this.type);
         } catch (IllegalArgumentException exception) {
             throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
         }
 
-        Struct struct = locals.getDefinition().ClassToType(actual).struct;
+        Struct struct = locals.getDefinition().getPainlessStructFromJavaClass(actual);
         constructor = struct.constructors.get(new Definition.MethodKey("<init>", arguments.size()));
 
         if (constructor != null) {

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

@@ -72,7 +72,7 @@ public final class ERegex extends AExpression {
         }
 
         constant = new Constant(
-            location, locals.getDefinition().PatternType.type, "regexAt$" + location.getOffset(), this::initializeConstant);
+            location, MethodWriter.getType(Pattern.class), "regexAt$" + location.getOffset(), this::initializeConstant);
         actual = Pattern.class;
     }
 

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

@@ -19,7 +19,6 @@
 
 package org.elasticsearch.painless.node;
 
-import org.elasticsearch.painless.Definition;
 import org.elasticsearch.painless.Globals;
 import org.elasticsearch.painless.Locals;
 import org.elasticsearch.painless.Location;
@@ -49,7 +48,7 @@ public final class EStatic extends AExpression {
     @Override
     void analyze(Locals locals) {
         try {
-            actual = Definition.TypeToClass(locals.getDefinition().getType(type));
+            actual = locals.getDefinition().getJavaClassFromPainlessType(type);
         } catch (IllegalArgumentException exception) {
             throw createError(new IllegalArgumentException("Not a type [" + type + "]."));
         }

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

@@ -63,9 +63,9 @@ public final class PBrace extends AStoreable {
         } else if (prefix.actual == def.class) {
             sub = new PSubDefArray(location, index);
         } else if (Map.class.isAssignableFrom(prefix.actual)) {
-            sub = new PSubMapShortcut(location, locals.getDefinition().ClassToType(prefix.actual).struct, index);
+            sub = new PSubMapShortcut(location, locals.getDefinition().getPainlessStructFromJavaClass(prefix.actual), index);
         } else if (List.class.isAssignableFrom(prefix.actual)) {
-            sub = new PSubListShortcut(location, locals.getDefinition().ClassToType(prefix.actual).struct, index);
+            sub = new PSubListShortcut(location, locals.getDefinition().getPainlessStructFromJavaClass(prefix.actual), index);
         } else {
             throw createError(
                 new IllegalArgumentException("Illegal array access on type [" + Definition.ClassToName(prefix.actual) + "]."));

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

@@ -71,10 +71,10 @@ public final class PCallInvoke extends AExpression {
             throw createError(new IllegalArgumentException("Illegal call [" + name + "] on array type."));
         }
 
-        Struct struct = locals.getDefinition().ClassToType(prefix.actual).struct;
+        Struct struct = locals.getDefinition().getPainlessStructFromJavaClass(prefix.actual);
 
         if (prefix.actual.isPrimitive()) {
-            struct = locals.getDefinition().ClassToType(Definition.getBoxedType(prefix.actual)).struct;
+            struct = locals.getDefinition().getPainlessStructFromJavaClass(Definition.getBoxedType(prefix.actual));
         }
 
         MethodKey methodKey = new MethodKey(name, arguments.size());

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

@@ -67,7 +67,7 @@ public final class PField extends AStoreable {
         } else if (prefix.actual == def.class) {
             sub = new PSubDefField(location, value);
         } else {
-            Struct struct = locals.getDefinition().ClassToType(prefix.actual).struct;
+            Struct struct = locals.getDefinition().getPainlessStructFromJavaClass(prefix.actual);
             Field field = prefix instanceof EStatic ? struct.staticMembers.get(value) : struct.members.get(value);
 
             if (field != null) {

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

@@ -20,7 +20,6 @@
 package org.elasticsearch.painless.node;
 
 import org.elasticsearch.painless.DefBootstrap;
-
 import org.elasticsearch.painless.Definition.def;
 import org.elasticsearch.painless.Globals;
 import org.elasticsearch.painless.Locals;

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

@@ -19,7 +19,6 @@
 
 package org.elasticsearch.painless.node;
 
-import org.elasticsearch.painless.Definition;
 import org.elasticsearch.painless.Globals;
 import org.elasticsearch.painless.Locals;
 import org.elasticsearch.painless.Locals.Variable;
@@ -68,7 +67,7 @@ public final class SCatch extends AStatement {
         Class<?> clazz;
 
         try {
-            clazz = Definition.TypeToClass(locals.getDefinition().getType(this.type));
+            clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type);
         } catch (IllegalArgumentException exception) {
             throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
         }

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

@@ -19,7 +19,6 @@
 
 package org.elasticsearch.painless.node;
 
-import org.elasticsearch.painless.Definition;
 import org.elasticsearch.painless.Globals;
 import org.elasticsearch.painless.Locals;
 import org.elasticsearch.painless.Locals.Variable;
@@ -63,7 +62,7 @@ public final class SDeclaration extends AStatement {
         Class<?> clazz;
 
         try {
-            clazz = Definition.TypeToClass(locals.getDefinition().getType(this.type));
+            clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type);
         } catch (IllegalArgumentException exception) {
             throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
         }

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

@@ -71,7 +71,7 @@ public class SEach extends AStatement {
         Class<?> clazz;
 
         try {
-            clazz = Definition.TypeToClass(locals.getDefinition().getType(this.type));
+            clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type);
         } catch (IllegalArgumentException exception) {
             throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
         }

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

@@ -119,7 +119,7 @@ public final class SFunction extends AStatement {
 
     void generateSignature(Definition definition) {
         try {
-            rtnType = Definition.TypeToClass(definition.getType(rtnTypeStr));
+            rtnType = definition.getJavaClassFromPainlessType(rtnTypeStr);
         } catch (IllegalArgumentException exception) {
             throw createError(new IllegalArgumentException("Illegal return type [" + rtnTypeStr + "] for function [" + name + "]."));
         }
@@ -133,7 +133,7 @@ public final class SFunction extends AStatement {
 
         for (int param = 0; param < this.paramTypeStrs.size(); ++param) {
             try {
-                Class<?> paramType = Definition.TypeToClass(definition.getType(this.paramTypeStrs.get(param)));
+                Class<?> paramType = definition.getJavaClassFromPainlessType(this.paramTypeStrs.get(param));
 
                 paramClasses[param] = Definition.defClassToObjectClass(paramType);
                 paramTypes.add(paramType);

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

@@ -25,7 +25,6 @@ import org.elasticsearch.painless.Definition;
 import org.elasticsearch.painless.Definition.Cast;
 import org.elasticsearch.painless.Definition.Method;
 import org.elasticsearch.painless.Definition.MethodKey;
-import org.elasticsearch.painless.Definition.Type;
 import org.elasticsearch.painless.Definition.def;
 import org.elasticsearch.painless.Globals;
 import org.elasticsearch.painless.Locals;
@@ -78,12 +77,11 @@ final class SSubEachIterable extends AStatement {
         if (expression.actual == def.class) {
             method = null;
         } else {
-            Type actualType = locals.getDefinition().ClassToType(expression.actual);
-            method = actualType.struct.methods.get(new MethodKey("iterator", 0));
+            method = locals.getDefinition().getPainlessStructFromJavaClass(expression.actual).methods.get(new MethodKey("iterator", 0));
 
             if (method == null) {
                 throw createError(new IllegalArgumentException(
-                    "Unable to create iterator for the type [" + actualType.name + "]."));
+                    "Unable to create iterator for the type [" + Definition.ClassToName(expression.actual) + "]."));
             }
         }
 

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

@@ -1,5 +1,3 @@
-package org.elasticsearch.painless;
-
 /*
  * Licensed to Elasticsearch under one or more contributor
  * license agreements. See the NOTICE file distributed with
@@ -19,6 +17,8 @@ package org.elasticsearch.painless;
  * under the License.
  */
 
+package org.elasticsearch.painless;
+
 import java.lang.invoke.CallSite;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;

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

@@ -26,9 +26,6 @@ import org.elasticsearch.common.logging.ESLoggerFactory;
 import org.elasticsearch.painless.Definition.Field;
 import org.elasticsearch.painless.Definition.Method;
 import org.elasticsearch.painless.Definition.Struct;
-import org.elasticsearch.painless.Definition.Type;
-import org.elasticsearch.painless.spi.Whitelist;
-
 import java.io.IOException;
 import java.io.PrintStream;
 import java.lang.reflect.Modifier;
@@ -44,12 +41,14 @@ import java.util.function.Consumer;
 
 import static java.util.Comparator.comparing;
 import static java.util.stream.Collectors.toList;
+import static org.elasticsearch.painless.spi.Whitelist.BASE_WHITELISTS;
 
 /**
  * Generates an API reference from the method and type whitelists in {@link Definition}.
  */
 public class PainlessDocGenerator {
-    private static final Definition definition = new Definition(Whitelist.BASE_WHITELISTS);
+
+    private static final Definition definition = new Definition(BASE_WHITELISTS);
     private static final Logger logger = ESLoggerFactory.getLogger(PainlessDocGenerator.class);
     private static final Comparator<Field> FIELD_NAME = comparing(f -> f.name);
     private static final Comparator<Method> METHOD_NAME = comparing(m -> m.name);
@@ -68,41 +67,41 @@ public class PainlessDocGenerator {
                 Files.newOutputStream(indexPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE),
                 false, StandardCharsets.UTF_8.name())) {
             emitGeneratedWarning(indexStream);
-            List<Type> types = definition.allSimpleTypes().stream().sorted(comparing(t -> t.name)).collect(toList());
-            for (Type type : types) {
-                if (type.clazz.isPrimitive()) {
+            List<Struct> structs = definition.getStructs().stream().sorted(comparing(t -> t.name)).collect(toList());
+            for (Struct struct : structs) {
+                if (struct.clazz.isPrimitive()) {
                     // Primitives don't have methods to reference
                     continue;
                 }
-                if ("def".equals(type.name)) {
+                if ("def".equals(struct.name)) {
                     // def is special but doesn't have any methods all of its own.
                     continue;
                 }
                 indexStream.print("include::");
-                indexStream.print(type.struct.name);
+                indexStream.print(struct.name);
                 indexStream.println(".asciidoc[]");
 
-                Path typePath = apiRootPath.resolve(type.struct.name + ".asciidoc");
-                logger.info("Writing [{}.asciidoc]", type.name);
+                Path typePath = apiRootPath.resolve(struct.name + ".asciidoc");
+                logger.info("Writing [{}.asciidoc]", struct.name);
                 try (PrintStream typeStream = new PrintStream(
                         Files.newOutputStream(typePath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE),
                         false, StandardCharsets.UTF_8.name())) {
                     emitGeneratedWarning(typeStream);
                     typeStream.print("[[");
-                    emitAnchor(typeStream, type.struct);
+                    emitAnchor(typeStream, struct);
                     typeStream.print("]]++");
-                    typeStream.print(type.name);
+                    typeStream.print(struct.name);
                     typeStream.println("++::");
 
                     Consumer<Field> documentField = field -> PainlessDocGenerator.documentField(typeStream, field);
                     Consumer<Method> documentMethod = method -> PainlessDocGenerator.documentMethod(typeStream, method);
-                    type.struct.staticMembers.values().stream().sorted(FIELD_NAME).forEach(documentField);
-                    type.struct.members.values().stream().sorted(FIELD_NAME).forEach(documentField);
-                    type.struct.staticMethods.values().stream().sorted(METHOD_NAME.thenComparing(NUMBER_OF_ARGS)).forEach(documentMethod);
-                    type.struct.constructors.values().stream().sorted(NUMBER_OF_ARGS).forEach(documentMethod);
+                    struct.staticMembers.values().stream().sorted(FIELD_NAME).forEach(documentField);
+                    struct.members.values().stream().sorted(FIELD_NAME).forEach(documentField);
+                    struct.staticMethods.values().stream().sorted(METHOD_NAME.thenComparing(NUMBER_OF_ARGS)).forEach(documentMethod);
+                    struct.constructors.values().stream().sorted(NUMBER_OF_ARGS).forEach(documentMethod);
                     Map<String, Struct> inherited = new TreeMap<>();
-                    type.struct.methods.values().stream().sorted(METHOD_NAME.thenComparing(NUMBER_OF_ARGS)).forEach(method -> {
-                        if (method.owner == type.struct) {
+                    struct.methods.values().stream().sorted(METHOD_NAME.thenComparing(NUMBER_OF_ARGS)).forEach(method -> {
+                        if (method.owner == struct) {
                             documentMethod(typeStream, method);
                         } else {
                             inherited.put(method.owner.name, method.owner);
@@ -139,7 +138,7 @@ public class PainlessDocGenerator {
             stream.print("static ");
         }
 
-        emitType(stream, definition.ClassToType(field.clazz));
+        emitType(stream, field.clazz);
         stream.print(' ');
 
         String javadocRoot = javadocRoot(field);
@@ -170,7 +169,7 @@ public class PainlessDocGenerator {
         }
 
         if (false == method.name.equals("<init>")) {
-            emitType(stream, definition.ClassToType(method.rtn));
+            emitType(stream, method.rtn);
             stream.print(' ');
         }
 
@@ -188,7 +187,7 @@ public class PainlessDocGenerator {
             } else {
                 stream.print(", ");
             }
-            emitType(stream, definition.ClassToType(arg));
+            emitType(stream, arg);
         }
         stream.print(")++");
 
@@ -234,19 +233,19 @@ public class PainlessDocGenerator {
     }
 
     /**
-     * Emit a {@link Type}. If the type is primitive or an array of primitives this just emits the name of the type. Otherwise this emits an
-     * internal link with the text.
+     * Emit a {@link Class}. If the type is primitive or an array of primitives this just emits the name of the type. Otherwise this emits
+       an internal link with the text.
      */
-    private static void emitType(PrintStream stream, Type type) {
-        emitStruct(stream, type.struct);
-        for (int i = 0; i < type.dimensions; i++) {
+    private static void emitType(PrintStream stream, Class<?> clazz) {
+        emitStruct(stream, definition.getPainlessStructFromJavaClass(clazz));
+        while ((clazz = clazz.getComponentType()) != null) {
             stream.print("[]");
         }
     }
 
     /**
-     * Emit a {@link Struct}. If the {@linkplain Struct} is primitive or def this just emits the name of the struct. Otherwise this emits an
-     * internal link with the name.
+     * Emit a {@link Struct}. If the {@linkplain Struct} is primitive or def this just emits the name of the struct. Otherwise this emits
+     * an internal link with the name.
      */
     private static void emitStruct(PrintStream stream, Struct struct) {
         if (false == struct.clazz.isPrimitive() && false == struct.name.equals("def")) {
@@ -279,14 +278,13 @@ public class PainlessDocGenerator {
             stream.print(method.owner.clazz.getName());
         }
         for (Class<?> clazz: method.arguments) {
-            Type arg = definition.ClassToType(clazz);
             if (first) {
                 first = false;
             } else {
                 stream.print("%2D");
             }
-            stream.print(arg.struct.clazz.getName());
-            if (arg.dimensions > 0) {
+            stream.print(clazz.getName());
+            if (clazz.isArray()) {
                 stream.print(":A");
             }
         }

+ 12 - 12
modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java

@@ -284,12 +284,12 @@ public class NodeToStringTests extends ESTestCase {
     }
 
     public void testENewArray() {
-        assertToString("(SSource (SReturn (ENewArray int dims (Args (ENumeric 10)))))", "return new int[10]");
-        assertToString("(SSource (SReturn (ENewArray int dims (Args (ENumeric 10) (ENumeric 4) (ENumeric 5)))))",
+        assertToString("(SSource (SReturn (ENewArray int[] dims (Args (ENumeric 10)))))", "return new int[10]");
+        assertToString("(SSource (SReturn (ENewArray int[][][] dims (Args (ENumeric 10) (ENumeric 4) (ENumeric 5)))))",
                 "return new int[10][4][5]");
-        assertToString("(SSource (SReturn (ENewArray int init (Args (ENumeric 1) (ENumeric 2) (ENumeric 3)))))",
+        assertToString("(SSource (SReturn (ENewArray int[] init (Args (ENumeric 1) (ENumeric 2) (ENumeric 3)))))",
                 "return new int[] {1, 2, 3}");
-        assertToString("(SSource (SReturn (ENewArray def init (Args (ENumeric 1) (ENumeric 2) (EString 'bird')))))",
+        assertToString("(SSource (SReturn (ENewArray def[] init (Args (ENumeric 1) (ENumeric 2) (EString 'bird')))))",
                 "return new def[] {1, 2, 'bird'}");
     }
 
@@ -372,7 +372,7 @@ public class NodeToStringTests extends ESTestCase {
         assertToString("(SSource (SReturn (PField nullSafe (EVariable params) a)))", "return params?.a");
         assertToString(
                   "(SSource\n"
-                + "  (SDeclBlock (SDeclaration int[] a (ENewArray int dims (Args (ENumeric 10)))))\n"
+                + "  (SDeclBlock (SDeclaration int[] a (ENewArray int[] dims (Args (ENumeric 10)))))\n"
                 + "  (SReturn (PField (EVariable a) length)))",
                   "int[] a = new int[10];\n"
                 + "return a.length");
@@ -403,7 +403,7 @@ public class NodeToStringTests extends ESTestCase {
 
     public void testPSubCallInvoke() {
         Location l = new Location(getTestName(), 0);
-        Struct c = definition.ClassToType(Integer.class).struct;
+        Struct c = definition.getPainlessStructFromJavaClass(Integer.class);
         Method m = c.methods.get(new MethodKey("toString", 0));
         PSubCallInvoke node = new PSubCallInvoke(l, m, null, emptyList());
         node.prefix = new EVariable(l, "a");
@@ -458,7 +458,7 @@ public class NodeToStringTests extends ESTestCase {
 
     public void testPSubField() {
         Location l = new Location(getTestName(), 0);
-        Struct s = definition.getType(Boolean.class.getSimpleName()).struct;
+        Struct s = definition.getPainlessStructFromJavaClass(Boolean.class);
         Field f = s.staticMembers.get("TRUE");
         PSubField node = new PSubField(l, f);
         node.prefix = new EStatic(l, "Boolean");
@@ -468,7 +468,7 @@ public class NodeToStringTests extends ESTestCase {
 
     public void testPSubListShortcut() {
         Location l = new Location(getTestName(), 0);
-        Struct s = definition.getType(List.class.getSimpleName()).struct;
+        Struct s = definition.getPainlessStructFromJavaClass(List.class);
         PSubListShortcut node = new PSubListShortcut(l, s, new EConstant(l, 1));
         node.prefix = new EVariable(l, "a");
         assertEquals("(PSubListShortcut (EVariable a) (EConstant Integer 1))", node.toString());
@@ -476,7 +476,7 @@ public class NodeToStringTests extends ESTestCase {
                 new PSubNullSafeCallInvoke(l, node).toString());
 
         l = new Location(getTestName(), 0);
-        s = definition.getType(List.class.getSimpleName()).struct;
+        s = definition.getPainlessStructFromJavaClass(List.class);
         node = new PSubListShortcut(l, s, new EBinary(l, Operation.ADD, new EConstant(l, 1), new EConstant(l, 4)));
         node.prefix = new EVariable(l, "a");
         assertEquals("(PSubListShortcut (EVariable a) (EBinary (EConstant Integer 1) + (EConstant Integer 4)))", node.toString());
@@ -484,7 +484,7 @@ public class NodeToStringTests extends ESTestCase {
 
     public void testPSubMapShortcut() {
         Location l = new Location(getTestName(), 0);
-        Struct s = definition.getType(Map.class.getSimpleName()).struct;
+        Struct s = definition.getPainlessStructFromJavaClass(Map.class);
         PSubMapShortcut node = new PSubMapShortcut(l, s, new EConstant(l, "cat"));
         node.prefix = new EVariable(l, "a");
         assertEquals("(PSubMapShortcut (EVariable a) (EConstant String 'cat'))", node.toString());
@@ -492,7 +492,7 @@ public class NodeToStringTests extends ESTestCase {
                 new PSubNullSafeCallInvoke(l, node).toString());
 
         l = new Location(getTestName(), 1);
-        s = definition.getType(Map.class.getSimpleName()).struct;
+        s = definition.getPainlessStructFromJavaClass(Map.class);
         node = new PSubMapShortcut(l, s, new EBinary(l, Operation.ADD, new EConstant(l, 1), new EConstant(l, 4)));
         node.prefix = new EVariable(l, "a");
         assertEquals("(PSubMapShortcut (EVariable a) (EBinary (EConstant Integer 1) + (EConstant Integer 4)))", node.toString());
@@ -500,7 +500,7 @@ public class NodeToStringTests extends ESTestCase {
 
     public void testPSubShortcut() {
         Location l = new Location(getTestName(), 0);
-        Struct s = definition.getType(FeatureTest.class.getName()).struct;
+        Struct s = definition.getPainlessStructFromJavaClass(FeatureTest.class);
         Method getter = s.methods.get(new MethodKey("getX", 0));
         Method setter = s.methods.get(new MethodKey("setX", 1));
         PSubShortcut node = new PSubShortcut(l, "x", FeatureTest.class.getName(), getter, setter);