فهرست منبع

[Painless] Generate Bridge Methods (#36097)

We use MethodHandles.asType to cast argument types into the appropriate parameter types for 
method calls when the target of the call is a def type at runtime. Currently, certain implicit casts 
using the def type are asymmetric. It is possible to cast Integer -> float as an argument to parameter, but not from int -> Float (boxed to primitive with upcasting is okay, but primitive to 
boxed with upcasting is not).

This PR introduces a solution to the issue by generating bridge methods for all whitelisted methods 
that have at least a single boxed type as an argument. The bridge method will conduct appropriate 
casts and then call the original method. This adds a bit of overhead for correctness. It should not be
used often as Painless avoids boxed types as much as possible.

Note that a large portion of this change is adding methods to do the appropriate def to boxed type 
casts and a few mechanical changes as well. The most important method for review is 
generateBridgeMethod in PainlessLookupBuilder.
Jack Conradson 6 سال پیش
والد
کامیت
2df4bd1f81

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

@@ -196,7 +196,7 @@ public final class Def {
          int numArguments = callSiteType.parameterCount();
          // simple case: no lambdas
          if (recipeString.isEmpty()) {
-             PainlessMethod painlessMethod =  painlessLookup.lookupRuntimePainlessMethod(receiverClass, name, numArguments - 1);
+             PainlessMethod painlessMethod = painlessLookup.lookupRuntimePainlessMethod(receiverClass, name, numArguments - 1);
 
              if (painlessMethod == null) {
                  throw new IllegalArgumentException("dynamic method " +
@@ -445,7 +445,7 @@ public final class Def {
         }
 
         throw new IllegalArgumentException(
-                "dynamic getter [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "] not found");
+                "dynamic setter [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "] not found");
     }
 
     /**
@@ -619,18 +619,17 @@ public final class Def {
         }
     }
 
+    // Conversion methods for def to primitive types.
 
-    // Conversion methods for Def to primitive types.
-
-    public static boolean DefToboolean(final Object value) {
+    public static boolean defToboolean(final Object value) {
         return (boolean)value;
     }
 
-    public static byte DefTobyteImplicit(final Object value) {
+    public static byte defTobyteImplicit(final Object value) {
         return (byte)value;
     }
 
-    public static short DefToshortImplicit(final Object value) {
+    public static short defToshortImplicit(final Object value) {
         if (value instanceof Byte) {
             return (byte)value;
         } else {
@@ -638,15 +637,11 @@ public final class Def {
         }
     }
 
-    public static char DefTocharImplicit(final Object value) {
-        if (value instanceof Byte) {
-            return (char)(byte)value;
-        } else {
-            return (char)value;
-        }
+    public static char defTocharImplicit(final Object value) {
+        return (char)value;
     }
 
-    public static int DefTointImplicit(final Object value) {
+    public static int defTointImplicit(final Object value) {
         if (value instanceof Byte) {
             return (byte)value;
         } else if (value instanceof Short) {
@@ -658,7 +653,7 @@ public final class Def {
         }
     }
 
-    public static long DefTolongImplicit(final Object value) {
+    public static long defTolongImplicit(final Object value) {
         if (value instanceof Byte) {
             return (byte)value;
         } else if (value instanceof Short) {
@@ -672,7 +667,7 @@ public final class Def {
         }
     }
 
-    public static float DefTofloatImplicit(final Object value) {
+    public static float defTofloatImplicit(final Object value) {
         if (value instanceof Byte) {
             return (byte)value;
         } else if (value instanceof Short) {
@@ -688,7 +683,7 @@ public final class Def {
         }
     }
 
-    public static double DefTodoubleImplicit(final Object value) {
+    public static double defTodoubleImplicit(final Object value) {
         if (value instanceof Byte) {
             return (byte)value;
         } else if (value instanceof Short) {
@@ -706,7 +701,7 @@ public final class Def {
         }
     }
 
-    public static byte DefTobyteExplicit(final Object value) {
+    public static byte defTobyteExplicit(final Object value) {
         if (value instanceof Character) {
             return (byte)(char)value;
         } else {
@@ -714,7 +709,7 @@ public final class Def {
         }
     }
 
-    public static short DefToshortExplicit(final Object value) {
+    public static short defToshortExplicit(final Object value) {
         if (value instanceof Character) {
             return (short)(char)value;
         } else {
@@ -722,15 +717,15 @@ public final class Def {
         }
     }
 
-    public static char DefTocharExplicit(final Object value) {
+    public static char defTocharExplicit(final Object value) {
         if (value instanceof Character) {
-            return ((Character)value);
+            return (char)value;
         } else {
             return (char)((Number)value).intValue();
         }
     }
 
-    public static int DefTointExplicit(final Object value) {
+    public static int defTointExplicit(final Object value) {
         if (value instanceof Character) {
             return (char)value;
         } else {
@@ -738,7 +733,7 @@ public final class Def {
         }
     }
 
-    public static long DefTolongExplicit(final Object value) {
+    public static long defTolongExplicit(final Object value) {
         if (value instanceof Character) {
             return (char)value;
         } else {
@@ -746,7 +741,7 @@ public final class Def {
         }
     }
 
-    public static float DefTofloatExplicit(final Object value) {
+    public static float defTofloatExplicit(final Object value) {
         if (value instanceof Character) {
             return (char)value;
         } else {
@@ -754,7 +749,7 @@ public final class Def {
         }
     }
 
-    public static double DefTodoubleExplicit(final Object value) {
+    public static double defTodoubleExplicit(final Object value) {
         if (value instanceof Character) {
             return (char)value;
         } else {
@@ -762,6 +757,172 @@ public final class Def {
         }
     }
 
+    // Conversion methods for def to boxed types.
+
+    public static Byte defToByteImplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else {
+            return (Byte)value;
+        }
+    }
+
+    public static Short defToShortImplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Byte) {
+            return (short)(byte)value;
+        } else {
+            return (Short)value;
+        }
+    }
+
+    public static Character defToCharacterImplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else {
+            return (Character)value;
+        }
+    }
+
+    public static Integer defToIntegerImplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Byte) {
+            return (int)(byte)value;
+        } else if (value instanceof Short) {
+            return (int)(short)value;
+        } else if (value instanceof Character) {
+            return (int)(char)value;
+        } else {
+            return (Integer)value;
+        }
+    }
+
+    public static Long defToLongImplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Byte) {
+            return (long)(byte)value;
+        } else if (value instanceof Short) {
+            return (long)(short)value;
+        } else if (value instanceof Character) {
+            return (long)(char)value;
+        } else if (value instanceof Integer) {
+            return (long)(int)value;
+        } else {
+            return (Long)value;
+        }
+    }
+
+    public static Float defToFloatImplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Byte) {
+            return (float)(byte)value;
+        } else if (value instanceof Short) {
+            return (float)(short)value;
+        } else if (value instanceof Character) {
+            return (float)(char)value;
+        } else if (value instanceof Integer) {
+            return (float)(int)value;
+        } else if (value instanceof Long) {
+            return (float)(long)value;
+        } else {
+            return (Float)value;
+        }
+    }
+
+    public static Double defToDoubleImplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Byte) {
+            return (double)(byte)value;
+        } else if (value instanceof Short) {
+            return (double)(short)value;
+        } else if (value instanceof Character) {
+            return (double)(char)value;
+        } else if (value instanceof Integer) {
+            return (double)(int)value;
+        } else if (value instanceof Long) {
+            return (double)(long)value;
+        } else if (value instanceof Float) {
+            return (double)(float)value;
+        } else {
+            return (Double)value;
+        }
+    }
+
+    public static Byte defToByteExplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Character) {
+            return (byte)(char)value;
+        } else {
+            return ((Number)value).byteValue();
+        }
+    }
+
+    public static Short defToShortExplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Character) {
+            return (short)(char)value;
+        } else {
+            return ((Number)value).shortValue();
+        }
+    }
+
+    public static Character defToCharacterExplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Character) {
+            return (Character)value;
+        } else {
+            return (char)((Number)value).intValue();
+        }
+    }
+
+    public static Integer defToIntegerExplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Character) {
+            return (int)(char)value;
+        } else {
+            return ((Number)value).intValue();
+        }
+    }
+
+    public static Long defToLongExplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Character) {
+            return (long)(char)value;
+        } else {
+            return ((Number)value).longValue();
+        }
+    }
+
+    public static Float defToFloatExplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Character) {
+            return (float)(char)value;
+        } else {
+            return ((Number)value).floatValue();
+        }
+    }
+
+    public static Double defToDoubleExplicit(final Object value) {
+        if (value == null) {
+            return null;
+        } else if (value instanceof Character) {
+            return (double)(char)value;
+        } else {
+            return ((Number)value).doubleValue();
+        }
+    }
+
     /**
      * "Normalizes" the index into a {@code Map} by making no change to the index.
      */

+ 21 - 0
modules/lang-painless/src/main/java/org/elasticsearch/painless/FeatureTest.java

@@ -39,10 +39,17 @@ public class FeatureTest {
         return x + y;
     }
 
+    /** static method with a type parameter Number */
+    public static int staticNumberTest(Number number) {
+        return number.intValue();
+    }
+
     private int x;
     private int y;
     public int z;
 
+    private Integer i;
+
     /** empty ctor */
     public FeatureTest() {
     }
@@ -73,6 +80,20 @@ public class FeatureTest {
         this.y = y;
     }
 
+    /** getter for i */
+    public Integer getI() {
+        return i;
+    }
+
+    /** setter for y */
+    public void setI(Integer i) {
+        this.i = i;
+    }
+
+    public Double mixedAdd(int i, Byte b, char c, Float f) {
+        return (double)(i + b + c + f);
+    }
+
     /** method taking two functions! */
     public Object twoFunctionsOfX(Function<Object,Object> f, Function<Object,Object> g) {
         return f.apply(g.apply(x));

+ 28 - 28
modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java

@@ -40,20 +40,20 @@ import java.util.List;
 import static org.elasticsearch.painless.WriterConstants.CHAR_TO_STRING;
 import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_HANDLE;
 import static org.elasticsearch.painless.WriterConstants.DEF_TO_BOOLEAN;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_BYTE_EXPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_BYTE_IMPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_CHAR_EXPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_CHAR_IMPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_DOUBLE_EXPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_DOUBLE_IMPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_FLOAT_EXPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_FLOAT_IMPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_INT_EXPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_INT_IMPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_LONG_EXPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_LONG_IMPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_SHORT_EXPLICIT;
-import static org.elasticsearch.painless.WriterConstants.DEF_TO_SHORT_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_BYTE_EXPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_BYTE_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_CHAR_EXPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_CHAR_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_DOUBLE_EXPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_DOUBLE_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_FLOAT_EXPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_FLOAT_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_INT_EXPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_INT_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_LONG_EXPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_LONG_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_SHORT_EXPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_SHORT_IMPLICIT;
 import static org.elasticsearch.painless.WriterConstants.DEF_UTIL_TYPE;
 import static org.elasticsearch.painless.WriterConstants.INDY_STRING_CONCAT_BOOTSTRAP_HANDLE;
 import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE;
@@ -146,25 +146,25 @@ public final class MethodWriter extends GeneratorAdapter {
                 if (cast.originalType == def.class) {
                     if (cast.explicitCast) {
                         if      (cast.targetType == Boolean.class)   invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
-                        else if (cast.targetType == Byte.class)      invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_EXPLICIT);
-                        else if (cast.targetType == Short.class)     invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_EXPLICIT);
-                        else if (cast.targetType == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_EXPLICIT);
-                        else if (cast.targetType == Integer.class)   invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_EXPLICIT);
-                        else if (cast.targetType == Long.class)      invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_EXPLICIT);
-                        else if (cast.targetType == Float.class)     invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_EXPLICIT);
-                        else if (cast.targetType == Double.class)    invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_EXPLICIT);
+                        else if (cast.targetType == Byte.class)      invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_BYTE_EXPLICIT);
+                        else if (cast.targetType == Short.class)     invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_SHORT_EXPLICIT);
+                        else if (cast.targetType == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_CHAR_EXPLICIT);
+                        else if (cast.targetType == Integer.class)   invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_INT_EXPLICIT);
+                        else if (cast.targetType == Long.class)      invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_LONG_EXPLICIT);
+                        else if (cast.targetType == Float.class)     invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_FLOAT_EXPLICIT);
+                        else if (cast.targetType == Double.class)    invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_DOUBLE_EXPLICIT);
                         else {
                             throw new IllegalStateException("Illegal tree structure.");
                         }
                     } else {
                         if      (cast.targetType == Boolean.class)   invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
-                        else if (cast.targetType == Byte.class)      invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_IMPLICIT);
-                        else if (cast.targetType == Short.class)     invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_IMPLICIT);
-                        else if (cast.targetType == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_IMPLICIT);
-                        else if (cast.targetType == Integer.class)   invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_IMPLICIT);
-                        else if (cast.targetType == Long.class)      invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_IMPLICIT);
-                        else if (cast.targetType == Float.class)     invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_IMPLICIT);
-                        else if (cast.targetType == Double.class)    invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_IMPLICIT);
+                        else if (cast.targetType == Byte.class)      invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_BYTE_IMPLICIT);
+                        else if (cast.targetType == Short.class)     invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_SHORT_IMPLICIT);
+                        else if (cast.targetType == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_CHAR_IMPLICIT);
+                        else if (cast.targetType == Integer.class)   invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_INT_IMPLICIT);
+                        else if (cast.targetType == Long.class)      invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_LONG_IMPLICIT);
+                        else if (cast.targetType == Float.class)     invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_FLOAT_IMPLICIT);
+                        else if (cast.targetType == Double.class)    invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_DOUBLE_IMPLICIT);
                         else {
                             throw new IllegalStateException("Illegal tree structure.");
                         }

+ 33 - 15
modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java

@@ -123,21 +123,39 @@ public final class WriterConstants {
             Map.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class);
 
     public static final Type DEF_UTIL_TYPE = Type.getType(Def.class);
-    public static final Method DEF_TO_BOOLEAN         = getAsmMethod(boolean.class, "DefToboolean"       , Object.class);
-    public static final Method DEF_TO_BYTE_IMPLICIT   = getAsmMethod(byte.class   , "DefTobyteImplicit"  , Object.class);
-    public static final Method DEF_TO_SHORT_IMPLICIT  = getAsmMethod(short.class  , "DefToshortImplicit" , Object.class);
-    public static final Method DEF_TO_CHAR_IMPLICIT   = getAsmMethod(char.class   , "DefTocharImplicit"  , Object.class);
-    public static final Method DEF_TO_INT_IMPLICIT    = getAsmMethod(int.class    , "DefTointImplicit"   , Object.class);
-    public static final Method DEF_TO_LONG_IMPLICIT   = getAsmMethod(long.class   , "DefTolongImplicit"  , Object.class);
-    public static final Method DEF_TO_FLOAT_IMPLICIT  = getAsmMethod(float.class  , "DefTofloatImplicit" , Object.class);
-    public static final Method DEF_TO_DOUBLE_IMPLICIT = getAsmMethod(double.class , "DefTodoubleImplicit", Object.class);
-    public static final Method DEF_TO_BYTE_EXPLICIT   = getAsmMethod(byte.class   , "DefTobyteExplicit"  , Object.class);
-    public static final Method DEF_TO_SHORT_EXPLICIT  = getAsmMethod(short.class  , "DefToshortExplicit" , Object.class);
-    public static final Method DEF_TO_CHAR_EXPLICIT   = getAsmMethod(char.class   , "DefTocharExplicit"  , Object.class);
-    public static final Method DEF_TO_INT_EXPLICIT    = getAsmMethod(int.class    , "DefTointExplicit"   , Object.class);
-    public static final Method DEF_TO_LONG_EXPLICIT   = getAsmMethod(long.class   , "DefTolongExplicit"  , Object.class);
-    public static final Method DEF_TO_FLOAT_EXPLICIT  = getAsmMethod(float.class  , "DefTofloatExplicit" , Object.class);
-    public static final Method DEF_TO_DOUBLE_EXPLICIT = getAsmMethod(double.class , "DefTodoubleExplicit", Object.class);
+
+    public static final Method DEF_TO_BOOLEAN = getAsmMethod(boolean.class, "defToboolean", Object.class);
+
+    public static final Method DEF_TO_P_BYTE_IMPLICIT   = getAsmMethod(byte.class   , "defTobyteImplicit"   , Object.class);
+    public static final Method DEF_TO_P_SHORT_IMPLICIT  = getAsmMethod(short.class  , "defToshortImplicit"  , Object.class);
+    public static final Method DEF_TO_P_CHAR_IMPLICIT   = getAsmMethod(char.class   , "defTocharImplicit"   , Object.class);
+    public static final Method DEF_TO_P_INT_IMPLICIT    = getAsmMethod(int.class    , "defTointImplicit"    , Object.class);
+    public static final Method DEF_TO_P_LONG_IMPLICIT   = getAsmMethod(long.class   , "defTolongImplicit"   , Object.class);
+    public static final Method DEF_TO_P_FLOAT_IMPLICIT  = getAsmMethod(float.class  , "defTofloatImplicit"  , Object.class);
+    public static final Method DEF_TO_P_DOUBLE_IMPLICIT = getAsmMethod(double.class , "defTodoubleImplicit" , Object.class);
+    public static final Method DEF_TO_P_BYTE_EXPLICIT   = getAsmMethod(byte.class   , "defTobyteExplicit"   , Object.class);
+    public static final Method DEF_TO_P_SHORT_EXPLICIT  = getAsmMethod(short.class  , "defToshortExplicit"  , Object.class);
+    public static final Method DEF_TO_P_CHAR_EXPLICIT   = getAsmMethod(char.class   , "defTocharExplicit"   , Object.class);
+    public static final Method DEF_TO_P_INT_EXPLICIT    = getAsmMethod(int.class    , "defTointExplicit"    , Object.class);
+    public static final Method DEF_TO_P_LONG_EXPLICIT   = getAsmMethod(long.class   , "defTolongExplicit"   , Object.class);
+    public static final Method DEF_TO_P_FLOAT_EXPLICIT  = getAsmMethod(float.class  , "defTofloatExplicit"  , Object.class);
+    public static final Method DEF_TO_P_DOUBLE_EXPLICIT = getAsmMethod(double.class , "defTodoubleExplicit" , Object.class);
+
+    public static final Method DEF_TO_B_BYTE_IMPLICIT      = getAsmMethod(Byte.class      , "defToByteImplicit"      , Object.class);
+    public static final Method DEF_TO_B_SHORT_IMPLICIT     = getAsmMethod(Short.class     , "defToShortImplicit"     , Object.class);
+    public static final Method DEF_TO_B_CHARACTER_IMPLICIT = getAsmMethod(Character.class , "defToCharacterImplicit" , Object.class);
+    public static final Method DEF_TO_B_INTEGER_IMPLICIT   = getAsmMethod(Integer.class   , "defToIntegerImplicit"   , Object.class);
+    public static final Method DEF_TO_B_LONG_IMPLICIT      = getAsmMethod(Long.class      , "defToLongImplicit"      , Object.class);
+    public static final Method DEF_TO_B_FLOAT_IMPLICIT     = getAsmMethod(Float.class     , "defToFloatImplicit"     , Object.class);
+    public static final Method DEF_TO_B_DOUBLE_IMPLICIT    = getAsmMethod(Double.class    , "defToDoubleImplicit"    , Object.class);
+    public static final Method DEF_TO_B_BYTE_EXPLICIT      = getAsmMethod(Byte.class      , "defToByteExplicit"      , Object.class);
+    public static final Method DEF_TO_B_SHORT_EXPLICIT     = getAsmMethod(Short.class     , "defToShortExplicit"     , Object.class);
+    public static final Method DEF_TO_B_CHARACTER_EXPLICIT = getAsmMethod(Character.class , "defToCharacterExplicit" , Object.class);
+    public static final Method DEF_TO_B_INTEGER_EXPLICIT   = getAsmMethod(Integer.class   , "defToIntegerExplicit"   , Object.class);
+    public static final Method DEF_TO_B_LONG_EXPLICIT      = getAsmMethod(Long.class      , "defToLongExplicit"      , Object.class);
+    public static final Method DEF_TO_B_FLOAT_EXPLICIT     = getAsmMethod(Float.class     , "defToFloatExplicit"     , Object.class);
+    public static final Method DEF_TO_B_DOUBLE_EXPLICIT    = getAsmMethod(Double.class    , "defToDoubleExplicit"    , Object.class);
+
     public static final Type DEF_ARRAY_LENGTH_METHOD_TYPE = Type.getMethodType(Type.INT_TYPE, Type.getType(Object.class));
 
     /** invokedynamic bootstrap for lambda expression/method references */

+ 7 - 10
modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java

@@ -27,36 +27,33 @@ import java.util.Objects;
 public final class PainlessClass {
 
     public final Map<String, PainlessConstructor> constructors;
-
     public final Map<String, PainlessMethod> staticMethods;
     public final Map<String, PainlessMethod> methods;
-
     public final Map<String, PainlessField> staticFields;
     public final Map<String, PainlessField> fields;
+    public final PainlessMethod functionalInterfaceMethod;
 
+    public final Map<String, PainlessMethod> runtimeMethods;
     public final Map<String, MethodHandle> getterMethodHandles;
     public final Map<String, MethodHandle> setterMethodHandles;
 
-    public final PainlessMethod functionalInterfaceMethod;
-
     PainlessClass(Map<String, PainlessConstructor> constructors,
             Map<String, PainlessMethod> staticMethods, Map<String, PainlessMethod> methods,
             Map<String, PainlessField> staticFields, Map<String, PainlessField> fields,
-            Map<String, MethodHandle> getterMethodHandles, Map<String, MethodHandle> setterMethodHandles,
-            PainlessMethod functionalInterfaceMethod) {
+            PainlessMethod functionalInterfaceMethod,
+            Map<String, PainlessMethod> runtimeMethods,
+            Map<String, MethodHandle> getterMethodHandles, Map<String, MethodHandle> setterMethodHandles) {
 
         this.constructors = Collections.unmodifiableMap(constructors);
-
         this.staticMethods = Collections.unmodifiableMap(staticMethods);
         this.methods = Collections.unmodifiableMap(methods);
-
         this.staticFields = Collections.unmodifiableMap(staticFields);
         this.fields = Collections.unmodifiableMap(fields);
+        this.functionalInterfaceMethod = functionalInterfaceMethod;
 
         this.getterMethodHandles = Collections.unmodifiableMap(getterMethodHandles);
         this.setterMethodHandles = Collections.unmodifiableMap(setterMethodHandles);
-
-        this.functionalInterfaceMethod = functionalInterfaceMethod;
+        this.runtimeMethods = Collections.unmodifiableMap(runtimeMethods);
     }
 
     @Override

+ 6 - 10
modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClassBuilder.java

@@ -27,36 +27,32 @@ import java.util.Objects;
 final class PainlessClassBuilder {
 
     final Map<String, PainlessConstructor> constructors;
-
     final Map<String, PainlessMethod> staticMethods;
     final Map<String, PainlessMethod> methods;
-
     final Map<String, PainlessField> staticFields;
     final Map<String, PainlessField> fields;
+    PainlessMethod functionalInterfaceMethod;
 
+    final Map<String, PainlessMethod> runtimeMethods;
     final Map<String, MethodHandle> getterMethodHandles;
     final Map<String, MethodHandle> setterMethodHandles;
 
-    PainlessMethod functionalInterfaceMethod;
-
     PainlessClassBuilder() {
         constructors = new HashMap<>();
-
         staticMethods = new HashMap<>();
         methods = new HashMap<>();
-
         staticFields = new HashMap<>();
         fields = new HashMap<>();
+        functionalInterfaceMethod = null;
 
+        runtimeMethods = new HashMap<>();
         getterMethodHandles = new HashMap<>();
         setterMethodHandles = new HashMap<>();
-
-        functionalInterfaceMethod = null;
     }
 
     PainlessClass build() {
-        return new PainlessClass(constructors, staticMethods, methods, staticFields, fields,
-                getterMethodHandles, setterMethodHandles, functionalInterfaceMethod);
+        return new PainlessClass(constructors, staticMethods, methods, staticFields, fields, functionalInterfaceMethod,
+                runtimeMethods, getterMethodHandles, setterMethodHandles);
     }
 
     @Override

+ 2 - 1
modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java

@@ -227,7 +227,8 @@ public final class PainlessLookup {
         Objects.requireNonNull(methodName);
 
         String painlessMethodKey = buildPainlessMethodKey(methodName, methodArity);
-        Function<PainlessClass, PainlessMethod> objectLookup = targetPainlessClass -> targetPainlessClass.methods.get(painlessMethodKey);
+        Function<PainlessClass, PainlessMethod> objectLookup =
+                targetPainlessClass -> targetPainlessClass.runtimeMethods.get(painlessMethodKey);
 
         return lookupRuntimePainlessObject(originalTargetClass, objectLookup);
     }

+ 225 - 33
modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookupBuilder.java

@@ -19,6 +19,10 @@
 
 package org.elasticsearch.painless.lookup;
 
+import org.elasticsearch.bootstrap.BootstrapInfo;
+import org.elasticsearch.painless.Def;
+import org.elasticsearch.painless.MethodWriter;
+import org.elasticsearch.painless.WriterConstants;
 import org.elasticsearch.painless.spi.Whitelist;
 import org.elasticsearch.painless.spi.WhitelistClass;
 import org.elasticsearch.painless.spi.WhitelistClassBinding;
@@ -26,6 +30,9 @@ import org.elasticsearch.painless.spi.WhitelistConstructor;
 import org.elasticsearch.painless.spi.WhitelistField;
 import org.elasticsearch.painless.spi.WhitelistInstanceBinding;
 import org.elasticsearch.painless.spi.WhitelistMethod;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.GeneratorAdapter;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
@@ -34,6 +41,13 @@ import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.PrivilegedAction;
+import java.security.SecureClassLoader;
+import java.security.cert.Certificate;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -41,6 +55,15 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.regex.Pattern;
 
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_B_BYTE_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_B_CHARACTER_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_B_DOUBLE_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_B_FLOAT_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_B_INTEGER_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_B_LONG_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_TO_B_SHORT_IMPLICIT;
+import static org.elasticsearch.painless.WriterConstants.DEF_UTIL_TYPE;
+import static org.elasticsearch.painless.WriterConstants.OBJECT_TYPE;
 import static org.elasticsearch.painless.lookup.PainlessLookupUtility.DEF_CLASS_NAME;
 import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessConstructorKey;
 import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessFieldKey;
@@ -51,16 +74,42 @@ import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typesToCan
 
 public final class PainlessLookupBuilder {
 
+    private static final class BridgeLoader extends SecureClassLoader {
+        BridgeLoader(ClassLoader parent) {
+            super(parent);
+        }
+
+        @Override
+        public Class<?> findClass(String name) throws ClassNotFoundException {
+            return Def.class.getName().equals(name) ? Def.class : super.findClass(name);
+        }
+
+        Class<?> defineBridge(String name, byte[] bytes) {
+            return defineClass(name, bytes, 0, bytes.length, CODESOURCE);
+        }
+    }
+
+    private static final CodeSource CODESOURCE;
+
     private static final Map<PainlessConstructor    , PainlessConstructor>     painlessConstructorCache     = new HashMap<>();
     private static final Map<PainlessMethod         , PainlessMethod>          painlessMethodCache          = new HashMap<>();
     private static final Map<PainlessField          , PainlessField>           painlessFieldCache           = new HashMap<>();
     private static final Map<PainlessClassBinding   , PainlessClassBinding>    painlessClassBindingCache    = new HashMap<>();
     private static final Map<PainlessInstanceBinding, PainlessInstanceBinding> painlessInstanceBindingCache = new HashMap<>();
+    private static final Map<PainlessMethod         , PainlessMethod>          painlessBridgeCache          = new HashMap<>();
 
     private static final Pattern CLASS_NAME_PATTERN  = Pattern.compile("^[_a-zA-Z][._a-zA-Z0-9]*$");
     private static final Pattern METHOD_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][_a-zA-Z0-9]*$");
     private static final Pattern FIELD_NAME_PATTERN  = Pattern.compile("^[_a-zA-Z][_a-zA-Z0-9]*$");
 
+    static {
+        try {
+            CODESOURCE = new CodeSource(new URL("file:" + BootstrapInfo.UNTRUSTED_CODEBASE), (Certificate[])null);
+        } catch (MalformedURLException mue) {
+            throw new RuntimeException(mue);
+        }
+    }
+
     public static PainlessLookup buildFromWhitelists(List<Whitelist> whitelists) {
         PainlessLookupBuilder painlessLookupBuilder = new PainlessLookupBuilder();
         String origin = "internal error";
@@ -1136,8 +1185,9 @@ public final class PainlessLookupBuilder {
 
     public PainlessLookup build() {
         copyPainlessClassMembers();
-        cacheRuntimeHandles();
         setFunctionalInterfaceMethods();
+        generateRuntimeMethods();
+        cacheRuntimeHandles();
 
         Map<Class<?>, PainlessClass> classesToPainlessClasses = new HashMap<>(classesToPainlessClassBuilders.size());
 
@@ -1234,38 +1284,6 @@ public final class PainlessLookupBuilder {
         }
     }
 
-    private void cacheRuntimeHandles() {
-        for (PainlessClassBuilder painlessClassBuilder : classesToPainlessClassBuilders.values()) {
-            cacheRuntimeHandles(painlessClassBuilder);
-        }
-    }
-
-    private void cacheRuntimeHandles(PainlessClassBuilder painlessClassBuilder) {
-        for (PainlessMethod painlessMethod : painlessClassBuilder.methods.values()) {
-            String methodName = painlessMethod.javaMethod.getName();
-            int typeParametersSize = painlessMethod.typeParameters.size();
-
-            if (typeParametersSize == 0 && methodName.startsWith("get") && methodName.length() > 3 &&
-                    Character.isUpperCase(methodName.charAt(3))) {
-                painlessClassBuilder.getterMethodHandles.putIfAbsent(
-                        Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4), painlessMethod.methodHandle);
-            } else if (typeParametersSize == 0 && methodName.startsWith("is") && methodName.length() > 2 &&
-                    Character.isUpperCase(methodName.charAt(2))) {
-                painlessClassBuilder.getterMethodHandles.putIfAbsent(
-                        Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3), painlessMethod.methodHandle);
-            } else if (typeParametersSize == 1 && methodName.startsWith("set") && methodName.length() > 3 &&
-                    Character.isUpperCase(methodName.charAt(3))) {
-                painlessClassBuilder.setterMethodHandles.putIfAbsent(
-                        Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4), painlessMethod.methodHandle);
-            }
-        }
-
-        for (PainlessField painlessField : painlessClassBuilder.fields.values()) {
-            painlessClassBuilder.getterMethodHandles.put(painlessField.javaField.getName(), painlessField.getterMethodHandle);
-            painlessClassBuilder.setterMethodHandles.put(painlessField.javaField.getName(), painlessField.setterMethodHandle);
-        }
-    }
-
     private void setFunctionalInterfaceMethods() {
         for (Map.Entry<Class<?>, PainlessClassBuilder> painlessClassBuilderEntry : classesToPainlessClassBuilders.entrySet()) {
             setFunctionalInterfaceMethod(painlessClassBuilderEntry.getKey(), painlessClassBuilderEntry.getValue());
@@ -1296,4 +1314,178 @@ public final class PainlessLookupBuilder {
             }
         }
     }
+
+    /**
+     * Creates a {@link Map} of PainlessMethodKeys to {@link PainlessMethod}s per {@link PainlessClass} stored as
+     * {@link PainlessClass#runtimeMethods} identical to {@link PainlessClass#methods} with the exception of generated
+     * bridge methods. A generated bridge method is created for each whitelisted method that has at least one parameter
+     * with a boxed type to cast from other numeric primitive/boxed types in a symmetric was not handled by
+     * {@link MethodHandle#asType(MethodType)}. As an example {@link MethodHandle#asType(MethodType)} legally casts
+     * from {@link Integer} to long but not from int to {@link Long}. Generated bridge methods cover the latter case.
+     * A generated bridge method replaces the method its a bridge to in the {@link PainlessClass#runtimeMethods}
+     * {@link Map}. The {@link PainlessClass#runtimeMethods} {@link Map} is used exclusively to look up methods at
+     * run-time resulting from calls with a def type value target.
+     */
+    private void generateRuntimeMethods() {
+        for (PainlessClassBuilder painlessClassBuilder : classesToPainlessClassBuilders.values()) {
+            painlessClassBuilder.runtimeMethods.putAll(painlessClassBuilder.methods);
+
+            for (PainlessMethod painlessMethod : painlessClassBuilder.runtimeMethods.values()) {
+                for (Class<?> typeParameter : painlessMethod.typeParameters) {
+                    if (
+                            typeParameter == Byte.class      ||
+                            typeParameter == Short.class     ||
+                            typeParameter == Character.class ||
+                            typeParameter == Integer.class   ||
+                            typeParameter == Long.class      ||
+                            typeParameter == Float.class     ||
+                            typeParameter == Double.class
+                    ) {
+                        generateBridgeMethod(painlessClassBuilder, painlessMethod);
+                    }
+                }
+            }
+        }
+    }
+
+    private void generateBridgeMethod(PainlessClassBuilder painlessClassBuilder, PainlessMethod painlessMethod) {
+        String painlessMethodKey = buildPainlessMethodKey(painlessMethod.javaMethod.getName(), painlessMethod.typeParameters.size());
+        PainlessMethod bridgePainlessMethod = painlessBridgeCache.get(painlessMethod);
+
+        if (bridgePainlessMethod == null) {
+            Method javaMethod = painlessMethod.javaMethod;
+            boolean isStatic = Modifier.isStatic(painlessMethod.javaMethod.getModifiers());
+
+            int bridgeClassFrames = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS;
+            int bridgeClassAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER | Opcodes.ACC_FINAL;
+            String bridgeClassName =
+                    "org/elasticsearch/painless/Bridge$" + javaMethod.getDeclaringClass().getSimpleName() + "$" + javaMethod.getName();
+            ClassWriter bridgeClassWriter = new ClassWriter(bridgeClassFrames);
+            bridgeClassWriter.visit(
+                    WriterConstants.CLASS_VERSION, bridgeClassAccess, bridgeClassName, null, OBJECT_TYPE.getInternalName(), null);
+
+            org.objectweb.asm.commons.Method bridgeConstructorType =
+                    new org.objectweb.asm.commons.Method("<init>", MethodType.methodType(void.class).toMethodDescriptorString());
+            GeneratorAdapter bridgeConstructorWriter =
+                    new GeneratorAdapter(Opcodes.ASM5, bridgeConstructorType, bridgeClassWriter.visitMethod(
+                            Opcodes.ACC_PRIVATE, bridgeConstructorType.getName(), bridgeConstructorType.getDescriptor(), null, null));
+            bridgeConstructorWriter.visitCode();
+            bridgeConstructorWriter.loadThis();
+            bridgeConstructorWriter.invokeConstructor(OBJECT_TYPE, bridgeConstructorType);
+            bridgeConstructorWriter.returnValue();
+            bridgeConstructorWriter.endMethod();
+
+            int bridgeTypeParameterOffset = isStatic ? 0 : 1;
+            List<Class<?>> bridgeTypeParameters = new ArrayList<>(javaMethod.getParameterTypes().length + bridgeTypeParameterOffset);
+
+            if (isStatic == false) {
+                bridgeTypeParameters.add(javaMethod.getDeclaringClass());
+            }
+
+            for (Class<?> typeParameter : javaMethod.getParameterTypes()) {
+                if (
+                        typeParameter == Byte.class      ||
+                        typeParameter == Short.class     ||
+                        typeParameter == Character.class ||
+                        typeParameter == Integer.class   ||
+                        typeParameter == Long.class      ||
+                        typeParameter == Float.class     ||
+                        typeParameter == Double.class
+                ) {
+                    bridgeTypeParameters.add(Object.class);
+                } else {
+                    bridgeTypeParameters.add(typeParameter);
+                }
+            }
+
+            MethodType bridgeMethodType = MethodType.methodType(painlessMethod.returnType, bridgeTypeParameters);
+            MethodWriter bridgeMethodWriter =
+                    new MethodWriter(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
+                            new org.objectweb.asm.commons.Method(
+                                    painlessMethod.javaMethod.getName(), bridgeMethodType.toMethodDescriptorString()),
+                            bridgeClassWriter, null, null);
+            bridgeMethodWriter.visitCode();
+
+            if (isStatic == false) {
+                bridgeMethodWriter.loadArg(0);
+            }
+
+            for (int typeParameterCount = 0; typeParameterCount < javaMethod.getParameterTypes().length; ++typeParameterCount) {
+                bridgeMethodWriter.loadArg(typeParameterCount + bridgeTypeParameterOffset);
+                Class<?> typeParameter = javaMethod.getParameterTypes()[typeParameterCount];
+
+                if      (typeParameter == Byte.class)      bridgeMethodWriter.invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_BYTE_IMPLICIT);
+                else if (typeParameter == Short.class)     bridgeMethodWriter.invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_SHORT_IMPLICIT);
+                else if (typeParameter == Character.class) bridgeMethodWriter.invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_CHARACTER_IMPLICIT);
+                else if (typeParameter == Integer.class)   bridgeMethodWriter.invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_INTEGER_IMPLICIT);
+                else if (typeParameter == Long.class)      bridgeMethodWriter.invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_LONG_IMPLICIT);
+                else if (typeParameter == Float.class)     bridgeMethodWriter.invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_FLOAT_IMPLICIT);
+                else if (typeParameter == Double.class)    bridgeMethodWriter.invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_DOUBLE_IMPLICIT);
+            }
+
+            bridgeMethodWriter.invokeMethodCall(painlessMethod);
+            bridgeMethodWriter.returnValue();
+            bridgeMethodWriter.endMethod();
+
+            bridgeClassWriter.visitEnd();
+
+            try {
+                BridgeLoader bridgeLoader = AccessController.doPrivileged(new PrivilegedAction<BridgeLoader>() {
+                    @Override
+                    public BridgeLoader run() {
+                        return new BridgeLoader(javaMethod.getDeclaringClass().getClassLoader());
+                    }
+                });
+
+                Class<?> bridgeClass = bridgeLoader.defineBridge(bridgeClassName.replace('/', '.'), bridgeClassWriter.toByteArray());
+                Method bridgeMethod = bridgeClass.getMethod(
+                        painlessMethod.javaMethod.getName(), bridgeTypeParameters.toArray(new Class<?>[0]));
+                MethodHandle bridgeHandle = MethodHandles.publicLookup().in(bridgeClass).unreflect(bridgeClass.getMethods()[0]);
+                bridgePainlessMethod = new PainlessMethod(bridgeMethod, bridgeClass,
+                        painlessMethod.returnType, bridgeTypeParameters, bridgeHandle, bridgeMethodType);
+                painlessClassBuilder.runtimeMethods.put(painlessMethodKey, bridgePainlessMethod);
+                painlessBridgeCache.put(painlessMethod, bridgePainlessMethod);
+            } catch (Exception exception) {
+                throw new IllegalStateException(
+                        "internal error occurred attempting to generate a bridge method [" + bridgeClassName + "]", exception);
+            }
+        } else {
+            painlessClassBuilder.runtimeMethods.put(painlessMethodKey, bridgePainlessMethod);
+        }
+    }
+
+    private void cacheRuntimeHandles() {
+        for (PainlessClassBuilder painlessClassBuilder : classesToPainlessClassBuilders.values()) {
+            cacheRuntimeHandles(painlessClassBuilder);
+        }
+    }
+
+    private void cacheRuntimeHandles(PainlessClassBuilder painlessClassBuilder) {
+        for (Map.Entry<String, PainlessMethod> painlessMethodEntry : painlessClassBuilder.methods.entrySet()) {
+            String methodKey = painlessMethodEntry.getKey();
+            PainlessMethod painlessMethod = painlessMethodEntry.getValue();
+            PainlessMethod bridgePainlessMethod = painlessClassBuilder.runtimeMethods.get(methodKey);
+            String methodName = painlessMethod.javaMethod.getName();
+            int typeParametersSize = painlessMethod.typeParameters.size();
+
+            if (typeParametersSize == 0 && methodName.startsWith("get") && methodName.length() > 3 &&
+                    Character.isUpperCase(methodName.charAt(3))) {
+                painlessClassBuilder.getterMethodHandles.putIfAbsent(
+                        Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4), bridgePainlessMethod.methodHandle);
+            } else if (typeParametersSize == 0 && methodName.startsWith("is") && methodName.length() > 2 &&
+                    Character.isUpperCase(methodName.charAt(2))) {
+                painlessClassBuilder.getterMethodHandles.putIfAbsent(
+                        Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3), bridgePainlessMethod.methodHandle);
+            } else if (typeParametersSize == 1 && methodName.startsWith("set") && methodName.length() > 3 &&
+                    Character.isUpperCase(methodName.charAt(3))) {
+                painlessClassBuilder.setterMethodHandles.putIfAbsent(
+                        Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4), bridgePainlessMethod.methodHandle);
+            }
+        }
+
+        for (PainlessField painlessField : painlessClassBuilder.fields.values()) {
+            painlessClassBuilder.getterMethodHandles.put(painlessField.javaField.getName(), painlessField.getterMethodHandle);
+            painlessClassBuilder.setterMethodHandles.put(painlessField.javaField.getName(), painlessField.setterMethodHandle);
+        }
+    }
 }

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

@@ -129,7 +129,7 @@ public final class SFunction extends AStatement {
         List<Class<?>> paramTypes = new ArrayList<>();
 
         for (int param = 0; param < this.paramTypeStrs.size(); ++param) {
-                Class<?> paramType = painlessLookup.canonicalTypeNameToType(this.paramTypeStrs.get(param));
+            Class<?> paramType = painlessLookup.canonicalTypeNameToType(this.paramTypeStrs.get(param));
 
             if (paramType == null) {
                 throw createError(new IllegalArgumentException(

+ 4 - 0
modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt

@@ -249,10 +249,14 @@ class org.elasticsearch.painless.FeatureTest no_import {
   (int,int)
   int getX()
   int getY()
+  Integer getI()
   void setX(int)
   void setY(int)
+  void setI(Integer)
   boolean overloadedStatic()
   boolean overloadedStatic(boolean)
+  int staticNumberTest(Number)
+  Double mixedAdd(int, Byte, char, Float)
   Object twoFunctionsOfX(Function,Function)
   void listInput(List)
   int org.elasticsearch.painless.FeatureTestAugmentation getTotal()

+ 18 - 0
modules/lang-painless/src/test/java/org/elasticsearch/painless/CastTests.java

@@ -327,4 +327,22 @@ public class CastTests extends ScriptTestCase {
             exec("Map map = ['a': 1,'b': 2,'c': 3]; def x = new HashMap(); x.put(1, map.clear());");
         });
     }
+
+    public void testBoxedDefCalls() {
+        assertEquals(1, exec("def x = 1; def y = 2.0; y.compareTo(x);"));
+        assertEquals(1, exec("def y = 2.0; y.compareTo(1);"));
+        assertEquals(1, exec("int x = 1; def y = 2.0; y.compareTo(x);"));
+        assertEquals(-1, exec("Integer x = Integer.valueOf(3); def y = 2.0; y.compareTo(x);"));
+        assertEquals(2, exec("def f = new org.elasticsearch.painless.FeatureTest(); f.i = (byte)2; f.i"));
+        assertEquals(4.0, exec(
+                "def x = new org.elasticsearch.painless.FeatureTest(); " +
+                "Byte i = Byte.valueOf(3); " +
+                "byte j = 1;" +
+                "Short s = Short.valueOf(-2);" +
+                "x.mixedAdd(j, i, (char)2, s)"
+        ));
+        assertNull(exec("def f = new org.elasticsearch.painless.FeatureTest(); f.i = null; f.i"));
+        expectScriptThrows(ClassCastException.class, () -> exec("def x = 2.0; def y = 1; y.compareTo(x);"));
+        expectScriptThrows(ClassCastException.class, () -> exec("float f = 1.0f; def y = 1; y.compareTo(f);"));
+    }
 }