Fix certain bad casts in Painless due to boxing/unboxing. (#23282)
This commit is contained in:
parent
7475175957
commit
fac2d954e3
File diff suppressed because it is too large
Load Diff
|
@ -87,6 +87,7 @@ public final class Definition {
|
||||||
public static final Type CHAR_OBJ_TYPE = getType("Character");
|
public static final Type CHAR_OBJ_TYPE = getType("Character");
|
||||||
public static final Type OBJECT_TYPE = getType("Object");
|
public static final Type OBJECT_TYPE = getType("Object");
|
||||||
public static final Type DEF_TYPE = getType("def");
|
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 STRING_TYPE = getType("String");
|
||||||
public static final Type EXCEPTION_TYPE = getType("Exception");
|
public static final Type EXCEPTION_TYPE = getType("Exception");
|
||||||
public static final Type PATTERN_TYPE = getType("Pattern");
|
public static final Type PATTERN_TYPE = getType("Pattern");
|
||||||
|
@ -434,23 +435,23 @@ public final class Definition {
|
||||||
public final Type from;
|
public final Type from;
|
||||||
public final Type to;
|
public final Type to;
|
||||||
public final boolean explicit;
|
public final boolean explicit;
|
||||||
public final boolean unboxFrom;
|
public final Type unboxFrom;
|
||||||
public final boolean unboxTo;
|
public final Type unboxTo;
|
||||||
public final boolean boxFrom;
|
public final Type boxFrom;
|
||||||
public final boolean boxTo;
|
public final Type boxTo;
|
||||||
|
|
||||||
public Cast(final Type from, final Type to, final boolean explicit) {
|
public Cast(final Type from, final Type to, final boolean explicit) {
|
||||||
this.from = from;
|
this.from = from;
|
||||||
this.to = to;
|
this.to = to;
|
||||||
this.explicit = explicit;
|
this.explicit = explicit;
|
||||||
this.unboxFrom = false;
|
this.unboxFrom = null;
|
||||||
this.unboxTo = false;
|
this.unboxTo = null;
|
||||||
this.boxFrom = false;
|
this.boxFrom = null;
|
||||||
this.boxTo = false;
|
this.boxTo = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cast(final Type from, final Type to, final boolean explicit,
|
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.from = from;
|
||||||
this.to = to;
|
this.to = to;
|
||||||
this.explicit = explicit;
|
this.explicit = explicit;
|
||||||
|
|
|
@ -131,51 +131,48 @@ public final class MethodWriter extends GeneratorAdapter {
|
||||||
|
|
||||||
public void writeCast(final Cast cast) {
|
public void writeCast(final Cast cast) {
|
||||||
if (cast != null) {
|
if (cast != null) {
|
||||||
final Type from = cast.from;
|
if (cast.from.sort == Sort.CHAR && cast.to.sort == Sort.STRING) {
|
||||||
final Type to = cast.to;
|
|
||||||
|
|
||||||
if (from.sort == Sort.CHAR && to.sort == Sort.STRING) {
|
|
||||||
invokeStatic(UTILITY_TYPE, CHAR_TO_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);
|
invokeStatic(UTILITY_TYPE, STRING_TO_CHAR);
|
||||||
} else if (cast.unboxFrom) {
|
} else if (cast.unboxFrom != null) {
|
||||||
if (from.sort == Sort.DEF) {
|
unbox(cast.unboxFrom.type);
|
||||||
|
writeCast(cast.from, cast.to);
|
||||||
|
} else if (cast.unboxTo != null) {
|
||||||
|
if (cast.from.sort == Sort.DEF) {
|
||||||
if (cast.explicit) {
|
if (cast.explicit) {
|
||||||
if (to.sort == Sort.BOOL) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
|
if (cast.to.sort == Sort.BOOL_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
|
||||||
else if (to.sort == Sort.BYTE) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_EXPLICIT);
|
else if (cast.to.sort == Sort.BYTE_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_EXPLICIT);
|
||||||
else if (to.sort == Sort.SHORT) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_EXPLICIT);
|
else if (cast.to.sort == Sort.SHORT_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_EXPLICIT);
|
||||||
else if (to.sort == Sort.CHAR) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_EXPLICIT);
|
else if (cast.to.sort == Sort.CHAR_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_EXPLICIT);
|
||||||
else if (to.sort == Sort.INT) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_EXPLICIT);
|
else if (cast.to.sort == Sort.INT_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_EXPLICIT);
|
||||||
else if (to.sort == Sort.LONG) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_EXPLICIT);
|
else if (cast.to.sort == Sort.LONG_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_EXPLICIT);
|
||||||
else if (to.sort == Sort.FLOAT) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_EXPLICIT);
|
else if (cast.to.sort == Sort.FLOAT_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_EXPLICIT);
|
||||||
else if (to.sort == Sort.DOUBLE) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_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 throw new IllegalStateException("Illegal tree structure.");
|
||||||
} else {
|
} else {
|
||||||
if (to.sort == Sort.BOOL) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
|
if (cast.to.sort == Sort.BOOL_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
|
||||||
else if (to.sort == Sort.BYTE) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_IMPLICIT);
|
else if (cast.to.sort == Sort.BYTE_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_IMPLICIT);
|
||||||
else if (to.sort == Sort.SHORT) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_IMPLICIT);
|
else if (cast.to.sort == Sort.SHORT_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_IMPLICIT);
|
||||||
else if (to.sort == Sort.CHAR) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_IMPLICIT);
|
else if (cast.to.sort == Sort.CHAR_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_IMPLICIT);
|
||||||
else if (to.sort == Sort.INT) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_IMPLICIT);
|
else if (cast.to.sort == Sort.INT_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_IMPLICIT);
|
||||||
else if (to.sort == Sort.LONG) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_IMPLICIT);
|
else if (cast.to.sort == Sort.LONG_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_IMPLICIT);
|
||||||
else if (to.sort == Sort.FLOAT) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_IMPLICIT);
|
else if (cast.to.sort == Sort.FLOAT_OBJ) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_IMPLICIT);
|
||||||
else if (to.sort == Sort.DOUBLE) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_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 throw new IllegalStateException("Illegal tree structure.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unbox(from.type);
|
writeCast(cast.from, cast.to);
|
||||||
writeCast(from, to);
|
unbox(cast.unboxTo.type);
|
||||||
}
|
}
|
||||||
} else if (cast.unboxTo) {
|
} else if (cast.boxFrom != null) {
|
||||||
writeCast(from, to);
|
box(cast.boxFrom.type);
|
||||||
unbox(to.type);
|
writeCast(cast.from, cast.to);
|
||||||
} else if (cast.boxFrom) {
|
} else if (cast.boxTo != null) {
|
||||||
box(from.type);
|
writeCast(cast.from, cast.to);
|
||||||
writeCast(from, to);
|
box(cast.boxTo.type);
|
||||||
} else if (cast.boxTo) {
|
|
||||||
writeCast(from, to);
|
|
||||||
box(to.type);
|
|
||||||
} else {
|
} else {
|
||||||
writeCast(from, to);
|
writeCast(cast.from, cast.to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
* Test that even with a cast, some things aren't allowed.
|
||||||
* (stuff that methodhandles explicitCastArguments would otherwise allow)
|
* (stuff that methodhandles explicitCastArguments would otherwise allow)
|
||||||
|
|
|
@ -54,6 +54,18 @@ public class FunctionTests extends ScriptTestCase {
|
||||||
assertThat(expected.getMessage(), containsString("Cannot generate an empty function"));
|
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() {
|
public void testDuplicates() {
|
||||||
Exception expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
Exception expected = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||||
exec("void test(int x) {x = 2;} void test(def y) {y = 3;} test()");
|
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"));
|
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() {
|
public void testInfiniteLoop() {
|
||||||
Error expected = expectScriptThrows(PainlessError.class, () -> {
|
Error expected = expectScriptThrows(PainlessError.class, () -> {
|
||||||
exec("void test() {boolean x = true; while (x) {}} test()");
|
exec("void test() {boolean x = true; while (x) {}} test()");
|
||||||
|
|
Loading…
Reference in New Issue