Fix certain bad casts in Painless due to boxing/unboxing. (#23282)

This commit is contained in:
Jack Conradson 2017-02-21 10:23:27 -08:00 committed by GitHub
parent 7475175957
commit fac2d954e3
5 changed files with 330 additions and 239 deletions

View File

@ -87,6 +87,7 @@ public final class Definition {
public static final Type CHAR_OBJ_TYPE = getType("Character");
public static final Type OBJECT_TYPE = getType("Object");
public static final Type DEF_TYPE = getType("def");
public static final Type NUMBER_TYPE = getType("Number");
public static final Type STRING_TYPE = getType("String");
public static final Type EXCEPTION_TYPE = getType("Exception");
public static final Type PATTERN_TYPE = getType("Pattern");
@ -434,23 +435,23 @@ public final class Definition {
public final Type from;
public final Type to;
public final boolean explicit;
public final boolean unboxFrom;
public final boolean unboxTo;
public final boolean boxFrom;
public final boolean boxTo;
public final Type unboxFrom;
public final Type unboxTo;
public final Type boxFrom;
public final Type boxTo;
public Cast(final Type from, final Type to, final boolean explicit) {
this.from = from;
this.to = to;
this.explicit = explicit;
this.unboxFrom = false;
this.unboxTo = false;
this.boxFrom = false;
this.boxTo = false;
this.unboxFrom = null;
this.unboxTo = null;
this.boxFrom = null;
this.boxTo = null;
}
public Cast(final Type from, final Type to, final boolean explicit,
final boolean unboxFrom, final boolean unboxTo, final boolean boxFrom, final boolean boxTo) {
final Type unboxFrom, final Type unboxTo, final Type boxFrom, final Type boxTo) {
this.from = from;
this.to = to;
this.explicit = explicit;

View File

@ -131,51 +131,48 @@ public final class MethodWriter extends GeneratorAdapter {
public void writeCast(final Cast cast) {
if (cast != null) {
final Type from = cast.from;
final Type to = cast.to;
if (from.sort == Sort.CHAR && to.sort == Sort.STRING) {
if (cast.from.sort == Sort.CHAR && cast.to.sort == Sort.STRING) {
invokeStatic(UTILITY_TYPE, CHAR_TO_STRING);
} else if (from.sort == Sort.STRING && to.sort == Sort.CHAR) {
} else if (cast.from.sort == Sort.STRING && cast.to.sort == Sort.CHAR) {
invokeStatic(UTILITY_TYPE, STRING_TO_CHAR);
} else if (cast.unboxFrom) {
if (from.sort == Sort.DEF) {
} else if (cast.unboxFrom != null) {
unbox(cast.unboxFrom.type);
writeCast(cast.from, cast.to);
} else if (cast.unboxTo != null) {
if (cast.from.sort == Sort.DEF) {
if (cast.explicit) {
if (to.sort == Sort.BOOL) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
else if (to.sort == Sort.BYTE) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_EXPLICIT);
else if (to.sort == Sort.SHORT) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_EXPLICIT);
else if (to.sort == Sort.CHAR) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_EXPLICIT);
else if (to.sort == Sort.INT) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_EXPLICIT);
else if (to.sort == Sort.LONG) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_EXPLICIT);
else if (to.sort == Sort.FLOAT) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_EXPLICIT);
else if (to.sort == Sort.DOUBLE) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_EXPLICIT);
if (cast.to.sort == Sort.BOOL_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
else if (cast.to.sort == Sort.BYTE_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_EXPLICIT);
else if (cast.to.sort == Sort.SHORT_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_EXPLICIT);
else if (cast.to.sort == Sort.CHAR_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_EXPLICIT);
else if (cast.to.sort == Sort.INT_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_EXPLICIT);
else if (cast.to.sort == Sort.LONG_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_EXPLICIT);
else if (cast.to.sort == Sort.FLOAT_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_EXPLICIT);
else if (cast.to.sort == Sort.DOUBLE_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_EXPLICIT);
else throw new IllegalStateException("Illegal tree structure.");
} else {
if (to.sort == Sort.BOOL) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
else if (to.sort == Sort.BYTE) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_IMPLICIT);
else if (to.sort == Sort.SHORT) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_IMPLICIT);
else if (to.sort == Sort.CHAR) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_IMPLICIT);
else if (to.sort == Sort.INT) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_IMPLICIT);
else if (to.sort == Sort.LONG) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_IMPLICIT);
else if (to.sort == Sort.FLOAT) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_IMPLICIT);
else if (to.sort == Sort.DOUBLE) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_IMPLICIT);
if (cast.to.sort == Sort.BOOL_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
else if (cast.to.sort == Sort.BYTE_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_IMPLICIT);
else if (cast.to.sort == Sort.SHORT_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_IMPLICIT);
else if (cast.to.sort == Sort.CHAR_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_IMPLICIT);
else if (cast.to.sort == Sort.INT_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_IMPLICIT);
else if (cast.to.sort == Sort.LONG_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_IMPLICIT);
else if (cast.to.sort == Sort.FLOAT_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_IMPLICIT);
else if (cast.to.sort == Sort.DOUBLE_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_IMPLICIT);
else throw new IllegalStateException("Illegal tree structure.");
}
} else {
unbox(from.type);
writeCast(from, to);
writeCast(cast.from, cast.to);
unbox(cast.unboxTo.type);
}
} else if (cast.unboxTo) {
writeCast(from, to);
unbox(to.type);
} else if (cast.boxFrom) {
box(from.type);
writeCast(from, to);
} else if (cast.boxTo) {
writeCast(from, to);
box(to.type);
} else if (cast.boxFrom != null) {
box(cast.boxFrom.type);
writeCast(cast.from, cast.to);
} else if (cast.boxTo != null) {
writeCast(cast.from, cast.to);
box(cast.boxTo.type);
} else {
writeCast(from, to);
writeCast(cast.from, cast.to);
}
}
}

View File

@ -286,6 +286,20 @@ public class CastTests extends ScriptTestCase {
});
}
public void testUnboxMethodParameters() {
assertEquals('a', exec("'a'.charAt(Integer.valueOf(0))"));
}
public void testIllegalCastInMethodArgument() {
assertEquals('a', exec("'a'.charAt(0)"));
Exception e = expectScriptThrows(ClassCastException.class, () -> exec("'a'.charAt(0L)"));
assertEquals("Cannot cast from [long] to [int].", e.getMessage());
e = expectScriptThrows(ClassCastException.class, () -> exec("'a'.charAt(0.0f)"));
assertEquals("Cannot cast from [float] to [int].", e.getMessage());
e = expectScriptThrows(ClassCastException.class, () -> exec("'a'.charAt(0.0d)"));
assertEquals("Cannot cast from [double] to [int].", e.getMessage());
}
/**
* Test that even with a cast, some things aren't allowed.
* (stuff that methodhandles explicitCastArguments would otherwise allow)

View File

@ -54,6 +54,18 @@ public class FunctionTests extends ScriptTestCase {
assertThat(expected.getMessage(), containsString("Cannot generate an empty function"));
}
public void testReturnsAreUnboxedIfNeeded() {
assertEquals((byte) 5, exec("byte get() {Byte.valueOf(5)} get()"));
assertEquals((short) 5, exec("short get() {Byte.valueOf(5)} get()"));
assertEquals(5, exec("int get() {Byte.valueOf(5)} get()"));
assertEquals((short) 5, exec("short get() {Short.valueOf(5)} get()"));
assertEquals(5, exec("int get() {Integer.valueOf(5)} get()"));
assertEquals(5.0f, exec("float get() {Float.valueOf(5)} get()"));
assertEquals(5.0d, exec("double get() {Float.valueOf(5)} get()"));
assertEquals(5.0d, exec("double get() {Double.valueOf(5)} get()"));
assertEquals(true, exec("boolean get() {Boolean.TRUE} get()"));
}
public void testDuplicates() {
Exception expected = expectScriptThrows(IllegalArgumentException.class, () -> {
exec("void test(int x) {x = 2;} void test(def y) {y = 3;} test()");
@ -61,6 +73,15 @@ public class FunctionTests extends ScriptTestCase {
assertThat(expected.getMessage(), containsString("Duplicate functions"));
}
public void testBadCastFromMethod() {
Exception e = expectScriptThrows(ClassCastException.class, () -> exec("int get() {5L} get()"));
assertEquals("Cannot cast from [long] to [int].", e.getMessage());
e = expectScriptThrows(ClassCastException.class, () -> exec("int get() {5.1f} get()"));
assertEquals("Cannot cast from [float] to [int].", e.getMessage());
e = expectScriptThrows(ClassCastException.class, () -> exec("int get() {5.1d} get()"));
assertEquals("Cannot cast from [double] to [int].", e.getMessage());
}
public void testInfiniteLoop() {
Error expected = expectScriptThrows(PainlessError.class, () -> {
exec("void test() {boolean x = true; while (x) {}} test()");