[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.
This commit is contained in:
parent
9d417984bd
commit
2df4bd1f81
|
@ -196,7 +196,7 @@ public final class Def {
|
||||||
int numArguments = callSiteType.parameterCount();
|
int numArguments = callSiteType.parameterCount();
|
||||||
// simple case: no lambdas
|
// simple case: no lambdas
|
||||||
if (recipeString.isEmpty()) {
|
if (recipeString.isEmpty()) {
|
||||||
PainlessMethod painlessMethod = painlessLookup.lookupRuntimePainlessMethod(receiverClass, name, numArguments - 1);
|
PainlessMethod painlessMethod = painlessLookup.lookupRuntimePainlessMethod(receiverClass, name, numArguments - 1);
|
||||||
|
|
||||||
if (painlessMethod == null) {
|
if (painlessMethod == null) {
|
||||||
throw new IllegalArgumentException("dynamic method " +
|
throw new IllegalArgumentException("dynamic method " +
|
||||||
|
@ -445,7 +445,7 @@ public final class Def {
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalArgumentException(
|
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;
|
return (boolean)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte DefTobyteImplicit(final Object value) {
|
public static byte defTobyteImplicit(final Object value) {
|
||||||
return (byte)value;
|
return (byte)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static short DefToshortImplicit(final Object value) {
|
public static short defToshortImplicit(final Object value) {
|
||||||
if (value instanceof Byte) {
|
if (value instanceof Byte) {
|
||||||
return (byte)value;
|
return (byte)value;
|
||||||
} else {
|
} else {
|
||||||
|
@ -638,15 +637,11 @@ public final class Def {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static char DefTocharImplicit(final Object value) {
|
public static char defTocharImplicit(final Object value) {
|
||||||
if (value instanceof Byte) {
|
return (char)value;
|
||||||
return (char)(byte)value;
|
|
||||||
} else {
|
|
||||||
return (char)value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int DefTointImplicit(final Object value) {
|
public static int defTointImplicit(final Object value) {
|
||||||
if (value instanceof Byte) {
|
if (value instanceof Byte) {
|
||||||
return (byte)value;
|
return (byte)value;
|
||||||
} else if (value instanceof Short) {
|
} 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) {
|
if (value instanceof Byte) {
|
||||||
return (byte)value;
|
return (byte)value;
|
||||||
} else if (value instanceof Short) {
|
} 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) {
|
if (value instanceof Byte) {
|
||||||
return (byte)value;
|
return (byte)value;
|
||||||
} else if (value instanceof Short) {
|
} 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) {
|
if (value instanceof Byte) {
|
||||||
return (byte)value;
|
return (byte)value;
|
||||||
} else if (value instanceof Short) {
|
} 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) {
|
if (value instanceof Character) {
|
||||||
return (byte)(char)value;
|
return (byte)(char)value;
|
||||||
} else {
|
} 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) {
|
if (value instanceof Character) {
|
||||||
return (short)(char)value;
|
return (short)(char)value;
|
||||||
} else {
|
} 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) {
|
if (value instanceof Character) {
|
||||||
return ((Character)value);
|
return (char)value;
|
||||||
} else {
|
} else {
|
||||||
return (char)((Number)value).intValue();
|
return (char)((Number)value).intValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int DefTointExplicit(final Object value) {
|
public static int defTointExplicit(final Object value) {
|
||||||
if (value instanceof Character) {
|
if (value instanceof Character) {
|
||||||
return (char)value;
|
return (char)value;
|
||||||
} else {
|
} 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) {
|
if (value instanceof Character) {
|
||||||
return (char)value;
|
return (char)value;
|
||||||
} else {
|
} 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) {
|
if (value instanceof Character) {
|
||||||
return (char)value;
|
return (char)value;
|
||||||
} else {
|
} 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) {
|
if (value instanceof Character) {
|
||||||
return (char)value;
|
return (char)value;
|
||||||
} else {
|
} 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.
|
* "Normalizes" the index into a {@code Map} by making no change to the index.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -39,10 +39,17 @@ public class FeatureTest {
|
||||||
return x + 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 x;
|
||||||
private int y;
|
private int y;
|
||||||
public int z;
|
public int z;
|
||||||
|
|
||||||
|
private Integer i;
|
||||||
|
|
||||||
/** empty ctor */
|
/** empty ctor */
|
||||||
public FeatureTest() {
|
public FeatureTest() {
|
||||||
}
|
}
|
||||||
|
@ -73,6 +80,20 @@ public class FeatureTest {
|
||||||
this.y = 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! */
|
/** method taking two functions! */
|
||||||
public Object twoFunctionsOfX(Function<Object,Object> f, Function<Object,Object> g) {
|
public Object twoFunctionsOfX(Function<Object,Object> f, Function<Object,Object> g) {
|
||||||
return f.apply(g.apply(x));
|
return f.apply(g.apply(x));
|
||||||
|
|
|
@ -40,20 +40,20 @@ import java.util.List;
|
||||||
import static org.elasticsearch.painless.WriterConstants.CHAR_TO_STRING;
|
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_BOOTSTRAP_HANDLE;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_BOOLEAN;
|
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_P_BYTE_EXPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_BYTE_IMPLICIT;
|
import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_BYTE_IMPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_CHAR_EXPLICIT;
|
import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_CHAR_EXPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_CHAR_IMPLICIT;
|
import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_CHAR_IMPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_DOUBLE_EXPLICIT;
|
import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_DOUBLE_EXPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_DOUBLE_IMPLICIT;
|
import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_DOUBLE_IMPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_FLOAT_EXPLICIT;
|
import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_FLOAT_EXPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_FLOAT_IMPLICIT;
|
import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_FLOAT_IMPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_INT_EXPLICIT;
|
import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_INT_EXPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_INT_IMPLICIT;
|
import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_INT_IMPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_LONG_EXPLICIT;
|
import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_LONG_EXPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_LONG_IMPLICIT;
|
import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_LONG_IMPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_SHORT_EXPLICIT;
|
import static org.elasticsearch.painless.WriterConstants.DEF_TO_P_SHORT_EXPLICIT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_TO_SHORT_IMPLICIT;
|
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.DEF_UTIL_TYPE;
|
||||||
import static org.elasticsearch.painless.WriterConstants.INDY_STRING_CONCAT_BOOTSTRAP_HANDLE;
|
import static org.elasticsearch.painless.WriterConstants.INDY_STRING_CONCAT_BOOTSTRAP_HANDLE;
|
||||||
import static org.elasticsearch.painless.WriterConstants.LAMBDA_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.originalType == def.class) {
|
||||||
if (cast.explicitCast) {
|
if (cast.explicitCast) {
|
||||||
if (cast.targetType == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
|
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 == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_BYTE_EXPLICIT);
|
||||||
else if (cast.targetType == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_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_CHAR_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_INT_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_LONG_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_FLOAT_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_DOUBLE_EXPLICIT);
|
else if (cast.targetType == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_DOUBLE_EXPLICIT);
|
||||||
else {
|
else {
|
||||||
throw new IllegalStateException("Illegal tree structure.");
|
throw new IllegalStateException("Illegal tree structure.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (cast.targetType == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
|
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 == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_BYTE_IMPLICIT);
|
||||||
else if (cast.targetType == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_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_CHAR_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_INT_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_LONG_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_FLOAT_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_DOUBLE_IMPLICIT);
|
else if (cast.targetType == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_P_DOUBLE_IMPLICIT);
|
||||||
else {
|
else {
|
||||||
throw new IllegalStateException("Illegal tree structure.");
|
throw new IllegalStateException("Illegal tree structure.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,21 +123,39 @@ public final class WriterConstants {
|
||||||
Map.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class);
|
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 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_BOOLEAN = getAsmMethod(boolean.class, "defToboolean", 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_P_BYTE_IMPLICIT = getAsmMethod(byte.class , "defTobyteImplicit" , Object.class);
|
||||||
public static final Method DEF_TO_INT_IMPLICIT = getAsmMethod(int.class , "DefTointImplicit" , Object.class);
|
public static final Method DEF_TO_P_SHORT_IMPLICIT = getAsmMethod(short.class , "defToshortImplicit" , Object.class);
|
||||||
public static final Method DEF_TO_LONG_IMPLICIT = getAsmMethod(long.class , "DefTolongImplicit" , Object.class);
|
public static final Method DEF_TO_P_CHAR_IMPLICIT = getAsmMethod(char.class , "defTocharImplicit" , Object.class);
|
||||||
public static final Method DEF_TO_FLOAT_IMPLICIT = getAsmMethod(float.class , "DefTofloatImplicit" , Object.class);
|
public static final Method DEF_TO_P_INT_IMPLICIT = getAsmMethod(int.class , "defTointImplicit" , Object.class);
|
||||||
public static final Method DEF_TO_DOUBLE_IMPLICIT = getAsmMethod(double.class , "DefTodoubleImplicit", Object.class);
|
public static final Method DEF_TO_P_LONG_IMPLICIT = getAsmMethod(long.class , "defTolongImplicit" , Object.class);
|
||||||
public static final Method DEF_TO_BYTE_EXPLICIT = getAsmMethod(byte.class , "DefTobyteExplicit" , Object.class);
|
public static final Method DEF_TO_P_FLOAT_IMPLICIT = getAsmMethod(float.class , "defTofloatImplicit" , Object.class);
|
||||||
public static final Method DEF_TO_SHORT_EXPLICIT = getAsmMethod(short.class , "DefToshortExplicit" , Object.class);
|
public static final Method DEF_TO_P_DOUBLE_IMPLICIT = getAsmMethod(double.class , "defTodoubleImplicit" , Object.class);
|
||||||
public static final Method DEF_TO_CHAR_EXPLICIT = getAsmMethod(char.class , "DefTocharExplicit" , Object.class);
|
public static final Method DEF_TO_P_BYTE_EXPLICIT = getAsmMethod(byte.class , "defTobyteExplicit" , Object.class);
|
||||||
public static final Method DEF_TO_INT_EXPLICIT = getAsmMethod(int.class , "DefTointExplicit" , Object.class);
|
public static final Method DEF_TO_P_SHORT_EXPLICIT = getAsmMethod(short.class , "defToshortExplicit" , Object.class);
|
||||||
public static final Method DEF_TO_LONG_EXPLICIT = getAsmMethod(long.class , "DefTolongExplicit" , Object.class);
|
public static final Method DEF_TO_P_CHAR_EXPLICIT = getAsmMethod(char.class , "defTocharExplicit" , Object.class);
|
||||||
public static final Method DEF_TO_FLOAT_EXPLICIT = getAsmMethod(float.class , "DefTofloatExplicit" , Object.class);
|
public static final Method DEF_TO_P_INT_EXPLICIT = getAsmMethod(int.class , "defTointExplicit" , Object.class);
|
||||||
public static final Method DEF_TO_DOUBLE_EXPLICIT = getAsmMethod(double.class , "DefTodoubleExplicit", 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));
|
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 */
|
/** invokedynamic bootstrap for lambda expression/method references */
|
||||||
|
|
|
@ -27,36 +27,33 @@ import java.util.Objects;
|
||||||
public final class PainlessClass {
|
public final class PainlessClass {
|
||||||
|
|
||||||
public final Map<String, PainlessConstructor> constructors;
|
public final Map<String, PainlessConstructor> constructors;
|
||||||
|
|
||||||
public final Map<String, PainlessMethod> staticMethods;
|
public final Map<String, PainlessMethod> staticMethods;
|
||||||
public final Map<String, PainlessMethod> methods;
|
public final Map<String, PainlessMethod> methods;
|
||||||
|
|
||||||
public final Map<String, PainlessField> staticFields;
|
public final Map<String, PainlessField> staticFields;
|
||||||
public final Map<String, PainlessField> fields;
|
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> getterMethodHandles;
|
||||||
public final Map<String, MethodHandle> setterMethodHandles;
|
public final Map<String, MethodHandle> setterMethodHandles;
|
||||||
|
|
||||||
public final PainlessMethod functionalInterfaceMethod;
|
|
||||||
|
|
||||||
PainlessClass(Map<String, PainlessConstructor> constructors,
|
PainlessClass(Map<String, PainlessConstructor> constructors,
|
||||||
Map<String, PainlessMethod> staticMethods, Map<String, PainlessMethod> methods,
|
Map<String, PainlessMethod> staticMethods, Map<String, PainlessMethod> methods,
|
||||||
Map<String, PainlessField> staticFields, Map<String, PainlessField> fields,
|
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.constructors = Collections.unmodifiableMap(constructors);
|
||||||
|
|
||||||
this.staticMethods = Collections.unmodifiableMap(staticMethods);
|
this.staticMethods = Collections.unmodifiableMap(staticMethods);
|
||||||
this.methods = Collections.unmodifiableMap(methods);
|
this.methods = Collections.unmodifiableMap(methods);
|
||||||
|
|
||||||
this.staticFields = Collections.unmodifiableMap(staticFields);
|
this.staticFields = Collections.unmodifiableMap(staticFields);
|
||||||
this.fields = Collections.unmodifiableMap(fields);
|
this.fields = Collections.unmodifiableMap(fields);
|
||||||
|
this.functionalInterfaceMethod = functionalInterfaceMethod;
|
||||||
|
|
||||||
this.getterMethodHandles = Collections.unmodifiableMap(getterMethodHandles);
|
this.getterMethodHandles = Collections.unmodifiableMap(getterMethodHandles);
|
||||||
this.setterMethodHandles = Collections.unmodifiableMap(setterMethodHandles);
|
this.setterMethodHandles = Collections.unmodifiableMap(setterMethodHandles);
|
||||||
|
this.runtimeMethods = Collections.unmodifiableMap(runtimeMethods);
|
||||||
this.functionalInterfaceMethod = functionalInterfaceMethod;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -27,36 +27,32 @@ import java.util.Objects;
|
||||||
final class PainlessClassBuilder {
|
final class PainlessClassBuilder {
|
||||||
|
|
||||||
final Map<String, PainlessConstructor> constructors;
|
final Map<String, PainlessConstructor> constructors;
|
||||||
|
|
||||||
final Map<String, PainlessMethod> staticMethods;
|
final Map<String, PainlessMethod> staticMethods;
|
||||||
final Map<String, PainlessMethod> methods;
|
final Map<String, PainlessMethod> methods;
|
||||||
|
|
||||||
final Map<String, PainlessField> staticFields;
|
final Map<String, PainlessField> staticFields;
|
||||||
final Map<String, PainlessField> fields;
|
final Map<String, PainlessField> fields;
|
||||||
|
PainlessMethod functionalInterfaceMethod;
|
||||||
|
|
||||||
|
final Map<String, PainlessMethod> runtimeMethods;
|
||||||
final Map<String, MethodHandle> getterMethodHandles;
|
final Map<String, MethodHandle> getterMethodHandles;
|
||||||
final Map<String, MethodHandle> setterMethodHandles;
|
final Map<String, MethodHandle> setterMethodHandles;
|
||||||
|
|
||||||
PainlessMethod functionalInterfaceMethod;
|
|
||||||
|
|
||||||
PainlessClassBuilder() {
|
PainlessClassBuilder() {
|
||||||
constructors = new HashMap<>();
|
constructors = new HashMap<>();
|
||||||
|
|
||||||
staticMethods = new HashMap<>();
|
staticMethods = new HashMap<>();
|
||||||
methods = new HashMap<>();
|
methods = new HashMap<>();
|
||||||
|
|
||||||
staticFields = new HashMap<>();
|
staticFields = new HashMap<>();
|
||||||
fields = new HashMap<>();
|
fields = new HashMap<>();
|
||||||
|
functionalInterfaceMethod = null;
|
||||||
|
|
||||||
|
runtimeMethods = new HashMap<>();
|
||||||
getterMethodHandles = new HashMap<>();
|
getterMethodHandles = new HashMap<>();
|
||||||
setterMethodHandles = new HashMap<>();
|
setterMethodHandles = new HashMap<>();
|
||||||
|
|
||||||
functionalInterfaceMethod = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PainlessClass build() {
|
PainlessClass build() {
|
||||||
return new PainlessClass(constructors, staticMethods, methods, staticFields, fields,
|
return new PainlessClass(constructors, staticMethods, methods, staticFields, fields, functionalInterfaceMethod,
|
||||||
getterMethodHandles, setterMethodHandles, functionalInterfaceMethod);
|
runtimeMethods, getterMethodHandles, setterMethodHandles);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -227,7 +227,8 @@ public final class PainlessLookup {
|
||||||
Objects.requireNonNull(methodName);
|
Objects.requireNonNull(methodName);
|
||||||
|
|
||||||
String painlessMethodKey = buildPainlessMethodKey(methodName, methodArity);
|
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);
|
return lookupRuntimePainlessObject(originalTargetClass, objectLookup);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
|
|
||||||
package org.elasticsearch.painless.lookup;
|
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.Whitelist;
|
||||||
import org.elasticsearch.painless.spi.WhitelistClass;
|
import org.elasticsearch.painless.spi.WhitelistClass;
|
||||||
import org.elasticsearch.painless.spi.WhitelistClassBinding;
|
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.WhitelistField;
|
||||||
import org.elasticsearch.painless.spi.WhitelistInstanceBinding;
|
import org.elasticsearch.painless.spi.WhitelistInstanceBinding;
|
||||||
import org.elasticsearch.painless.spi.WhitelistMethod;
|
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.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
|
@ -34,6 +41,13 @@ import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
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.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -41,6 +55,15 @@ import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.regex.Pattern;
|
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.DEF_CLASS_NAME;
|
||||||
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessConstructorKey;
|
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessConstructorKey;
|
||||||
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessFieldKey;
|
import static org.elasticsearch.painless.lookup.PainlessLookupUtility.buildPainlessFieldKey;
|
||||||
|
@ -51,16 +74,42 @@ import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typesToCan
|
||||||
|
|
||||||
public final class PainlessLookupBuilder {
|
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<PainlessConstructor , PainlessConstructor> painlessConstructorCache = new HashMap<>();
|
||||||
private static final Map<PainlessMethod , PainlessMethod> painlessMethodCache = new HashMap<>();
|
private static final Map<PainlessMethod , PainlessMethod> painlessMethodCache = new HashMap<>();
|
||||||
private static final Map<PainlessField , PainlessField> painlessFieldCache = new HashMap<>();
|
private static final Map<PainlessField , PainlessField> painlessFieldCache = new HashMap<>();
|
||||||
private static final Map<PainlessClassBinding , PainlessClassBinding> painlessClassBindingCache = new HashMap<>();
|
private static final Map<PainlessClassBinding , PainlessClassBinding> painlessClassBindingCache = new HashMap<>();
|
||||||
private static final Map<PainlessInstanceBinding, PainlessInstanceBinding> painlessInstanceBindingCache = 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 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 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]*$");
|
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) {
|
public static PainlessLookup buildFromWhitelists(List<Whitelist> whitelists) {
|
||||||
PainlessLookupBuilder painlessLookupBuilder = new PainlessLookupBuilder();
|
PainlessLookupBuilder painlessLookupBuilder = new PainlessLookupBuilder();
|
||||||
String origin = "internal error";
|
String origin = "internal error";
|
||||||
|
@ -1136,8 +1185,9 @@ public final class PainlessLookupBuilder {
|
||||||
|
|
||||||
public PainlessLookup build() {
|
public PainlessLookup build() {
|
||||||
copyPainlessClassMembers();
|
copyPainlessClassMembers();
|
||||||
cacheRuntimeHandles();
|
|
||||||
setFunctionalInterfaceMethods();
|
setFunctionalInterfaceMethods();
|
||||||
|
generateRuntimeMethods();
|
||||||
|
cacheRuntimeHandles();
|
||||||
|
|
||||||
Map<Class<?>, PainlessClass> classesToPainlessClasses = new HashMap<>(classesToPainlessClassBuilders.size());
|
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() {
|
private void setFunctionalInterfaceMethods() {
|
||||||
for (Map.Entry<Class<?>, PainlessClassBuilder> painlessClassBuilderEntry : classesToPainlessClassBuilders.entrySet()) {
|
for (Map.Entry<Class<?>, PainlessClassBuilder> painlessClassBuilderEntry : classesToPainlessClassBuilders.entrySet()) {
|
||||||
setFunctionalInterfaceMethod(painlessClassBuilderEntry.getKey(), painlessClassBuilderEntry.getValue());
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ public final class SFunction extends AStatement {
|
||||||
List<Class<?>> paramTypes = new ArrayList<>();
|
List<Class<?>> paramTypes = new ArrayList<>();
|
||||||
|
|
||||||
for (int param = 0; param < this.paramTypeStrs.size(); ++param) {
|
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) {
|
if (paramType == null) {
|
||||||
throw createError(new IllegalArgumentException(
|
throw createError(new IllegalArgumentException(
|
||||||
|
|
|
@ -249,10 +249,14 @@ class org.elasticsearch.painless.FeatureTest no_import {
|
||||||
(int,int)
|
(int,int)
|
||||||
int getX()
|
int getX()
|
||||||
int getY()
|
int getY()
|
||||||
|
Integer getI()
|
||||||
void setX(int)
|
void setX(int)
|
||||||
void setY(int)
|
void setY(int)
|
||||||
|
void setI(Integer)
|
||||||
boolean overloadedStatic()
|
boolean overloadedStatic()
|
||||||
boolean overloadedStatic(boolean)
|
boolean overloadedStatic(boolean)
|
||||||
|
int staticNumberTest(Number)
|
||||||
|
Double mixedAdd(int, Byte, char, Float)
|
||||||
Object twoFunctionsOfX(Function,Function)
|
Object twoFunctionsOfX(Function,Function)
|
||||||
void listInput(List)
|
void listInput(List)
|
||||||
int org.elasticsearch.painless.FeatureTestAugmentation getTotal()
|
int org.elasticsearch.painless.FeatureTestAugmentation getTotal()
|
||||||
|
|
|
@ -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());");
|
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);"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue