/* * 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"); }); } }