content: /*
   * Licensed 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 io.airlift.bytecode.expression;
  
  import com.google.common.primitives.Primitives;
  import org.junit.jupiter.api.Test;
  
  import static io.airlift.bytecode.expression.BytecodeExpressionAssertions.assertBytecodeExpression;
  import static io.airlift.bytecode.expression.BytecodeExpressions.getStatic;
  import static org.assertj.core.api.Assertions.assertThat;
  import static org.assertj.core.api.Assertions.assertThatThrownBy;
  
  public class TestCastBytecodeExpression
  {
      public static final Object OBJECT_FIELD = "foo";
      public static final boolean BOOLEAN_FIELD = true;
      public static final byte BYTE_FIELD = 99;
      public static final char CHAR_FIELD = 11;
      public static final short SHORT_FIELD = 22;
      public static final int INT_FIELD = 33;
      public static final long LONG_FIELD = 44;
      public static final float FLOAT_FIELD = 3.3f;
      public static final double DOUBLE_FIELD = 4.4;
  
      @Test
      void testDownCastObject()
              throws Exception
      {
          assertBytecodeExpression(getStatic(getClass(), "OBJECT_FIELD").cast(String.class).invoke("length", int.class),
                  ((String) OBJECT_FIELD).length(),
                  "((String) " + getClass().getSimpleName() + ".OBJECT_FIELD).length()");
      }
  
      @Test
      void testCastBetweenObjectAndPrimitive()
              throws Exception
      {
          assertCast(getStatic(getClass(), "INT_FIELD"), 33, Object.class);
          assertCast(getStatic(getClass(), "INT_FIELD").cast(Object.class), 33, int.class);
      }
  
      @Test
      void testInvalildCast()
      {
          // Cast between a boxed primitive and a primitive that are different
          assertInvalidCast(getStatic(getClass(), "INT_FIELD"), Double.class);
          assertInvalidCast(getStatic(getClass(), "INT_FIELD").cast(Integer.class), double.class);
  
          // Cast between two different boxed primitives
          assertInvalidCast(getStatic(getClass(), "INT_FIELD").cast(Integer.class), Double.class);
  
          // Cast between a primitive and an object (that is not java.lang.Object)
          assertInvalidCast(getStatic(getClass(), "OBJECT_FIELD").cast(String.class), double.class);
          assertInvalidCast(getStatic(getClass(), "INT_FIELD"), String.class);
      }
  
      @Test
      void testCastPrimitive()
              throws Exception
      {
          assertPrimitiveCast("BOOLEAN_FIELD", boolean.class, BOOLEAN_FIELD);
  
          assertPrimitiveCast("BYTE_FIELD", byte.class, BYTE_FIELD);
          assertPrimitiveCast("BYTE_FIELD", char.class, (char) BYTE_FIELD);
          assertPrimitiveCast("BYTE_FIELD", short.class, (short) BYTE_FIELD);
          assertPrimitiveCast("BYTE_FIELD", int.class, (int) BYTE_FIELD);
          assertPrimitiveCast("BYTE_FIELD", long.class, (long) BYTE_FIELD);
          assertPrimitiveCast("BYTE_FIELD", float.class, (float) BYTE_FIELD);
          assertPrimitiveCast("BYTE_FIELD", double.class, (double) BYTE_FIELD);
  
          assertPrimitiveCast("CHAR_FIELD", byte.class, (byte) CHAR_FIELD);
          assertPrimitiveCast("CHAR_FIELD", char.class, CHAR_FIELD);
          assertPrimitiveCast("CHAR_FIELD", short.class, (short) CHAR_FIELD);
          assertPrimitiveCast("CHAR_FIELD", int.class, (int) CHAR_FIELD);
          assertPrimitiveCast("CHAR_FIELD", long.class, (long) CHAR_FIELD);
          assertPrimitiveCast("CHAR_FIELD", float.class, (float) CHAR_FIELD);
          assertPrimitiveCast("CHAR_FIELD", double.class, (double) CHAR_FIELD);
  
          assertPrimitiveCast("SHORT_FIELD", byte.class, (byte) SHORT_FIELD);
          assertPrimitiveCast("SHORT_FIELD", char.class, (char) SHORT_FIELD);
          assertPrimitiveCast("SHORT_FIELD", short.class, SHORT_FIELD);
          assertPrimitiveCast("SHORT_FIELD", int.class, (int) SHORT_FIELD);
          assertPrimitiveCast("SHORT_FIELD", long.class, (long) SHORT_FIELD);
          assertPrimitiveCast("SHORT_FIELD", float.class, (float) SHORT_FIELD);
          assertPrimitiveCast("SHORT_FIELD", double.class, (double) SHORT_FIELD);
  
          assertPrimitiveCast("INT_FIELD", byte.class, (byte) INT_FIELD);
          assertPrimitiveCast("INT_FIELD", char.class, (char) INT_FIELD);
          assertPrimitiveCast("INT_FIELD", short.class, (short) INT_FIELD);
          assertPrimitiveCast("INT_FIELD", int.class, INT_FIELD);
          assertPrimitiveCast("INT_FIELD", long.class, (long) INT_FIELD);
          assertPrimitiveCast("INT_FIELD", float.class, (float) INT_FIELD);
          assertPrimitiveCast("INT_FIELD", double.class, (double) INT_FIELD);
  
          assertPrimitiveCast("LONG_FIELD", byte.class, (byte) LONG_FIELD);
          assertPrimitiveCast("LONG_FIELD", char.class, (char) LONG_FIELD);
          assertPrimitiveCast("LONG_FIELD", short.class, (short) LONG_FIELD);
          assertPrimitiveCast("LONG_FIELD", int.class, (int) LONG_FIELD);
          assertPrimitiveCast("LONG_FIELD", long.class, LONG_FIELD);
          assertPrimitiveCast("LONG_FIELD", float.class, (float) LONG_FIELD);
          assertPrimitiveCast("LONG_FIELD", double.class, (double) LONG_FIELD);
  
          assertPrimitiveCast("FLOAT_FIELD", byte.class, (byte) FLOAT_FIELD);
          assertPrimitiveCast("FLOAT_FIELD", char.class, (char) FLOAT_FIELD);
          assertPrimitiveCast("FLOAT_FIELD", short.class, (short) FLOAT_FIELD);
          assertPrimitiveCast("FLOAT_FIELD", int.class, (int) FLOAT_FIELD);
          assertPrimitiveCast("FLOAT_FIELD", long.class, (long) FLOAT_FIELD);
          assertPrimitiveCast("FLOAT_FIELD", float.class, FLOAT_FIELD);
          assertPrimitiveCast("FLOAT_FIELD", double.class, (double) FLOAT_FIELD);
  
          assertPrimitiveCast("DOUBLE_FIELD", byte.class, (byte) DOUBLE_FIELD);
          assertPrimitiveCast("DOUBLE_FIELD", char.class, (char) DOUBLE_FIELD);
          assertPrimitiveCast("DOUBLE_FIELD", short.class, (short) DOUBLE_FIELD);
          assertPrimitiveCast("DOUBLE_FIELD", int.class, (int) DOUBLE_FIELD);
          assertPrimitiveCast("DOUBLE_FIELD", long.class, (long) DOUBLE_FIELD);
          assertPrimitiveCast("DOUBLE_FIELD", float.class, (float) DOUBLE_FIELD);
          assertPrimitiveCast("DOUBLE_FIELD", double.class, DOUBLE_FIELD);
      }
  
      void assertPrimitiveCast(String fieldName, Class<?> castToType, Object expected)
              throws Exception
      {
          // simple cast
          BytecodeExpression baseExpression = getStatic(getClass(), fieldName);
          assertCast(baseExpression, expected, castToType);
  
          // box result
          baseExpression = baseExpression.cast(castToType);
          Class<?> boxedType = Primitives.wrap(castToType);
          assertCast(baseExpression, expected, boxedType);
  
          // unbox the boxed result
          baseExpression = baseExpression.cast(boxedType);
          assertCast(baseExpression, expected, castToType);
      }
  
      static void assertCast(BytecodeExpression expression, Object expectedValue, Class<?> castToType)
              throws Exception
      {
          BytecodeExpression castExpression = expression.cast(castToType);
          assertBytecodeExpression(castExpression, expectedValue, expectedCastRendering(expression.toString(), castToType));
          assertThat(castExpression.getType().getJavaClassName()).isEqualTo(castToType.getName());
      }
  
      static void assertInvalidCast(BytecodeExpression expression, Class<?> castToType)
      {
          assertThatThrownBy(() -> expression.cast(castToType))
                  .isInstanceOf(IllegalArgumentException.class);
      }
  
      static String expectedCastRendering(String expectedRendering, Class<?> castToType)
      {
          return "((" + castToType.getSimpleName() + ") " + expectedRendering + ")";
      }
  }
  