Skip to content

Commit

Permalink
[Painless] Generate Bridge Methods (#36097)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jdconrad committed Dec 7, 2018
1 parent ba8314c commit cfd9c83
Show file tree
Hide file tree
Showing 11 changed files with 531 additions and 123 deletions.
211 changes: 186 additions & 25 deletions modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ static MethodHandle lookupMethod(PainlessLookup painlessLookup, Map<String, Loca
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 " +
Expand Down Expand Up @@ -445,7 +445,7 @@ static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class<?> receive
}

throw new IllegalArgumentException(
"dynamic getter [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "] not found");
"dynamic setter [" + typeToCanonicalTypeName(receiverClass) + ", " + name + "] not found");
}

/**
Expand Down Expand Up @@ -619,34 +619,29 @@ static MethodHandle lookupIterator(Class<?> receiverClass) {
}
}

// 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 {
return (short)value;
}
}

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) {
Expand All @@ -658,7 +653,7 @@ public static int DefTointImplicit(final Object value) {
}
}

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) {
Expand All @@ -672,7 +667,7 @@ public static long DefTolongImplicit(final Object value) {
}
}

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) {
Expand All @@ -688,7 +683,7 @@ public static float DefTofloatImplicit(final Object value) {
}
}

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) {
Expand All @@ -706,62 +701,228 @@ public static double DefTodoubleImplicit(final Object value) {
}
}

public static byte DefTobyteExplicit(final Object value) {
public static byte defTobyteExplicit(final Object value) {
if (value instanceof Character) {
return (byte)(char)value;
} else {
return ((Number)value).byteValue();
}
}

public static short DefToshortExplicit(final Object value) {
public static short defToshortExplicit(final Object value) {
if (value instanceof Character) {
return (short)(char)value;
} else {
return ((Number)value).shortValue();
}
}

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 {
return ((Number)value).intValue();
}
}

public static long DefTolongExplicit(final Object value) {
public static long defTolongExplicit(final Object value) {
if (value instanceof Character) {
return (char)value;
} else {
return ((Number)value).longValue();
}
}

public static float DefTofloatExplicit(final Object value) {
public static float defTofloatExplicit(final Object value) {
if (value instanceof Character) {
return (char)value;
} else {
return ((Number)value).floatValue();
}
}

public static double DefTodoubleExplicit(final Object value) {
public static double defTodoubleExplicit(final Object value) {
if (value instanceof Character) {
return (char)value;
} else {
return ((Number)value).doubleValue();
}
}

// 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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,17 @@ public static float staticAddFloatsTest(float x, float y) {
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() {
}
Expand Down Expand Up @@ -73,6 +80,20 @@ public void setY(int y) {
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));
Expand Down

0 comments on commit cfd9c83

Please sign in to comment.