org.testifyproject.bytebuddy.implementation.bytecode.ByteCodeAppender Maven / Gradle / Ivy
package org.testifyproject.bytebuddy.implementation.bytecode;
import org.testifyproject.bytebuddy.description.method.MethodDescription;
import org.testifyproject.bytebuddy.implementation.Implementation;
import org.testifyproject.bytebuddy.jar.asm.MethodVisitor;
import java.util.Arrays;
import java.util.List;
/**
* An appender that generates the byte code for a given method. This is done by writing the byte code instructions to
* the given ASM {@link org.testifyproject.bytebuddy.jar.asm.MethodVisitor}.
*
* The {@code ByteCodeAppender} is not allowed to write
* annotations to the method or call the {@link org.testifyproject.bytebuddy.jar.asm.MethodVisitor#visitCode()},
* {@link org.testifyproject.bytebuddy.jar.asm.MethodVisitor#visitMaxs(int, int)} or {@link org.testifyproject.bytebuddy.jar.asm.MethodVisitor#visitEnd()}
* methods which is both done by the entity delegating the call to the {@code ByteCodeAppender}. This is done in order
* to allow for the concatenation of several byte code appenders and therefore a more modular description of method
* implementations.
*/
public interface ByteCodeAppender {
/**
* Applies this byte code appender to a type creation process.
*
* @param methodVisitor The method visitor to which the byte code appender writes its code to.
* @param implementationContext The implementation context of the current type creation process.
* @param instrumentedMethod The method that is the target of the instrumentation.
* @return The required size for the applied byte code to run.
*/
Size apply(MethodVisitor methodVisitor,
Implementation.Context implementationContext,
MethodDescription instrumentedMethod);
/**
* An immutable description of both the operand stack size and the size of the local variable array that is
* required to run the code generated by this {@code ByteCodeAppender}.
*/
class Size {
/**
* The size of the operand stack.
*/
private final int operandStackSize;
/**
* The size of the local variable array.
*/
private final int localVariableSize;
/**
* @param operandStackSize The operand stack size that is required for running given byte code.
* @param localVariableSize The local variable array size that is required for running given byte code.
*/
public Size(int operandStackSize, int localVariableSize) {
this.operandStackSize = operandStackSize;
this.localVariableSize = localVariableSize;
}
/**
* Returns the required operand stack size.
*
* @return The required operand stack size.
*/
public int getOperandStackSize() {
return operandStackSize;
}
/**
* Returns the required size of the local variable array.
*
* @return The required size of the local variable array.
*/
public int getLocalVariableSize() {
return localVariableSize;
}
/**
* Merges two sizes in order to describe the size that is required by both size descriptions.
*
* @param other The other size description.
* @return A size description incorporating both size requirements.
*/
public Size merge(Size other) {
return new Size(Math.max(operandStackSize, other.operandStackSize), Math.max(localVariableSize, other.localVariableSize));
}
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& localVariableSize == ((Size) other).localVariableSize
&& operandStackSize == ((Size) other).operandStackSize;
}
@Override
public int hashCode() {
return 31 * operandStackSize + localVariableSize;
}
@Override
public String toString() {
return "ByteCodeAppender.Size{operandStackSize=" + operandStackSize + ", localVariableSize=" + localVariableSize + '}';
}
}
/**
* A compound appender that combines a given number of other byte code appenders.
*/
class Compound implements ByteCodeAppender {
/**
* The byte code appenders that are represented by this compound appender in their application order.
*/
private final List extends ByteCodeAppender> byteCodeAppenders;
/**
* Creates a new compound byte code appender.
*
* @param byteCodeAppender The byte code appenders to combine in their order.
*/
public Compound(ByteCodeAppender... byteCodeAppender) {
this(Arrays.asList(byteCodeAppender));
}
/**
* Creates a new compound byte code appender.
*
* @param byteCodeAppenders The byte code appenders to combine in their order.
*/
public Compound(List extends ByteCodeAppender> byteCodeAppenders) {
this.byteCodeAppenders = byteCodeAppenders;
}
@Override
public Size apply(MethodVisitor methodVisitor,
Implementation.Context implementationContext,
MethodDescription instrumentedMethod) {
Size size = new Size(0, instrumentedMethod.getStackSize());
for (ByteCodeAppender byteCodeAppender : byteCodeAppenders) {
size = size.merge(byteCodeAppender.apply(methodVisitor, implementationContext, instrumentedMethod));
}
return size;
}
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& byteCodeAppenders.equals(((Compound) other).byteCodeAppenders);
}
@Override
public int hashCode() {
return byteCodeAppenders.hashCode();
}
@Override
public String toString() {
return "ByteCodeAppender.Compound{byteCodeAppenders=" + byteCodeAppenders + '}';
}
}
/**
* A simple byte code appender that only represents a given array of
* {@link StackManipulation}s.
*/
class Simple implements ByteCodeAppender {
/**
* A compound stack manipulation to be applied for this byte code appender.
*/
private final StackManipulation stackManipulation;
/**
* Creates a new simple byte code appender which represents the given stack manipulation.
*
* @param stackManipulation The stack manipulations to apply for this byte code appender in their application
* order.
*/
public Simple(StackManipulation... stackManipulation) {
this.stackManipulation = new StackManipulation.Compound(stackManipulation);
}
@Override
public Size apply(MethodVisitor methodVisitor,
Implementation.Context implementationContext,
MethodDescription instrumentedMethod) {
return new Size(stackManipulation.apply(methodVisitor, implementationContext).getMaximalSize(),
instrumentedMethod.getStackSize());
}
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& stackManipulation.equals(((Simple) other).stackManipulation);
}
@Override
public int hashCode() {
return stackManipulation.hashCode();
}
@Override
public String toString() {
return "ByteCodeAppender.Simple{stackManipulation=" + stackManipulation + '}';
}
}
}