some cleanup

This commit is contained in:
Robert Muir 2016-06-15 12:32:24 -04:00
parent 6487940a79
commit d4db8c9c82
5 changed files with 94 additions and 21 deletions

View File

@ -235,15 +235,21 @@ public final class DefBootstrap {
/** /**
* Does a slow lookup for the operator * Does a slow lookup for the operator
*/ */
private MethodHandle lookup(int flavor, String name, Object[] args) throws Throwable { private MethodHandle lookup(Object[] args) throws Throwable {
switch(flavor) { switch(flavor) {
case UNARY_OPERATOR: case UNARY_OPERATOR:
case SHIFT_OPERATOR: case SHIFT_OPERATOR:
if ((flags & OPERATOR_COMPOUND_ASSIGNMENT) != 0) {
return lookupGeneric(); // XXX: optimize better.
}
// 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)
return DefMath.lookupUnary(args[0].getClass(), name); return DefMath.lookupUnary(args[0].getClass(), name);
case BINARY_OPERATOR: case BINARY_OPERATOR:
if ((flags & OPERATOR_COMPOUND_ASSIGNMENT) != 0) {
return lookupGeneric(); // XXX: optimize better.
}
if (args[0] == null || args[1] == null) { if (args[0] == null || args[1] == null) {
return DefMath.lookupGeneric(name); // can handle nulls, if supported return lookupGeneric(); // can handle nulls, if supported
} else { } else {
return DefMath.lookupBinary(args[0].getClass(), args[1].getClass(), name); return DefMath.lookupBinary(args[0].getClass(), args[1].getClass(), name);
} }
@ -251,6 +257,23 @@ public final class DefBootstrap {
} }
} }
private MethodHandle lookupGeneric() throws Throwable {
MethodHandle generic = DefMath.lookupGeneric(name);
if ((flags & OPERATOR_COMPOUND_ASSIGNMENT) != 0) {
assert flavor == BINARY_OPERATOR || flavor == SHIFT_OPERATOR;
// adapt dynamic cast to the generic method and the callsite's return value
MethodHandle cast = DYNAMIC_CAST.asType(MethodType.methodType(type().returnType(),
generic.type().returnType(),
generic.type().parameterType(0)));
// drop the RHS parameter
cast = MethodHandles.dropArguments(cast, 2, generic.type().parameterType(1));
// combine: f(x,y) -> g(f(x,y), x, y);
return MethodHandles.foldArguments(cast, generic);
} else {
return generic;
}
}
/** /**
* Called when a new type is encountered (or, when we have encountered more than {@code MAX_DEPTH} * Called when a new type is encountered (or, when we have encountered more than {@code MAX_DEPTH}
* types at this call site and given up on caching). * types at this call site and given up on caching).
@ -259,13 +282,13 @@ public final class DefBootstrap {
Object fallback(Object[] args) throws Throwable { Object fallback(Object[] args) throws Throwable {
if (initialized) { if (initialized) {
// caching defeated // caching defeated
MethodHandle generic = DefMath.lookupGeneric(name); MethodHandle generic = lookupGeneric();
setTarget(generic.asType(type())); setTarget(generic.asType(type()));
return generic.invokeWithArguments(args); return generic.invokeWithArguments(args);
} }
final MethodType type = type(); final MethodType type = type();
final MethodHandle target = lookup(flavor, name, args).asType(type); final MethodHandle target = lookup(args).asType(type);
final MethodHandle test; final MethodHandle test;
if (flavor == BINARY_OPERATOR || flavor == SHIFT_OPERATOR) { if (flavor == BINARY_OPERATOR || flavor == SHIFT_OPERATOR) {
@ -301,7 +324,7 @@ public final class DefBootstrap {
// very special cases, where even the receiver can be null (see JLS rules for string concat) // very special cases, where even the receiver can be null (see JLS rules for string concat)
// we wrap + with an NPE catcher, and use our generic method in that case. // we wrap + with an NPE catcher, and use our generic method in that case.
if (flavor == BINARY_OPERATOR && (flags & OPERATOR_ALLOWS_NULL) != 0) { if (flavor == BINARY_OPERATOR && (flags & OPERATOR_ALLOWS_NULL) != 0) {
MethodHandle handler = MethodHandles.dropArguments(DefMath.lookupGeneric(name).asType(type()), MethodHandle handler = MethodHandles.dropArguments(lookupGeneric().asType(type()),
0, 0,
NullPointerException.class); NullPointerException.class);
guard = MethodHandles.catchException(guard, NullPointerException.class, handler); guard = MethodHandles.catchException(guard, NullPointerException.class, handler);
@ -321,7 +344,6 @@ public final class DefBootstrap {
return leftObject.getClass() == clazz; return leftObject.getClass() == clazz;
} }
/** /**
* guard method for inline caching: checks the first argument is the same * guard method for inline caching: checks the first argument is the same
* as the cached first argument. * as the cached first argument.
@ -338,9 +360,23 @@ public final class DefBootstrap {
return leftObject.getClass() == left && rightObject.getClass() == right; return leftObject.getClass() == left && rightObject.getClass() == right;
} }
/**
* 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) {
if (lhs != null) {
return lhs.getClass().cast(returnValue);
} else {
return returnValue;
}
}
private static final MethodHandle CHECK_LHS; private static final MethodHandle CHECK_LHS;
private static final MethodHandle CHECK_RHS; private static final MethodHandle CHECK_RHS;
private static final MethodHandle CHECK_BOTH; private static final MethodHandle CHECK_BOTH;
private static final MethodHandle DYNAMIC_CAST;
private static final MethodHandle FALLBACK; private static final MethodHandle FALLBACK;
static { static {
final Lookup lookup = MethodHandles.lookup(); final Lookup lookup = MethodHandles.lookup();
@ -351,6 +387,8 @@ public final class DefBootstrap {
MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class)); MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class));
CHECK_BOTH = lookup.findStatic(lookup.lookupClass(), "checkBoth", CHECK_BOTH = lookup.findStatic(lookup.lookupClass(), "checkBoth",
MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class)); MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class));
DYNAMIC_CAST = lookup.findStatic(lookup.lookupClass(), "dynamicCast",
MethodType.methodType(Object.class, Object.class, Object.class));
FALLBACK = lookup.findVirtual(lookup.lookupClass(), "fallback", FALLBACK = lookup.findVirtual(lookup.lookupClass(), "fallback",
MethodType.methodType(Object.class, Object[].class)); MethodType.methodType(Object.class, Object[].class));
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
@ -416,6 +454,10 @@ 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) {
// we just don't need it anywhere else.
throw new BootstrapMethodError("This parameter is only supported for BINARY_OPERATORs");
}
return new MIC(name, type, flavor, flags); return new MIC(name, type, flavor, flags);
default: default:
throw new BootstrapMethodError("Illegal static bootstrap parameter for flavor: " + flavor); throw new BootstrapMethodError("Illegal static bootstrap parameter for flavor: " + flavor);

View File

@ -263,48 +263,54 @@ 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, Operation operation) { public void writeDynamicBinaryInstruction(Location location, Type returnType, Type lhs, Type rhs, Operation operation, boolean compoundAssignment) {
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(); 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, 0); invokeDynamic("mul", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
break; break;
case DIV: case DIV:
invokeDynamic("div", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, 0); invokeDynamic("div", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
break; break;
case REM: case REM:
invokeDynamic("rem", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, 0); invokeDynamic("rem", descriptor, DEF_BOOTSTRAP_HANDLE, 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,
// so we don't need a special NPE guard. // so we don't need a special NPE guard.
// otherwise, we need to allow nulls for possible string concatenation. // otherwise, we need to allow nulls for possible string concatenation.
boolean hasPrimitiveArg = lhs.clazz.isPrimitive() || rhs.clazz.isPrimitive(); boolean hasPrimitiveArg = lhs.clazz.isPrimitive() || rhs.clazz.isPrimitive();
int flags = hasPrimitiveArg ? 0 : DefBootstrap.OPERATOR_ALLOWS_NULL; if (!hasPrimitiveArg) {
flags |= DefBootstrap.OPERATOR_ALLOWS_NULL;
}
invokeDynamic("add", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags); invokeDynamic("add", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
break; break;
case SUB: case SUB:
invokeDynamic("sub", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, 0); invokeDynamic("sub", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
break; break;
case LSH: case LSH:
invokeDynamic("lsh", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.SHIFT_OPERATOR, 0); invokeDynamic("lsh", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.SHIFT_OPERATOR, flags);
break; break;
case USH: case USH:
invokeDynamic("ush", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.SHIFT_OPERATOR, 0); invokeDynamic("ush", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.SHIFT_OPERATOR, flags);
break; break;
case RSH: case RSH:
invokeDynamic("rsh", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.SHIFT_OPERATOR, 0); invokeDynamic("rsh", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.SHIFT_OPERATOR, flags);
break; break;
case BWAND: case BWAND:
invokeDynamic("and", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, 0); invokeDynamic("and", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
break; break;
case XOR: case XOR:
invokeDynamic("xor", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, 0); invokeDynamic("xor", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
break; break;
case BWOR: case BWOR:
invokeDynamic("or", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, 0); invokeDynamic("or", descriptor, DEF_BOOTSTRAP_HANDLE, DefBootstrap.BINARY_OPERATOR, flags);
break; break;
default: default:
throw location.createError(new IllegalStateException("Illegal tree structure.")); throw location.createError(new IllegalStateException("Illegal tree structure."));

View File

@ -612,7 +612,7 @@ public final class EBinary extends AExpression {
right.write(writer); right.write(writer);
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); writer.writeDynamicBinaryInstruction(location, actual, left.actual, right.actual, operation, false);
} else { } else {
writer.writeBinaryInstruction(location, actual, operation); writer.writeBinaryInstruction(location, actual, operation);
} }

View File

@ -351,7 +351,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); Definition.DEF_TYPE, Definition.DEF_TYPE, operation, true);
} else { } else {
writer.writeBinaryInstruction(location, promote, operation); writer.writeBinaryInstruction(location, promote, operation);
} }

View File

@ -406,4 +406,29 @@ public class AdditionTests extends ScriptTestCase {
assertEquals(15D, exec("double x = 5.0; x += 10; return x;")); assertEquals(15D, exec("double x = 5.0; x += 10; return x;"));
assertEquals(-5D, exec("double x = 5.0; x += -10; return x;")); assertEquals(-5D, exec("double x = 5.0; x += -10; return x;"));
} }
public void testDefCompoundAssignmentLHS() {
// byte
assertEquals((byte) 15, exec("def x = (byte)5; x += 10; return x;"));
assertEquals((byte) -5, exec("def x = (byte)5; x += -10; return x;"));
// short
assertEquals((short) 15, exec("def x = (short)5; x += 10; return x;"));
assertEquals((short) -5, exec("def x = (short)5; x += -10; return x;"));
// char
assertEquals((char) 15, exec("def x = (char)5; x += 10; return x;"));
assertEquals((char) 5, exec("def x = (char)10; x += -5; return x;"));
// int
assertEquals(15, exec("def x = 5; x += 10; return x;"));
assertEquals(-5, exec("def x = 5; x += -10; return x;"));
// long
assertEquals(15L, exec("def x = 5L; x += 10; return x;"));
assertEquals(-5L, exec("def x = 5L; x += -10; return x;"));
// float
assertEquals(15F, exec("def x = 5f; x += 10; return x;"));
assertEquals(-5F, exec("def x = 5f; x += -10; return x;"));
// double
assertEquals(15D, exec("def x = 5.0; x += 10; return x;"));
assertEquals(-5D, exec("def x = 5.0; x += -10; return x;"));
}
} }