All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.bytebuddy.implementation.FixedValue Maven / Gradle / Ivy

Go to download

Byte Buddy is a Java library for creating Java classes at run time. This artifact is a build of Byte Buddy with all ASM dependencies repackaged into its own name space.

There is a newer version: 1.15.11
Show newest version
package net.bytebuddy.implementation;

import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.implementation.bytecode.constant.*;
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
import net.bytebuddy.utility.JavaInstance;
import net.bytebuddy.utility.JavaType;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;

import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.utility.ByteBuddyCommons.isValidIdentifier;
import static net.bytebuddy.utility.ByteBuddyCommons.nonNull;

/**
 * This implementation returns a fixed value for a method. Other than the
 * {@link net.bytebuddy.implementation.StubMethod} implementation, this implementation allows
 * to determine a specific value which must be assignable to the returning value of any instrumented method. Otherwise,
 * an exception will be thrown.
 */
public abstract class FixedValue implements Implementation {

    /**
     * The assigner that is used for assigning the fixed value to a method's return type.
     */
    protected final Assigner assigner;

    /**
     * Determines if the runtime type of a fixed value should be considered for the assignment to a return type.
     */
    protected final boolean dynamicallyTyped;

    /**
     * Creates a new fixed value implementation.
     *
     * @param assigner         The assigner to use for assigning the fixed value to the return type of the instrumented value.
     * @param dynamicallyTyped If {@code true}, the runtime type of the given value will be considered for assigning
     *                         the return type.
     */
    protected FixedValue(Assigner assigner, boolean dynamicallyTyped) {
        this.assigner = assigner;
        this.dynamicallyTyped = dynamicallyTyped;
    }

    /**
     * Creates a fixed value implementation that returns {@code null} as a fixed value. This value is inlined into
     * the method and does not create a field.
     *
     * @return An implementation that represents the {@code null} value.
     */
    public static Implementation nullValue() {
        return value((Object) null);
    }

    /**
     * Creates a fixed value implementation that returns a fixed value. If the value can be inlined into the created
     * class, i.e. can be added to the constant pool of a class, no explicit type initialization will be required for
     * the created dynamic class. Otherwise, a static field will be created in the dynamic type which will be initialized
     * with the given value. The following Java types can be inlined:
     * 
    *
  • The {@link java.lang.String} type.
  • *
  • The {@link java.lang.Class} type.
  • *
  • All primitive types and their wrapper type, i.e. {@link java.lang.Boolean}, {@link java.lang.Byte}, * {@link java.lang.Short}, {@link java.lang.Character}, {@link java.lang.Integer}, {@link java.lang.Long}, * {@link java.lang.Float} and {@link java.lang.Double}.
  • *
  • A {@code null} reference.
  • *
  • From Java 7 on, instances of {@code java.lang.invoke.MethodType} and {@code java.lang.invoke.MethodHandle}.
  • *
*

 

* If possible, the constant pool value is substituted by a byte code instruction that creates the value. (This is * possible for integer types and types that are presented by integers inside the JVM ({@code boolean}, {@code byte}, * {@code short}, {@code char}) and for the {@code null} value. Additionally, several common constants of * the {@code float}, {@code double} and {@code long} types can be represented by opcode constants. *

 

* Note that method handles or (method) types require to be visible to a class's class loader. * * @param fixedValue The fixed value to be returned by methods that are instrumented by this implementation. * @return An implementation for the given {@code fixedValue}. */ public static AssignerConfigurable value(Object fixedValue) { if (fixedValue == null) { return new ForPoolValue(NullConstant.INSTANCE, TypeDescription.OBJECT, Assigner.DEFAULT, Assigner.DYNAMICALLY_TYPED); } Class type = fixedValue.getClass(); if (type == String.class) { return new ForPoolValue(new TextConstant((String) fixedValue), TypeDescription.STRING, Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } else if (type == Class.class) { return new ForPoolValue(ClassConstant.of(new TypeDescription.ForLoadedType(type)), TypeDescription.CLASS, Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } else if (type == Boolean.class) { return new ForPoolValue(IntegerConstant.forValue((Boolean) fixedValue), new TypeDescription.ForLoadedType(boolean.class), Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } else if (type == Byte.class) { return new ForPoolValue(IntegerConstant.forValue((Byte) fixedValue), new TypeDescription.ForLoadedType(byte.class), Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } else if (type == Short.class) { return new ForPoolValue(IntegerConstant.forValue((Short) fixedValue), new TypeDescription.ForLoadedType(short.class), Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } else if (type == Character.class) { return new ForPoolValue(IntegerConstant.forValue((Character) fixedValue), new TypeDescription.ForLoadedType(char.class), Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } else if (type == Integer.class) { return new ForPoolValue(IntegerConstant.forValue((Integer) fixedValue), new TypeDescription.ForLoadedType(int.class), Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } else if (type == Long.class) { return new ForPoolValue(LongConstant.forValue((Long) fixedValue), new TypeDescription.ForLoadedType(long.class), Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } else if (type == Float.class) { return new ForPoolValue(FloatConstant.forValue((Float) fixedValue), new TypeDescription.ForLoadedType(float.class), Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } else if (type == Double.class) { return new ForPoolValue(DoubleConstant.forValue((Double) fixedValue), new TypeDescription.ForLoadedType(double.class), Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } else if (JavaType.METHOD_HANDLE.getTypeStub().isAssignableFrom(type)) { return new ForPoolValue(MethodHandleConstant.of(JavaInstance.MethodHandle.of(fixedValue)), new TypeDescription.ForLoadedType(type), Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } else if (JavaType.METHOD_TYPE.getTypeStub().represents(type)) { return new ForPoolValue(MethodTypeConstant.of(JavaInstance.MethodType.of(fixedValue)), new TypeDescription.ForLoadedType(type), Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } else { return reference(fixedValue); } } /** * Other than {@link net.bytebuddy.implementation.FixedValue#value(Object)}, this function * will create a fixed value implementation that will always defined a field in the instrumented class. As a result, * object identity will be preserved between the given {@code fixedValue} and the value that is returned by * instrumented methods. *

 

* As an exception, the {@code null} value is always presented by a constant value and is never stored in a static * field. * * @param fixedValue The fixed value to be returned by methods that are instrumented by this implementation. * @return An implementation for the given {@code fixedValue}. */ public static AssignerConfigurable reference(Object fixedValue) { return fixedValue == null ? new ForPoolValue(NullConstant.INSTANCE, TypeDescription.OBJECT, Assigner.DEFAULT, Assigner.DYNAMICALLY_TYPED) : new ForStaticField(fixedValue, Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } /** * Other than {@link net.bytebuddy.implementation.FixedValue#value(Object)}, this function * will create a fixed value implementation that will always defined a field in the instrumented class. As a result, * object identity will be preserved between the given {@code fixedValue} and the value that is returned by * instrumented methods. The field name can be explicitly determined. *

 

* As an exception, the {@code null} value cannot be used for this implementation but will cause an exception. * * @param fixedValue The fixed value to be returned by methods that are instrumented by this implementation. * @param fieldName The name of the field for storing the fixed value. * @return An implementation for the given {@code fixedValue}. */ public static AssignerConfigurable reference(Object fixedValue, String fieldName) { if (fixedValue == null) { throw new IllegalArgumentException("The fixed value must not be null"); } return new ForStaticField(isValidIdentifier(fieldName), fixedValue, Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } /** * Returns the given type in form of a loaded type. The value is loaded from the written class's constant pool. * * @param fixedValue The type to return from the method. * @return An implementation for the given {@code fixedValue}. */ public static AssignerConfigurable value(TypeDescription fixedValue) { return new ForPoolValue(ClassConstant.of(fixedValue), TypeDescription.CLASS, Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } /** * Returns the loaded version of the given {@link JavaInstance}. The value is loaded from the written class's constant pool. * * @param fixedValue The type to return from the method. * @return An implementation for the given {@code fixedValue}. */ public static AssignerConfigurable value(JavaInstance fixedValue) { return new ForPoolValue(fixedValue.asStackManipulation(), fixedValue.getInstanceType(), Assigner.DEFAULT, Assigner.STATICALLY_TYPED); } /** * Blueprint method that for applying the actual implementation. * * @param methodVisitor The method visitor to which the implementation is applied to. * @param implementationContext The implementation context for the given implementation. * @param instrumentedMethod The instrumented method that is target of the implementation. * @param fixedValueType A description of the type of the fixed value that is loaded by the * {@code valueLoadingInstruction}. * @param valueLoadingInstruction A stack manipulation that represents the loading of the fixed value onto the * operand stack. * @return A representation of the stack and variable array sized that are required for this implementation. */ protected ByteCodeAppender.Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod, TypeDescription fixedValueType, StackManipulation valueLoadingInstruction) { StackManipulation assignment = assigner.assign(fixedValueType, instrumentedMethod.getReturnType(), dynamicallyTyped); if (!assignment.isValid()) { throw new IllegalArgumentException("Cannot return value of type " + fixedValueType + " for " + instrumentedMethod); } StackManipulation.Size stackSize = new StackManipulation.Compound( valueLoadingInstruction, assignment, MethodReturn.returning(instrumentedMethod.getReturnType()) ).apply(methodVisitor, implementationContext); return new ByteCodeAppender.Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize()); } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && dynamicallyTyped == ((FixedValue) other).dynamicallyTyped && assigner.equals(((FixedValue) other).assigner); } @Override public int hashCode() { return 31 * assigner.hashCode() + (dynamicallyTyped ? 1 : 0); } /** * Represents a fixed value implementation that is using a default assigner for attempting to assign * the fixed value to the return type of the instrumented method. */ public interface AssignerConfigurable extends Implementation { /** * Defines an explicit assigner to this fixed value implementation. * * @param assigner The assigner to use for assigning the fixed value to the return type of the * instrumented value. * @param dynamicallyTyped If {@code true}, the runtime type of the given value will be considered for * assigning the return type. * @return A fixed value implementation that makes use of the given assigner. */ Implementation withAssigner(Assigner assigner, boolean dynamicallyTyped); } /** * A fixed value implementation that represents its fixed value as a value that is written to the instrumented * class's constant pool. */ protected static class ForPoolValue extends FixedValue implements AssignerConfigurable, ByteCodeAppender { /** * The stack manipulation which is responsible for loading the fixed value onto the operand stack. */ private final StackManipulation valueLoadInstruction; /** * The type of the fixed value. */ private final TypeDescription loadedType; /** * Creates a new constant pool fixed value implementation. * * @param valueLoadInstruction The instruction that is responsible for loading the constant pool value onto the * operand stack. * @param loadedType A type description representing the loaded type. * @param assigner The assigner to use for assigning the fixed value to the return type of the * instrumented value. * @param dynamicallyTyped If {@code true}, the runtime type of the given value will be considered for * assigning the return type. */ private ForPoolValue(StackManipulation valueLoadInstruction, TypeDescription loadedType, Assigner assigner, boolean dynamicallyTyped) { super(assigner, dynamicallyTyped); this.valueLoadInstruction = valueLoadInstruction; this.loadedType = loadedType; } @Override public Implementation withAssigner(Assigner assigner, boolean dynamicallyTyped) { return new ForPoolValue(valueLoadInstruction, loadedType, nonNull(assigner), dynamicallyTyped); } @Override public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; } @Override public ByteCodeAppender appender(Target implementationTarget) { return this; } @Override public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { return apply(methodVisitor, implementationContext, instrumentedMethod, loadedType, valueLoadInstruction); } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && super.equals(other) && loadedType.equals(((ForPoolValue) other).loadedType) && valueLoadInstruction.equals(((ForPoolValue) other).valueLoadInstruction); } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + valueLoadInstruction.hashCode(); result = 31 * result + loadedType.hashCode(); return result; } @Override public String toString() { return "FixedValue.ForPoolValue{" + "valueLoadInstruction=" + valueLoadInstruction + ", loadedType=" + loadedType + ", assigner=" + assigner + ", dynamicallyTyped=" + dynamicallyTyped + '}'; } } /** * A fixed value implementation that represents its fixed value as a static field of the instrumented class. */ protected static class ForStaticField extends FixedValue implements AssignerConfigurable { /** * The prefix of the static field that is created for storing the fixed value. */ private static final String PREFIX = "fixedValue"; /** * The name of the field in which the fixed value is stored. */ private final String fieldName; /** * The value that is to be stored in the static field. */ private final Object fixedValue; /** * The type if the field for storing the fixed value. */ private final TypeDescription fieldType; /** * Creates a new static field fixed value implementation with a random name for the field containing the fixed * value. * * @param fixedValue The fixed value to be returned. * @param assigner The assigner to use for assigning the fixed value to the return type of the * instrumented value. * @param dynamicallyTyped If {@code true}, the runtime type of the given value will be considered for * assigning the return type. */ protected ForStaticField(Object fixedValue, Assigner assigner, boolean dynamicallyTyped) { this(String.format("%s$%d", PREFIX, Math.abs(fixedValue.hashCode())), fixedValue, assigner, dynamicallyTyped); } /** * Creates a new static field fixed value implementation. * * @param fieldName The name of the field for storing the fixed value. * @param fixedValue The fixed value to be returned. * @param assigner The assigner to use for assigning the fixed value to the return type of the * instrumented value. * @param dynamicallyTyped If {@code true}, the runtime type of the given value will be considered for * assigning the return type. */ protected ForStaticField(String fieldName, Object fixedValue, Assigner assigner, boolean dynamicallyTyped) { super(assigner, dynamicallyTyped); this.fieldName = fieldName; this.fixedValue = fixedValue; fieldType = new TypeDescription.ForLoadedType(fixedValue.getClass()); } @Override public Implementation withAssigner(Assigner assigner, boolean dynamicallyTyped) { return new ForStaticField(fieldName, fixedValue, nonNull(assigner), dynamicallyTyped); } @Override public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType .withField(fieldName, fieldType, Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC) .withInitializer(LoadedTypeInitializer.ForStaticField.nonAccessible(fieldName, fixedValue)); } @Override public ByteCodeAppender appender(Target implementationTarget) { return new StaticFieldByteCodeAppender(implementationTarget.getTypeDescription()); } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && fieldName.equals(((ForStaticField) other).fieldName) && fixedValue.equals(((ForStaticField) other).fixedValue) && super.equals(other); } @Override public int hashCode() { return 31 * 31 * super.hashCode() + 31 * fieldName.hashCode() + fixedValue.hashCode(); } @Override public String toString() { return "FixedValue.ForStaticField{" + "fieldName='" + fieldName + '\'' + ", fieldType=" + fieldType + ", fixedValue=" + fixedValue + ", assigner=" + assigner + ", dynamicallyTyped=" + dynamicallyTyped + '}'; } /** * A byte code appender for returning the fixed value that was stored in a static field. */ private class StaticFieldByteCodeAppender implements ByteCodeAppender { /** * The stack manipulation that loads the fixed value onto the operand stack. */ private final StackManipulation fieldGetAccess; /** * Creates a new byte code appender for returning a value of a static field from an instrumented method. * * @param instrumentedType The instrumented type that is subject of the instrumentation. */ private StaticFieldByteCodeAppender(TypeDescription instrumentedType) { fieldGetAccess = FieldAccess.forField(instrumentedType.getDeclaredFields().filter((named(fieldName))).getOnly()).getter(); } @Override public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { return ForStaticField.this.apply(methodVisitor, implementationContext, instrumentedMethod, fieldType, fieldGetAccess); } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && fieldGetAccess.equals(((StaticFieldByteCodeAppender) other).fieldGetAccess); } @Override public int hashCode() { return fieldGetAccess.hashCode(); } @Override public String toString() { return "StaticFieldByteCodeAppender{fieldGetAccess=" + fieldGetAccess + '}'; } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy