/*
* Copyright 2014 - 2018 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;
import net.bytebuddy.build.
HashCodeAndEqualsPlugin;
import net.bytebuddy.description.method.
MethodDescription;
import net.bytebuddy.implementation.
Implementation;
import net.bytebuddy.jar.asm.
MethodVisitor;
import java.util.
ArrayList;
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 net.bytebuddy.jar.asm.MethodVisitor}.
* <p> </p>
* The {@code ByteCodeAppender} is not allowed to write
* annotations to the method or call the {@link net.bytebuddy.jar.asm.MethodVisitor#visitCode()},
* {@link net.bytebuddy.jar.asm.MethodVisitor#visitMaxs(int, int)} or {@link net.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}.
*/
@
HashCodeAndEqualsPlugin.
Enhance
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));
}
}
/**
* A compound appender that combines a given number of other byte code appenders.
*/
@
HashCodeAndEqualsPlugin.
Enhance
class
Compound implements
ByteCodeAppender {
/**
* The byte code appenders that are represented by this compound appender in their application order.
*/
private final
List<
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 = new
ArrayList<
ByteCodeAppender>();
for (
ByteCodeAppender byteCodeAppender :
byteCodeAppenders) {
if (
byteCodeAppender instanceof
Compound) {
this.
byteCodeAppenders.
addAll(((
Compound)
byteCodeAppender).
byteCodeAppenders);
} else {
this.
byteCodeAppenders.
add(
byteCodeAppender);
}
}
}
/**
* {@inheritDoc}
*/
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;
}
}
/**
* A simple byte code appender that only represents a given array of
* {@link StackManipulation}s.
*/
@
HashCodeAndEqualsPlugin.
Enhance
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(
Arrays.
asList(
stackManipulation));
}
/**
* Creates a new simple byte code appender which represents the given stack manipulation.
*
* @param stackManipulations The stack manipulations to apply for this byte code appender in their application order.
*/
public
Simple(
List<? extends
StackManipulation>
stackManipulations) {
this.
stackManipulation = new
StackManipulation.
Compound(
stackManipulations);
}
/**
* {@inheritDoc}
*/
public
Size apply(
MethodVisitor methodVisitor,
Implementation.
Context implementationContext,
MethodDescription instrumentedMethod) {
return new
Size(
stackManipulation.
apply(
methodVisitor,
implementationContext).
getMaximalSize(),
instrumentedMethod.
getStackSize());
}
}
}