package net.bytebuddy.implementation.bytecode.assign.primitive; import lombok.EqualsAndHashCode; import net.bytebuddy.description.type.TypeDefinition; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.bytecode.StackManipulation; import net.bytebuddy.implementation.bytecode.StackSize; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; /** * This delegate is responsible for widening a primitive type to represent a <i>larger</i> primitive type. The * rules for this widening are equivalent to those in the <a href="http://docs.oracle.com/javase/specs/">JLS</a>. */ public enum PrimitiveWideningDelegate { /** * The widening delegate for {@code boolean} values. */ BOOLEAN(StackManipulation.Trivial.INSTANCE, // to boolean StackManipulation.Illegal.INSTANCE, // to byte StackManipulation.Illegal.INSTANCE, // to short StackManipulation.Illegal.INSTANCE, // to character StackManipulation.Illegal.INSTANCE, // to integer StackManipulation.Illegal.INSTANCE, // to long StackManipulation.Illegal.INSTANCE, // to float StackManipulation.Illegal.INSTANCE), // to double /** * The widening delegate for {@code byte} values. */ BYTE(StackManipulation.Illegal.INSTANCE, // to boolean StackManipulation.Trivial.INSTANCE, // to byte StackManipulation.Trivial.INSTANCE, // to short StackManipulation.Illegal.INSTANCE, // to character StackManipulation.Trivial.INSTANCE, // to integer new WideningStackManipulation(Opcodes.I2L, StackSize.SINGLE.toIncreasingSize()), // to long new WideningStackManipulation(Opcodes.I2F, StackSize.ZERO.toIncreasingSize()), // to float new WideningStackManipulation(Opcodes.I2L, StackSize.SINGLE.toIncreasingSize())), // to double /** * The widening delegate for {@code short} values. */ SHORT(StackManipulation.Illegal.INSTANCE, // to boolean StackManipulation.Illegal.INSTANCE, // to byte StackManipulation.Trivial.INSTANCE, // to short StackManipulation.Illegal.INSTANCE, // to character StackManipulation.Trivial.INSTANCE, // to integer new WideningStackManipulation(Opcodes.I2L, StackSize.SINGLE.toIncreasingSize()), // to long new WideningStackManipulation(Opcodes.I2F, StackSize.ZERO.toIncreasingSize()), // to float new WideningStackManipulation(Opcodes.I2D, StackSize.SINGLE.toIncreasingSize())), // to double /** * The widening delegate for {@code char} values. */ CHARACTER(StackManipulation.Illegal.INSTANCE, // to boolean StackManipulation.Illegal.INSTANCE, // to byte StackManipulation.Illegal.INSTANCE, // to short StackManipulation.Trivial.INSTANCE, // to character StackManipulation.Trivial.INSTANCE, // to integer new WideningStackManipulation(Opcodes.I2L, StackSize.SINGLE.toIncreasingSize()), // to long new WideningStackManipulation(Opcodes.I2F, StackSize.ZERO.toIncreasingSize()), // to float new WideningStackManipulation(Opcodes.I2D, StackSize.SINGLE.toIncreasingSize())), // to double /** * The widening delegate for {@code int} values. */ INTEGER(StackManipulation.Illegal.INSTANCE, // to boolean StackManipulation.Illegal.INSTANCE, // to byte StackManipulation.Illegal.INSTANCE, // to short StackManipulation.Illegal.INSTANCE, // to character StackManipulation.Trivial.INSTANCE, // to integer new WideningStackManipulation(Opcodes.I2L, StackSize.SINGLE.toIncreasingSize()), // to long new WideningStackManipulation(Opcodes.I2F, StackSize.ZERO.toIncreasingSize()), // to float new WideningStackManipulation(Opcodes.I2D, StackSize.SINGLE.toIncreasingSize())), // to double /** * The widening delegate for {@code long} values. */ LONG(StackManipulation.Illegal.INSTANCE, // to boolean StackManipulation.Illegal.INSTANCE, // to byte StackManipulation.Illegal.INSTANCE, // to short StackManipulation.Illegal.INSTANCE, // to character StackManipulation.Illegal.INSTANCE, // to integer StackManipulation.Trivial.INSTANCE, // to long new WideningStackManipulation(Opcodes.L2F, StackSize.SINGLE.toDecreasingSize()), // to float new WideningStackManipulation(Opcodes.L2D, StackSize.ZERO.toIncreasingSize())), // to double /** * The widening delegate for {@code float} values. */ FLOAT(StackManipulation.Illegal.INSTANCE, // to boolean StackManipulation.Illegal.INSTANCE, // to byte StackManipulation.Illegal.INSTANCE, // to short StackManipulation.Illegal.INSTANCE, // to character StackManipulation.Illegal.INSTANCE, // to integer StackManipulation.Illegal.INSTANCE, // to long StackManipulation.Trivial.INSTANCE, // to float new WideningStackManipulation(Opcodes.F2D, StackSize.SINGLE.toIncreasingSize())), // to double /** * The widening delegate for {@code double} values. */ DOUBLE(StackManipulation.Illegal.INSTANCE, // to boolean StackManipulation.Illegal.INSTANCE, // to byte StackManipulation.Illegal.INSTANCE, // to short StackManipulation.Illegal.INSTANCE, // to character StackManipulation.Illegal.INSTANCE, // to integer StackManipulation.Illegal.INSTANCE, // to long StackManipulation.Illegal.INSTANCE, // to float StackManipulation.Trivial.INSTANCE); // to double /** * A stack manipulation that widens the type that is represented by this instance to a {@code boolean}. */ private final StackManipulation toBooleanStackManipulation; /** * A stack manipulation that widens the type that is represented by this instance to a {@code byte}. */ private final StackManipulation toByteStackManipulation; /** * A stack manipulation that widens the type that is represented by this instance to a {@code short}. */ private final StackManipulation toShortStackManipulation; /** * A stack manipulation that widens the type that is represented by this instance to a {@code char}. */ private final StackManipulation toCharacterStackManipulation; /** * A stack manipulation that widens the type that is represented by this instance to a {@code int}. */ private final StackManipulation toIntegerStackManipulation; /** * A stack manipulation that widens the type that is represented by this instance to a {@code long}. */ private final StackManipulation toLongStackManipulation; /** * A stack manipulation that widens the type that is represented by this instance to a {@code float}. */ private final StackManipulation toFloatStackManipulation; /** * A stack manipulation that widens the type that is represented by this instance to a {@code double}. */ private final StackManipulation toDoubleStackManipulation; /** * Creates a new primitive widening delegate. * * @param toBooleanStackManipulation A stack manipulation that widens the type that is represented by this * instance to a {@code boolean}. * @param toByteStackManipulation A stack manipulation that widens the type that is represented by this * instance to a {@code byte}. * @param toShortStackManipulation A stack manipulation that widens the type that is represented by this * instance to a {@code short}. * @param toCharacterStackManipulation A stack manipulation that widens the type that is represented by this * instance to a {@code char}. * @param toIntegerStackManipulation A stack manipulation that widens the type that is represented by this * instance to a {@code int}. * @param toLongStackManipulation A stack manipulation that widens the type that is represented by this * instance to a {@code long}. * @param toFloatStackManipulation A stack manipulation that widens the type that is represented by this * instance to a {@code float}. * @param toDoubleStackManipulation A stack manipulation that widens the type that is represented by this * instance to a {@code double}. */ PrimitiveWideningDelegate(StackManipulation toBooleanStackManipulation, StackManipulation toByteStackManipulation, StackManipulation toShortStackManipulation, StackManipulation toCharacterStackManipulation, StackManipulation toIntegerStackManipulation, StackManipulation toLongStackManipulation, StackManipulation toFloatStackManipulation, StackManipulation toDoubleStackManipulation) { this.toBooleanStackManipulation = toBooleanStackManipulation; this.toByteStackManipulation = toByteStackManipulation; this.toShortStackManipulation = toShortStackManipulation; this.toCharacterStackManipulation = toCharacterStackManipulation; this.toIntegerStackManipulation = toIntegerStackManipulation; this.toLongStackManipulation = toLongStackManipulation; this.toFloatStackManipulation = toFloatStackManipulation; this.toDoubleStackManipulation = toDoubleStackManipulation; } /** * Locates the delegate that is capable of widening the given type into another type. * * @param typeDefinition A non-void primitive type that is to be widened into another type. * @return A delegate for the given type. */ public static PrimitiveWideningDelegate forPrimitive(TypeDefinition typeDefinition) { if (typeDefinition.represents(boolean.class)) { return BOOLEAN; } else if (typeDefinition.represents(byte.class)) { return BYTE; } else if (typeDefinition.represents(short.class)) { return SHORT; } else if (typeDefinition.represents(char.class)) { return CHARACTER; } else if (typeDefinition.represents(int.class)) { return INTEGER; } else if (typeDefinition.represents(long.class)) { return LONG; } else if (typeDefinition.represents(float.class)) { return FLOAT; } else if (typeDefinition.represents(double.class)) { return DOUBLE; } else { throw new IllegalArgumentException("Not a primitive, non-void type: " + typeDefinition); } } /** * Attempts to widen the represented type into another type. * * @param typeDefinition A non-void primitive type that is the expected result of the widening operation. * @return A widening instruction or an illegal stack manipulation if such widening is not legitimate. */ public StackManipulation widenTo(TypeDefinition typeDefinition) { if (typeDefinition.represents(boolean.class)) { return toBooleanStackManipulation; } else if (typeDefinition.represents(byte.class)) { return toByteStackManipulation; } else if (typeDefinition.represents(short.class)) { return toShortStackManipulation; } else if (typeDefinition.represents(char.class)) { return toCharacterStackManipulation; } else if (typeDefinition.represents(int.class)) { return toIntegerStackManipulation; } else if (typeDefinition.represents(long.class)) { return toLongStackManipulation; } else if (typeDefinition.represents(float.class)) { return toFloatStackManipulation; } else if (typeDefinition.represents(double.class)) { return toDoubleStackManipulation; } else { throw new IllegalArgumentException("Not a primitive non-void type: " + typeDefinition); } } /** * A stack manipulation that widens a primitive type into a more general primitive type. */ @EqualsAndHashCode protected static class WideningStackManipulation implements StackManipulation { /** * The opcode for executing the conversion. */ private final int conversionOpcode; /** * The size change of applying the conversion. */ private final Size size; /** * Creates a new widening stack manipulation. * * @param conversionOpcode The opcode for executing the conversion. * @param size The size change of applying the conversion. */ protected WideningStackManipulation(int conversionOpcode, Size size) { this.conversionOpcode = conversionOpcode; this.size = size; } @Override public boolean isValid() { return true; } @Override public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { methodVisitor.visitInsn(conversionOpcode); return size; } } }