diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/CompilerSettings.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/CompilerSettings.java
index 79b75b32b98..94d7a1305d6 100644
--- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/CompilerSettings.java
+++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/CompilerSettings.java
@@ -33,6 +33,11 @@ public final class CompilerSettings {
* Constant to be used for enabling additional internal compilation checks (slower).
*/
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.
@@ -44,6 +49,11 @@ public final class CompilerSettings {
* makes things slower too, it is only for debugging.
*/
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
@@ -78,4 +88,20 @@ public final class CompilerSettings {
public void setPicky(boolean 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;
+ }
}
diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java
index 8014a807b3e..5461771bca6 100644
--- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java
+++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java
@@ -286,7 +286,8 @@ public final class Def {
MethodType nestedType = MethodType.methodType(interfaceType.clazz, captures);
CallSite nested = DefBootstrap.bootstrap(lookup,
call,
- nestedType,
+ nestedType,
+ 0,
DefBootstrap.REFERENCE,
interfaceType.name);
filter = nested.dynamicInvoker();
diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java
index 9174911b79c..9640629cb87 100644
--- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java
+++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java
@@ -27,6 +27,7 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
+import java.lang.invoke.WrongMethodTypeException;
/**
* 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. +=).
- *
+ *
+ * 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;
+
+ /**
+ * static bootstrap parameter indicating an explicit cast to the return type.
+ *
+ * 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).
@@ -97,7 +107,7 @@ public final class DefBootstrap {
private final Object[] args;
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);
if (type.parameterType(0) != Object.class) {
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.flavor = flavor;
this.args = args;
+ this.depth = initialDepth;
MethodHandle fallback = FALLBACK.bindTo(this)
.asCollector(Object[].class, type.parameterCount())
@@ -226,11 +237,14 @@ public final class DefBootstrap {
private final int flavor;
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);
this.name = name;
this.flavor = flavor;
this.flags = flags;
+ if (initialDepth > 0) {
+ initialized = true;
+ }
MethodHandle fallback = FALLBACK.bindTo(this)
.asCollector(Object[].class, type.parameterCount())
@@ -248,7 +262,9 @@ public final class DefBootstrap {
case SHIFT_OPERATOR:
// 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);
- 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);
}
return unary;
@@ -257,7 +273,9 @@ public final class DefBootstrap {
return lookupGeneric(); // can handle nulls, casts if supported
} else {
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);
}
return binary;
@@ -267,11 +285,15 @@ public final class DefBootstrap {
}
private MethodHandle lookupGeneric() {
- if ((flags & OPERATOR_COMPOUND_ASSIGNMENT) != 0) {
- return DefMath.lookupGenericWithCast(name);
- } else {
- return DefMath.lookupGeneric(name);
+ MethodHandle target = DefMath.lookupGeneric(name);
+ if ((flags & OPERATOR_EXPLICIT_CAST) != 0) {
+ // static cast to the return type
+ 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 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;
if (flavor == BINARY_OPERATOR || flavor == SHIFT_OPERATOR) {
@@ -384,12 +414,16 @@ public final class DefBootstrap {
/**
* invokeDynamic bootstrap method
*
- * In addition to ordinary parameters, we also take a static parameter {@code flavor} which
- * tells us what type of dynamic call it is (and which part of whitelist to look at).
+ * In addition to ordinary parameters, we also take some static parameters:
+ *
+ *
{@code initialDepth}: initial call site depth. this is used to exercise megamorphic fallback.
+ *
{@code flavor}: type of dynamic call it is (and which part of whitelist to look at).
+ *
{@code args}: flavor-specific args.
+ *
*
* 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
switch(flavor) {
// "function-call" like things get a polymorphic cache
@@ -408,7 +442,7 @@ public final class DefBootstrap {
if (args.length != numLambdas + 1) {
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 STORE:
case ARRAY_LOAD:
@@ -417,7 +451,7 @@ public final class DefBootstrap {
if (args.length > 0) {
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:
if (args.length != 1) {
throw new BootstrapMethodError("Invalid number of parameters for reference call");
@@ -425,7 +459,7 @@ public final class DefBootstrap {
if (args[0] instanceof String == false) {
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
case UNARY_OPERATOR:
@@ -442,11 +476,11 @@ public final class DefBootstrap {
// we just don't need it anywhere else.
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.
- 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:
throw new BootstrapMethodError("Illegal static bootstrap parameter for flavor: " + flavor);
}
diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java
index f56256f6cb2..f89b1429f38 100644
--- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java
+++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java
@@ -1027,25 +1027,7 @@ public class DefMath {
* class if its not a boxed type.
*/
private static Class> unbox(Class> clazz) {
- if (clazz == Boolean.class) {
- 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;
- }
+ return MethodType.methodType(clazz).unwrap().returnType();
}
/** Unary promotion. All Objects are promoted to Object. */
@@ -1146,40 +1128,50 @@ public class DefMath {
public static MethodHandle lookupGeneric(String name) {
return TYPE_OP_MAPPING.get(Object.class).get(name);
}
-
-
+
/**
* 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.
* 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) {
- Class> c = lhs.getClass();
- 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);
+ return dynamicCast(lhs.getClass(), returnValue);
} else {
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 */
static Number getNumber(Object o) {
if (o instanceof Number) {
@@ -1192,28 +1184,40 @@ public class DefMath {
}
private static final MethodHandle DYNAMIC_CAST;
+ private static final MethodHandle DYNAMIC_RECEIVER_CAST;
static {
final Lookup lookup = MethodHandles.lookup();
try {
DYNAMIC_CAST = lookup.findStatic(lookup.lookupClass(),
- "dynamicCast",
- MethodType.methodType(Object.class, Object.class, Object.class));
+ "dynamicCast",
+ 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) {
throw new AssertionError(e);
}
}
/** Looks up generic method, with a dynamic cast to the receiver's type. (compound assignment) */
- public static MethodHandle lookupGenericWithCast(String name) {
- MethodHandle generic = lookupGeneric(name);
- // adapt dynamic cast to the generic method
- MethodHandle cast = DYNAMIC_CAST.asType(MethodType.methodType(generic.type().returnType(),
- generic.type().returnType(),
- generic.type().parameterType(0)));
+ public static MethodHandle dynamicCast(MethodHandle target) {
+ // adapt dynamic receiver cast to the generic method
+ MethodHandle cast = DYNAMIC_RECEIVER_CAST.asType(MethodType.methodType(target.type().returnType(),
+ target.type().returnType(),
+ target.type().parameterType(0)));
// 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);
- 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) */
@@ -1221,12 +1225,18 @@ public class DefMath {
MethodType newType = MethodType.methodType(classA).unwrap();
MethodType targetType = MethodType.methodType(target.type().returnType()).unwrap();
+ // don't do a conversion if types are the same. explicitCastArguments has this opto,
+ // but we do it explicitly, to make the boolean check simpler
if (newType.returnType() == targetType.returnType()) {
- return target; // no conversion
+ return target;
}
- // this is safe for our uses of it here only, because we change just the return value,
- // the original method itself does all the type checks correctly.
+ // we don't allow the to/from boolean conversions of explicitCastArguments
+ if (newType.returnType() == boolean.class || targetType.returnType() == boolean.class) {
+ throw new ClassCastException("Cannot cast " + targetType.returnType() + " to " + newType.returnType());
+ }
+
+ // null return values are not possible for our arguments.
return MethodHandles.explicitCastArguments(target, target.type().changeReturnType(newType.returnType()));
}
}
diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java
index 6241b5b4207..97686655a97 100644
--- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java
+++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java
@@ -30,6 +30,7 @@ import org.objectweb.asm.commons.Method;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.BitSet;
import java.util.Deque;
import java.util.List;
@@ -78,15 +79,17 @@ import static org.elasticsearch.painless.WriterConstants.UTILITY_TYPE;
*/
public final class MethodWriter extends GeneratorAdapter {
private final BitSet statements;
+ private final CompilerSettings settings;
private final Deque> stringConcatArgs =
(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),
access, method.getName(), method.getDescriptor());
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 */
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);
- String descriptor = methodType.getDescriptor();
- int flags = 0;
- if (compoundAssignment) {
- flags |= DefBootstrap.OPERATOR_COMPOUND_ASSIGNMENT;
- }
switch (operation) {
case MUL:
- invokeDynamic("mul", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
+ invokeDefCall("mul", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case DIV:
- invokeDynamic("div", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
+ invokeDefCall("div", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case REM:
- invokeDynamic("rem", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
+ invokeDefCall("rem", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case ADD:
// 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) {
flags |= DefBootstrap.OPERATOR_ALLOWS_NULL;
}
- invokeDynamic("add", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
+ invokeDefCall("add", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case SUB:
- invokeDynamic("sub", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
+ invokeDefCall("sub", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case LSH:
- invokeDynamic("lsh", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.SHIFT_OPERATOR, flags);
+ invokeDefCall("lsh", methodType, DefBootstrap.SHIFT_OPERATOR, flags);
break;
case USH:
- invokeDynamic("ush", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.SHIFT_OPERATOR, flags);
+ invokeDefCall("ush", methodType, DefBootstrap.SHIFT_OPERATOR, flags);
break;
case RSH:
- invokeDynamic("rsh", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.SHIFT_OPERATOR, flags);
+ invokeDefCall("rsh", methodType, DefBootstrap.SHIFT_OPERATOR, flags);
break;
case BWAND:
- invokeDynamic("and", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
+ invokeDefCall("and", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case XOR:
- invokeDynamic("xor", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
+ invokeDefCall("xor", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
case BWOR:
- invokeDynamic("or", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
+ invokeDefCall("or", methodType, DefBootstrap.BINARY_OPERATOR, flags);
break;
default:
throw location.createError(new IllegalStateException("Illegal tree structure."));
@@ -391,4 +389,19 @@ public final class MethodWriter extends GeneratorAdapter {
public void visitEnd() {
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);
+ }
}
diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngineService.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngineService.java
index ba98c7ee62f..31c4e22dd0c 100644
--- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngineService.java
+++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngineService.java
@@ -127,6 +127,12 @@ public final class PainlessScriptEngineService extends AbstractComponent impleme
if (value != null) {
compilerSettings.setPicky(Boolean.parseBoolean(value));
}
+
+ value = copy.remove(CompilerSettings.INITIAL_CALL_SITE_DEPTH);
+
+ if (value != null) {
+ compilerSettings.setInitialCallSiteDepth(Integer.parseInt(value));
+ }
if (!copy.isEmpty()) {
throw new IllegalArgumentException("Unrecognized compile-time parameter(s): " + copy);
diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java
index 6960e906339..d581ba8518a 100644
--- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java
+++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java
@@ -85,9 +85,10 @@ public final class WriterConstants {
public final static Method MATCHER_FIND = getAsmMethod(boolean.class, "find");
/** dynamic callsite bootstrap signature */
- public final static MethodType DEF_BOOTSTRAP_TYPE =
- MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, Object[].class);
- public final static Handle DEF_BOOTSTRAP_HANDLE =
+ final static MethodType DEF_BOOTSTRAP_TYPE =
+ MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class,
+ int.class, int.class, Object[].class);
+ final static Handle DEF_BOOTSTRAP_HANDLE =
new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(DefBootstrap.class),
"bootstrap", DEF_BOOTSTRAP_TYPE.toMethodDescriptorString(), false);
diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java
index b92bd6733c9..a84247afe25 100644
--- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java
+++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java
@@ -241,7 +241,7 @@ public final class Walker extends PainlessParserBaseVisitor