/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.painless;
/** Tests for explicit casts */
public class CastTests extends ScriptTestCase {
/**
* Unary operator with explicit cast
*/
public void testUnaryOperator() {
assertEquals((byte)5, exec("long x = 5L; return (byte) (+x);"));
assertEquals((short)5, exec("long x = 5L; return (short) (+x);"));
assertEquals((char)5, exec("long x = 5L; return (char) (+x);"));
assertEquals(5, exec("long x = 5L; return (int) (+x);"));
assertEquals(5F, exec("long x = 5L; return (float) (+x);"));
assertEquals(5L, exec("long x = 5L; return (long) (+x);"));
assertEquals(5D, exec("long x = 5L; return (double) (+x);"));
}
/**
* Binary operators with explicit cast
*/
public void testBinaryOperator() {
assertEquals((byte)6, exec("long x = 5L; return (byte) (x + 1);"));
assertEquals((short)6, exec("long x = 5L; return (short) (x + 1);"));
assertEquals((char)6, exec("long x = 5L; return (char) (x + 1);"));
assertEquals(6, exec("long x = 5L; return (int) (x + 1);"));
assertEquals(6F, exec("long x = 5L; return (float) (x + 1);"));
assertEquals(6L, exec("long x = 5L; return (long) (x + 1);"));
assertEquals(6D, exec("long x = 5L; return (double) (x + 1);"));
}
/**
* Binary compound assignment with explicit cast
*/
public void testBinaryCompoundAssignment() {
assertEquals((byte)6, exec("long x = 5L; return (byte) (x += 1);"));
assertEquals((short)6, exec("long x = 5L; return (short) (x += 1);"));
assertEquals((char)6, exec("long x = 5L; return (char) (x += 1);"));
assertEquals(6, exec("long x = 5L; return (int) (x += 1);"));
assertEquals(6F, exec("long x = 5L; return (float) (x += 1);"));
assertEquals(6L, exec("long x = 5L; return (long) (x += 1);"));
assertEquals(6D, exec("long x = 5L; return (double) (x += 1);"));
}
/**
* Binary compound prefix with explicit cast
*/
public void testBinaryPrefix() {
assertEquals((byte)6, exec("long x = 5L; return (byte) (++x);"));
assertEquals((short)6, exec("long x = 5L; return (short) (++x);"));
assertEquals((char)6, exec("long x = 5L; return (char) (++x);"));
assertEquals(6, exec("long x = 5L; return (int) (++x);"));
assertEquals(6F, exec("long x = 5L; return (float) (++x);"));
assertEquals(6L, exec("long x = 5L; return (long) (++x);"));
assertEquals(6D, exec("long x = 5L; return (double) (++x);"));
}
/**
* Binary compound postifx with explicit cast
*/
public void testBinaryPostfix() {
assertEquals((byte)5, exec("long x = 5L; return (byte) (x++);"));
assertEquals((short)5, exec("long x = 5L; return (short) (x++);"));
assertEquals((char)5, exec("long x = 5L; return (char) (x++);"));
assertEquals(5, exec("long x = 5L; return (int) (x++);"));
assertEquals(5F, exec("long x = 5L; return (float) (x++);"));
assertEquals(5L, exec("long x = 5L; return (long) (x++);"));
assertEquals(5D, exec("long x = 5L; return (double) (x++);"));
}
/**
* Shift operators with explicit cast
*/
public void testShiftOperator() {
assertEquals((byte)10, exec("long x = 5L; return (byte) (x << 1);"));
assertEquals((short)10, exec("long x = 5L; return (short) (x << 1);"));
assertEquals((char)10, exec("long x = 5L; return (char) (x << 1);"));
assertEquals(10, exec("long x = 5L; return (int) (x << 1);"));
assertEquals(10F, exec("long x = 5L; return (float) (x << 1);"));
assertEquals(10L, exec("long x = 5L; return (long) (x << 1);"));
assertEquals(10D, exec("long x = 5L; return (double) (x << 1);"));
}
/**
* Shift compound assignment with explicit cast
*/
public void testShiftCompoundAssignment() {
assertEquals((byte)10, exec("long x = 5L; return (byte) (x <<= 1);"));
assertEquals((short)10, exec("long x = 5L; return (short) (x <<= 1);"));
assertEquals((char)10, exec("long x = 5L; return (char) (x <<= 1);"));
assertEquals(10, exec("long x = 5L; return (int) (x <<= 1);"));
assertEquals(10F, exec("long x = 5L; return (float) (x <<= 1);"));
assertEquals(10L, exec("long x = 5L; return (long) (x <<= 1);"));
assertEquals(10D, exec("long x = 5L; return (double) (x <<= 1);"));
}
/**
* Test that without a cast, we fail when conversions would narrow.
*/
public void testIllegalConversions() {
expectScriptThrows(ClassCastException.class, () -> {
exec("long x = 5L; int y = +x; return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("long x = 5L; int y = (x + x); return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("boolean x = true; int y = +x; return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("boolean x = true; int y = (x ^ false); return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("long x = 5L; boolean y = +x; return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("long x = 5L; boolean y = (x + x); return y");
});
}
/**
* Test that even with a cast, some things aren't allowed.
*/
public void testIllegalExplicitConversions() {
expectScriptThrows(ClassCastException.class, () -> {
exec("boolean x = true; int y = (int) +x; return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("boolean x = true; int y = (int) (x ^ false); return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("long x = 5L; boolean y = (boolean) +x; return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("long x = 5L; boolean y = (boolean) (x + x); return y");
});
}
/**
* Currently these do not adopt the return value, we issue a separate cast!
*/
public void testMethodCallDef() {
assertEquals(5, exec("def x = 5; return (int)x.longValue();"));
}
/**
* Currently these do not adopt the argument value, we issue a separate cast!
*/
public void testArgumentsDef() {
assertEquals(5, exec("def x = 5L; return (+(int)x);"));
assertEquals(6, exec("def x = 5; def y = 1L; return x + (int)y"));
assertEquals('b', exec("def x = 'abcdeg'; def y = 1L; x.charAt((int)y)"));
}
/**
* Unary operators adopt the return value
*/
public void testUnaryOperatorDef() {
assertEquals((byte)5, exec("def x = 5L; return (byte) (+x);"));
assertEquals((short)5, exec("def x = 5L; return (short) (+x);"));
assertEquals((char)5, exec("def x = 5L; return (char) (+x);"));
assertEquals(5, exec("def x = 5L; return (int) (+x);"));
assertEquals(5F, exec("def x = 5L; return (float) (+x);"));
assertEquals(5L, exec("def x = 5L; return (long) (+x);"));
assertEquals(5D, exec("def x = 5L; return (double) (+x);"));
}
/**
* Binary operators adopt the return value
*/
public void testBinaryOperatorDef() {
assertEquals((byte)6, exec("def x = 5L; return (byte) (x + 1);"));
assertEquals((short)6, exec("def x = 5L; return (short) (x + 1);"));
assertEquals((char)6, exec("def x = 5L; return (char) (x + 1);"));
assertEquals(6, exec("def x = 5L; return (int) (x + 1);"));
assertEquals(6F, exec("def x = 5L; return (float) (x + 1);"));
assertEquals(6L, exec("def x = 5L; return (long) (x + 1);"));
assertEquals(6D, exec("def x = 5L; return (double) (x + 1);"));
}
/**
* Binary operators don't yet adopt the return value with compound assignment
*/
public void testBinaryCompoundAssignmentDef() {
assertEquals((byte)6, exec("def x = 5L; return (byte) (x += 1);"));
assertEquals((short)6, exec("def x = 5L; return (short) (x += 1);"));
assertEquals((char)6, exec("def x = 5L; return (char) (x += 1);"));
assertEquals(6, exec("def x = 5L; return (int) (x += 1);"));
assertEquals(6F, exec("def x = 5L; return (float) (x += 1);"));
assertEquals(6L, exec("def x = 5L; return (long) (x += 1);"));
assertEquals(6D, exec("def x = 5L; return (double) (x += 1);"));
}
/**
* Binary operators don't yet adopt the return value with compound assignment
*/
public void testBinaryCompoundAssignmentPrefix() {
assertEquals((byte)6, exec("def x = 5L; return (byte) (++x);"));
assertEquals((short)6, exec("def x = 5L; return (short) (++x);"));
assertEquals((char)6, exec("def x = 5L; return (char) (++x);"));
assertEquals(6, exec("def x = 5L; return (int) (++x);"));
assertEquals(6F, exec("def x = 5L; return (float) (++x);"));
assertEquals(6L, exec("def x = 5L; return (long) (++x);"));
assertEquals(6D, exec("def x = 5L; return (double) (++x);"));
}
/**
* Binary operators don't yet adopt the return value with compound assignment
*/
public void testBinaryCompoundAssignmentPostfix() {
assertEquals((byte)5, exec("def x = 5L; return (byte) (x++);"));
assertEquals((short)5, exec("def x = 5L; return (short) (x++);"));
assertEquals((char)5, exec("def x = 5L; return (char) (x++);"));
assertEquals(5, exec("def x = 5L; return (int) (x++);"));
assertEquals(5F, exec("def x = 5L; return (float) (x++);"));
assertEquals(5L, exec("def x = 5L; return (long) (x++);"));
assertEquals(5D, exec("def x = 5L; return (double) (x++);"));
}
/**
* Shift operators adopt the return value
*/
public void testShiftOperatorDef() {
assertEquals((byte)10, exec("def x = 5L; return (byte) (x << 1);"));
assertEquals((short)10, exec("def x = 5L; return (short) (x << 1);"));
assertEquals((char)10, exec("def x = 5L; return (char) (x << 1);"));
assertEquals(10, exec("def x = 5L; return (int) (x << 1);"));
assertEquals(10F, exec("def x = 5L; return (float) (x << 1);"));
assertEquals(10L, exec("def x = 5L; return (long) (x << 1);"));
assertEquals(10D, exec("def x = 5L; return (double) (x << 1);"));
}
/**
* Shift operators don't yet adopt the return value with compound assignment
*/
public void testShiftCompoundAssignmentDef() {
assertEquals((byte)10, exec("def x = 5L; return (byte) (x <<= 1);"));
assertEquals((short)10, exec("def x = 5L; return (short) (x <<= 1);"));
assertEquals((char)10, exec("def x = 5L; return (char) (x <<= 1);"));
assertEquals(10, exec("def x = 5L; return (int) (x <<= 1);"));
assertEquals(10F, exec("def x = 5L; return (float) (x <<= 1);"));
assertEquals(10L, exec("def x = 5L; return (long) (x <<= 1);"));
assertEquals(10D, exec("def x = 5L; return (double) (x <<= 1);"));
}
/**
* Test that without a cast, we fail when conversions would narrow.
*/
public void testIllegalConversionsDef() {
expectScriptThrows(ClassCastException.class, () -> {
exec("def x = 5L; int y = +x; return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("def x = 5L; int y = (x + x); return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("def x = true; int y = +x; return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("def x = true; int y = (x ^ false); return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("def x = 5L; boolean y = +x; return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("def x = 5L; boolean y = (x + x); return y");
});
}
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)
*/
public void testIllegalExplicitConversionsDef() {
expectScriptThrows(ClassCastException.class, () -> {
exec("def x = true; int y = (int) +x; return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("def x = true; int y = (int) (x ^ false); return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("def x = 5L; boolean y = (boolean) +x; return y");
});
expectScriptThrows(ClassCastException.class, () -> {
exec("def x = 5L; boolean y = (boolean) (x + x); return y");
});
}
}