/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.runtime.internal;
import static com.github.anba.es6draft.runtime.AbstractOperations.EqualityComparison;
import static com.github.anba.es6draft.runtime.AbstractOperations.RelationalComparison;
import static com.github.anba.es6draft.runtime.AbstractOperations.StrictEqualityComparison;
import static com.github.anba.es6draft.runtime.internal.ScriptRuntime.CheckCallable;
import static com.github.anba.es6draft.runtime.internal.ScriptRuntime.CheckConstructor;
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.util.ArrayList;
import java.util.Arrays;
import com.github.anba.es6draft.ast.BinaryExpression;
import com.github.anba.es6draft.compiler.assembler.Handle;
import com.github.anba.es6draft.compiler.assembler.MethodName;
import com.github.anba.es6draft.compiler.assembler.MethodTypeDescriptor;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.types.Constructor;
import com.github.anba.es6draft.runtime.types.ScriptObject;
import com.github.anba.es6draft.runtime.types.Type;
import com.github.anba.es6draft.runtime.types.builtins.BuiltinConstructor;
import com.github.anba.es6draft.runtime.types.builtins.BuiltinFunction;
import com.github.anba.es6draft.runtime.types.builtins.FunctionObject;
/**
*
*/
public final class Bootstrap {
private Bootstrap() {
}
private static final class CallNames {
static final String CALL = "expression::call";
static final String CONSTRUCT = "expression::construct";
static final String SUPER = "expression::super";
static final String CONCAT = "expression::concat";
static final String ADD = "expression::add";
static final String EQ = "expression::equals";
static final String SHEQ = "expression::strictEquals";
static final String LT = "expression::lessThan";
static final String GT = "expression::greaterThan";
static final String LE = "expression::lessThanEquals";
static final String GE = "expression::greaterThanEquals";
}
private static final class Descriptors {
static final MethodTypeDescriptor ADD = MethodTypeDescriptor.methodType(Object.class,
Object.class, Object.class, ExecutionContext.class);
static final MethodTypeDescriptor CMP = MethodTypeDescriptor.methodType(boolean.class,
Object.class, Object.class, ExecutionContext.class);
static final MethodTypeDescriptor EQ = MethodTypeDescriptor.methodType(boolean.class,
Object.class, Object.class, ExecutionContext.class);
static final MethodTypeDescriptor STRICT_EQ = MethodTypeDescriptor.methodType(
boolean.class, Object.class, Object.class);
static final MethodTypeDescriptor CALL = MethodTypeDescriptor.methodType(Object.class,
Object.class, ExecutionContext.class, Object.class, Object[].class);
static final MethodTypeDescriptor CONSTRUCT = MethodTypeDescriptor.methodType(
ScriptObject.class, Object.class, ExecutionContext.class, Object[].class);
static final MethodTypeDescriptor SUPER = MethodTypeDescriptor.methodType(
ScriptObject.class, Constructor.class, ExecutionContext.class, Constructor.class,
Object[].class);
}
private static final Handle BOOTSTRAP;
static {
MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class,
String.class, MethodType.class);
BOOTSTRAP = MethodName.findStatic(Bootstrap.class, "bootstrapDynamic", mt).toHandle();
}
/**
* Returns the invokedynamic instruction name for call expressions.
*
* @return the invokedynamic instruction name
*/
public static String getCallName() {
return CallNames.CALL;
}
/**
* Returns the method descriptor for call expressions.
*
* @return the method descriptor
*/
public static MethodTypeDescriptor getCallMethodDescriptor() {
return Descriptors.CALL;
}
/**
* Returns the bootstrapping handle for call expressions.
*
* @return the bootstrapping handle
*/
public static Handle getCallBootstrap() {
return BOOTSTRAP;
}
private static final MethodHandle callSetupMH;
private static final MethodHandle callGenericMH;
private static final MethodHandle testFunctionObjectMH, testBuiltinFunctionMH;
static {
MethodLookup lookup = new MethodLookup(MethodHandles.lookup());
testFunctionObjectMH = lookup.findStatic("testFunctionObject",
MethodType.methodType(boolean.class, Object.class, Object.class));
testBuiltinFunctionMH = lookup.findStatic("testBuiltinFunction",
MethodType.methodType(boolean.class, Object.class, Object.class));
callGenericMH = lookup.findStatic("callGeneric", MethodType.methodType(Object.class,
Object.class, ExecutionContext.class, Object.class, Object[].class));
callSetupMH = lookup.findStatic("callSetup", MethodType.methodType(MethodHandle.class,
MutableCallSite.class, Object.class, ExecutionContext.class, Object.class,
Object[].class));
}
@SuppressWarnings("unused")
private static MethodHandle callSetup(MutableCallSite callsite, Object function,
ExecutionContext cx, Object thisValue, Object[] arguments) {
MethodHandle target, test;
if (function instanceof FunctionObject) {
FunctionObject fn = (FunctionObject) function;
test = MethodHandles.insertArguments(testFunctionObjectMH, 1, fn.getMethodInfo());
target = fn.getCallMethod();
} else if (function instanceof BuiltinFunction) {
BuiltinFunction fn = (BuiltinFunction) function;
test = MethodHandles.insertArguments(testBuiltinFunctionMH, 1, fn.getMethodInfo());
target = fn.getCallMethod();
} else {
target = test = null;
}
return setCallSiteTarget(callsite, target, test, callGenericMH);
}
@SuppressWarnings("unused")
private static boolean testFunctionObject(Object function, Object methodInfo) {
return function instanceof FunctionObject
&& ((FunctionObject) function).getMethodInfo() == methodInfo;
}
@SuppressWarnings("unused")
private static boolean testBuiltinFunction(Object function, Object methodInfo) {
return function instanceof BuiltinFunction
&& ((BuiltinFunction) function).getMethodInfo() == methodInfo;
}
@SuppressWarnings("unused")
private static Object callGeneric(Object function, ExecutionContext callerContext,
Object thisValue, Object[] arguments) {
return CheckCallable(function, callerContext).call(callerContext, thisValue, arguments);
}
/**
* Returns the invokedynamic instruction name for construct expressions.
*
* @return the invokedynamic instruction name
*/
public static String getConstructName() {
return CallNames.CONSTRUCT;
}
/**
* Returns the method descriptor for construct expressions.
*
* @return the method descriptor
*/
public static MethodTypeDescriptor getConstructMethodDescriptor() {
return Descriptors.CONSTRUCT;
}
/**
* Returns the bootstrapping handle for construct expressions.
*
* @return the bootstrapping handle
*/
public static Handle getConstructBootstrap() {
return BOOTSTRAP;
}
private static final MethodHandle constructSetupMH;
private static final MethodHandle constructGenericMH;
static {
MethodLookup lookup = new MethodLookup(MethodHandles.lookup());
constructGenericMH = lookup.findStatic("constructGeneric", MethodType.methodType(
ScriptObject.class, Object.class, ExecutionContext.class, Object[].class));
constructSetupMH = lookup.findStatic("constructSetup", MethodType.methodType(
MethodHandle.class, MutableCallSite.class, Object.class, ExecutionContext.class,
Object[].class));
}
@SuppressWarnings("unused")
private static MethodHandle constructSetup(MutableCallSite callsite, Object constructor,
ExecutionContext cx, Object[] arguments) {
MethodHandle target, test;
if (constructor instanceof FunctionObject && constructor instanceof Constructor) {
FunctionObject fn = (FunctionObject) constructor;
test = MethodHandles.insertArguments(testFunctionObjectMH, 1, fn.getMethodInfo());
target = fn.getConstructMethod();
} else if (constructor instanceof BuiltinConstructor) {
BuiltinConstructor fn = (BuiltinConstructor) constructor;
test = MethodHandles.insertArguments(testBuiltinFunctionMH, 1, fn.getMethodInfo());
target = fn.getConstructMethod();
} else {
target = test = null;
}
if (target != null) {
// Insert constructor as newTarget argument.
target = target.asType(target.type().changeParameterType(2, constructor.getClass()));
target = MethodHandles.permuteArguments(target, target.type().dropParameterTypes(2, 3),
0, 1, 0, 2);
}
return setCallSiteTarget(callsite, target, test, constructGenericMH);
}
@SuppressWarnings("unused")
private static ScriptObject constructGeneric(Object constructor,
ExecutionContext callerContext, Object[] arguments) {
return CheckConstructor(constructor, callerContext).construct(callerContext,
(Constructor) constructor, arguments);
}
/**
* Returns the invokedynamic instruction name for super() expressions.
*
* @return the invokedynamic instruction name
*/
public static String getSuperName() {
return CallNames.SUPER;
}
/**
* Returns the method descriptor for super() expressions.
*
* @return the method descriptor
*/
public static MethodTypeDescriptor getSuperMethodDescriptor() {
return Descriptors.SUPER;
}
/**
* Returns the bootstrapping handle for super() expressions.
*
* @return the bootstrapping handle
*/
public static Handle getSuperBootstrap() {
return BOOTSTRAP;
}
private static final MethodHandle superSetupMH;
private static final MethodHandle superGenericMH;
static {
MethodLookup lookup = new MethodLookup(MethodHandles.lookup());
superGenericMH = lookup.findStatic("superGeneric", MethodType.methodType(
ScriptObject.class, Constructor.class, ExecutionContext.class, Constructor.class,
Object[].class));
superSetupMH = lookup.findStatic("superSetup", MethodType.methodType(MethodHandle.class,
MutableCallSite.class, Constructor.class, ExecutionContext.class,
Constructor.class, Object[].class));
}
@SuppressWarnings("unused")
private static MethodHandle superSetup(MutableCallSite callsite, Constructor constructor,
ExecutionContext cx, Constructor newTarget, Object[] arguments) {
MethodHandle target, test;
if (constructor instanceof FunctionObject && constructor instanceof Constructor) {
FunctionObject fn = (FunctionObject) constructor;
test = MethodHandles.insertArguments(testFunctionObjectMH, 1, fn.getMethodInfo());
target = fn.getConstructMethod();
} else if (constructor instanceof BuiltinConstructor) {
BuiltinConstructor fn = (BuiltinConstructor) constructor;
test = MethodHandles.insertArguments(testBuiltinFunctionMH, 1, fn.getMethodInfo());
target = fn.getConstructMethod();
} else {
target = test = null;
}
if (test != null) {
test = test.asType(test.type().changeParameterType(0, Constructor.class));
}
return setCallSiteTarget(callsite, target, test, superGenericMH);
}
@SuppressWarnings("unused")
private static ScriptObject superGeneric(Constructor constructor,
ExecutionContext callerContext, Constructor newTarget, Object[] arguments) {
return constructor.construct(callerContext, newTarget, arguments);
}
/**
* Returns the invokedynamic instruction name for concat expressions.
*
* @return the invokedynamic instruction name
*/
public static String getConcatName() {
return CallNames.CONCAT;
}
/**
* Returns the method descriptor for concat expressions.
*
* @param numberOfStrings
* the number of strings
* @return the method descriptor
*/
public static MethodTypeDescriptor getConcatMethodDescriptor(int numberOfStrings) {
Class<?>[] parameters = new Class<?>[numberOfStrings + 1];
parameters[0] = ExecutionContext.class;
Arrays.fill(parameters, 1, parameters.length, CharSequence.class);
return MethodTypeDescriptor.methodType(CharSequence.class, parameters);
}
/**
* Returns the bootstrapping handle for concat expressions.
*
* @return the bootstrapping handle
*/
public static Handle getConcatBootstrap() {
return BOOTSTRAP;
}
private static final int MAX_STRING_SEGMENT_SIZE = 5;
private static final int CONCAT_MIN_PARAMS = 2;
private static final int CONCAT_MAX_SPECIALIZATION = 10;
private static final MethodHandle[] testConcatMH;
private static final MethodHandle[] concatConsMH;
private static final MethodHandle[] concatMH;
static {
MethodLookup lookup = new MethodLookup(MethodHandles.lookup());
final int methods = CONCAT_MAX_SPECIALIZATION - CONCAT_MIN_PARAMS + 1;
MethodHandle[] test = new MethodHandle[methods + 1];
ArrayList<Class<?>> testParams = new ArrayList<>();
testParams.add(CharSequence.class);
for (int i = CONCAT_MIN_PARAMS; i <= CONCAT_MAX_SPECIALIZATION; ++i) {
testParams.add(CharSequence.class);
test[i - CONCAT_MIN_PARAMS] = dropContext(lookup.findStatic("testConcat",
MethodType.methodType(boolean.class, testParams)));
}
test[methods] = dropContext(lookup.findStatic("testConcat",
MethodType.methodType(boolean.class, CharSequence[].class)));
testConcatMH = test;
MethodHandle[] cons = new MethodHandle[methods + 1];
ArrayList<Class<?>> consParams = new ArrayList<>();
consParams.add(ExecutionContext.class);
consParams.add(CharSequence.class);
for (int i = CONCAT_MIN_PARAMS; i <= CONCAT_MAX_SPECIALIZATION; ++i) {
consParams.add(CharSequence.class);
cons[i - CONCAT_MIN_PARAMS] = lookup.findStatic("concatCons",
MethodType.methodType(CharSequence.class, consParams));
}
cons[methods] = lookup.findStatic("concatCons", MethodType.methodType(CharSequence.class,
ExecutionContext.class, CharSequence[].class));
concatConsMH = cons;
MethodHandle[] concat = new MethodHandle[methods + 1];
ArrayList<Class<?>> concatParams = new ArrayList<>();
concatParams.add(CharSequence.class);
for (int i = CONCAT_MIN_PARAMS; i <= CONCAT_MAX_SPECIALIZATION; ++i) {
concatParams.add(CharSequence.class);
concat[i - CONCAT_MIN_PARAMS] = dropContext(lookup.findStatic("concat",
MethodType.methodType(CharSequence.class, concatParams)));
}
concat[methods] = dropContext(lookup.findStatic("concat",
MethodType.methodType(CharSequence.class, CharSequence[].class)));
concatMH = concat;
}
private static MethodHandle dropContext(MethodHandle mh) {
return MethodHandles.dropArguments(mh, 0, ExecutionContext.class);
}
private static void concatSetup(MutableCallSite callsite, MethodType type) {
MethodHandle target, test, generic;
int numberOfStrings = type.parameterCount() - 1; // CharSequence..., ExecutionContext
if (numberOfStrings <= CONCAT_MAX_SPECIALIZATION) {
assert numberOfStrings >= CONCAT_MIN_PARAMS;
int index = numberOfStrings - CONCAT_MIN_PARAMS;
target = concatMH[index];
test = testConcatMH[index];
generic = concatConsMH[index];
setCallSiteTarget(callsite, target, test, generic);
} else {
final int index = CONCAT_MAX_SPECIALIZATION - CONCAT_MIN_PARAMS + 1;
target = concatMH[index].asCollector(CharSequence[].class, numberOfStrings);
test = testConcatMH[index].asCollector(CharSequence[].class, numberOfStrings);
generic = concatConsMH[index].asCollector(CharSequence[].class, numberOfStrings);
}
setCallSiteTarget(callsite, target, test, generic);
}
@SuppressWarnings("unused")
private static boolean testConcat(CharSequence s1, CharSequence s2) {
/* @formatter:off */
int n = s1.length() + s2.length();
if (n < 0) return false;
return n <= (2 * MAX_STRING_SEGMENT_SIZE);
/* @formatter:on */
}
@SuppressWarnings("unused")
private static boolean testConcat(CharSequence s1, CharSequence s2, CharSequence s3) {
/* @formatter:off */
int n = s1.length();
n += s2.length();
if (n < 0) return false;
n += s3.length();
if (n < 0) return false;
return n <= (3 * MAX_STRING_SEGMENT_SIZE);
/* @formatter:on */
}
@SuppressWarnings("unused")
private static boolean testConcat(CharSequence s1, CharSequence s2, CharSequence s3, CharSequence s4) {
/* @formatter:off */
int n = s1.length();
n += s2.length();
if (n < 0) return false;
n += s3.length();
if (n < 0) return false;
n += s4.length();
if (n < 0) return false;
return n <= (4 * MAX_STRING_SEGMENT_SIZE);
/* @formatter:on */
}
@SuppressWarnings("unused")
private static boolean testConcat(CharSequence s1, CharSequence s2, CharSequence s3, CharSequence s4,
CharSequence s5) {
/* @formatter:off */
int n = s1.length();
n += s2.length();
if (n < 0) return false;
n += s3.length();
if (n < 0) return false;
n += s4.length();
if (n < 0) return false;
n += s5.length();
if (n < 0) return false;
return n <= (5 * MAX_STRING_SEGMENT_SIZE);
/* @formatter:on */
}
@SuppressWarnings("unused")
private static boolean testConcat(CharSequence s1, CharSequence s2, CharSequence s3, CharSequence s4,
CharSequence s5, CharSequence s6) {
/* @formatter:off */
int n = s1.length();
n += s2.length();
if (n < 0) return false;
n += s3.length();
if (n < 0) return false;
n += s4.length();
if (n < 0) return false;
n += s5.length();
if (n < 0) return false;
n += s6.length();
if (n < 0) return false;
return n <= (6 * MAX_STRING_SEGMENT_SIZE);
/* @formatter:on */
}
@SuppressWarnings("unused")
private static boolean testConcat(CharSequence s1, CharSequence s2, CharSequence s3, CharSequence s4,
CharSequence s5, CharSequence s6, CharSequence s7) {
/* @formatter:off */
int n = s1.length();
n += s2.length();
if (n < 0) return false;
n += s3.length();
if (n < 0) return false;
n += s4.length();
if (n < 0) return false;
n += s5.length();
if (n < 0) return false;
n += s6.length();
if (n < 0) return false;
n += s7.length();
if (n < 0) return false;
return n <= (7 * MAX_STRING_SEGMENT_SIZE);
/* @formatter:on */
}
@SuppressWarnings("unused")
private static boolean testConcat(CharSequence s1, CharSequence s2, CharSequence s3, CharSequence s4,
CharSequence s5, CharSequence s6, CharSequence s7, CharSequence s8) {
/* @formatter:off */
int n = s1.length();
n += s2.length();
if (n < 0) return false;
n += s3.length();
if (n < 0) return false;
n += s4.length();
if (n < 0) return false;
n += s5.length();
if (n < 0) return false;
n += s6.length();
if (n < 0) return false;
n += s7.length();
if (n < 0) return false;
n += s8.length();
if (n < 0) return false;
return n <= (8 * MAX_STRING_SEGMENT_SIZE);
/* @formatter:on */
}
@SuppressWarnings("unused")
private static boolean testConcat(CharSequence s1, CharSequence s2, CharSequence s3, CharSequence s4,
CharSequence s5, CharSequence s6, CharSequence s7, CharSequence s8, CharSequence s9) {
/* @formatter:off */
int n = s1.length();
n += s2.length();
if (n < 0) return false;
n += s3.length();
if (n < 0) return false;
n += s4.length();
if (n < 0) return false;
n += s5.length();
if (n < 0) return false;
n += s6.length();
if (n < 0) return false;
n += s7.length();
if (n < 0) return false;
n += s8.length();
if (n < 0) return false;
n += s9.length();
if (n < 0) return false;
return n <= (9 * MAX_STRING_SEGMENT_SIZE);
/* @formatter:on */
}
@SuppressWarnings("unused")
private static boolean testConcat(CharSequence s1, CharSequence s2, CharSequence s3, CharSequence s4,
CharSequence s5, CharSequence s6, CharSequence s7, CharSequence s8, CharSequence s9, CharSequence s10) {
/* @formatter:off */
int n = s1.length();
n += s2.length();
if (n < 0) return false;
n += s3.length();
if (n < 0) return false;
n += s4.length();
if (n < 0) return false;
n += s5.length();
if (n < 0) return false;
n += s6.length();
if (n < 0) return false;
n += s7.length();
if (n < 0) return false;
n += s8.length();
if (n < 0) return false;
n += s9.length();
if (n < 0) return false;
n += s10.length();
if (n < 0) return false;
return n <= (10 * MAX_STRING_SEGMENT_SIZE);
/* @formatter:on */
}
@SuppressWarnings("unused")
private static boolean testConcat(CharSequence[] strings) {
int n = 0;
for (CharSequence charSequence : strings) {
n += charSequence.length();
if (n < 0) {
return false;
}
}
return n <= (strings.length * MAX_STRING_SEGMENT_SIZE);
}
@SuppressWarnings("unused")
private static CharSequence concatCons(ExecutionContext cx, CharSequence s1, CharSequence s2) {
return ScriptRuntime.add(s1, s2, cx);
}
@SuppressWarnings("unused")
private static CharSequence concatCons(ExecutionContext cx, CharSequence s1, CharSequence s2,
CharSequence s3) {
return ScriptRuntime.add(ScriptRuntime.add(s1, s2, cx), s3, cx);
}
@SuppressWarnings("unused")
private static CharSequence concatCons(ExecutionContext cx, CharSequence s1, CharSequence s2,
CharSequence s3, CharSequence s4) {
return ScriptRuntime.add(ScriptRuntime.add(ScriptRuntime.add(s1, s2, cx), s3, cx), s4, cx);
}
@SuppressWarnings("unused")
private static CharSequence concatCons(ExecutionContext cx, CharSequence s1, CharSequence s2,
CharSequence s3, CharSequence s4, CharSequence s5) {
CharSequence s = ScriptRuntime.add(s1, s2, cx);
s = ScriptRuntime.add(s, s3, cx);
s = ScriptRuntime.add(s, s4, cx);
s = ScriptRuntime.add(s, s5, cx);
return s;
}
@SuppressWarnings("unused")
private static CharSequence concatCons(ExecutionContext cx, CharSequence s1, CharSequence s2,
CharSequence s3, CharSequence s4, CharSequence s5, CharSequence s6) {
CharSequence s = ScriptRuntime.add(s1, s2, cx);
s = ScriptRuntime.add(s, s3, cx);
s = ScriptRuntime.add(s, s4, cx);
s = ScriptRuntime.add(s, s5, cx);
s = ScriptRuntime.add(s, s6, cx);
return s;
}
@SuppressWarnings("unused")
private static CharSequence concatCons(ExecutionContext cx, CharSequence s1, CharSequence s2,
CharSequence s3, CharSequence s4, CharSequence s5, CharSequence s6, CharSequence s7) {
CharSequence s = ScriptRuntime.add(s1, s2, cx);
s = ScriptRuntime.add(s, s3, cx);
s = ScriptRuntime.add(s, s4, cx);
s = ScriptRuntime.add(s, s5, cx);
s = ScriptRuntime.add(s, s6, cx);
s = ScriptRuntime.add(s, s7, cx);
return s;
}
@SuppressWarnings("unused")
private static CharSequence concatCons(ExecutionContext cx, CharSequence s1, CharSequence s2,
CharSequence s3, CharSequence s4, CharSequence s5, CharSequence s6, CharSequence s7,
CharSequence s8) {
CharSequence s = ScriptRuntime.add(s1, s2, cx);
s = ScriptRuntime.add(s, s3, cx);
s = ScriptRuntime.add(s, s4, cx);
s = ScriptRuntime.add(s, s5, cx);
s = ScriptRuntime.add(s, s6, cx);
s = ScriptRuntime.add(s, s7, cx);
s = ScriptRuntime.add(s, s8, cx);
return s;
}
@SuppressWarnings("unused")
private static CharSequence concatCons(ExecutionContext cx, CharSequence s1, CharSequence s2,
CharSequence s3, CharSequence s4, CharSequence s5, CharSequence s6, CharSequence s7,
CharSequence s8, CharSequence s9) {
CharSequence s = ScriptRuntime.add(s1, s2, cx);
s = ScriptRuntime.add(s, s3, cx);
s = ScriptRuntime.add(s, s4, cx);
s = ScriptRuntime.add(s, s5, cx);
s = ScriptRuntime.add(s, s6, cx);
s = ScriptRuntime.add(s, s7, cx);
s = ScriptRuntime.add(s, s8, cx);
s = ScriptRuntime.add(s, s9, cx);
return s;
}
@SuppressWarnings("unused")
private static CharSequence concatCons(ExecutionContext cx, CharSequence s1, CharSequence s2,
CharSequence s3, CharSequence s4, CharSequence s5, CharSequence s6, CharSequence s7,
CharSequence s8, CharSequence s9, CharSequence s10) {
CharSequence s = ScriptRuntime.add(s1, s2, cx);
s = ScriptRuntime.add(s, s3, cx);
s = ScriptRuntime.add(s, s4, cx);
s = ScriptRuntime.add(s, s5, cx);
s = ScriptRuntime.add(s, s6, cx);
s = ScriptRuntime.add(s, s7, cx);
s = ScriptRuntime.add(s, s8, cx);
s = ScriptRuntime.add(s, s9, cx);
s = ScriptRuntime.add(s, s10, cx);
return s;
}
@SuppressWarnings("unused")
private static CharSequence concatCons(ExecutionContext cx, CharSequence[] strings) {
CharSequence s = "";
for (CharSequence cs : strings) {
s = ScriptRuntime.add(s, cs, cx);
}
return s;
}
@SuppressWarnings("unused")
private static CharSequence concat(CharSequence s1, CharSequence s2) {
int s1len = s1.length(), s2len = s2.length();
if (s1len == 0) {
return s2;
}
if (s2len == 0) {
return s1;
}
char[] ca = new char[s1len + s2len];
s1.toString().getChars(0, s1len, ca, 0);
s2.toString().getChars(0, s2len, ca, s1len);
return new String(ca);
}
@SuppressWarnings("unused")
private static CharSequence concat(CharSequence s1, CharSequence s2, CharSequence s3) {
int s1len = s1.length(), s2len = s2.length(), s3len = s3.length();
char[] ca = new char[s1len + s2len + s3len];
if (s1len != 0)
s1.toString().getChars(0, s1len, ca, 0);
if (s2len != 0)
s2.toString().getChars(0, s2len, ca, s1len);
if (s3len != 0)
s3.toString().getChars(0, s3len, ca, s1len + s2len);
return new String(ca);
}
@SuppressWarnings("unused")
private static CharSequence concat(CharSequence s1, CharSequence s2, CharSequence s3,
CharSequence s4) {
int s1len = s1.length(), s2len = s2.length(), s3len = s3.length(), s4len = s4.length();
char[] ca = new char[s1len + s2len + s3len + s4len];
if (s1len != 0)
s1.toString().getChars(0, s1len, ca, 0);
if (s2len != 0)
s2.toString().getChars(0, s2len, ca, s1len);
if (s3len != 0)
s3.toString().getChars(0, s3len, ca, s1len + s2len);
if (s4len != 0)
s4.toString().getChars(0, s4len, ca, s1len + s2len + s3len);
return new String(ca);
}
@SuppressWarnings("unused")
private static CharSequence concat(CharSequence s1, CharSequence s2, CharSequence s3,
CharSequence s4, CharSequence s5) {
int s1len = s1.length(), s2len = s2.length(), s3len = s3.length(), s4len = s4.length();
int s5len = s5.length();
char[] ca = new char[s1len + s2len + s3len + s4len + s5len];
if (s1len != 0)
s1.toString().getChars(0, s1len, ca, 0);
if (s2len != 0)
s2.toString().getChars(0, s2len, ca, s1len);
if (s3len != 0)
s3.toString().getChars(0, s3len, ca, s1len + s2len);
if (s4len != 0)
s4.toString().getChars(0, s4len, ca, s1len + s2len + s3len);
if (s5len != 0)
s5.toString().getChars(0, s5len, ca, s1len + s2len + s3len + s4len);
return new String(ca);
}
@SuppressWarnings("unused")
private static CharSequence concat(CharSequence s1, CharSequence s2, CharSequence s3,
CharSequence s4, CharSequence s5, CharSequence s6) {
int s1len = s1.length(), s2len = s2.length(), s3len = s3.length(), s4len = s4.length();
int s5len = s5.length(), s6len = s6.length();
char[] ca = new char[s1len + s2len + s3len + s4len + s5len + s6len];
if (s1len != 0)
s1.toString().getChars(0, s1len, ca, 0);
if (s2len != 0)
s2.toString().getChars(0, s2len, ca, s1len);
if (s3len != 0)
s3.toString().getChars(0, s3len, ca, s1len + s2len);
if (s4len != 0)
s4.toString().getChars(0, s4len, ca, s1len + s2len + s3len);
if (s5len != 0)
s5.toString().getChars(0, s5len, ca, s1len + s2len + s3len + s4len);
if (s6len != 0)
s6.toString().getChars(0, s6len, ca, s1len + s2len + s3len + s4len + s5len);
return new String(ca);
}
@SuppressWarnings("unused")
private static CharSequence concat(CharSequence s1, CharSequence s2, CharSequence s3,
CharSequence s4, CharSequence s5, CharSequence s6, CharSequence s7) {
int s1len = s1.length(), s2len = s2.length(), s3len = s3.length(), s4len = s4.length();
int s5len = s5.length(), s6len = s6.length(), s7len = s7.length();
char[] ca = new char[s1len + s2len + s3len + s4len + s5len + s6len + s7len];
if (s1len != 0)
s1.toString().getChars(0, s1len, ca, 0);
if (s2len != 0)
s2.toString().getChars(0, s2len, ca, s1len);
if (s3len != 0)
s3.toString().getChars(0, s3len, ca, s1len + s2len);
if (s4len != 0)
s4.toString().getChars(0, s4len, ca, s1len + s2len + s3len);
if (s5len != 0)
s5.toString().getChars(0, s5len, ca, s1len + s2len + s3len + s4len);
if (s6len != 0)
s6.toString().getChars(0, s6len, ca, s1len + s2len + s3len + s4len + s5len);
if (s7len != 0)
s7.toString().getChars(0, s7len, ca, s1len + s2len + s3len + s4len + s5len + s6len);
return new String(ca);
}
@SuppressWarnings("unused")
private static CharSequence concat(CharSequence s1, CharSequence s2, CharSequence s3,
CharSequence s4, CharSequence s5, CharSequence s6, CharSequence s7, CharSequence s8) {
int s1len = s1.length(), s2len = s2.length(), s3len = s3.length(), s4len = s4.length();
int s5len = s5.length(), s6len = s6.length(), s7len = s7.length(), s8len = s8.length();
char[] ca = new char[s1len + s2len + s3len + s4len + s5len + s6len + s7len + s8len];
if (s1len != 0)
s1.toString().getChars(0, s1len, ca, 0);
if (s2len != 0)
s2.toString().getChars(0, s2len, ca, s1len);
if (s3len != 0)
s3.toString().getChars(0, s3len, ca, s1len + s2len);
if (s4len != 0)
s4.toString().getChars(0, s4len, ca, s1len + s2len + s3len);
if (s5len != 0)
s5.toString().getChars(0, s5len, ca, s1len + s2len + s3len + s4len);
if (s6len != 0)
s6.toString().getChars(0, s6len, ca, s1len + s2len + s3len + s4len + s5len);
if (s7len != 0)
s7.toString().getChars(0, s7len, ca, s1len + s2len + s3len + s4len + s5len + s6len);
if (s8len != 0)
s8.toString().getChars(0, s8len, ca,
s1len + s2len + s3len + s4len + s5len + s6len + s7len);
return new String(ca);
}
@SuppressWarnings("unused")
private static CharSequence concat(CharSequence s1, CharSequence s2, CharSequence s3,
CharSequence s4, CharSequence s5, CharSequence s6, CharSequence s7, CharSequence s8,
CharSequence s9) {
int s1len = s1.length(), s2len = s2.length(), s3len = s3.length(), s4len = s4.length();
int s5len = s5.length(), s6len = s6.length(), s7len = s7.length(), s8len = s8.length();
int s9len = s9.length();
char[] ca = new char[s1len + s2len + s3len + s4len + s5len + s6len + s7len + s8len + s9len];
if (s1len != 0)
s1.toString().getChars(0, s1len, ca, 0);
if (s2len != 0)
s2.toString().getChars(0, s2len, ca, s1len);
if (s3len != 0)
s3.toString().getChars(0, s3len, ca, s1len + s2len);
if (s4len != 0)
s4.toString().getChars(0, s4len, ca, s1len + s2len + s3len);
if (s5len != 0)
s5.toString().getChars(0, s5len, ca, s1len + s2len + s3len + s4len);
if (s6len != 0)
s6.toString().getChars(0, s6len, ca, s1len + s2len + s3len + s4len + s5len);
if (s7len != 0)
s7.toString().getChars(0, s7len, ca, s1len + s2len + s3len + s4len + s5len + s6len);
if (s8len != 0)
s8.toString().getChars(0, s8len, ca,
s1len + s2len + s3len + s4len + s5len + s6len + s7len);
if (s9len != 0)
s9.toString().getChars(0, s9len, ca,
s1len + s2len + s3len + s4len + s5len + s6len + s7len + s8len);
return new String(ca);
}
@SuppressWarnings("unused")
private static CharSequence concat(CharSequence s1, CharSequence s2, CharSequence s3,
CharSequence s4, CharSequence s5, CharSequence s6, CharSequence s7, CharSequence s8,
CharSequence s9, CharSequence s10) {
int s1len = s1.length(), s2len = s2.length(), s3len = s3.length(), s4len = s4.length();
int s5len = s5.length(), s6len = s6.length(), s7len = s7.length(), s8len = s8.length();
int s9len = s9.length(), s10len = s10.length();
char[] ca = new char[s1len + s2len + s3len + s4len + s5len + s6len + s7len + s8len + s9len
+ s10len];
if (s1len != 0)
s1.toString().getChars(0, s1len, ca, 0);
if (s2len != 0)
s2.toString().getChars(0, s2len, ca, s1len);
if (s3len != 0)
s3.toString().getChars(0, s3len, ca, s1len + s2len);
if (s4len != 0)
s4.toString().getChars(0, s4len, ca, s1len + s2len + s3len);
if (s5len != 0)
s5.toString().getChars(0, s5len, ca, s1len + s2len + s3len + s4len);
if (s6len != 0)
s6.toString().getChars(0, s6len, ca, s1len + s2len + s3len + s4len + s5len);
if (s7len != 0)
s7.toString().getChars(0, s7len, ca, s1len + s2len + s3len + s4len + s5len + s6len);
if (s8len != 0)
s8.toString().getChars(0, s8len, ca,
s1len + s2len + s3len + s4len + s5len + s6len + s7len);
if (s9len != 0)
s9.toString().getChars(0, s9len, ca,
s1len + s2len + s3len + s4len + s5len + s6len + s7len + s8len);
if (s10len != 0)
s10.toString().getChars(0, s10len, ca,
s1len + s2len + s3len + s4len + s5len + s6len + s7len + s8len + s9len);
return new String(ca);
}
@SuppressWarnings("unused")
private static CharSequence concat(CharSequence[] strings) {
StringBuilder sb = new StringBuilder(MAX_STRING_SEGMENT_SIZE * CONCAT_MAX_SPECIALIZATION);
for (CharSequence s : strings) {
sb.append(s);
}
return sb.toString();
}
/**
* Returns the invokedynamic instruction name for the given binary operator.
*
* @param binary
* the binary operator
* @return the invokedynamic instruction name
*/
public static String getName(BinaryExpression.Operator binary) {
switch (binary) {
case ADD:
return CallNames.ADD;
case EQ:
return CallNames.EQ;
case SHEQ:
return CallNames.SHEQ;
case LT:
return CallNames.LT;
case GT:
return CallNames.GT;
case LE:
return CallNames.LE;
case GE:
return CallNames.GE;
case NE:
case SHNE:
default:
throw new UnsupportedOperationException(binary.toString());
}
}
/**
* Returns the method descriptor for the given binary operator.
*
* @param binary
* the binary operator
* @return the method descriptor
*/
public static MethodTypeDescriptor getMethodDescriptor(BinaryExpression.Operator binary) {
switch (binary) {
case ADD:
return Descriptors.ADD;
case EQ:
return Descriptors.EQ;
case SHEQ:
return Descriptors.STRICT_EQ;
case LT:
case GT:
case LE:
case GE:
return Descriptors.CMP;
case NE:
case SHNE:
default:
throw new UnsupportedOperationException(binary.toString());
}
}
/**
* Returns the bootstrapping handle for the given binary operator.
*
* @param binary
* the binary operator
* @return the bootstrapping handle
*/
public static Handle getBootstrap(BinaryExpression.Operator binary) {
switch (binary) {
case ADD:
case EQ:
case SHEQ:
case LT:
case GT:
case LE:
case GE:
return BOOTSTRAP;
case NE:
case SHNE:
default:
throw new UnsupportedOperationException(binary.toString());
}
}
private static final MethodHandle addSetupMH, relCmpSetupMH, eqCmpSetupMH, strictEqCmpSetupMH;
private static final MethodHandle addStringMH, addNumberMH, addGenericMH;
private static final MethodHandle relCmpStringMH, relCmpNumberMH, relCmpGenericMH;
private static final MethodHandle lessThanMH, greaterThanMH, lessThanEqualsMH,
greaterThanEqualsMH;
private static final MethodHandle eqCmpStringMH, eqCmpNumberMH, eqCmpBooleanMH, eqCmpGenericMH;
private static final MethodHandle strictEqCmpStringMH, strictEqCmpNumberMH,
strictEqCmpBooleanMH, strictEqCmpGenericMH;
private static final MethodHandle testStringMH, testNumberMH, testBooleanMH;
static {
MethodLookup lookup = new MethodLookup(MethodHandles.lookup());
testStringMH = lookup.findStatic("testString",
MethodType.methodType(boolean.class, Object.class, Object.class));
testNumberMH = lookup.findStatic("testNumber",
MethodType.methodType(boolean.class, Object.class, Object.class));
testBooleanMH = lookup.findStatic("testBoolean",
MethodType.methodType(boolean.class, Object.class, Object.class));
addStringMH = lookup.findStatic("addString", MethodType.methodType(CharSequence.class,
CharSequence.class, CharSequence.class, ExecutionContext.class));
MethodHandle addNumber = lookup.findStatic("addNumber",
MethodType.methodType(Double.class, Number.class, Number.class));
addNumberMH = MethodHandles.dropArguments(addNumber, 2, ExecutionContext.class);
addGenericMH = lookup.findStatic("addGeneric", MethodType.methodType(Object.class,
Object.class, Object.class, ExecutionContext.class));
MethodHandle relCmpString = lookup.findStatic("relCmpString",
MethodType.methodType(int.class, CharSequence.class, CharSequence.class));
relCmpStringMH = MethodHandles.dropArguments(relCmpString, 2, ExecutionContext.class);
MethodHandle relCmpNumber = lookup.findStatic("relCmpNumber",
MethodType.methodType(int.class, Number.class, Number.class));
relCmpNumberMH = MethodHandles.dropArguments(relCmpNumber, 2, ExecutionContext.class);
relCmpGenericMH = lookup.findStatic("relCmpGeneric", MethodType.methodType(int.class,
Object.class, Object.class, RelationalOperator.class, ExecutionContext.class));
lessThanMH = lookup.findStatic("lessThan", MethodType.methodType(boolean.class, int.class));
greaterThanMH = lookup.findStatic("greaterThan",
MethodType.methodType(boolean.class, int.class));
lessThanEqualsMH = lookup.findStatic("lessThanEquals",
MethodType.methodType(boolean.class, int.class));
greaterThanEqualsMH = lookup.findStatic("greaterThanEquals",
MethodType.methodType(boolean.class, int.class));
MethodHandle eqCmpString = lookup.findStatic("eqCmpString",
MethodType.methodType(boolean.class, CharSequence.class, CharSequence.class));
MethodHandle eqCmpNumber = lookup.findStatic("eqCmpNumber",
MethodType.methodType(boolean.class, Number.class, Number.class));
MethodHandle eqCmpBoolean = lookup.findStatic("eqCmpBoolean",
MethodType.methodType(boolean.class, Boolean.class, Boolean.class));
eqCmpStringMH = MethodHandles.dropArguments(eqCmpString, 2, ExecutionContext.class);
eqCmpNumberMH = MethodHandles.dropArguments(eqCmpNumber, 2, ExecutionContext.class);
eqCmpBooleanMH = MethodHandles.dropArguments(eqCmpBoolean, 2, ExecutionContext.class);
eqCmpGenericMH = lookup.findStatic("eqCmpGeneric", MethodType.methodType(boolean.class,
Object.class, Object.class, ExecutionContext.class));
strictEqCmpStringMH = eqCmpString;
strictEqCmpNumberMH = eqCmpNumber;
strictEqCmpBooleanMH = eqCmpBoolean;
strictEqCmpGenericMH = lookup.findStatic("strictEqCmpGeneric",
MethodType.methodType(boolean.class, Object.class, Object.class));
addSetupMH = lookup.findStatic("addSetup", MethodType.methodType(MethodHandle.class,
MutableCallSite.class, Object.class, Object.class, ExecutionContext.class));
relCmpSetupMH = lookup.findStatic("relCmpSetup", MethodType.methodType(MethodHandle.class,
MutableCallSite.class, RelationalOperator.class, Object.class, Object.class,
ExecutionContext.class));
eqCmpSetupMH = lookup.findStatic("eqCmpSetup", MethodType.methodType(MethodHandle.class,
MutableCallSite.class, Object.class, Object.class, ExecutionContext.class));
strictEqCmpSetupMH = lookup.findStatic("strictEqCmpSetup", MethodType.methodType(
MethodHandle.class, MutableCallSite.class, Object.class, Object.class));
}
@SuppressWarnings("unused")
private static CharSequence addString(CharSequence arg1, CharSequence arg2, ExecutionContext cx) {
return ScriptRuntime.add(arg1, arg2, cx);
}
@SuppressWarnings("unused")
private static Double addNumber(Number arg1, Number arg2) {
return arg1.doubleValue() + arg2.doubleValue();
}
@SuppressWarnings("unused")
private static Object addGeneric(Object arg1, Object arg2, ExecutionContext cx) {
return ScriptRuntime.add(arg1, arg2, cx);
}
@SuppressWarnings("unused")
private static int relCmpString(CharSequence arg1, CharSequence arg2) {
int c = arg1.toString().compareTo(arg2.toString());
return c < 0 ? 1 : 0;
}
@SuppressWarnings("unused")
private static int relCmpNumber(Number arg1, Number arg2) {
double nx = arg1.doubleValue();
double ny = arg2.doubleValue();
return Double.isNaN(nx) || Double.isNaN(ny) ? -1 : nx < ny ? 1 : 0;
}
@SuppressWarnings("unused")
private static int relCmpGeneric(Object arg1, Object arg2, RelationalOperator op,
ExecutionContext cx) {
return RelationalComparison(cx, arg1, arg2, op.leftFirst());
}
enum RelationalOperator {
LessThan, GreaterThan, LessThanEquals, GreaterThanEquals;
boolean leftFirst() {
switch (this) {
case LessThan:
return true;
case GreaterThan:
return false;
case LessThanEquals:
return false;
case GreaterThanEquals:
return true;
default:
throw new AssertionError();
}
}
}
@SuppressWarnings("unused")
private static boolean lessThan(int result) {
return result > 0;
}
@SuppressWarnings("unused")
private static boolean greaterThan(int result) {
return result > 0;
}
@SuppressWarnings("unused")
private static boolean lessThanEquals(int result) {
return result == 0;
}
@SuppressWarnings("unused")
private static boolean greaterThanEquals(int result) {
return result == 0;
}
@SuppressWarnings("unused")
private static boolean eqCmpString(CharSequence arg1, CharSequence arg2) {
return arg1.length() == arg2.length() && arg1.toString().equals(arg2.toString());
}
@SuppressWarnings("unused")
private static boolean eqCmpNumber(Number arg1, Number arg2) {
return arg1.doubleValue() == arg2.doubleValue();
}
@SuppressWarnings("unused")
private static boolean eqCmpBoolean(Boolean arg1, Boolean arg2) {
return arg1.booleanValue() == arg2.booleanValue();
}
@SuppressWarnings("unused")
private static boolean eqCmpGeneric(Object arg1, Object arg2, ExecutionContext cx) {
return EqualityComparison(cx, arg1, arg2);
}
@SuppressWarnings("unused")
private static boolean strictEqCmpGeneric(Object arg1, Object arg2) {
return StrictEqualityComparison(arg1, arg2);
}
private static boolean testString(Object arg1, Object arg2) {
return Type.isString(arg1) && Type.isString(arg2);
}
private static boolean testNumber(Object arg1, Object arg2) {
return Type.isNumber(arg1) && Type.isNumber(arg2);
}
private static boolean testBoolean(Object arg1, Object arg2) {
return Type.isBoolean(arg1) && Type.isBoolean(arg2);
}
private static Type getType(Object arg1, Object arg2) {
if (testString(arg1, arg2)) {
return Type.String;
}
if (testNumber(arg1, arg2)) {
return Type.Number;
}
if (testBoolean(arg1, arg2)) {
return Type.Boolean;
}
return Type.Object;
}
private static MethodHandle getTestFor(Type type) {
switch (type) {
case Boolean:
return testBooleanMH;
case Number:
return testNumberMH;
case String:
return testStringMH;
default:
return null;
}
}
@SuppressWarnings("unused")
private static MethodHandle addSetup(MutableCallSite callsite, Object arg1, Object arg2,
ExecutionContext cx) {
Type type = getType(arg1, arg2);
MethodHandle target;
if (type == Type.String) {
target = addStringMH;
} else if (type == Type.Number) {
target = addNumberMH;
} else {
target = null;
}
return setCallSiteTarget(callsite, target, getTestFor(type), addGenericMH);
}
@SuppressWarnings("unused")
private static MethodHandle relCmpSetup(MutableCallSite callsite, RelationalOperator op,
Object arg1, Object arg2, ExecutionContext cx) {
Type type = getType(arg1, arg2);
MethodHandle target;
if (type == Type.String) {
target = filterReturnValue(relCmpStringMH, op);
} else if (type == Type.Number) {
target = filterReturnValue(relCmpNumberMH, op);
} else {
target = null;
}
return setCallSiteTarget(callsite, target, getTestFor(type),
filterReturnValue(MethodHandles.insertArguments(relCmpGenericMH, 2, op), op));
}
private static MethodHandle filterReturnValue(MethodHandle mh, RelationalOperator op) {
return MethodHandles.filterReturnValue(mh, returnFilter(op));
}
private static MethodHandle returnFilter(RelationalOperator op) {
switch (op) {
case LessThan:
return lessThanMH;
case GreaterThan:
return greaterThanMH;
case LessThanEquals:
return lessThanEqualsMH;
case GreaterThanEquals:
return greaterThanEqualsMH;
default:
throw new AssertionError();
}
}
@SuppressWarnings("unused")
private static MethodHandle eqCmpSetup(MutableCallSite callsite, Object arg1, Object arg2,
ExecutionContext cx) {
Type type = getType(arg1, arg2);
MethodHandle target;
if (type == Type.String) {
target = eqCmpStringMH;
} else if (type == Type.Number) {
target = eqCmpNumberMH;
} else if (type == Type.Boolean) {
target = eqCmpBooleanMH;
} else {
target = null;
}
return setCallSiteTarget(callsite, target, getTestFor(type), eqCmpGenericMH);
}
@SuppressWarnings("unused")
private static MethodHandle strictEqCmpSetup(MutableCallSite callsite, Object arg1, Object arg2) {
Type type = getType(arg1, arg2);
MethodHandle target;
if (type == Type.String) {
target = strictEqCmpStringMH;
} else if (type == Type.Number) {
target = strictEqCmpNumberMH;
} else if (type == Type.Boolean) {
target = strictEqCmpBooleanMH;
} else {
target = null;
}
return setCallSiteTarget(callsite, target, getTestFor(type), strictEqCmpGenericMH);
}
private static MethodHandle setCallSiteTarget(MutableCallSite callsite, MethodHandle target,
MethodHandle test, MethodHandle generic) {
MethodHandle callSiteTarget;
if (target != null) {
target = target.asType(callsite.type());
if (test != null) {
MethodHandle fallback = createFallback(callsite, generic);
callSiteTarget = MethodHandles.guardWithTest(test, target, fallback);
} else {
callSiteTarget = target;
}
} else {
callSiteTarget = target = generic;
}
callsite.setTarget(callSiteTarget);
return target;
}
private static MethodHandle createFallback(MutableCallSite callsite, MethodHandle generic) {
// only perform fallback to generic for now
MethodHandle fallback = MethodHandles.insertArguments(switchToGenericMH, 0, callsite,
generic);
return setupCallSiteTarget(callsite.type(), fallback);
}
private static MethodHandle setupCallSiteTarget(MethodType type, MethodHandle target) {
return MethodHandles.foldArguments(MethodHandles.exactInvoker(type), target);
}
private static final MethodHandle switchToGenericMH;
static {
MethodLookup lookup = new MethodLookup(MethodHandles.lookup());
switchToGenericMH = lookup.findStatic("switchToGeneric", MethodType.methodType(
MethodHandle.class, MutableCallSite.class, MethodHandle.class));
}
@SuppressWarnings("unused")
private static MethodHandle switchToGeneric(MutableCallSite callsite, MethodHandle generic) {
callsite.setTarget(generic);
return generic;
}
private static final ConstantCallSite stackOverFlow_Add;
private static final ConstantCallSite stackOverFlow_Cmp;
private static final ConstantCallSite stackOverFlow_Eq;
private static final ConstantCallSite stackOverFlow_StrictEq;
private static final ConstantCallSite stackOverFlow_Call;
private static final ConstantCallSite stackOverFlow_Construct;
private static final ConstantCallSite stackOverFlow_Super;
private static final MethodHandle stackOverFlow_Concat;
static {
MethodLookup lookup = new MethodLookup(MethodHandles.lookup());
stackOverFlow_Add = new ConstantCallSite(lookup.findStatic("stackOverFlow_Add", MethodType
.methodType(Object.class, Object.class, Object.class, ExecutionContext.class)));
stackOverFlow_Cmp = new ConstantCallSite(lookup.findStatic("stackOverFlow_Cmp", MethodType
.methodType(int.class, Object.class, Object.class, ExecutionContext.class)));
stackOverFlow_Eq = new ConstantCallSite(lookup.findStatic("stackOverFlow_Eq", MethodType
.methodType(boolean.class, Object.class, Object.class, ExecutionContext.class)));
stackOverFlow_StrictEq = new ConstantCallSite(lookup.findStatic("stackOverFlow_StrictEq",
MethodType.methodType(boolean.class, Object.class, Object.class)));
stackOverFlow_Call = new ConstantCallSite(lookup.findStatic("stackOverFlow_Call",
MethodType.methodType(Object.class, Object.class, ExecutionContext.class,
Object.class, Object[].class)));
stackOverFlow_Construct = new ConstantCallSite(lookup.findStatic("stackOverFlow_Construct",
MethodType.methodType(ScriptObject.class, Object.class, ExecutionContext.class,
Object[].class)));
stackOverFlow_Super = new ConstantCallSite(lookup.findStatic("stackOverFlow_Super",
MethodType.methodType(ScriptObject.class, Constructor.class,
ExecutionContext.class, Constructor.class, Object[].class)));
stackOverFlow_Concat = lookup.findStatic("stackOverFlow_Concat",
MethodType.methodType(CharSequence.class));
}
@SuppressWarnings("unused")
private static Object stackOverFlow_Add(Object arg1, Object arg2, ExecutionContext cx) {
throw new StackOverflowError("bootstrap stack overflow");
}
@SuppressWarnings("unused")
private static int stackOverFlow_Cmp(Object arg1, Object arg2, ExecutionContext cx) {
throw new StackOverflowError("bootstrap stack overflow");
}
@SuppressWarnings("unused")
private static boolean stackOverFlow_Eq(Object arg1, Object arg2, ExecutionContext cx) {
throw new StackOverflowError("bootstrap stack overflow");
}
@SuppressWarnings("unused")
private static boolean stackOverFlow_StrictEq(Object arg1, Object arg2) {
throw new StackOverflowError("bootstrap stack overflow");
}
@SuppressWarnings("unused")
private static Object stackOverFlow_Call(Object fun, ExecutionContext cx, Object thisValue,
Object[] arguments) {
throw new StackOverflowError("bootstrap stack overflow");
}
@SuppressWarnings("unused")
private static ScriptObject stackOverFlow_Construct(Object constructor, ExecutionContext cx,
Object[] arguments) {
throw new StackOverflowError("bootstrap stack overflow");
}
@SuppressWarnings("unused")
private static ScriptObject stackOverFlow_Super(Constructor constructor, ExecutionContext cx,
Constructor newTarget, Object[] arguments) {
throw new StackOverflowError("bootstrap stack overflow");
}
@SuppressWarnings("unused")
private static CharSequence stackOverFlow_Concat() {
throw new StackOverflowError("bootstrap stack overflow");
}
/**
* The invokedynamic bootstrapping method.
*
* @param caller
* the caller lookup
* @param name
* the instruction name
* @param type
* the expected method type
* @return the invokedynamic call-site object
*/
public static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name,
MethodType type) {
// System.out.printf("caller: %s\n", caller);
// System.out.printf("name: %s\n", name);
// System.out.printf("type: %s\n", type);
try {
MutableCallSite callsite = new MutableCallSite(type);
MethodHandle setup;
switch (name) {
case CallNames.CALL:
setup = MethodHandles.insertArguments(callSetupMH, 0, callsite);
break;
case CallNames.CONSTRUCT:
setup = MethodHandles.insertArguments(constructSetupMH, 0, callsite);
break;
case CallNames.SUPER:
setup = MethodHandles.insertArguments(superSetupMH, 0, callsite);
break;
case CallNames.ADD:
setup = MethodHandles.insertArguments(addSetupMH, 0, callsite);
break;
case CallNames.EQ:
setup = MethodHandles.insertArguments(eqCmpSetupMH, 0, callsite);
break;
case CallNames.SHEQ:
setup = MethodHandles.insertArguments(strictEqCmpSetupMH, 0, callsite);
break;
case CallNames.LT:
setup = MethodHandles.insertArguments(relCmpSetupMH, 0, callsite,
RelationalOperator.LessThan);
break;
case CallNames.GT:
setup = MethodHandles.insertArguments(relCmpSetupMH, 0, callsite,
RelationalOperator.GreaterThan);
break;
case CallNames.LE:
setup = MethodHandles.insertArguments(relCmpSetupMH, 0, callsite,
RelationalOperator.LessThanEquals);
break;
case CallNames.GE:
setup = MethodHandles.insertArguments(relCmpSetupMH, 0, callsite,
RelationalOperator.GreaterThanEquals);
break;
case CallNames.CONCAT:
concatSetup(callsite, type);
return callsite;
default:
throw new IllegalArgumentException(name);
}
callsite.setTarget(setupCallSiteTarget(type, setup));
return callsite;
} catch (StackOverflowError e) {
switch (name) {
case CallNames.CALL:
return stackOverFlow_Call;
case CallNames.CONSTRUCT:
return stackOverFlow_Construct;
case CallNames.SUPER:
return stackOverFlow_Super;
case CallNames.CONCAT:
return new ConstantCallSite(MethodHandles.dropArguments(stackOverFlow_Concat, 0,
type.parameterArray()));
case CallNames.ADD:
return stackOverFlow_Add;
case CallNames.EQ:
return stackOverFlow_Eq;
case CallNames.SHEQ:
return stackOverFlow_StrictEq;
case CallNames.LT:
case CallNames.GT:
case CallNames.LE:
case CallNames.GE:
return stackOverFlow_Cmp;
default:
throw new IllegalArgumentException(name);
}
}
}
}