com.fitbur.mockito.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveUnboxingDelegate Maven / Gradle / Ivy
package com.fitbur.mockito.bytebuddy.implementation.bytecode.assign.primitive;
import com.fitbur.mockito.bytebuddy.description.type.TypeDefinition;
import com.fitbur.mockito.bytebuddy.description.type.TypeDescription;
import com.fitbur.mockito.bytebuddy.implementation.Implementation;
import com.fitbur.mockito.bytebuddy.implementation.bytecode.StackManipulation;
import com.fitbur.mockito.bytebuddy.implementation.bytecode.StackSize;
import com.fitbur.mockito.bytebuddy.implementation.bytecode.assign.Assigner;
import com.fitbur.mockito.bytebuddy.jar.asm.MethodVisitor;
import com.fitbur.mockito.bytebuddy.jar.asm.Opcodes;
/**
* This delegate is responsible for unboxing a wrapper type to their primitive equivalents.
*/
public enum PrimitiveUnboxingDelegate implements StackManipulation {
/**
* The unboxing delegate for {@code Boolean} types.
*/
BOOLEAN(Boolean.class, boolean.class, StackSize.ZERO, "booleanValue", "()Z"),
/**
* The unboxing delegate for {@code Byte} types.
*/
BYTE(Byte.class, byte.class, StackSize.ZERO, "byteValue", "()B"),
/**
* The unboxing delegate for {@code Short} types.
*/
SHORT(Short.class, short.class, StackSize.ZERO, "shortValue", "()S"),
/**
* The unboxing delegate for {@code Character} types.
*/
CHARACTER(Character.class, char.class, StackSize.ZERO, "charValue", "()C"),
/**
* The unboxing delegate for {@code Integer} types.
*/
INTEGER(Integer.class, int.class, StackSize.ZERO, "intValue", "()I"),
/**
* The unboxing delegate for {@code Long} types.
*/
LONG(Long.class, long.class, StackSize.SINGLE, "longValue", "()J"),
/**
* The unboxing delegate for {@code Float} types.
*/
FLOAT(Float.class, float.class, StackSize.ZERO, "floatValue", "()F"),
/**
* The unboxing delegate for {@code Double} types.
*/
DOUBLE(Double.class, double.class, StackSize.SINGLE, "doubleValue", "()D");
/**
* The wrapper type of the represented primitive type.
*/
private final TypeDescription wrapperType;
/**
* The represented primitive type.
*/
private final TypeDescription primitiveType;
/**
* The size increase after a wrapper type was unwrapped.
*/
private final Size size;
/**
* The name of the method for unboxing a wrapper value to its primitive value.
*/
private final String unboxingMethodName;
/**
* The descriptor of the method for unboxing a wrapper value to its primitive value.
*/
private final String unboxingMethodDescriptor;
/**
* Creates a new primitive unboxing delegate.
*
* @param wrapperType The wrapper type of the represented primitive type.
* @param primitiveType The represented primitive type.
* @param sizeDifference The size difference between the wrapper type and its primitive value.
* @param unboxingMethodName The name of the method for unboxing a wrapper value to its primitive value.
* @param unboxingMethodDescriptor The descriptor of the method for unboxing a wrapper value to its primitive value.
*/
PrimitiveUnboxingDelegate(Class> wrapperType,
Class> primitiveType,
StackSize sizeDifference,
String unboxingMethodName,
String unboxingMethodDescriptor) {
this.size = sizeDifference.toIncreasingSize();
this.wrapperType = new TypeDescription.ForLoadedType(wrapperType);
this.primitiveType = new TypeDescription.ForLoadedType(primitiveType);
this.unboxingMethodName = unboxingMethodName;
this.unboxingMethodDescriptor = unboxingMethodDescriptor;
}
/**
* Locates a primitive unboxing delegate for a given primitive type.
*
* @param typeDescription A description of the primitive type.
* @return A corresponding primitive unboxing delegate.
*/
protected static PrimitiveUnboxingDelegate forPrimitive(TypeDescription.Generic typeDescription) {
if (typeDescription.represents(boolean.class)) {
return BOOLEAN;
} else if (typeDescription.represents(byte.class)) {
return BYTE;
} else if (typeDescription.represents(short.class)) {
return SHORT;
} else if (typeDescription.represents(char.class)) {
return CHARACTER;
} else if (typeDescription.represents(int.class)) {
return INTEGER;
} else if (typeDescription.represents(long.class)) {
return LONG;
} else if (typeDescription.represents(float.class)) {
return FLOAT;
} else if (typeDescription.represents(double.class)) {
return DOUBLE;
} else {
throw new IllegalArgumentException("Expected non-void primitive type instead of " + typeDescription);
}
}
/**
* Creates an unboxing responsible that is capable of unboxing a wrapper type.
*
* - If the reference type represents a wrapper type, the wrapper type will simply be unboxed.
* - If the reference type does not represent a wrapper type, the wrapper type will be inferred by the primitive target
* type that is later given to the
* {@link com.fitbur.mockito.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveUnboxingDelegate.UnboxingResponsible}
* in order to then check if the given type is assignable to the inferred wrapper type.
*
*
* @param typeDefinition A non-primitive type.
* @return An unboxing responsible capable of performing an unboxing operation while considering a further assignment
* of the unboxed value.
*/
public static UnboxingResponsible forReferenceType(TypeDefinition typeDefinition) {
if (typeDefinition.isPrimitive()) {
throw new IllegalArgumentException("Expected reference type instead of " + typeDefinition);
} else if (typeDefinition.represents(Boolean.class)) {
return ExplicitlyTypedUnboxingResponsible.BOOLEAN;
} else if (typeDefinition.represents(Byte.class)) {
return ExplicitlyTypedUnboxingResponsible.BYTE;
} else if (typeDefinition.represents(Short.class)) {
return ExplicitlyTypedUnboxingResponsible.SHORT;
} else if (typeDefinition.represents(Character.class)) {
return ExplicitlyTypedUnboxingResponsible.CHARACTER;
} else if (typeDefinition.represents(Integer.class)) {
return ExplicitlyTypedUnboxingResponsible.INTEGER;
} else if (typeDefinition.represents(Long.class)) {
return ExplicitlyTypedUnboxingResponsible.LONG;
} else if (typeDefinition.represents(Float.class)) {
return ExplicitlyTypedUnboxingResponsible.FLOAT;
} else if (typeDefinition.represents(Double.class)) {
return ExplicitlyTypedUnboxingResponsible.DOUBLE;
} else {
return new ImplicitlyTypedUnboxingResponsible(typeDefinition.asGenericType());
}
}
/**
* Returns the wrapper type that this unboxing delegate represents.
*
* @return A generic version of this delegate's wrapper type.
*/
protected TypeDescription.Generic getWrapperType() {
return wrapperType.asGenericType();
}
@Override
public boolean isValid() {
return true;
}
@Override
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
wrapperType.asErasure().getInternalName(),
unboxingMethodName,
unboxingMethodDescriptor,
false);
return size;
}
@Override
public String toString() {
return "PrimitiveUnboxingDelegate." + name();
}
/**
* An explicitly types unboxing responsible is applied for directly unboxing a wrapper type.
*/
protected enum ExplicitlyTypedUnboxingResponsible implements UnboxingResponsible {
/**
* An unboxing responsible for unboxing a {@link java.lang.Boolean} type.
*/
BOOLEAN(PrimitiveUnboxingDelegate.BOOLEAN),
/**
* An unboxing responsible for unboxing a {@link java.lang.Byte} type.
*/
BYTE(PrimitiveUnboxingDelegate.BYTE),
/**
* An unboxing responsible for unboxing a {@link java.lang.Short} type.
*/
SHORT(PrimitiveUnboxingDelegate.SHORT),
/**
* An unboxing responsible for unboxing a {@link java.lang.Character} type.
*/
CHARACTER(PrimitiveUnboxingDelegate.CHARACTER),
/**
* An unboxing responsible for unboxing a {@link java.lang.Integer} type.
*/
INTEGER(PrimitiveUnboxingDelegate.INTEGER),
/**
* An unboxing responsible for unboxing a {@link java.lang.Long} type.
*/
LONG(PrimitiveUnboxingDelegate.LONG),
/**
* An unboxing responsible for unboxing a {@link java.lang.Float} type.
*/
FLOAT(PrimitiveUnboxingDelegate.FLOAT),
/**
* An unboxing responsible for unboxing a {@link java.lang.Double} type.
*/
DOUBLE(PrimitiveUnboxingDelegate.DOUBLE);
/**
* The primitive unboxing delegate for handling the given wrapper type.
*/
private final PrimitiveUnboxingDelegate primitiveUnboxingDelegate;
/**
* Creates a new explicitly typed unboxing responsible.
*
* @param primitiveUnboxingDelegate The primitive unboxing delegate for handling the given wrapper type.
*/
ExplicitlyTypedUnboxingResponsible(PrimitiveUnboxingDelegate primitiveUnboxingDelegate) {
this.primitiveUnboxingDelegate = primitiveUnboxingDelegate;
}
@Override
public StackManipulation assignUnboxedTo(TypeDescription.Generic targetType, Assigner assigner, Assigner.Typing typing) {
return new Compound(
primitiveUnboxingDelegate,
PrimitiveWideningDelegate.forPrimitive(primitiveUnboxingDelegate.primitiveType).widenTo(targetType));
}
@Override
public String toString() {
return "PrimitiveUnboxingDelegate.ExplicitlyTypedUnboxingResponsible." + name();
}
}
/**
* Implementations represent an unboxing delegate that is able to perform the unboxing operation.
*/
public interface UnboxingResponsible {
/**
* Attempts to unbox the represented type in order to assign the unboxed value to the given target type
* while using the assigner that is provided by the method call.
*
* @param target The type that is the desired outcome of the assignment.
* @param assigner The assigner used to assign the unboxed type to the target type.
* @param typing Determines if a type-casting should be attempted for incompatible types.
* @return A stack manipulation representing this assignment if such an assignment is possible. An illegal
* assignment otherwise.
*/
StackManipulation assignUnboxedTo(TypeDescription.Generic target, Assigner assigner, Assigner.Typing typing);
}
/**
* An unboxing responsible for an implicitly typed value. This implementation is applied for source types that
* were not found to be of a given wrapper type. Instead, this unboxing responsible tries to assign the
* source type to the primitive target type's wrapper type before performing an unboxing operation.
*/
protected static class ImplicitlyTypedUnboxingResponsible implements UnboxingResponsible {
/**
* The original type which should be unboxed but is not of any known wrapper type.
*/
private final TypeDescription.Generic originalType;
/**
* Creates a new implicitly typed unboxing responsible.
*
* @param originalType The original type which should be unboxed but is not of any known wrapper type.
*/
protected ImplicitlyTypedUnboxingResponsible(TypeDescription.Generic originalType) {
this.originalType = originalType;
}
@Override
public StackManipulation assignUnboxedTo(TypeDescription.Generic target, Assigner assigner, Assigner.Typing typing) {
PrimitiveUnboxingDelegate primitiveUnboxingDelegate = PrimitiveUnboxingDelegate.forPrimitive(target);
return new Compound(
assigner.assign(originalType, primitiveUnboxingDelegate.getWrapperType(), typing),
primitiveUnboxingDelegate);
}
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& originalType.equals(((ImplicitlyTypedUnboxingResponsible) other).originalType);
}
@Override
public int hashCode() {
return originalType.hashCode();
}
@Override
public String toString() {
return "PrimitiveUnboxingDelegate.ImplicitlyTypedUnboxingResponsible{originalType=" + originalType + '}';
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy