package scouter.bytebuddy.implementation.bytecode.assign.primitive;
import scouter.bytebuddy.description.type.TypeDefinition;
import scouter.bytebuddy.description.type.TypeDescription;
import scouter.bytebuddy.implementation.Implementation;
import scouter.bytebuddy.implementation.bytecode.StackManipulation;
import scouter.bytebuddy.implementation.bytecode.StackSize;
import scouter.bytebuddy.implementation.bytecode.assign.Assigner;
import scouter.bytebuddy.jar.asm.MethodVisitor;
import scouter.bytebuddy.jar.asm.Opcodes;
/**
* This delegate is responsible for boxing a primitive types to their wrapper equivalents.
*/
public enum PrimitiveBoxingDelegate {
/**
* The boxing delegate for {@code boolean} values.
*/
BOOLEAN(Boolean.class, StackSize.ZERO, "valueOf", "(Z)Ljava/lang/Boolean;"),
/**
* The boxing delegate for {@code byte} values.
*/
BYTE(Byte.class, StackSize.ZERO, "valueOf", "(B)Ljava/lang/Byte;"),
/**
* The boxing delegate for {@code short} values.
*/
SHORT(Short.class, StackSize.ZERO, "valueOf", "(S)Ljava/lang/Short;"),
/**
* The boxing delegate for {@code char} values.
*/
CHARACTER(Character.class, StackSize.ZERO, "valueOf", "(C)Ljava/lang/Character;"),
/**
* The boxing delegate for {@code int} values.
*/
INTEGER(Integer.class, StackSize.ZERO, "valueOf", "(I)Ljava/lang/Integer;"),
/**
* The boxing delegate for {@code long} values.
*/
LONG(Long.class, StackSize.SINGLE, "valueOf", "(J)Ljava/lang/Long;"),
/**
* The boxing delegate for {@code float} values.
*/
FLOAT(Float.class, StackSize.ZERO, "valueOf", "(F)Ljava/lang/Float;"),
/**
* The boxing delegate for {@code double} values.
*/
DOUBLE(Double.class, StackSize.SINGLE, "valueOf", "(D)Ljava/lang/Double;");
/**
* A description of a wrapper type.
*/
private final TypeDescription wrapperType;
/**
* The size decrease after a primitive type was wrapped.
*/
private final StackManipulation.Size size;
/**
* The name of the method for boxing a primitive value as its wrapper type.
*/
private final String boxingMethodName;
/**
* The descriptor of the method for boxing a primitive value as its wrapper type.
*/
private final String boxingMethodDescriptor;
/**
* Creates a new primitive boxing delegate.
*
* @param wrapperType A description of a wrapper type.
* @param sizeDifference The size difference between a primitive type and its wrapper type.
* @param boxingMethodName The name of the method for boxing a primitive value as its wrapper type.
* @param boxingMethodDescriptor The descriptor of the method for boxing a primitive value as its wrapper type.
*/
PrimitiveBoxingDelegate(Class<?> wrapperType,
StackSize sizeDifference,
String boxingMethodName,
String boxingMethodDescriptor) {
this.wrapperType = new TypeDescription.ForLoadedType(wrapperType);
this.size = sizeDifference.toDecreasingSize();
this.boxingMethodName = boxingMethodName;
this.boxingMethodDescriptor = boxingMethodDescriptor;
}
/**
* Locates a boxing delegate for a given primitive type.
*
* @param typeDefinition A non-void primitive type.
* @return A delegate capable of boxing the given primitve type.
*/
public static PrimitiveBoxingDelegate 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 non-void, primitive type: " + typeDefinition);
}
}
/**
* Creates a stack manipulation that boxes the represented primitive type and applies a chained assignment
* to the result of this boxing operation.
*
* @param target The type that is target of the assignment operation.
* @param chainedAssigner The assigner that is to be used to perform the chained assignment.
* @param typing Determines if an assignment to an incompatible type should be enforced by a casting.
* @return A stack manipulation that represents the described assignment operation.
*/
public StackManipulation assignBoxedTo(TypeDescription.Generic target, Assigner chainedAssigner, Assigner.Typing typing) {
return new BoxingStackManipulation(chainedAssigner.assign(wrapperType.asGenericType(), target, typing));
}
/**
* A stack manipulation for boxing a primitive type into its wrapper type.
*/
private class BoxingStackManipulation implements StackManipulation {
/**
* A stack manipulation that is applied after the boxing of the top-most value on the operand stack.
*/
private final StackManipulation stackManipulation;
/**
* Creates a new boxing stack manipulation.
*
* @param stackManipulation A stack manipulation that is applied after the boxing of the top-most value on
* the operand stack.
*/
public BoxingStackManipulation(StackManipulation stackManipulation) {
this.stackManipulation = stackManipulation;
}
@Override
public boolean isValid() {
return stackManipulation.isValid();
}
@Override
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
wrapperType.getInternalName(),
boxingMethodName,
boxingMethodDescriptor,
false);
return size.aggregate(stackManipulation.apply(methodVisitor, implementationContext));
}
}
}