Skip to content

Commit 13b1f19

Browse files
authored
[Painless] Add extensive tests for def to primitive casts (#36455)
This adds tests for each possible cast of def to a primitive type both implicit and explicit. This also fixes a minor bug where we were only checking the type of a def to be Number in some explicit casts. This does not work because it allows possible unintended casts from BigInteger and BigDecimal to primitive types since they both extend Number but are not included as part of the Painless casting model.
1 parent 6481f2e commit 13b1f19

File tree

3 files changed

+449
-17
lines changed

3 files changed

+449
-17
lines changed

modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java

Lines changed: 109 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -622,23 +622,41 @@ static MethodHandle lookupIterator(Class<?> receiverClass) {
622622
// Conversion methods for def to primitive types.
623623

624624
public static boolean defToboolean(final Object value) {
625-
return (boolean)value;
625+
if (value instanceof Boolean) {
626+
return (boolean) value;
627+
} else {
628+
throw new ClassCastException(
629+
"cannot cast def [" + PainlessLookupUtility.typeToUnboxedType(value.getClass()).getCanonicalName() + "] to boolean");
630+
}
626631
}
627632

628633
public static byte defTobyteImplicit(final Object value) {
629-
return (byte)value;
634+
if (value instanceof Byte) {
635+
return (byte)value;
636+
} else {
637+
throw new ClassCastException("cannot implicitly cast " +
638+
"def [" + PainlessLookupUtility.typeToUnboxedType(value.getClass()).getCanonicalName() + "] to byte");
639+
}
630640
}
631641

632642
public static short defToshortImplicit(final Object value) {
633643
if (value instanceof Byte) {
634644
return (byte)value;
635-
} else {
645+
} else if (value instanceof Short) {
636646
return (short)value;
647+
} else {
648+
throw new ClassCastException("cannot implicitly cast " +
649+
"def [" + PainlessLookupUtility.typeToUnboxedType(value.getClass()).getCanonicalName() + "] to short");
637650
}
638651
}
639652

640653
public static char defTocharImplicit(final Object value) {
641-
return (char)value;
654+
if (value instanceof Character) {
655+
return (char)value;
656+
} else {
657+
throw new ClassCastException("cannot implicitly cast " +
658+
"def [" + PainlessLookupUtility.typeToUnboxedType(value.getClass()).getCanonicalName() + "] to char");
659+
}
642660
}
643661

644662
public static int defTointImplicit(final Object value) {
@@ -648,8 +666,11 @@ public static int defTointImplicit(final Object value) {
648666
return (short)value;
649667
} else if (value instanceof Character) {
650668
return (char)value;
651-
} else {
669+
} else if (value instanceof Integer) {
652670
return (int)value;
671+
} else {
672+
throw new ClassCastException("cannot implicitly cast " +
673+
"def [" + PainlessLookupUtility.typeToUnboxedType(value.getClass()).getCanonicalName() + "] to int");
653674
}
654675
}
655676

@@ -662,8 +683,12 @@ public static long defTolongImplicit(final Object value) {
662683
return (char)value;
663684
} else if (value instanceof Integer) {
664685
return (int)value;
665-
} else {
686+
} else if (value instanceof Long) {
666687
return (long)value;
688+
} else {
689+
throw new ClassCastException(
690+
"cannot implicitly cast " +
691+
"def [" + PainlessLookupUtility.typeToUnboxedType(value.getClass()).getCanonicalName() + "] to long");
667692
}
668693
}
669694

@@ -678,8 +703,12 @@ public static float defTofloatImplicit(final Object value) {
678703
return (int)value;
679704
} else if (value instanceof Long) {
680705
return (long)value;
681-
} else {
706+
} else if (value instanceof Float) {
682707
return (float)value;
708+
} else {
709+
throw new ClassCastException(
710+
"cannot implicitly cast " +
711+
"def [" + PainlessLookupUtility.typeToUnboxedType(value.getClass()).getCanonicalName() + "] to float");
683712
}
684713
}
685714

@@ -696,64 +725,129 @@ public static double defTodoubleImplicit(final Object value) {
696725
return (long)value;
697726
} else if (value instanceof Float) {
698727
return (float)value;
699-
} else {
728+
} else if (value instanceof Double) {
700729
return (double)value;
730+
} else {
731+
throw new ClassCastException("cannot implicitly cast def [" + value.getClass().getCanonicalName() + "] to double");
701732
}
702733
}
703734

704735
public static byte defTobyteExplicit(final Object value) {
705736
if (value instanceof Character) {
706737
return (byte)(char)value;
707-
} else {
738+
} else if (
739+
value instanceof Byte ||
740+
value instanceof Short ||
741+
value instanceof Integer ||
742+
value instanceof Long ||
743+
value instanceof Float ||
744+
value instanceof Double
745+
) {
708746
return ((Number)value).byteValue();
747+
} else {
748+
throw new ClassCastException("cannot explicitly cast def [" + value.getClass().getCanonicalName() + "] to byte");
709749
}
710750
}
711751

712752
public static short defToshortExplicit(final Object value) {
713753
if (value instanceof Character) {
714754
return (short)(char)value;
715-
} else {
755+
} else if (
756+
value instanceof Byte ||
757+
value instanceof Short ||
758+
value instanceof Integer ||
759+
value instanceof Long ||
760+
value instanceof Float ||
761+
value instanceof Double
762+
) {
716763
return ((Number)value).shortValue();
764+
} else {
765+
throw new ClassCastException("cannot explicitly cast def [" + value.getClass().getCanonicalName() + "] to short");
717766
}
718767
}
719768

720769
public static char defTocharExplicit(final Object value) {
721770
if (value instanceof Character) {
722771
return (char)value;
723-
} else {
772+
} else if (
773+
value instanceof Byte ||
774+
value instanceof Short ||
775+
value instanceof Integer ||
776+
value instanceof Long ||
777+
value instanceof Float ||
778+
value instanceof Double
779+
) {
724780
return (char)((Number)value).intValue();
781+
} else {
782+
throw new ClassCastException("cannot explicitly cast def [" + value.getClass().getCanonicalName() + "] to char");
725783
}
726784
}
727785

728786
public static int defTointExplicit(final Object value) {
729787
if (value instanceof Character) {
730788
return (char)value;
731-
} else {
789+
} else if (
790+
value instanceof Byte ||
791+
value instanceof Short ||
792+
value instanceof Integer ||
793+
value instanceof Long ||
794+
value instanceof Float ||
795+
value instanceof Double
796+
) {
732797
return ((Number)value).intValue();
798+
} else {
799+
throw new ClassCastException("cannot explicitly cast def [" + value.getClass().getCanonicalName() + "] to int");
733800
}
734801
}
735802

736803
public static long defTolongExplicit(final Object value) {
737804
if (value instanceof Character) {
738805
return (char)value;
739-
} else {
806+
} else if (
807+
value instanceof Byte ||
808+
value instanceof Short ||
809+
value instanceof Integer ||
810+
value instanceof Long ||
811+
value instanceof Float ||
812+
value instanceof Double
813+
) {
740814
return ((Number)value).longValue();
815+
} else {
816+
throw new ClassCastException("cannot explicitly cast def [" + value.getClass().getCanonicalName() + "] to long");
741817
}
742818
}
743819

744820
public static float defTofloatExplicit(final Object value) {
745821
if (value instanceof Character) {
746822
return (char)value;
747-
} else {
823+
} else if (
824+
value instanceof Byte ||
825+
value instanceof Short ||
826+
value instanceof Integer ||
827+
value instanceof Long ||
828+
value instanceof Float ||
829+
value instanceof Double
830+
) {
748831
return ((Number)value).floatValue();
832+
} else {
833+
throw new ClassCastException("cannot explicitly cast def [" + value.getClass().getCanonicalName() + "] to float");
749834
}
750835
}
751836

752837
public static double defTodoubleExplicit(final Object value) {
753838
if (value instanceof Character) {
754839
return (char)value;
755-
} else {
840+
} else if (
841+
value instanceof Byte ||
842+
value instanceof Short ||
843+
value instanceof Integer ||
844+
value instanceof Long ||
845+
value instanceof Float ||
846+
value instanceof Double
847+
) {
756848
return ((Number)value).doubleValue();
849+
} else {
850+
throw new ClassCastException("cannot explicitly cast def [" + value.getClass().getCanonicalName() + "] to double");
757851
}
758852
}
759853

modules/lang-painless/src/test/java/org/elasticsearch/painless/BasicExpressionTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,12 @@ public void testIllegalDefCast() {
115115
Exception exception = expectScriptThrows(ClassCastException.class, () -> {
116116
exec("def x = 1.0; int y = x; return y;");
117117
});
118-
assertTrue(exception.getMessage().contains("cannot be cast"));
118+
assertTrue(exception.getMessage().contains("cannot implicitly cast"));
119119

120120
exception = expectScriptThrows(ClassCastException.class, () -> {
121121
exec("def x = (short)1; byte y = x; return y;");
122122
});
123-
assertTrue(exception.getMessage().contains("cannot be cast"));
123+
assertTrue(exception.getMessage().contains("cannot implicitly cast"));
124124
}
125125

126126
public void testCat() {

0 commit comments

Comments
 (0)