Fix explicit casts and improve tests.
This commit is contained in:
parent
8f96dd53aa
commit
8d9fa7e0b5
|
@ -33,6 +33,11 @@ public final class CompilerSettings {
|
||||||
* Constant to be used for enabling additional internal compilation checks (slower).
|
* Constant to be used for enabling additional internal compilation checks (slower).
|
||||||
*/
|
*/
|
||||||
public static final String PICKY = "picky";
|
public static final String PICKY = "picky";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For testing: do not use.
|
||||||
|
*/
|
||||||
|
public static final String INITIAL_CALL_SITE_DEPTH = "initialCallSiteDepth";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of statements allowed to be run in a loop.
|
* The maximum number of statements allowed to be run in a loop.
|
||||||
|
@ -44,6 +49,11 @@ public final class CompilerSettings {
|
||||||
* makes things slower too, it is only for debugging.
|
* makes things slower too, it is only for debugging.
|
||||||
*/
|
*/
|
||||||
private boolean picky = false;
|
private boolean picky = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For testing. Do not use.
|
||||||
|
*/
|
||||||
|
private int initialCallSiteDepth = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value for the cumulative total number of statements that can be made in all loops
|
* Returns the value for the cumulative total number of statements that can be made in all loops
|
||||||
|
@ -78,4 +88,20 @@ public final class CompilerSettings {
|
||||||
public void setPicky(boolean picky) {
|
public void setPicky(boolean picky) {
|
||||||
this.picky = picky;
|
this.picky = picky;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns initial call site depth. This means we pretend we've already seen N different types,
|
||||||
|
* to better exercise fallback code in tests.
|
||||||
|
*/
|
||||||
|
public int getInitialCallSiteDepth() {
|
||||||
|
return initialCallSiteDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For testing megamorphic fallbacks. Do not use.
|
||||||
|
* @see #getInitialCallSiteDepth()
|
||||||
|
*/
|
||||||
|
public void setInitialCallSiteDepth(int depth) {
|
||||||
|
this.initialCallSiteDepth = depth;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,7 +279,8 @@ public final class Def {
|
||||||
MethodType nestedType = MethodType.methodType(interfaceType.clazz, captures);
|
MethodType nestedType = MethodType.methodType(interfaceType.clazz, captures);
|
||||||
CallSite nested = DefBootstrap.bootstrap(lookup,
|
CallSite nested = DefBootstrap.bootstrap(lookup,
|
||||||
call,
|
call,
|
||||||
nestedType,
|
nestedType,
|
||||||
|
0,
|
||||||
DefBootstrap.REFERENCE,
|
DefBootstrap.REFERENCE,
|
||||||
interfaceType.name);
|
interfaceType.name);
|
||||||
filter = nested.dynamicInvoker();
|
filter = nested.dynamicInvoker();
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodHandles.Lookup;
|
import java.lang.invoke.MethodHandles.Lookup;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.invoke.MutableCallSite;
|
import java.lang.invoke.MutableCallSite;
|
||||||
|
import java.lang.invoke.WrongMethodTypeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Painless invokedynamic bootstrap for the call site.
|
* Painless invokedynamic bootstrap for the call site.
|
||||||
|
@ -80,9 +81,18 @@ public final class DefBootstrap {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* static bootstrap parameter indicating the binary operator is part of compound assignment (e.g. +=).
|
* static bootstrap parameter indicating the binary operator is part of compound assignment (e.g. +=).
|
||||||
*
|
* <p>
|
||||||
|
* may require {@link MethodHandles#explicitCastArguments}, or a dynamic cast
|
||||||
|
* to cast back to the receiver's type, depending on types seen.
|
||||||
*/
|
*/
|
||||||
public static final int OPERATOR_COMPOUND_ASSIGNMENT = 1 << 1;
|
public static final int OPERATOR_COMPOUND_ASSIGNMENT = 1 << 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* static bootstrap parameter indicating an explicit cast to the return type.
|
||||||
|
* <p>
|
||||||
|
* may require {@link MethodHandles#explicitCastArguments}, depending on types seen.
|
||||||
|
*/
|
||||||
|
public static final int OPERATOR_EXPLICIT_CAST = 1 << 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CallSite that implements the polymorphic inlining cache (PIC).
|
* CallSite that implements the polymorphic inlining cache (PIC).
|
||||||
|
@ -97,7 +107,7 @@ public final class DefBootstrap {
|
||||||
private final Object[] args;
|
private final Object[] args;
|
||||||
int depth; // pkg-protected for testing
|
int depth; // pkg-protected for testing
|
||||||
|
|
||||||
PIC(Lookup lookup, String name, MethodType type, int flavor, Object[] args) {
|
PIC(Lookup lookup, String name, MethodType type, int initialDepth, int flavor, Object[] args) {
|
||||||
super(type);
|
super(type);
|
||||||
if (type.parameterType(0) != Object.class) {
|
if (type.parameterType(0) != Object.class) {
|
||||||
throw new BootstrapMethodError("The receiver type (1st arg) of invokedynamic descriptor must be Object.");
|
throw new BootstrapMethodError("The receiver type (1st arg) of invokedynamic descriptor must be Object.");
|
||||||
|
@ -106,6 +116,7 @@ public final class DefBootstrap {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.flavor = flavor;
|
this.flavor = flavor;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
|
this.depth = initialDepth;
|
||||||
|
|
||||||
MethodHandle fallback = FALLBACK.bindTo(this)
|
MethodHandle fallback = FALLBACK.bindTo(this)
|
||||||
.asCollector(Object[].class, type.parameterCount())
|
.asCollector(Object[].class, type.parameterCount())
|
||||||
|
@ -226,11 +237,14 @@ public final class DefBootstrap {
|
||||||
private final int flavor;
|
private final int flavor;
|
||||||
private final int flags;
|
private final int flags;
|
||||||
|
|
||||||
MIC(String name, MethodType type, int flavor, int flags) {
|
MIC(String name, MethodType type, int initialDepth, int flavor, int flags) {
|
||||||
super(type);
|
super(type);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.flavor = flavor;
|
this.flavor = flavor;
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
|
if (initialDepth > 0) {
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
MethodHandle fallback = FALLBACK.bindTo(this)
|
MethodHandle fallback = FALLBACK.bindTo(this)
|
||||||
.asCollector(Object[].class, type.parameterCount())
|
.asCollector(Object[].class, type.parameterCount())
|
||||||
|
@ -248,7 +262,9 @@ public final class DefBootstrap {
|
||||||
case SHIFT_OPERATOR:
|
case SHIFT_OPERATOR:
|
||||||
// shifts are treated as unary, as java allows long arguments without a cast (but bits are ignored)
|
// shifts are treated as unary, as java allows long arguments without a cast (but bits are ignored)
|
||||||
MethodHandle unary = DefMath.lookupUnary(args[0].getClass(), name);
|
MethodHandle unary = DefMath.lookupUnary(args[0].getClass(), name);
|
||||||
if ((flags & OPERATOR_COMPOUND_ASSIGNMENT) != 0) {
|
if ((flags & OPERATOR_EXPLICIT_CAST) != 0) {
|
||||||
|
unary = DefMath.cast(type().returnType(), unary);
|
||||||
|
} else if ((flags & OPERATOR_COMPOUND_ASSIGNMENT) != 0) {
|
||||||
unary = DefMath.cast(args[0].getClass(), unary);
|
unary = DefMath.cast(args[0].getClass(), unary);
|
||||||
}
|
}
|
||||||
return unary;
|
return unary;
|
||||||
|
@ -257,7 +273,9 @@ public final class DefBootstrap {
|
||||||
return lookupGeneric(); // can handle nulls, casts if supported
|
return lookupGeneric(); // can handle nulls, casts if supported
|
||||||
} else {
|
} else {
|
||||||
MethodHandle binary = DefMath.lookupBinary(args[0].getClass(), args[1].getClass(), name);
|
MethodHandle binary = DefMath.lookupBinary(args[0].getClass(), args[1].getClass(), name);
|
||||||
if ((flags & OPERATOR_COMPOUND_ASSIGNMENT) != 0) {
|
if ((flags & OPERATOR_EXPLICIT_CAST) != 0) {
|
||||||
|
binary = DefMath.cast(type().returnType(), binary);
|
||||||
|
} else if ((flags & OPERATOR_COMPOUND_ASSIGNMENT) != 0) {
|
||||||
binary = DefMath.cast(args[0].getClass(), binary);
|
binary = DefMath.cast(args[0].getClass(), binary);
|
||||||
}
|
}
|
||||||
return binary;
|
return binary;
|
||||||
|
@ -267,11 +285,15 @@ public final class DefBootstrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodHandle lookupGeneric() {
|
private MethodHandle lookupGeneric() {
|
||||||
if ((flags & OPERATOR_COMPOUND_ASSIGNMENT) != 0) {
|
MethodHandle target = DefMath.lookupGeneric(name);
|
||||||
return DefMath.lookupGenericWithCast(name);
|
if ((flags & OPERATOR_EXPLICIT_CAST) != 0) {
|
||||||
} else {
|
// static cast to the return type
|
||||||
return DefMath.lookupGeneric(name);
|
target = DefMath.dynamicCast(target, type().returnType());
|
||||||
|
} else if ((flags & OPERATOR_COMPOUND_ASSIGNMENT) != 0) {
|
||||||
|
// dynamic cast to the receiver's type
|
||||||
|
target = DefMath.dynamicCast(target);
|
||||||
}
|
}
|
||||||
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -288,7 +310,15 @@ public final class DefBootstrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
final MethodType type = type();
|
final MethodType type = type();
|
||||||
final MethodHandle target = lookup(args).asType(type);
|
MethodHandle target = lookup(args);
|
||||||
|
// for math operators: WrongMethodType can be confusing. convert into a ClassCastException if they screw up.
|
||||||
|
try {
|
||||||
|
target = target.asType(type);
|
||||||
|
} catch (WrongMethodTypeException e) {
|
||||||
|
Exception exc = new ClassCastException("Cannot cast from: " + target.type().returnType() + " to " + type.returnType());
|
||||||
|
exc.initCause(e);
|
||||||
|
throw exc;
|
||||||
|
}
|
||||||
|
|
||||||
final MethodHandle test;
|
final MethodHandle test;
|
||||||
if (flavor == BINARY_OPERATOR || flavor == SHIFT_OPERATOR) {
|
if (flavor == BINARY_OPERATOR || flavor == SHIFT_OPERATOR) {
|
||||||
|
@ -384,12 +414,16 @@ public final class DefBootstrap {
|
||||||
/**
|
/**
|
||||||
* invokeDynamic bootstrap method
|
* invokeDynamic bootstrap method
|
||||||
* <p>
|
* <p>
|
||||||
* In addition to ordinary parameters, we also take a static parameter {@code flavor} which
|
* In addition to ordinary parameters, we also take some static parameters:
|
||||||
* tells us what type of dynamic call it is (and which part of whitelist to look at).
|
* <ul>
|
||||||
|
* <li>{@code initialDepth}: initial call site depth. this is used to exercise megamorphic fallback.
|
||||||
|
* <li>{@code flavor}: type of dynamic call it is (and which part of whitelist to look at).
|
||||||
|
* <li>{@code args}: flavor-specific args.
|
||||||
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic
|
* see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic
|
||||||
*/
|
*/
|
||||||
public static CallSite bootstrap(Lookup lookup, String name, MethodType type, int flavor, Object... args) {
|
public static CallSite bootstrap(Lookup lookup, String name, MethodType type, int initialDepth, int flavor, Object... args) {
|
||||||
// validate arguments
|
// validate arguments
|
||||||
switch(flavor) {
|
switch(flavor) {
|
||||||
// "function-call" like things get a polymorphic cache
|
// "function-call" like things get a polymorphic cache
|
||||||
|
@ -408,7 +442,7 @@ public final class DefBootstrap {
|
||||||
if (args.length != numLambdas + 1) {
|
if (args.length != numLambdas + 1) {
|
||||||
throw new BootstrapMethodError("Illegal number of parameters: expected " + numLambdas + " references");
|
throw new BootstrapMethodError("Illegal number of parameters: expected " + numLambdas + " references");
|
||||||
}
|
}
|
||||||
return new PIC(lookup, name, type, flavor, args);
|
return new PIC(lookup, name, type, initialDepth, flavor, args);
|
||||||
case LOAD:
|
case LOAD:
|
||||||
case STORE:
|
case STORE:
|
||||||
case ARRAY_LOAD:
|
case ARRAY_LOAD:
|
||||||
|
@ -417,7 +451,7 @@ public final class DefBootstrap {
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
throw new BootstrapMethodError("Illegal static bootstrap parameters for flavor: " + flavor);
|
throw new BootstrapMethodError("Illegal static bootstrap parameters for flavor: " + flavor);
|
||||||
}
|
}
|
||||||
return new PIC(lookup, name, type, flavor, args);
|
return new PIC(lookup, name, type, initialDepth, flavor, args);
|
||||||
case REFERENCE:
|
case REFERENCE:
|
||||||
if (args.length != 1) {
|
if (args.length != 1) {
|
||||||
throw new BootstrapMethodError("Invalid number of parameters for reference call");
|
throw new BootstrapMethodError("Invalid number of parameters for reference call");
|
||||||
|
@ -425,7 +459,7 @@ public final class DefBootstrap {
|
||||||
if (args[0] instanceof String == false) {
|
if (args[0] instanceof String == false) {
|
||||||
throw new BootstrapMethodError("Illegal parameter for reference call: " + args[0]);
|
throw new BootstrapMethodError("Illegal parameter for reference call: " + args[0]);
|
||||||
}
|
}
|
||||||
return new PIC(lookup, name, type, flavor, args);
|
return new PIC(lookup, name, type, initialDepth, flavor, args);
|
||||||
|
|
||||||
// operators get monomorphic cache, with a generic impl for a fallback
|
// operators get monomorphic cache, with a generic impl for a fallback
|
||||||
case UNARY_OPERATOR:
|
case UNARY_OPERATOR:
|
||||||
|
@ -442,11 +476,11 @@ public final class DefBootstrap {
|
||||||
// we just don't need it anywhere else.
|
// we just don't need it anywhere else.
|
||||||
throw new BootstrapMethodError("This parameter is only supported for BINARY_OPERATORs");
|
throw new BootstrapMethodError("This parameter is only supported for BINARY_OPERATORs");
|
||||||
}
|
}
|
||||||
if ((flags & OPERATOR_COMPOUND_ASSIGNMENT) != 0 && flavor != BINARY_OPERATOR) {
|
if ((flags & OPERATOR_COMPOUND_ASSIGNMENT) != 0 && flavor != BINARY_OPERATOR && flavor != SHIFT_OPERATOR) {
|
||||||
// we just don't need it anywhere else.
|
// we just don't need it anywhere else.
|
||||||
throw new BootstrapMethodError("This parameter is only supported for BINARY_OPERATORs");
|
throw new BootstrapMethodError("This parameter is only supported for BINARY/SHIFT_OPERATORs");
|
||||||
}
|
}
|
||||||
return new MIC(name, type, flavor, flags);
|
return new MIC(name, type, initialDepth, flavor, flags);
|
||||||
default:
|
default:
|
||||||
throw new BootstrapMethodError("Illegal static bootstrap parameter for flavor: " + flavor);
|
throw new BootstrapMethodError("Illegal static bootstrap parameter for flavor: " + flavor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.lang.invoke.MethodType;
|
||||||
import java.lang.invoke.MethodHandles.Lookup;
|
import java.lang.invoke.MethodHandles.Lookup;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -1027,25 +1028,7 @@ public class DefMath {
|
||||||
* class if its not a boxed type.
|
* class if its not a boxed type.
|
||||||
*/
|
*/
|
||||||
private static Class<?> unbox(Class<?> clazz) {
|
private static Class<?> unbox(Class<?> clazz) {
|
||||||
if (clazz == Boolean.class) {
|
return MethodType.methodType(clazz).unwrap().returnType();
|
||||||
return boolean.class;
|
|
||||||
} else if (clazz == Byte.class) {
|
|
||||||
return byte.class;
|
|
||||||
} else if (clazz == Short.class) {
|
|
||||||
return short.class;
|
|
||||||
} else if (clazz == Character.class) {
|
|
||||||
return char.class;
|
|
||||||
} else if (clazz == Integer.class) {
|
|
||||||
return int.class;
|
|
||||||
} else if (clazz == Long.class) {
|
|
||||||
return long.class;
|
|
||||||
} else if (clazz == Float.class) {
|
|
||||||
return float.class;
|
|
||||||
} else if (clazz == Double.class) {
|
|
||||||
return double.class;
|
|
||||||
} else {
|
|
||||||
return clazz;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Unary promotion. All Objects are promoted to Object. */
|
/** Unary promotion. All Objects are promoted to Object. */
|
||||||
|
@ -1146,40 +1129,50 @@ public class DefMath {
|
||||||
public static MethodHandle lookupGeneric(String name) {
|
public static MethodHandle lookupGeneric(String name) {
|
||||||
return TYPE_OP_MAPPING.get(Object.class).get(name);
|
return TYPE_OP_MAPPING.get(Object.class).get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Slow dynamic cast: casts {@code returnValue} to the runtime type of {@code lhs}
|
* Slow dynamic cast: casts {@code returnValue} to the runtime type of {@code lhs}
|
||||||
* based upon inspection. If {@code lhs} is null, no cast takes place.
|
* based upon inspection. If {@code lhs} is null, no cast takes place.
|
||||||
* This is used for the generic fallback case of compound assignment.
|
* This is used for the generic fallback case of compound assignment.
|
||||||
*/
|
*/
|
||||||
static Object dynamicCast(Object returnValue, Object lhs) {
|
static Object dynamicReceiverCast(Object returnValue, Object lhs) {
|
||||||
if (lhs != null) {
|
if (lhs != null) {
|
||||||
Class<?> c = lhs.getClass();
|
return dynamicCast(lhs.getClass(), returnValue);
|
||||||
if (c == returnValue.getClass()) {
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
if (c == Integer.class) {
|
|
||||||
return getNumber(returnValue).intValue();
|
|
||||||
} else if (c == Long.class) {
|
|
||||||
return getNumber(returnValue).longValue();
|
|
||||||
} else if (c == Double.class) {
|
|
||||||
return getNumber(returnValue).doubleValue();
|
|
||||||
} else if (c == Float.class) {
|
|
||||||
return getNumber(returnValue).floatValue();
|
|
||||||
} else if (c == Short.class) {
|
|
||||||
return getNumber(returnValue).shortValue();
|
|
||||||
} else if (c == Byte.class) {
|
|
||||||
return getNumber(returnValue).byteValue();
|
|
||||||
} else if (c == Character.class) {
|
|
||||||
return (char) getNumber(returnValue).intValue();
|
|
||||||
}
|
|
||||||
return lhs.getClass().cast(returnValue);
|
|
||||||
} else {
|
} else {
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slow dynamic cast: casts {@code value} to an instance of {@code clazz}
|
||||||
|
* based upon inspection. If {@code lhs} is null, no cast takes place.
|
||||||
|
*/
|
||||||
|
static Object dynamicCast(Class<?> clazz, Object value) {
|
||||||
|
if (value != null) {
|
||||||
|
if (clazz == value.getClass()) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (clazz == Integer.class) {
|
||||||
|
return getNumber(value).intValue();
|
||||||
|
} else if (clazz == Long.class) {
|
||||||
|
return getNumber(value).longValue();
|
||||||
|
} else if (clazz == Double.class) {
|
||||||
|
return getNumber(value).doubleValue();
|
||||||
|
} else if (clazz == Float.class) {
|
||||||
|
return getNumber(value).floatValue();
|
||||||
|
} else if (clazz == Short.class) {
|
||||||
|
return getNumber(value).shortValue();
|
||||||
|
} else if (clazz == Byte.class) {
|
||||||
|
return getNumber(value).byteValue();
|
||||||
|
} else if (clazz == Character.class) {
|
||||||
|
return (char) getNumber(value).intValue();
|
||||||
|
}
|
||||||
|
return clazz.cast(value);
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Slowly returns a Number for o. Just for supporting dynamicCast */
|
/** Slowly returns a Number for o. Just for supporting dynamicCast */
|
||||||
static Number getNumber(Object o) {
|
static Number getNumber(Object o) {
|
||||||
if (o instanceof Number) {
|
if (o instanceof Number) {
|
||||||
|
@ -1192,28 +1185,40 @@ public class DefMath {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final MethodHandle DYNAMIC_CAST;
|
private static final MethodHandle DYNAMIC_CAST;
|
||||||
|
private static final MethodHandle DYNAMIC_RECEIVER_CAST;
|
||||||
static {
|
static {
|
||||||
final Lookup lookup = MethodHandles.lookup();
|
final Lookup lookup = MethodHandles.lookup();
|
||||||
try {
|
try {
|
||||||
DYNAMIC_CAST = lookup.findStatic(lookup.lookupClass(),
|
DYNAMIC_CAST = lookup.findStatic(lookup.lookupClass(),
|
||||||
"dynamicCast",
|
"dynamicCast",
|
||||||
MethodType.methodType(Object.class, Object.class, Object.class));
|
MethodType.methodType(Object.class, Class.class, Object.class));
|
||||||
|
DYNAMIC_RECEIVER_CAST = lookup.findStatic(lookup.lookupClass(),
|
||||||
|
"dynamicReceiverCast",
|
||||||
|
MethodType.methodType(Object.class, Object.class, Object.class));
|
||||||
} catch (ReflectiveOperationException e) {
|
} catch (ReflectiveOperationException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Looks up generic method, with a dynamic cast to the receiver's type. (compound assignment) */
|
/** Looks up generic method, with a dynamic cast to the receiver's type. (compound assignment) */
|
||||||
public static MethodHandle lookupGenericWithCast(String name) {
|
public static MethodHandle dynamicCast(MethodHandle target) {
|
||||||
MethodHandle generic = lookupGeneric(name);
|
// adapt dynamic receiver cast to the generic method
|
||||||
// adapt dynamic cast to the generic method
|
MethodHandle cast = DYNAMIC_RECEIVER_CAST.asType(MethodType.methodType(target.type().returnType(),
|
||||||
MethodHandle cast = DYNAMIC_CAST.asType(MethodType.methodType(generic.type().returnType(),
|
target.type().returnType(),
|
||||||
generic.type().returnType(),
|
target.type().parameterType(0)));
|
||||||
generic.type().parameterType(0)));
|
|
||||||
// drop the RHS parameter
|
// drop the RHS parameter
|
||||||
cast = MethodHandles.dropArguments(cast, 2, generic.type().parameterType(1));
|
cast = MethodHandles.dropArguments(cast, 2, target.type().parameterType(1));
|
||||||
// combine: f(x,y) -> g(f(x,y), x, y);
|
// combine: f(x,y) -> g(f(x,y), x, y);
|
||||||
return MethodHandles.foldArguments(cast, generic);
|
return MethodHandles.foldArguments(cast, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Looks up generic method, with a dynamic cast to the specified type. (explicit assignment) */
|
||||||
|
public static MethodHandle dynamicCast(MethodHandle target, Class<?> desired) {
|
||||||
|
// adapt dynamic cast to the generic method
|
||||||
|
desired = MethodType.methodType(desired).wrap().returnType();
|
||||||
|
// bind to the boxed type
|
||||||
|
MethodHandle cast = DYNAMIC_CAST.bindTo(desired);
|
||||||
|
return MethodHandles.filterReturnValue(target, cast);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Forces a cast to class A for target (only if types differ) */
|
/** Forces a cast to class A for target (only if types differ) */
|
||||||
|
@ -1221,12 +1226,18 @@ public class DefMath {
|
||||||
MethodType newType = MethodType.methodType(classA).unwrap();
|
MethodType newType = MethodType.methodType(classA).unwrap();
|
||||||
MethodType targetType = MethodType.methodType(target.type().returnType()).unwrap();
|
MethodType targetType = MethodType.methodType(target.type().returnType()).unwrap();
|
||||||
|
|
||||||
|
// don't do a conversion if types are the same. explicitCastArguments has this opto,
|
||||||
|
// but we do it explicitly, to make the boolean check simpler
|
||||||
if (newType.returnType() == targetType.returnType()) {
|
if (newType.returnType() == targetType.returnType()) {
|
||||||
return target; // no conversion
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is safe for our uses of it here only, because we change just the return value,
|
// we don't allow the to/from boolean conversions of explicitCastArguments
|
||||||
// the original method itself does all the type checks correctly.
|
if (newType.returnType() == boolean.class || targetType.returnType() == boolean.class) {
|
||||||
|
throw new ClassCastException("Cannot cast " + targetType.returnType() + " to " + newType.returnType());
|
||||||
|
}
|
||||||
|
|
||||||
|
// null return values are not possible for our arguments.
|
||||||
return MethodHandles.explicitCastArguments(target, target.type().changeReturnType(newType.returnType()));
|
return MethodHandles.explicitCastArguments(target, target.type().changeReturnType(newType.returnType()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.objectweb.asm.commons.Method;
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -78,15 +79,17 @@ import static org.elasticsearch.painless.WriterConstants.UTILITY_TYPE;
|
||||||
*/
|
*/
|
||||||
public final class MethodWriter extends GeneratorAdapter {
|
public final class MethodWriter extends GeneratorAdapter {
|
||||||
private final BitSet statements;
|
private final BitSet statements;
|
||||||
|
private final CompilerSettings settings;
|
||||||
|
|
||||||
private final Deque<List<org.objectweb.asm.Type>> stringConcatArgs =
|
private final Deque<List<org.objectweb.asm.Type>> stringConcatArgs =
|
||||||
(INDY_STRING_CONCAT_BOOTSTRAP_HANDLE == null) ? null : new ArrayDeque<>();
|
(INDY_STRING_CONCAT_BOOTSTRAP_HANDLE == null) ? null : new ArrayDeque<>();
|
||||||
|
|
||||||
public MethodWriter(int access, Method method, ClassVisitor cw, BitSet statements) {
|
public MethodWriter(int access, Method method, ClassVisitor cw, BitSet statements, CompilerSettings settings) {
|
||||||
super(Opcodes.ASM5, cw.visitMethod(access, method.getName(), method.getDescriptor(), null, null),
|
super(Opcodes.ASM5, cw.visitMethod(access, method.getName(), method.getDescriptor(), null, null),
|
||||||
access, method.getName(), method.getDescriptor());
|
access, method.getName(), method.getDescriptor());
|
||||||
|
|
||||||
this.statements = statements;
|
this.statements = statements;
|
||||||
|
this.settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -269,23 +272,18 @@ public final class MethodWriter extends GeneratorAdapter {
|
||||||
|
|
||||||
/** Writes a dynamic binary instruction: returnType, lhs, and rhs can be different */
|
/** Writes a dynamic binary instruction: returnType, lhs, and rhs can be different */
|
||||||
public void writeDynamicBinaryInstruction(Location location, Type returnType, Type lhs, Type rhs,
|
public void writeDynamicBinaryInstruction(Location location, Type returnType, Type lhs, Type rhs,
|
||||||
Operation operation, boolean compoundAssignment) {
|
Operation operation, int flags) {
|
||||||
org.objectweb.asm.Type methodType = org.objectweb.asm.Type.getMethodType(returnType.type, lhs.type, rhs.type);
|
org.objectweb.asm.Type methodType = org.objectweb.asm.Type.getMethodType(returnType.type, lhs.type, rhs.type);
|
||||||
String descriptor = methodType.getDescriptor();
|
|
||||||
|
|
||||||
int flags = 0;
|
|
||||||
if (compoundAssignment) {
|
|
||||||
flags |= DefBootstrap.OPERATOR_COMPOUND_ASSIGNMENT;
|
|
||||||
}
|
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case MUL:
|
case MUL:
|
||||||
invokeDynamic("mul", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
|
invokeDefCall("mul", methodType, DefBootstrap.BINARY_OPERATOR, flags);
|
||||||
break;
|
break;
|
||||||
case DIV:
|
case DIV:
|
||||||
invokeDynamic("div", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
|
invokeDefCall("div", methodType, DefBootstrap.BINARY_OPERATOR, flags);
|
||||||
break;
|
break;
|
||||||
case REM:
|
case REM:
|
||||||
invokeDynamic("rem", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
|
invokeDefCall("rem", methodType, DefBootstrap.BINARY_OPERATOR, flags);
|
||||||
break;
|
break;
|
||||||
case ADD:
|
case ADD:
|
||||||
// if either side is primitive, then the + operator should always throw NPE on null,
|
// if either side is primitive, then the + operator should always throw NPE on null,
|
||||||
|
@ -295,28 +293,28 @@ public final class MethodWriter extends GeneratorAdapter {
|
||||||
if (!hasPrimitiveArg) {
|
if (!hasPrimitiveArg) {
|
||||||
flags |= DefBootstrap.OPERATOR_ALLOWS_NULL;
|
flags |= DefBootstrap.OPERATOR_ALLOWS_NULL;
|
||||||
}
|
}
|
||||||
invokeDynamic("add", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
|
invokeDefCall("add", methodType, DefBootstrap.BINARY_OPERATOR, flags);
|
||||||
break;
|
break;
|
||||||
case SUB:
|
case SUB:
|
||||||
invokeDynamic("sub", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
|
invokeDefCall("sub", methodType, DefBootstrap.BINARY_OPERATOR, flags);
|
||||||
break;
|
break;
|
||||||
case LSH:
|
case LSH:
|
||||||
invokeDynamic("lsh", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.SHIFT_OPERATOR, flags);
|
invokeDefCall("lsh", methodType, DefBootstrap.SHIFT_OPERATOR, flags);
|
||||||
break;
|
break;
|
||||||
case USH:
|
case USH:
|
||||||
invokeDynamic("ush", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.SHIFT_OPERATOR, flags);
|
invokeDefCall("ush", methodType, DefBootstrap.SHIFT_OPERATOR, flags);
|
||||||
break;
|
break;
|
||||||
case RSH:
|
case RSH:
|
||||||
invokeDynamic("rsh", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.SHIFT_OPERATOR, flags);
|
invokeDefCall("rsh", methodType, DefBootstrap.SHIFT_OPERATOR, flags);
|
||||||
break;
|
break;
|
||||||
case BWAND:
|
case BWAND:
|
||||||
invokeDynamic("and", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
|
invokeDefCall("and", methodType, DefBootstrap.BINARY_OPERATOR, flags);
|
||||||
break;
|
break;
|
||||||
case XOR:
|
case XOR:
|
||||||
invokeDynamic("xor", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
|
invokeDefCall("xor", methodType, DefBootstrap.BINARY_OPERATOR, flags);
|
||||||
break;
|
break;
|
||||||
case BWOR:
|
case BWOR:
|
||||||
invokeDynamic("or", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
|
invokeDefCall("or", methodType, DefBootstrap.BINARY_OPERATOR, flags);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw location.createError(new IllegalStateException("Illegal tree structure."));
|
throw location.createError(new IllegalStateException("Illegal tree structure."));
|
||||||
|
@ -391,4 +389,19 @@ public final class MethodWriter extends GeneratorAdapter {
|
||||||
public void visitEnd() {
|
public void visitEnd() {
|
||||||
throw new AssertionError("Should never call this method on MethodWriter, use endMethod() instead");
|
throw new AssertionError("Should never call this method on MethodWriter, use endMethod() instead");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a dynamic call for a def method.
|
||||||
|
* @param name method name
|
||||||
|
* @param methodType callsite signature
|
||||||
|
* @param flavor type of call
|
||||||
|
* @param params flavor-specific parameters
|
||||||
|
*/
|
||||||
|
public void invokeDefCall(String name, org.objectweb.asm.Type methodType, int flavor, Object... params) {
|
||||||
|
Object[] args = new Object[params.length + 2];
|
||||||
|
args[0] = settings.getInitialCallSiteDepth();
|
||||||
|
args[1] = flavor;
|
||||||
|
System.arraycopy(params, 0, args, 2, params.length);
|
||||||
|
invokeDynamic(name, methodType.getDescriptor(), DEF_BOOTSTRAP_HANDLE, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,12 @@ public final class PainlessScriptEngineService extends AbstractComponent impleme
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
compilerSettings.setPicky(Boolean.parseBoolean(value));
|
compilerSettings.setPicky(Boolean.parseBoolean(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value = copy.remove(CompilerSettings.INITIAL_CALL_SITE_DEPTH);
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
compilerSettings.setInitialCallSiteDepth(Integer.parseInt(value));
|
||||||
|
}
|
||||||
|
|
||||||
if (!copy.isEmpty()) {
|
if (!copy.isEmpty()) {
|
||||||
throw new IllegalArgumentException("Unrecognized compile-time parameter(s): " + copy);
|
throw new IllegalArgumentException("Unrecognized compile-time parameter(s): " + copy);
|
||||||
|
|
|
@ -85,9 +85,10 @@ public final class WriterConstants {
|
||||||
public final static Method MATCHER_FIND = getAsmMethod(boolean.class, "find");
|
public final static Method MATCHER_FIND = getAsmMethod(boolean.class, "find");
|
||||||
|
|
||||||
/** dynamic callsite bootstrap signature */
|
/** dynamic callsite bootstrap signature */
|
||||||
public final static MethodType DEF_BOOTSTRAP_TYPE =
|
final static MethodType DEF_BOOTSTRAP_TYPE =
|
||||||
MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, Object[].class);
|
MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class,
|
||||||
public final static Handle DEF_BOOTSTRAP_HANDLE =
|
int.class, int.class, Object[].class);
|
||||||
|
final static Handle DEF_BOOTSTRAP_HANDLE =
|
||||||
new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(DefBootstrap.class),
|
new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(DefBootstrap.class),
|
||||||
"bootstrap", DEF_BOOTSTRAP_TYPE.toMethodDescriptorString(), false);
|
"bootstrap", DEF_BOOTSTRAP_TYPE.toMethodDescriptorString(), false);
|
||||||
|
|
||||||
|
|
|
@ -241,7 +241,7 @@ public final class Walker extends PainlessParserBaseVisitor<Object> {
|
||||||
statements.add((AStatement)visit(statement));
|
statements.add((AStatement)visit(statement));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SSource(sourceName, sourceText, debugStream, (MainMethodReserved)reserved.pop(),
|
return new SSource(settings, sourceName, sourceText, debugStream, (MainMethodReserved)reserved.pop(),
|
||||||
location(ctx), functions, globals, statements);
|
location(ctx), functions, globals, statements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
package org.elasticsearch.painless.node;
|
package org.elasticsearch.painless.node;
|
||||||
|
|
||||||
import org.elasticsearch.painless.AnalyzerCaster;
|
import org.elasticsearch.painless.AnalyzerCaster;
|
||||||
|
import org.elasticsearch.painless.DefBootstrap;
|
||||||
import org.elasticsearch.painless.Definition;
|
import org.elasticsearch.painless.Definition;
|
||||||
import org.elasticsearch.painless.Globals;
|
import org.elasticsearch.painless.Globals;
|
||||||
import org.elasticsearch.painless.Definition.Sort;
|
import org.elasticsearch.painless.Definition.Sort;
|
||||||
|
@ -42,6 +43,7 @@ public final class EBinary extends AExpression {
|
||||||
Type shiftDistance; // for shifts, the RHS is promoted independently
|
Type shiftDistance; // for shifts, the RHS is promoted independently
|
||||||
|
|
||||||
boolean cat = false;
|
boolean cat = false;
|
||||||
|
boolean originallyExplicit = false; // record whether there was originally an explicit cast
|
||||||
|
|
||||||
public EBinary(Location location, Operation operation, AExpression left, AExpression right) {
|
public EBinary(Location location, Operation operation, AExpression left, AExpression right) {
|
||||||
super(location);
|
super(location);
|
||||||
|
@ -53,6 +55,7 @@ public final class EBinary extends AExpression {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void analyze(Locals locals) {
|
void analyze(Locals locals) {
|
||||||
|
originallyExplicit = explicit;
|
||||||
if (operation == Operation.MUL) {
|
if (operation == Operation.MUL) {
|
||||||
analyzeMul(locals);
|
analyzeMul(locals);
|
||||||
} else if (operation == Operation.DIV) {
|
} else if (operation == Operation.DIV) {
|
||||||
|
@ -639,7 +642,13 @@ public final class EBinary extends AExpression {
|
||||||
right.write(writer, globals);
|
right.write(writer, globals);
|
||||||
|
|
||||||
if (promote.sort == Sort.DEF || (shiftDistance != null && shiftDistance.sort == Sort.DEF)) {
|
if (promote.sort == Sort.DEF || (shiftDistance != null && shiftDistance.sort == Sort.DEF)) {
|
||||||
writer.writeDynamicBinaryInstruction(location, actual, left.actual, right.actual, operation, false);
|
// def calls adopt the wanted return value. if there was a narrowing cast,
|
||||||
|
// we need to flag that so that its done at runtime.
|
||||||
|
int flags = 0;
|
||||||
|
if (originallyExplicit) {
|
||||||
|
flags |= DefBootstrap.OPERATOR_EXPLICIT_CAST;
|
||||||
|
}
|
||||||
|
writer.writeDynamicBinaryInstruction(location, actual, left.actual, right.actual, operation, flags);
|
||||||
} else {
|
} else {
|
||||||
writer.writeBinaryInstruction(location, actual, operation);
|
writer.writeBinaryInstruction(location, actual, operation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ import org.elasticsearch.painless.Locals.Variable;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
import org.objectweb.asm.Type;
|
import org.objectweb.asm.Type;
|
||||||
|
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_HANDLE;
|
|
||||||
import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE;
|
import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE;
|
||||||
|
|
||||||
import java.lang.invoke.LambdaMetafactory;
|
import java.lang.invoke.LambdaMetafactory;
|
||||||
|
@ -90,8 +89,8 @@ public class ECapturingFunctionRef extends AExpression implements ILambda {
|
||||||
} else if (ref == null) {
|
} else if (ref == null) {
|
||||||
// typed interface, dynamic implementation
|
// typed interface, dynamic implementation
|
||||||
writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.getSlot());
|
writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.getSlot());
|
||||||
String descriptor = Type.getMethodType(expected.type, captured.type.type).getDescriptor();
|
Type methodType = Type.getMethodType(expected.type, captured.type.type);
|
||||||
writer.invokeDynamic(call, descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.REFERENCE, expected.name);
|
writer.invokeDefCall(call, methodType, DefBootstrap.REFERENCE, expected.name);
|
||||||
} else {
|
} else {
|
||||||
// typed interface, typed implementation
|
// typed interface, typed implementation
|
||||||
writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.getSlot());
|
writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.getSlot());
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.elasticsearch.painless.Definition.Sort;
|
||||||
import org.elasticsearch.painless.Definition.Type;
|
import org.elasticsearch.painless.Definition.Type;
|
||||||
import org.elasticsearch.painless.Location;
|
import org.elasticsearch.painless.Location;
|
||||||
import org.elasticsearch.painless.AnalyzerCaster;
|
import org.elasticsearch.painless.AnalyzerCaster;
|
||||||
|
import org.elasticsearch.painless.DefBootstrap;
|
||||||
import org.elasticsearch.painless.Operation;
|
import org.elasticsearch.painless.Operation;
|
||||||
import org.elasticsearch.painless.Locals;
|
import org.elasticsearch.painless.Locals;
|
||||||
import org.elasticsearch.painless.MethodWriter;
|
import org.elasticsearch.painless.MethodWriter;
|
||||||
|
@ -214,7 +215,10 @@ public final class EChain extends AExpression {
|
||||||
|
|
||||||
expression.expected = expression.actual;
|
expression.expected = expression.actual;
|
||||||
} else if (shift) {
|
} else if (shift) {
|
||||||
if (shiftDistance.sort == Sort.LONG) {
|
if (promote.sort == Sort.DEF) {
|
||||||
|
// shifts are promoted independently, but for the def type, we need object.
|
||||||
|
expression.expected = promote;
|
||||||
|
} else if (shiftDistance.sort == Sort.LONG) {
|
||||||
expression.expected = Definition.INT_TYPE;
|
expression.expected = Definition.INT_TYPE;
|
||||||
expression.explicit = true;
|
expression.explicit = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -353,7 +357,7 @@ public final class EChain extends AExpression {
|
||||||
// write the operation instruction for compound assignment
|
// write the operation instruction for compound assignment
|
||||||
if (promote.sort == Sort.DEF) {
|
if (promote.sort == Sort.DEF) {
|
||||||
writer.writeDynamicBinaryInstruction(location, promote,
|
writer.writeDynamicBinaryInstruction(location, promote,
|
||||||
Definition.DEF_TYPE, Definition.DEF_TYPE, operation, true);
|
Definition.DEF_TYPE, Definition.DEF_TYPE, operation, DefBootstrap.OPERATOR_COMPOUND_ASSIGNMENT);
|
||||||
} else {
|
} else {
|
||||||
writer.writeBinaryInstruction(location, promote, operation);
|
writer.writeBinaryInstruction(location, promote, operation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ import org.elasticsearch.painless.MethodWriter;
|
||||||
|
|
||||||
import static org.elasticsearch.painless.WriterConstants.OBJECTS_TYPE;
|
import static org.elasticsearch.painless.WriterConstants.OBJECTS_TYPE;
|
||||||
import static org.elasticsearch.painless.WriterConstants.EQUALS;
|
import static org.elasticsearch.painless.WriterConstants.EQUALS;
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_HANDLE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a comparison expression.
|
* Represents a comparison expression.
|
||||||
|
@ -498,8 +497,7 @@ public final class EComp extends AExpression {
|
||||||
if (right.isNull) {
|
if (right.isNull) {
|
||||||
writer.ifNull(jump);
|
writer.ifNull(jump);
|
||||||
} else if (!left.isNull && (operation == Operation.EQ || operation == Operation.NE)) {
|
} else if (!left.isNull && (operation == Operation.EQ || operation == Operation.NE)) {
|
||||||
writer.invokeDynamic("eq", descriptor.getDescriptor(), DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR,
|
writer.invokeDefCall("eq", descriptor, DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
||||||
DefBootstrap.OPERATOR_ALLOWS_NULL);
|
|
||||||
writejump = false;
|
writejump = false;
|
||||||
} else {
|
} else {
|
||||||
writer.ifCmp(promotedType.type, MethodWriter.EQ, jump);
|
writer.ifCmp(promotedType.type, MethodWriter.EQ, jump);
|
||||||
|
@ -508,23 +506,22 @@ public final class EComp extends AExpression {
|
||||||
if (right.isNull) {
|
if (right.isNull) {
|
||||||
writer.ifNonNull(jump);
|
writer.ifNonNull(jump);
|
||||||
} else if (!left.isNull && (operation == Operation.EQ || operation == Operation.NE)) {
|
} else if (!left.isNull && (operation == Operation.EQ || operation == Operation.NE)) {
|
||||||
writer.invokeDynamic("eq", descriptor.getDescriptor(), DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR,
|
writer.invokeDefCall("eq", descriptor, DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
||||||
DefBootstrap.OPERATOR_ALLOWS_NULL);
|
|
||||||
writer.ifZCmp(MethodWriter.EQ, jump);
|
writer.ifZCmp(MethodWriter.EQ, jump);
|
||||||
} else {
|
} else {
|
||||||
writer.ifCmp(promotedType.type, MethodWriter.NE, jump);
|
writer.ifCmp(promotedType.type, MethodWriter.NE, jump);
|
||||||
}
|
}
|
||||||
} else if (lt) {
|
} else if (lt) {
|
||||||
writer.invokeDynamic("lt", descriptor.getDescriptor(), DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, 0);
|
writer.invokeDefCall("lt", descriptor, DefBootstrap.BINARY_OPERATOR, 0);
|
||||||
writejump = false;
|
writejump = false;
|
||||||
} else if (lte) {
|
} else if (lte) {
|
||||||
writer.invokeDynamic("lte", descriptor.getDescriptor(), DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, 0);
|
writer.invokeDefCall("lte", descriptor, DefBootstrap.BINARY_OPERATOR, 0);
|
||||||
writejump = false;
|
writejump = false;
|
||||||
} else if (gt) {
|
} else if (gt) {
|
||||||
writer.invokeDynamic("gt", descriptor.getDescriptor(), DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, 0);
|
writer.invokeDefCall("gt", descriptor, DefBootstrap.BINARY_OPERATOR, 0);
|
||||||
writejump = false;
|
writejump = false;
|
||||||
} else if (gte) {
|
} else if (gte) {
|
||||||
writer.invokeDynamic("gte", descriptor.getDescriptor(), DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, 0);
|
writer.invokeDefCall("gte", descriptor, DefBootstrap.BINARY_OPERATOR, 0);
|
||||||
writejump = false;
|
writejump = false;
|
||||||
} else {
|
} else {
|
||||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||||
|
|
|
@ -31,8 +31,6 @@ import org.elasticsearch.painless.Locals;
|
||||||
import org.objectweb.asm.Label;
|
import org.objectweb.asm.Label;
|
||||||
import org.elasticsearch.painless.MethodWriter;
|
import org.elasticsearch.painless.MethodWriter;
|
||||||
|
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_HANDLE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a unary math expression.
|
* Represents a unary math expression.
|
||||||
*/
|
*/
|
||||||
|
@ -41,6 +39,7 @@ public final class EUnary extends AExpression {
|
||||||
final Operation operation;
|
final Operation operation;
|
||||||
AExpression child;
|
AExpression child;
|
||||||
Type promote;
|
Type promote;
|
||||||
|
boolean originallyExplicit = false; // record whether there was originally an explicit cast
|
||||||
|
|
||||||
public EUnary(Location location, Operation operation, AExpression child) {
|
public EUnary(Location location, Operation operation, AExpression child) {
|
||||||
super(location);
|
super(location);
|
||||||
|
@ -51,6 +50,7 @@ public final class EUnary extends AExpression {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void analyze(Locals locals) {
|
void analyze(Locals locals) {
|
||||||
|
originallyExplicit = explicit;
|
||||||
if (operation == Operation.NOT) {
|
if (operation == Operation.NOT) {
|
||||||
analyzeNot(locals);
|
analyzeNot(locals);
|
||||||
} else if (operation == Operation.BWNOT) {
|
} else if (operation == Operation.BWNOT) {
|
||||||
|
@ -203,10 +203,16 @@ public final class EUnary extends AExpression {
|
||||||
Sort sort = promote.sort;
|
Sort sort = promote.sort;
|
||||||
child.write(writer, globals);
|
child.write(writer, globals);
|
||||||
|
|
||||||
|
// def calls adopt the wanted return value. if there was a narrowing cast,
|
||||||
|
// we need to flag that so that its done at runtime.
|
||||||
|
int defFlags = 0;
|
||||||
|
if (originallyExplicit) {
|
||||||
|
defFlags |= DefBootstrap.OPERATOR_EXPLICIT_CAST;
|
||||||
|
}
|
||||||
if (operation == Operation.BWNOT) {
|
if (operation == Operation.BWNOT) {
|
||||||
if (sort == Sort.DEF) {
|
if (sort == Sort.DEF) {
|
||||||
org.objectweb.asm.Type descriptor = org.objectweb.asm.Type.getMethodType(actual.type, child.actual.type);
|
org.objectweb.asm.Type descriptor = org.objectweb.asm.Type.getMethodType(actual.type, child.actual.type);
|
||||||
writer.invokeDynamic("not", descriptor.getDescriptor(), DEF_BOOTSTRAP_HANDLE, DefBootstrap.UNARY_OPERATOR, 0);
|
writer.invokeDefCall("not", descriptor, DefBootstrap.UNARY_OPERATOR, defFlags);
|
||||||
} else {
|
} else {
|
||||||
if (sort == Sort.INT) {
|
if (sort == Sort.INT) {
|
||||||
writer.push(-1);
|
writer.push(-1);
|
||||||
|
@ -221,14 +227,14 @@ public final class EUnary extends AExpression {
|
||||||
} else if (operation == Operation.SUB) {
|
} else if (operation == Operation.SUB) {
|
||||||
if (sort == Sort.DEF) {
|
if (sort == Sort.DEF) {
|
||||||
org.objectweb.asm.Type descriptor = org.objectweb.asm.Type.getMethodType(actual.type, child.actual.type);
|
org.objectweb.asm.Type descriptor = org.objectweb.asm.Type.getMethodType(actual.type, child.actual.type);
|
||||||
writer.invokeDynamic("neg", descriptor.getDescriptor(), DEF_BOOTSTRAP_HANDLE, DefBootstrap.UNARY_OPERATOR, 0);
|
writer.invokeDefCall("neg", descriptor, DefBootstrap.UNARY_OPERATOR, defFlags);
|
||||||
} else {
|
} else {
|
||||||
writer.math(MethodWriter.NEG, actual.type);
|
writer.math(MethodWriter.NEG, actual.type);
|
||||||
}
|
}
|
||||||
} else if (operation == Operation.ADD) {
|
} else if (operation == Operation.ADD) {
|
||||||
if (sort == Sort.DEF) {
|
if (sort == Sort.DEF) {
|
||||||
org.objectweb.asm.Type descriptor = org.objectweb.asm.Type.getMethodType(actual.type, child.actual.type);
|
org.objectweb.asm.Type descriptor = org.objectweb.asm.Type.getMethodType(actual.type, child.actual.type);
|
||||||
writer.invokeDynamic("plus", descriptor.getDescriptor(), DEF_BOOTSTRAP_HANDLE, DefBootstrap.UNARY_OPERATOR, 0);
|
writer.invokeDefCall("plus", descriptor, DefBootstrap.UNARY_OPERATOR, defFlags);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
throw createError(new IllegalStateException("Illegal tree structure."));
|
||||||
|
|
|
@ -27,8 +27,6 @@ import org.elasticsearch.painless.Locals;
|
||||||
import org.objectweb.asm.Type;
|
import org.objectweb.asm.Type;
|
||||||
import org.elasticsearch.painless.MethodWriter;
|
import org.elasticsearch.painless.MethodWriter;
|
||||||
|
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_HANDLE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an array load/store or shortcut on a def type. (Internal only.)
|
* Represents an array load/store or shortcut on a def type. (Internal only.)
|
||||||
*/
|
*/
|
||||||
|
@ -62,15 +60,15 @@ final class LDefArray extends ALink implements IDefLink {
|
||||||
void load(MethodWriter writer, Globals globals) {
|
void load(MethodWriter writer, Globals globals) {
|
||||||
writer.writeDebugInfo(location);
|
writer.writeDebugInfo(location);
|
||||||
|
|
||||||
String desc = Type.getMethodDescriptor(after.type, Definition.DEF_TYPE.type, index.actual.type);
|
Type methodType = Type.getMethodType(after.type, Definition.DEF_TYPE.type, index.actual.type);
|
||||||
writer.invokeDynamic("arrayLoad", desc, DEF_BOOTSTRAP_HANDLE, DefBootstrap.ARRAY_LOAD);
|
writer.invokeDefCall("arrayLoad", methodType, DefBootstrap.ARRAY_LOAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void store(MethodWriter writer, Globals globals) {
|
void store(MethodWriter writer, Globals globals) {
|
||||||
writer.writeDebugInfo(location);
|
writer.writeDebugInfo(location);
|
||||||
|
|
||||||
String desc = Type.getMethodDescriptor(Definition.VOID_TYPE.type, Definition.DEF_TYPE.type, index.actual.type, after.type);
|
Type methodType = Type.getMethodType(Definition.VOID_TYPE.type, Definition.DEF_TYPE.type, index.actual.type, after.type);
|
||||||
writer.invokeDynamic("arrayStore", desc, DEF_BOOTSTRAP_HANDLE, DefBootstrap.ARRAY_STORE);
|
writer.invokeDefCall("arrayStore", methodType, DefBootstrap.ARRAY_STORE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,6 @@ import org.objectweb.asm.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_HANDLE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a method call made on a def type. (Internal only.)
|
* Represents a method call made on a def type. (Internal only.)
|
||||||
*/
|
*/
|
||||||
|
@ -92,32 +90,30 @@ final class LDefCall extends ALink implements IDefLink {
|
||||||
void load(MethodWriter writer, Globals globals) {
|
void load(MethodWriter writer, Globals globals) {
|
||||||
writer.writeDebugInfo(location);
|
writer.writeDebugInfo(location);
|
||||||
|
|
||||||
StringBuilder signature = new StringBuilder();
|
List<Type> parameterTypes = new ArrayList<>();
|
||||||
|
|
||||||
signature.append('(');
|
|
||||||
// first parameter is the receiver, we never know its type: always Object
|
// first parameter is the receiver, we never know its type: always Object
|
||||||
signature.append(Definition.DEF_TYPE.type.getDescriptor());
|
parameterTypes.add(Definition.DEF_TYPE.type);
|
||||||
|
|
||||||
|
// append each argument
|
||||||
for (AExpression argument : arguments) {
|
for (AExpression argument : arguments) {
|
||||||
signature.append(argument.actual.type.getDescriptor());
|
parameterTypes.add(argument.actual.type);
|
||||||
if (argument instanceof ILambda) {
|
if (argument instanceof ILambda) {
|
||||||
ILambda lambda = (ILambda) argument;
|
ILambda lambda = (ILambda) argument;
|
||||||
for (Type capture : lambda.getCaptures()) {
|
for (Type capture : lambda.getCaptures()) {
|
||||||
signature.append(capture.getDescriptor());
|
parameterTypes.add(capture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
argument.write(writer, globals);
|
argument.write(writer, globals);
|
||||||
}
|
}
|
||||||
|
|
||||||
signature.append(')');
|
// create method type from return value and arguments
|
||||||
// return value
|
Type methodType = Type.getMethodType(after.type, parameterTypes.toArray(new Type[0]));
|
||||||
signature.append(after.type.getDescriptor());
|
|
||||||
|
|
||||||
List<Object> args = new ArrayList<>();
|
List<Object> args = new ArrayList<>();
|
||||||
args.add(DefBootstrap.METHOD_CALL);
|
|
||||||
args.add(recipe);
|
args.add(recipe);
|
||||||
args.addAll(pointers);
|
args.addAll(pointers);
|
||||||
writer.invokeDynamic(name, signature.toString(), DEF_BOOTSTRAP_HANDLE, args.toArray());
|
writer.invokeDefCall(name, methodType, DefBootstrap.METHOD_CALL, args.toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -27,8 +27,6 @@ import org.elasticsearch.painless.Locals;
|
||||||
import org.objectweb.asm.Type;
|
import org.objectweb.asm.Type;
|
||||||
import org.elasticsearch.painless.MethodWriter;
|
import org.elasticsearch.painless.MethodWriter;
|
||||||
|
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_HANDLE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a field load/store or shortcut on a def type. (Internal only.)
|
* Represents a field load/store or shortcut on a def type. (Internal only.)
|
||||||
*/
|
*/
|
||||||
|
@ -59,15 +57,15 @@ final class LDefField extends ALink implements IDefLink {
|
||||||
void load(MethodWriter writer, Globals globals) {
|
void load(MethodWriter writer, Globals globals) {
|
||||||
writer.writeDebugInfo(location);
|
writer.writeDebugInfo(location);
|
||||||
|
|
||||||
String desc = Type.getMethodDescriptor(after.type, Definition.DEF_TYPE.type);
|
Type methodType = Type.getMethodType(after.type, Definition.DEF_TYPE.type);
|
||||||
writer.invokeDynamic(value, desc, DEF_BOOTSTRAP_HANDLE, DefBootstrap.LOAD);
|
writer.invokeDefCall(value, methodType, DefBootstrap.LOAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void store(MethodWriter writer, Globals globals) {
|
void store(MethodWriter writer, Globals globals) {
|
||||||
writer.writeDebugInfo(location);
|
writer.writeDebugInfo(location);
|
||||||
|
|
||||||
String desc = Type.getMethodDescriptor(Definition.VOID_TYPE.type, Definition.DEF_TYPE.type, after.type);
|
Type methodType = Type.getMethodType(Definition.VOID_TYPE.type, Definition.DEF_TYPE.type, after.type);
|
||||||
writer.invokeDynamic(value, desc, DEF_BOOTSTRAP_HANDLE, DefBootstrap.STORE);
|
writer.invokeDefCall(value, methodType, DefBootstrap.STORE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ import org.elasticsearch.painless.Locals.Variable;
|
||||||
import org.objectweb.asm.Label;
|
import org.objectweb.asm.Label;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_HANDLE;
|
|
||||||
import static org.elasticsearch.painless.WriterConstants.ITERATOR_HASNEXT;
|
import static org.elasticsearch.painless.WriterConstants.ITERATOR_HASNEXT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.ITERATOR_NEXT;
|
import static org.elasticsearch.painless.WriterConstants.ITERATOR_NEXT;
|
||||||
import static org.elasticsearch.painless.WriterConstants.ITERATOR_TYPE;
|
import static org.elasticsearch.painless.WriterConstants.ITERATOR_TYPE;
|
||||||
|
@ -193,8 +192,8 @@ public class SEach extends AStatement {
|
||||||
|
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
Type itr = Definition.getType("Iterator");
|
Type itr = Definition.getType("Iterator");
|
||||||
String desc = org.objectweb.asm.Type.getMethodDescriptor(itr.type, Definition.DEF_TYPE.type);
|
org.objectweb.asm.Type methodType = org.objectweb.asm.Type.getMethodType(itr.type, Definition.DEF_TYPE.type);
|
||||||
writer.invokeDynamic("iterator", desc, DEF_BOOTSTRAP_HANDLE, DefBootstrap.ITERATOR);
|
writer.invokeDefCall("iterator", methodType, DefBootstrap.ITERATOR);
|
||||||
} else if (java.lang.reflect.Modifier.isInterface(method.owner.clazz.getModifiers())) {
|
} else if (java.lang.reflect.Modifier.isInterface(method.owner.clazz.getModifiers())) {
|
||||||
writer.invokeInterface(method.owner.type, method.method);
|
writer.invokeInterface(method.owner.type, method.method);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.painless.node;
|
package org.elasticsearch.painless.node;
|
||||||
|
|
||||||
|
import org.elasticsearch.painless.CompilerSettings;
|
||||||
import org.elasticsearch.painless.Constant;
|
import org.elasticsearch.painless.Constant;
|
||||||
import org.elasticsearch.painless.Def;
|
import org.elasticsearch.painless.Def;
|
||||||
import org.elasticsearch.painless.Definition;
|
import org.elasticsearch.painless.Definition;
|
||||||
|
@ -143,12 +144,12 @@ public class SFunction extends AStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Writes the function to given ClassVisitor. */
|
/** Writes the function to given ClassVisitor. */
|
||||||
void write (ClassVisitor writer, Globals globals) {
|
void write (ClassVisitor writer, CompilerSettings settings, Globals globals) {
|
||||||
int access = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
|
int access = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
|
||||||
if (synthetic) {
|
if (synthetic) {
|
||||||
access |= Opcodes.ACC_SYNTHETIC;
|
access |= Opcodes.ACC_SYNTHETIC;
|
||||||
}
|
}
|
||||||
final MethodWriter function = new MethodWriter(access, method.method, writer, globals.getStatements());
|
final MethodWriter function = new MethodWriter(access, method.method, writer, globals.getStatements(), settings);
|
||||||
write(function, globals);
|
write(function, globals);
|
||||||
function.endMethod();
|
function.endMethod();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.painless.node;
|
package org.elasticsearch.painless.node;
|
||||||
|
|
||||||
|
import org.elasticsearch.painless.CompilerSettings;
|
||||||
import org.elasticsearch.painless.Constant;
|
import org.elasticsearch.painless.Constant;
|
||||||
import org.elasticsearch.painless.Definition.Method;
|
import org.elasticsearch.painless.Definition.Method;
|
||||||
import org.elasticsearch.painless.Definition.MethodKey;
|
import org.elasticsearch.painless.Definition.MethodKey;
|
||||||
|
@ -59,6 +60,7 @@ public final class SSource extends AStatement {
|
||||||
final String name;
|
final String name;
|
||||||
final String source;
|
final String source;
|
||||||
final Printer debugStream;
|
final Printer debugStream;
|
||||||
|
final CompilerSettings settings;
|
||||||
final MainMethodReserved reserved;
|
final MainMethodReserved reserved;
|
||||||
final List<SFunction> functions;
|
final List<SFunction> functions;
|
||||||
final Globals globals;
|
final Globals globals;
|
||||||
|
@ -67,10 +69,11 @@ public final class SSource extends AStatement {
|
||||||
private Locals mainMethod;
|
private Locals mainMethod;
|
||||||
private byte[] bytes;
|
private byte[] bytes;
|
||||||
|
|
||||||
public SSource(String name, String source, Printer debugStream, MainMethodReserved reserved, Location location,
|
public SSource(CompilerSettings settings, String name, String source, Printer debugStream,
|
||||||
|
MainMethodReserved reserved, Location location,
|
||||||
List<SFunction> functions, Globals globals, List<AStatement> statements) {
|
List<SFunction> functions, Globals globals, List<AStatement> statements) {
|
||||||
super(location);
|
super(location);
|
||||||
|
this.settings = settings;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.debugStream = debugStream;
|
this.debugStream = debugStream;
|
||||||
|
@ -151,7 +154,7 @@ public final class SSource extends AStatement {
|
||||||
visitor.visitSource(Location.computeSourceName(name, source), null);
|
visitor.visitSource(Location.computeSourceName(name, source), null);
|
||||||
|
|
||||||
// Write the constructor:
|
// Write the constructor:
|
||||||
MethodWriter constructor = new MethodWriter(Opcodes.ACC_PUBLIC, CONSTRUCTOR, visitor, globals.getStatements());
|
MethodWriter constructor = new MethodWriter(Opcodes.ACC_PUBLIC, CONSTRUCTOR, visitor, globals.getStatements(), settings);
|
||||||
constructor.visitCode();
|
constructor.visitCode();
|
||||||
constructor.loadThis();
|
constructor.loadThis();
|
||||||
constructor.loadArgs();
|
constructor.loadArgs();
|
||||||
|
@ -160,14 +163,14 @@ public final class SSource extends AStatement {
|
||||||
constructor.endMethod();
|
constructor.endMethod();
|
||||||
|
|
||||||
// Write the execute method:
|
// Write the execute method:
|
||||||
MethodWriter execute = new MethodWriter(Opcodes.ACC_PUBLIC, EXECUTE, visitor, globals.getStatements());
|
MethodWriter execute = new MethodWriter(Opcodes.ACC_PUBLIC, EXECUTE, visitor, globals.getStatements(), settings);
|
||||||
execute.visitCode();
|
execute.visitCode();
|
||||||
write(execute, globals);
|
write(execute, globals);
|
||||||
execute.endMethod();
|
execute.endMethod();
|
||||||
|
|
||||||
// Write all functions:
|
// Write all functions:
|
||||||
for (SFunction function : functions) {
|
for (SFunction function : functions) {
|
||||||
function.write(visitor, globals);
|
function.write(visitor, settings, globals);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write all synthetic functions. Note that this process may add more :)
|
// Write all synthetic functions. Note that this process may add more :)
|
||||||
|
@ -175,7 +178,7 @@ public final class SSource extends AStatement {
|
||||||
List<SFunction> current = new ArrayList<>(globals.getSyntheticMethods().values());
|
List<SFunction> current = new ArrayList<>(globals.getSyntheticMethods().values());
|
||||||
globals.getSyntheticMethods().clear();
|
globals.getSyntheticMethods().clear();
|
||||||
for (SFunction function : current) {
|
for (SFunction function : current) {
|
||||||
function.write(visitor, globals);
|
function.write(visitor, settings, globals);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +198,7 @@ public final class SSource extends AStatement {
|
||||||
|
|
||||||
// Initialize the constants in a static initializer
|
// Initialize the constants in a static initializer
|
||||||
final MethodWriter clinit = new MethodWriter(Opcodes.ACC_STATIC,
|
final MethodWriter clinit = new MethodWriter(Opcodes.ACC_STATIC,
|
||||||
WriterConstants.CLINIT, visitor, globals.getStatements());
|
WriterConstants.CLINIT, visitor, globals.getStatements(), settings);
|
||||||
for (Constant constant : inits) {
|
for (Constant constant : inits) {
|
||||||
constant.initializer.accept(clinit);
|
constant.initializer.accept(clinit);
|
||||||
clinit.putStatic(CLASS_TYPE, constant.name, constant.type);
|
clinit.putStatic(CLASS_TYPE, constant.name, constant.type);
|
||||||
|
|
|
@ -0,0 +1,298 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.painless;
|
||||||
|
|
||||||
|
/** Tests for explicit casts */
|
||||||
|
public class CastTests extends ScriptTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unary operator with explicit cast
|
||||||
|
*/
|
||||||
|
public void testUnaryOperator() {
|
||||||
|
assertEquals((byte)5, exec("long x = 5L; return (byte) (+x);"));
|
||||||
|
assertEquals((short)5, exec("long x = 5L; return (short) (+x);"));
|
||||||
|
assertEquals((char)5, exec("long x = 5L; return (char) (+x);"));
|
||||||
|
assertEquals(5, exec("long x = 5L; return (int) (+x);"));
|
||||||
|
assertEquals(5F, exec("long x = 5L; return (float) (+x);"));
|
||||||
|
assertEquals(5L, exec("long x = 5L; return (long) (+x);"));
|
||||||
|
assertEquals(5D, exec("long x = 5L; return (double) (+x);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binary operators with explicit cast
|
||||||
|
*/
|
||||||
|
public void testBinaryOperator() {
|
||||||
|
assertEquals((byte)6, exec("long x = 5L; return (byte) (x + 1);"));
|
||||||
|
assertEquals((short)6, exec("long x = 5L; return (short) (x + 1);"));
|
||||||
|
assertEquals((char)6, exec("long x = 5L; return (char) (x + 1);"));
|
||||||
|
assertEquals(6, exec("long x = 5L; return (int) (x + 1);"));
|
||||||
|
assertEquals(6F, exec("long x = 5L; return (float) (x + 1);"));
|
||||||
|
assertEquals(6L, exec("long x = 5L; return (long) (x + 1);"));
|
||||||
|
assertEquals(6D, exec("long x = 5L; return (double) (x + 1);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binary compound assignment with explicit cast
|
||||||
|
*/
|
||||||
|
public void testBinaryCompoundAssignment() {
|
||||||
|
assertEquals((byte)6, exec("long x = 5L; return (byte) (x += 1);"));
|
||||||
|
assertEquals((short)6, exec("long x = 5L; return (short) (x += 1);"));
|
||||||
|
assertEquals((char)6, exec("long x = 5L; return (char) (x += 1);"));
|
||||||
|
assertEquals(6, exec("long x = 5L; return (int) (x += 1);"));
|
||||||
|
assertEquals(6F, exec("long x = 5L; return (float) (x += 1);"));
|
||||||
|
assertEquals(6L, exec("long x = 5L; return (long) (x += 1);"));
|
||||||
|
assertEquals(6D, exec("long x = 5L; return (double) (x += 1);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binary compound prefix with explicit cast
|
||||||
|
*/
|
||||||
|
public void testBinaryPrefix() {
|
||||||
|
assertEquals((byte)6, exec("long x = 5L; return (byte) (++x);"));
|
||||||
|
assertEquals((short)6, exec("long x = 5L; return (short) (++x);"));
|
||||||
|
assertEquals((char)6, exec("long x = 5L; return (char) (++x);"));
|
||||||
|
assertEquals(6, exec("long x = 5L; return (int) (++x);"));
|
||||||
|
assertEquals(6F, exec("long x = 5L; return (float) (++x);"));
|
||||||
|
assertEquals(6L, exec("long x = 5L; return (long) (++x);"));
|
||||||
|
assertEquals(6D, exec("long x = 5L; return (double) (++x);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binary compound postifx with explicit cast
|
||||||
|
*/
|
||||||
|
public void testBinaryPostfix() {
|
||||||
|
assertEquals((byte)5, exec("long x = 5L; return (byte) (x++);"));
|
||||||
|
assertEquals((short)5, exec("long x = 5L; return (short) (x++);"));
|
||||||
|
assertEquals((char)5, exec("long x = 5L; return (char) (x++);"));
|
||||||
|
assertEquals(5, exec("long x = 5L; return (int) (x++);"));
|
||||||
|
assertEquals(5F, exec("long x = 5L; return (float) (x++);"));
|
||||||
|
assertEquals(5L, exec("long x = 5L; return (long) (x++);"));
|
||||||
|
assertEquals(5D, exec("long x = 5L; return (double) (x++);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shift operators with explicit cast
|
||||||
|
*/
|
||||||
|
public void testShiftOperator() {
|
||||||
|
assertEquals((byte)10, exec("long x = 5L; return (byte) (x << 1);"));
|
||||||
|
assertEquals((short)10, exec("long x = 5L; return (short) (x << 1);"));
|
||||||
|
assertEquals((char)10, exec("long x = 5L; return (char) (x << 1);"));
|
||||||
|
assertEquals(10, exec("long x = 5L; return (int) (x << 1);"));
|
||||||
|
assertEquals(10F, exec("long x = 5L; return (float) (x << 1);"));
|
||||||
|
assertEquals(10L, exec("long x = 5L; return (long) (x << 1);"));
|
||||||
|
assertEquals(10D, exec("long x = 5L; return (double) (x << 1);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shift compound assignment with explicit cast
|
||||||
|
*/
|
||||||
|
public void testShiftCompoundAssignment() {
|
||||||
|
assertEquals((byte)10, exec("long x = 5L; return (byte) (x <<= 1);"));
|
||||||
|
assertEquals((short)10, exec("long x = 5L; return (short) (x <<= 1);"));
|
||||||
|
assertEquals((char)10, exec("long x = 5L; return (char) (x <<= 1);"));
|
||||||
|
assertEquals(10, exec("long x = 5L; return (int) (x <<= 1);"));
|
||||||
|
assertEquals(10F, exec("long x = 5L; return (float) (x <<= 1);"));
|
||||||
|
assertEquals(10L, exec("long x = 5L; return (long) (x <<= 1);"));
|
||||||
|
assertEquals(10D, exec("long x = 5L; return (double) (x <<= 1);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that without a cast, we fail when conversions would narrow.
|
||||||
|
*/
|
||||||
|
public void testIllegalConversions() {
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("long x = 5L; int y = +x; return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("long x = 5L; int y = (x + x); return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("boolean x = true; int y = +x; return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("boolean x = true; int y = (x ^ false); return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("long x = 5L; boolean y = +x; return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("long x = 5L; boolean y = (x + x); return y");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that even with a cast, some things aren't allowed.
|
||||||
|
*/
|
||||||
|
public void testIllegalExplicitConversions() {
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("boolean x = true; int y = (int) +x; return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("boolean x = true; int y = (int) (x ^ false); return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("long x = 5L; boolean y = (boolean) +x; return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("long x = 5L; boolean y = (boolean) (x + x); return y");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently these do not adopt the return value, we issue a separate cast!
|
||||||
|
*/
|
||||||
|
public void testMethodCallDef() {
|
||||||
|
assertEquals(5, exec("def x = 5; return (int)x.longValue();"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unary operators adopt the return value
|
||||||
|
*/
|
||||||
|
public void testUnaryOperatorDef() {
|
||||||
|
assertEquals((byte)5, exec("def x = 5L; return (byte) (+x);"));
|
||||||
|
assertEquals((short)5, exec("def x = 5L; return (short) (+x);"));
|
||||||
|
assertEquals((char)5, exec("def x = 5L; return (char) (+x);"));
|
||||||
|
assertEquals(5, exec("def x = 5L; return (int) (+x);"));
|
||||||
|
assertEquals(5F, exec("def x = 5L; return (float) (+x);"));
|
||||||
|
assertEquals(5L, exec("def x = 5L; return (long) (+x);"));
|
||||||
|
assertEquals(5D, exec("def x = 5L; return (double) (+x);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binary operators adopt the return value
|
||||||
|
*/
|
||||||
|
public void testBinaryOperatorDef() {
|
||||||
|
assertEquals((byte)6, exec("def x = 5L; return (byte) (x + 1);"));
|
||||||
|
assertEquals((short)6, exec("def x = 5L; return (short) (x + 1);"));
|
||||||
|
assertEquals((char)6, exec("def x = 5L; return (char) (x + 1);"));
|
||||||
|
assertEquals(6, exec("def x = 5L; return (int) (x + 1);"));
|
||||||
|
assertEquals(6F, exec("def x = 5L; return (float) (x + 1);"));
|
||||||
|
assertEquals(6L, exec("def x = 5L; return (long) (x + 1);"));
|
||||||
|
assertEquals(6D, exec("def x = 5L; return (double) (x + 1);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binary operators don't yet adopt the return value with compound assignment
|
||||||
|
*/
|
||||||
|
public void testBinaryCompoundAssignmentDef() {
|
||||||
|
assertEquals((byte)6, exec("def x = 5L; return (byte) (x += 1);"));
|
||||||
|
assertEquals((short)6, exec("def x = 5L; return (short) (x += 1);"));
|
||||||
|
assertEquals((char)6, exec("def x = 5L; return (char) (x += 1);"));
|
||||||
|
assertEquals(6, exec("def x = 5L; return (int) (x += 1);"));
|
||||||
|
assertEquals(6F, exec("def x = 5L; return (float) (x += 1);"));
|
||||||
|
assertEquals(6L, exec("def x = 5L; return (long) (x += 1);"));
|
||||||
|
assertEquals(6D, exec("def x = 5L; return (double) (x += 1);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binary operators don't yet adopt the return value with compound assignment
|
||||||
|
*/
|
||||||
|
public void testBinaryCompoundAssignmentPrefix() {
|
||||||
|
assertEquals((byte)6, exec("def x = 5L; return (byte) (++x);"));
|
||||||
|
assertEquals((short)6, exec("def x = 5L; return (short) (++x);"));
|
||||||
|
assertEquals((char)6, exec("def x = 5L; return (char) (++x);"));
|
||||||
|
assertEquals(6, exec("def x = 5L; return (int) (++x);"));
|
||||||
|
assertEquals(6F, exec("def x = 5L; return (float) (++x);"));
|
||||||
|
assertEquals(6L, exec("def x = 5L; return (long) (++x);"));
|
||||||
|
assertEquals(6D, exec("def x = 5L; return (double) (++x);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binary operators don't yet adopt the return value with compound assignment
|
||||||
|
*/
|
||||||
|
public void testBinaryCompoundAssignmentPostfix() {
|
||||||
|
assertEquals((byte)5, exec("def x = 5L; return (byte) (x++);"));
|
||||||
|
assertEquals((short)5, exec("def x = 5L; return (short) (x++);"));
|
||||||
|
assertEquals((char)5, exec("def x = 5L; return (char) (x++);"));
|
||||||
|
assertEquals(5, exec("def x = 5L; return (int) (x++);"));
|
||||||
|
assertEquals(5F, exec("def x = 5L; return (float) (x++);"));
|
||||||
|
assertEquals(5L, exec("def x = 5L; return (long) (x++);"));
|
||||||
|
assertEquals(5D, exec("def x = 5L; return (double) (x++);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shift operators adopt the return value
|
||||||
|
*/
|
||||||
|
public void testShiftOperatorDef() {
|
||||||
|
assertEquals((byte)10, exec("def x = 5L; return (byte) (x << 1);"));
|
||||||
|
assertEquals((short)10, exec("def x = 5L; return (short) (x << 1);"));
|
||||||
|
assertEquals((char)10, exec("def x = 5L; return (char) (x << 1);"));
|
||||||
|
assertEquals(10, exec("def x = 5L; return (int) (x << 1);"));
|
||||||
|
assertEquals(10F, exec("def x = 5L; return (float) (x << 1);"));
|
||||||
|
assertEquals(10L, exec("def x = 5L; return (long) (x << 1);"));
|
||||||
|
assertEquals(10D, exec("def x = 5L; return (double) (x << 1);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shift operators don't yet adopt the return value with compound assignment
|
||||||
|
*/
|
||||||
|
public void testShiftCompoundAssignmentDef() {
|
||||||
|
assertEquals((byte)10, exec("def x = 5L; return (byte) (x <<= 1);"));
|
||||||
|
assertEquals((short)10, exec("def x = 5L; return (short) (x <<= 1);"));
|
||||||
|
assertEquals((char)10, exec("def x = 5L; return (char) (x <<= 1);"));
|
||||||
|
assertEquals(10, exec("def x = 5L; return (int) (x <<= 1);"));
|
||||||
|
assertEquals(10F, exec("def x = 5L; return (float) (x <<= 1);"));
|
||||||
|
assertEquals(10L, exec("def x = 5L; return (long) (x <<= 1);"));
|
||||||
|
assertEquals(10D, exec("def x = 5L; return (double) (x <<= 1);"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that without a cast, we fail when conversions would narrow.
|
||||||
|
*/
|
||||||
|
public void testIllegalConversionsDef() {
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("def x = 5L; int y = +x; return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("def x = 5L; int y = (x + x); return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("def x = true; int y = +x; return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("def x = true; int y = (x ^ false); return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("def x = 5L; boolean y = +x; return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("def x = 5L; boolean y = (x + x); return y");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that even with a cast, some things aren't allowed.
|
||||||
|
* (stuff that methodhandles explicitCastArguments would otherwise allow)
|
||||||
|
*/
|
||||||
|
public void testIllegalExplicitConversionsDef() {
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("def x = true; int y = (int) +x; return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("def x = true; int y = (int) (x ^ false); return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("def x = 5L; boolean y = (boolean) +x; return y");
|
||||||
|
});
|
||||||
|
expectScriptThrows(ClassCastException.class, () -> {
|
||||||
|
exec("def x = 5L; boolean y = (boolean) (x + x); return y");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ public class DefBootstrapTests extends ESTestCase {
|
||||||
CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
||||||
"toString",
|
"toString",
|
||||||
MethodType.methodType(String.class, Object.class),
|
MethodType.methodType(String.class, Object.class),
|
||||||
|
0,
|
||||||
DefBootstrap.METHOD_CALL, 0L);
|
DefBootstrap.METHOD_CALL, 0L);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertDepthEquals(site, 0);
|
assertDepthEquals(site, 0);
|
||||||
|
@ -53,6 +54,7 @@ public class DefBootstrapTests extends ESTestCase {
|
||||||
CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
||||||
"toString",
|
"toString",
|
||||||
MethodType.methodType(String.class, Object.class),
|
MethodType.methodType(String.class, Object.class),
|
||||||
|
0,
|
||||||
DefBootstrap.METHOD_CALL, 0L);
|
DefBootstrap.METHOD_CALL, 0L);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertDepthEquals(site, 0);
|
assertDepthEquals(site, 0);
|
||||||
|
@ -75,6 +77,7 @@ public class DefBootstrapTests extends ESTestCase {
|
||||||
CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
||||||
"toString",
|
"toString",
|
||||||
MethodType.methodType(String.class, Object.class),
|
MethodType.methodType(String.class, Object.class),
|
||||||
|
0,
|
||||||
DefBootstrap.METHOD_CALL, 0L);
|
DefBootstrap.METHOD_CALL, 0L);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertDepthEquals(site, 0);
|
assertDepthEquals(site, 0);
|
||||||
|
@ -98,6 +101,7 @@ public class DefBootstrapTests extends ESTestCase {
|
||||||
DefBootstrap.PIC site = (DefBootstrap.PIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.PIC site = (DefBootstrap.PIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
||||||
"size",
|
"size",
|
||||||
MethodType.methodType(int.class, Object.class),
|
MethodType.methodType(int.class, Object.class),
|
||||||
|
0,
|
||||||
DefBootstrap.METHOD_CALL, 0L);
|
DefBootstrap.METHOD_CALL, 0L);
|
||||||
site.depth = DefBootstrap.PIC.MAX_DEPTH; // mark megamorphic
|
site.depth = DefBootstrap.PIC.MAX_DEPTH; // mark megamorphic
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
|
@ -127,6 +131,7 @@ public class DefBootstrapTests extends ESTestCase {
|
||||||
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
||||||
"add",
|
"add",
|
||||||
MethodType.methodType(Object.class, Object.class, Object.class),
|
MethodType.methodType(Object.class, Object.class, Object.class),
|
||||||
|
0,
|
||||||
DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertEquals("nulltest", (Object)handle.invokeExact((Object)null, (Object)"test"));
|
assertEquals("nulltest", (Object)handle.invokeExact((Object)null, (Object)"test"));
|
||||||
|
@ -136,6 +141,7 @@ public class DefBootstrapTests extends ESTestCase {
|
||||||
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
||||||
"add",
|
"add",
|
||||||
MethodType.methodType(Object.class, Object.class, Object.class),
|
MethodType.methodType(Object.class, Object.class, Object.class),
|
||||||
|
0,
|
||||||
DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertEquals(2, (Object)handle.invokeExact((Object)1, (Object)1));
|
assertEquals(2, (Object)handle.invokeExact((Object)1, (Object)1));
|
||||||
|
@ -146,6 +152,7 @@ public class DefBootstrapTests extends ESTestCase {
|
||||||
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
||||||
"eq",
|
"eq",
|
||||||
MethodType.methodType(boolean.class, Object.class, Object.class),
|
MethodType.methodType(boolean.class, Object.class, Object.class),
|
||||||
|
0,
|
||||||
DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertFalse((boolean) handle.invokeExact((Object)null, (Object)"test"));
|
assertFalse((boolean) handle.invokeExact((Object)null, (Object)"test"));
|
||||||
|
@ -156,6 +163,7 @@ public class DefBootstrapTests extends ESTestCase {
|
||||||
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
||||||
"eq",
|
"eq",
|
||||||
MethodType.methodType(boolean.class, Object.class, Object.class),
|
MethodType.methodType(boolean.class, Object.class, Object.class),
|
||||||
|
0,
|
||||||
DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertTrue((boolean) handle.invokeExact((Object)1, (Object)1));
|
assertTrue((boolean) handle.invokeExact((Object)1, (Object)1));
|
||||||
|
@ -171,6 +179,7 @@ public class DefBootstrapTests extends ESTestCase {
|
||||||
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
||||||
"add",
|
"add",
|
||||||
MethodType.methodType(Object.class, int.class, Object.class),
|
MethodType.methodType(Object.class, int.class, Object.class),
|
||||||
|
0,
|
||||||
DefBootstrap.BINARY_OPERATOR, 0);
|
DefBootstrap.BINARY_OPERATOR, 0);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
expectThrows(NullPointerException.class, () -> {
|
expectThrows(NullPointerException.class, () -> {
|
||||||
|
@ -182,6 +191,7 @@ public class DefBootstrapTests extends ESTestCase {
|
||||||
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
||||||
"add",
|
"add",
|
||||||
MethodType.methodType(Object.class, int.class, Object.class),
|
MethodType.methodType(Object.class, int.class, Object.class),
|
||||||
|
0,
|
||||||
DefBootstrap.BINARY_OPERATOR, 0);
|
DefBootstrap.BINARY_OPERATOR, 0);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertEquals(2, (Object)handle.invokeExact(1, (Object)1));
|
assertEquals(2, (Object)handle.invokeExact(1, (Object)1));
|
||||||
|
|
|
@ -243,18 +243,22 @@ public class DefOptimizationTests extends ScriptTestCase {
|
||||||
public void testAddOptNullGuards() {
|
public void testAddOptNullGuards() {
|
||||||
// needs null guard
|
// needs null guard
|
||||||
assertBytecodeHasPattern("def x = 1; def y = 2; return x + y",
|
assertBytecodeHasPattern("def x = 1; def y = 2; return x + y",
|
||||||
"(?s).*INVOKEDYNAMIC add.*arguments:\\s+" + DefBootstrap.BINARY_OPERATOR
|
"(?s).*INVOKEDYNAMIC add.*arguments:\\s+" + "\\d+"
|
||||||
|
+ ",\\s+" + DefBootstrap.BINARY_OPERATOR
|
||||||
+ ",\\s+" + DefBootstrap.OPERATOR_ALLOWS_NULL + ".*");
|
+ ",\\s+" + DefBootstrap.OPERATOR_ALLOWS_NULL + ".*");
|
||||||
// still needs null guard, NPE is the wrong thing!
|
// still needs null guard, NPE is the wrong thing!
|
||||||
assertBytecodeHasPattern("def x = 1; def y = 2; double z = x + y",
|
assertBytecodeHasPattern("def x = 1; def y = 2; double z = x + y",
|
||||||
"(?s).*INVOKEDYNAMIC add.*arguments:\\s+" + DefBootstrap.BINARY_OPERATOR
|
"(?s).*INVOKEDYNAMIC add.*arguments:\\s+" + "\\d+"
|
||||||
|
+ ",\\s+" + DefBootstrap.BINARY_OPERATOR
|
||||||
+ ",\\s+" + DefBootstrap.OPERATOR_ALLOWS_NULL + ".*");
|
+ ",\\s+" + DefBootstrap.OPERATOR_ALLOWS_NULL + ".*");
|
||||||
// a primitive argument is present: no null guard needed
|
// a primitive argument is present: no null guard needed
|
||||||
assertBytecodeHasPattern("def x = 1; int y = 2; return x + y",
|
assertBytecodeHasPattern("def x = 1; int y = 2; return x + y",
|
||||||
"(?s).*INVOKEDYNAMIC add.*arguments:\\s+" + DefBootstrap.BINARY_OPERATOR
|
"(?s).*INVOKEDYNAMIC add.*arguments:\\s+" + "\\d+"
|
||||||
|
+ ",\\s+" + DefBootstrap.BINARY_OPERATOR
|
||||||
+ ",\\s+" + 0 + ".*");
|
+ ",\\s+" + 0 + ".*");
|
||||||
assertBytecodeHasPattern("int x = 1; def y = 2; return x + y",
|
assertBytecodeHasPattern("int x = 1; def y = 2; return x + y",
|
||||||
"(?s).*INVOKEDYNAMIC add.*arguments:\\s+" + DefBootstrap.BINARY_OPERATOR
|
"(?s).*INVOKEDYNAMIC add.*arguments:\\s+" + "\\d+"
|
||||||
|
+ ",\\s+" + DefBootstrap.BINARY_OPERATOR
|
||||||
+ ",\\s+" + 0 + ".*");
|
+ ",\\s+" + 0 + ".*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ public abstract class ScriptTestCase extends ESTestCase {
|
||||||
public Object exec(String script, Map<String, Object> vars) {
|
public Object exec(String script, Map<String, Object> vars) {
|
||||||
Map<String,String> compilerSettings = new HashMap<>();
|
Map<String,String> compilerSettings = new HashMap<>();
|
||||||
compilerSettings.put(CompilerSettings.PICKY, "true");
|
compilerSettings.put(CompilerSettings.PICKY, "true");
|
||||||
|
compilerSettings.put(CompilerSettings.INITIAL_CALL_SITE_DEPTH, random().nextBoolean() ? "0" : "10");
|
||||||
return exec(script, vars, compilerSettings, null);
|
return exec(script, vars, compilerSettings, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue