net.bytebuddy.implementation.bytecode.assign.primitive.PrimitiveUnboxingDelegate Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of byte-buddy-dep Show documentation
Show all versions of byte-buddy-dep Show documentation
Byte Buddy is a Java library for creating Java classes at run time.
This artifact is a build of Byte Buddy with a remaining dependency onto ASM.
You should never depend on this module without repackaging Byte Buddy and ASM into your own namespace.
/*
* Copyright 2014 - Present Rafael Winterhalter
*
* 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 net.bytebuddy.implementation.bytecode.assign.primitive;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.StackSize;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.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 = TypeDescription.ForLoadedType.of(wrapperType);
this.primitiveType = TypeDescription.ForLoadedType.of(primitiveType);
this.unboxingMethodName = unboxingMethodName;
this.unboxingMethodDescriptor = unboxingMethodDescriptor;
}
/**
* Locates a primitive unboxing delegate for a given primitive type.
*
* @param typeDefinition A description of the primitive type.
* @return A corresponding primitive unboxing delegate.
*/
public static PrimitiveUnboxingDelegate 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("Expected non-void primitive type instead of " + typeDefinition);
}
}
/**
* 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 net.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();
}
/**
* {@inheritDoc}
*/
public boolean isValid() {
return true;
}
/**
* {@inheritDoc}
*/
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
wrapperType.asErasure().getInternalName(),
unboxingMethodName,
unboxingMethodDescriptor,
false);
return size;
}
/**
* 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;
}
/**
* {@inheritDoc}
*/
public StackManipulation assignUnboxedTo(TypeDescription.Generic targetType, Assigner assigner, Assigner.Typing typing) {
return new Compound(
primitiveUnboxingDelegate,
PrimitiveWideningDelegate.forPrimitive(primitiveUnboxingDelegate.primitiveType).widenTo(targetType));
}
}
/**
* 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.
*/
@HashCodeAndEqualsPlugin.Enhance
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;
}
/**
* {@inheritDoc}
*/
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);
}
}
}