/*
* Copyright 2014 - 2020 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.attribute;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.ParameterDescription;
import net.bytebuddy.description.method.ParameterList;
import net.bytebuddy.description.type.TypeDescription;
import org.objectweb.asm.MethodVisitor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static net.bytebuddy.matcher.ElementMatchers.*;
An appender that writes attributes or annotations to a given ASM MethodVisitor
. /**
* An appender that writes attributes or annotations to a given ASM {@link org.objectweb.asm.MethodVisitor}.
*/
public interface MethodAttributeAppender {
Applies this attribute appender to a given method visitor.
Params: - methodVisitor – The method visitor to which the attributes that are represented by this attribute
appender are written to.
- methodDescription – The description of the method for which the given method visitor creates an
instrumentation for.
- annotationValueFilter – The annotation value filter to apply when the annotations are written.
/**
* Applies this attribute appender to a given method visitor.
*
* @param methodVisitor The method visitor to which the attributes that are represented by this attribute
* appender are written to.
* @param methodDescription The description of the method for which the given method visitor creates an
* instrumentation for.
* @param annotationValueFilter The annotation value filter to apply when the annotations are written.
*/
void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter);
A method attribute appender that does not append any attributes.
/**
* A method attribute appender that does not append any attributes.
*/
enum NoOp implements MethodAttributeAppender, Factory {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodAttributeAppender make(TypeDescription typeDescription) {
return this;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter) {
/* do nothing */
}
}
A factory that creates method attribute appenders for a given type.
/**
* A factory that creates method attribute appenders for a given type.
*/
interface Factory {
Returns a method attribute appender that is applicable for a given type description.
Params: - typeDescription – The type for which a method attribute appender is to be applied for.
Returns: The method attribute appender which should be applied for the given type.
/**
* Returns a method attribute appender that is applicable for a given type description.
*
* @param typeDescription The type for which a method attribute appender is to be applied for.
* @return The method attribute appender which should be applied for the given type.
*/
MethodAttributeAppender make(TypeDescription typeDescription);
A method attribute appender factory that combines several method attribute appender factories to be
represented as a single factory.
/**
* A method attribute appender factory that combines several method attribute appender factories to be
* represented as a single factory.
*/
@HashCodeAndEqualsPlugin.Enhance
class Compound implements Factory {
The factories this compound factory represents in their application order.
/**
* The factories this compound factory represents in their application order.
*/
private final List<Factory> factories;
Creates a new compound method attribute appender factory.
Params: - factory – The factories that are to be combined by this compound factory in the order of their application.
/**
* Creates a new compound method attribute appender factory.
*
* @param factory The factories that are to be combined by this compound factory in the order of their application.
*/
public Compound(Factory... factory) {
this(Arrays.asList(factory));
}
Creates a new compound method attribute appender factory.
Params: - factories – The factories that are to be combined by this compound factory in the order of their application.
/**
* Creates a new compound method attribute appender factory.
*
* @param factories The factories that are to be combined by this compound factory in the order of their application.
*/
public Compound(List<? extends Factory> factories) {
this.factories = new ArrayList<Factory>();
for (Factory factory : factories) {
if (factory instanceof Compound) {
this.factories.addAll(((Compound) factory).factories);
} else if (!(factory instanceof NoOp)) {
this.factories.add(factory);
}
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodAttributeAppender make(TypeDescription typeDescription) {
List<MethodAttributeAppender> methodAttributeAppenders = new ArrayList<MethodAttributeAppender>(factories.size());
for (Factory factory : factories) {
methodAttributeAppenders.add(factory.make(typeDescription));
}
return new MethodAttributeAppender.Compound(methodAttributeAppenders);
}
}
}
Implementation of a method attribute appender that writes all annotations of the instrumented method to the
method that is being created. This includes method and parameter annotations.
Important: This attribute appender does not apply for annotation types within the jdk.internal.
namespace which are silently ignored. If such annotations should be inherited, they need to be added explicitly.
/**
* <p>
* Implementation of a method attribute appender that writes all annotations of the instrumented method to the
* method that is being created. This includes method and parameter annotations.
* </p>
* <p>
* <b>Important</b>: This attribute appender does not apply for annotation types within the {@code jdk.internal.} namespace
* which are silently ignored. If such annotations should be inherited, they need to be added explicitly.
* </p>
*/
enum ForInstrumentedMethod implements MethodAttributeAppender, Factory {
Appends all annotations of the instrumented method but not the annotations of the method's receiver type if such a type exists.
/**
* Appends all annotations of the instrumented method but not the annotations of the method's receiver type if such a type exists.
*/
EXCLUDING_RECEIVER {
@Override
protected AnnotationAppender appendReceiver(AnnotationAppender annotationAppender,
AnnotationValueFilter annotationValueFilter,
MethodDescription methodDescription) {
return annotationAppender;
}
},
Appends all annotations of the instrumented method including the annotations of the method's receiver type if such a type exists.
If a method is overridden, the annotations can be misplaced if the overriding class does not expose a similar structure to
the method that declared the method, i.e. the same amount of type variables and similar owner types. If this is not the case,
type annotations are appended as if the overridden method was declared by the original type. This does not corrupt the resulting
class file but it might result in type annotations not being visible via core reflection. This might however confuse other tools
that parse the resulting class file manually.
/**
* <p>
* Appends all annotations of the instrumented method including the annotations of the method's receiver type if such a type exists.
* </p>
* <p>
* If a method is overridden, the annotations can be misplaced if the overriding class does not expose a similar structure to
* the method that declared the method, i.e. the same amount of type variables and similar owner types. If this is not the case,
* type annotations are appended as if the overridden method was declared by the original type. This does not corrupt the resulting
* class file but it might result in type annotations not being visible via core reflection. This might however confuse other tools
* that parse the resulting class file manually.
* </p>
*/
INCLUDING_RECEIVER {
@Override
protected AnnotationAppender appendReceiver(AnnotationAppender annotationAppender,
AnnotationValueFilter annotationValueFilter,
MethodDescription methodDescription) {
TypeDescription.Generic receiverType = methodDescription.getReceiverType();
return receiverType == null
? annotationAppender
: receiverType.accept(AnnotationAppender.ForTypeAnnotations.ofReceiverType(annotationAppender, annotationValueFilter));
}
};
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodAttributeAppender make(TypeDescription typeDescription) {
return this;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter) {
AnnotationAppender annotationAppender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnMethod(methodVisitor));
annotationAppender = methodDescription.getReturnType().accept(AnnotationAppender.ForTypeAnnotations.ofMethodReturnType(annotationAppender,
annotationValueFilter));
annotationAppender = AnnotationAppender.ForTypeAnnotations.ofTypeVariable(annotationAppender,
annotationValueFilter,
AnnotationAppender.ForTypeAnnotations.VARIABLE_ON_INVOKEABLE,
methodDescription.getTypeVariables());
for (AnnotationDescription annotation : methodDescription.getDeclaredAnnotations().filter(not(annotationType(nameStartsWith("jdk.internal."))))) {
annotationAppender = annotationAppender.append(annotation, annotationValueFilter);
}
for (ParameterDescription parameterDescription : methodDescription.getParameters()) {
AnnotationAppender parameterAppender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnMethodParameter(methodVisitor,
parameterDescription.getIndex()));
parameterAppender = parameterDescription.getType().accept(AnnotationAppender.ForTypeAnnotations.ofMethodParameterType(parameterAppender,
annotationValueFilter,
parameterDescription.getIndex()));
for (AnnotationDescription annotation : parameterDescription.getDeclaredAnnotations()) {
parameterAppender = parameterAppender.append(annotation, annotationValueFilter);
}
}
annotationAppender = appendReceiver(annotationAppender, annotationValueFilter, methodDescription);
int exceptionTypeIndex = 0;
for (TypeDescription.Generic exceptionType : methodDescription.getExceptionTypes()) {
annotationAppender = exceptionType.accept(AnnotationAppender.ForTypeAnnotations.ofExceptionType(annotationAppender,
annotationValueFilter,
exceptionTypeIndex++));
}
}
Appends the annotations of the instrumented method's receiver type if this is enabled and such a type exists.
Params: - annotationAppender – The annotation appender to use.
- annotationValueFilter – The annotation value filter to apply when the annotations are written.
- methodDescription – The instrumented method.
Returns: The resulting annotation appender.
/**
* Appends the annotations of the instrumented method's receiver type if this is enabled and such a type exists.
*
* @param annotationAppender The annotation appender to use.
* @param annotationValueFilter The annotation value filter to apply when the annotations are written.
* @param methodDescription The instrumented method.
* @return The resulting annotation appender.
*/
protected abstract AnnotationAppender appendReceiver(AnnotationAppender annotationAppender,
AnnotationValueFilter annotationValueFilter,
MethodDescription methodDescription);
}
Appends an annotation to a method or method parameter. The visibility of the annotation is determined by the annotation type's RetentionPolicy
annotation. /**
* Appends an annotation to a method or method parameter. The visibility of the annotation is determined by the
* annotation type's {@link java.lang.annotation.RetentionPolicy} annotation.
*/
@HashCodeAndEqualsPlugin.Enhance
class Explicit implements MethodAttributeAppender, Factory {
The target to which the annotations are written to.
/**
* The target to which the annotations are written to.
*/
private final Target target;
the annotations this method attribute appender is writing to its target.
/**
* the annotations this method attribute appender is writing to its target.
*/
private final List<? extends AnnotationDescription> annotations;
Creates a new appender for appending an annotation to a method.
Params: - parameterIndex – The index of the parameter to which the annotations should be written.
- annotations – The annotations that should be written.
/**
* Creates a new appender for appending an annotation to a method.
*
* @param parameterIndex The index of the parameter to which the annotations should be written.
* @param annotations The annotations that should be written.
*/
public Explicit(int parameterIndex, List<? extends AnnotationDescription> annotations) {
this(new Target.OnMethodParameter(parameterIndex), annotations);
}
Creates a new appender for appending an annotation to a method.
Params: - annotations – The annotations that should be written.
/**
* Creates a new appender for appending an annotation to a method.
*
* @param annotations The annotations that should be written.
*/
public Explicit(List<? extends AnnotationDescription> annotations) {
this(Target.OnMethod.INSTANCE, annotations);
}
Creates an explicit annotation appender for a either a method or one of its parameters..
Params: - target – The target to which the annotation should be written to.
- annotations – The annotations to write.
/**
* Creates an explicit annotation appender for a either a method or one of its parameters..
*
* @param target The target to which the annotation should be written to.
* @param annotations The annotations to write.
*/
protected Explicit(Target target, List<? extends AnnotationDescription> annotations) {
this.target = target;
this.annotations = annotations;
}
Creates a method attribute appender factory that writes all annotations of a given method, both the method
annotations themselves and all annotations that are defined for every parameter.
Params: - methodDescription – The method from which to extract the annotations.
Returns: A method attribute appender factory for an appender that writes all annotations of the supplied method.
/**
* Creates a method attribute appender factory that writes all annotations of a given method, both the method
* annotations themselves and all annotations that are defined for every parameter.
*
* @param methodDescription The method from which to extract the annotations.
* @return A method attribute appender factory for an appender that writes all annotations of the supplied method.
*/
public static Factory of(MethodDescription methodDescription) {
return new Factory.Compound(ofMethodAnnotations(methodDescription), ofParameterAnnotations(methodDescription));
}
Creates a method attribute appender factory that writes all method annotations that are defined on the given method.
Params: - methodDescription – The method from which to extract the method annotations.
Returns: A method attribute appender factory for an appender that writes all method annotations of the supplied method.
/**
* Creates a method attribute appender factory that writes all method annotations that are defined on the given method.
*
* @param methodDescription The method from which to extract the method annotations.
* @return A method attribute appender factory for an appender that writes all method annotations of the supplied method.
*/
public static Factory ofMethodAnnotations(MethodDescription methodDescription) {
return new Explicit(methodDescription.getDeclaredAnnotations());
}
Creates a method attribute appender factory that writes all annotations that are defined for every parameter
of the given method.
Params: - methodDescription – The method from which to extract the parameter annotations.
Returns: A method attribute appender factory for an appender that writes all parameter annotations of the supplied method.
/**
* Creates a method attribute appender factory that writes all annotations that are defined for every parameter
* of the given method.
*
* @param methodDescription The method from which to extract the parameter annotations.
* @return A method attribute appender factory for an appender that writes all parameter annotations of the supplied method.
*/
public static Factory ofParameterAnnotations(MethodDescription methodDescription) {
ParameterList<?> parameters = methodDescription.getParameters();
List<MethodAttributeAppender.Factory> factories = new ArrayList<MethodAttributeAppender.Factory>(parameters.size());
for (ParameterDescription parameter : parameters) {
factories.add(new Explicit(parameter.getIndex(), parameter.getDeclaredAnnotations()));
}
return new Factory.Compound(factories);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodAttributeAppender make(TypeDescription typeDescription) {
return this;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter) {
AnnotationAppender appender = new AnnotationAppender.Default(target.make(methodVisitor, methodDescription));
for (AnnotationDescription annotation : annotations) {
appender = appender.append(annotation, annotationValueFilter);
}
}
Represents the target on which this method attribute appender should write its annotations to.
/**
* Represents the target on which this method attribute appender should write its annotations to.
*/
protected interface Target {
Materializes the target for a given creation process.
Params: - methodVisitor – The method visitor to which the attributes that are represented by this
attribute appender are written to.
- methodDescription – The description of the method for which the given method visitor creates an
instrumentation for.
Returns: The target of the annotation appender this target represents.
/**
* Materializes the target for a given creation process.
*
* @param methodVisitor The method visitor to which the attributes that are represented by this
* attribute appender are written to.
* @param methodDescription The description of the method for which the given method visitor creates an
* instrumentation for.
* @return The target of the annotation appender this target represents.
*/
AnnotationAppender.Target make(MethodVisitor methodVisitor, MethodDescription methodDescription);
A method attribute appender target for writing annotations directly onto the method.
/**
* A method attribute appender target for writing annotations directly onto the method.
*/
enum OnMethod implements Target {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public AnnotationAppender.Target make(MethodVisitor methodVisitor, MethodDescription methodDescription) {
return new AnnotationAppender.Target.OnMethod(methodVisitor);
}
}
A method attribute appender target for writing annotations onto a given method parameter.
/**
* A method attribute appender target for writing annotations onto a given method parameter.
*/
@HashCodeAndEqualsPlugin.Enhance
class OnMethodParameter implements Target {
The index of the parameter to write the annotation to.
/**
* The index of the parameter to write the annotation to.
*/
private final int parameterIndex;
Creates a target for a method attribute appender for a method parameter of the given index.
Params: - parameterIndex – The index of the target parameter.
/**
* Creates a target for a method attribute appender for a method parameter of the given index.
*
* @param parameterIndex The index of the target parameter.
*/
protected OnMethodParameter(int parameterIndex) {
this.parameterIndex = parameterIndex;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public AnnotationAppender.Target make(MethodVisitor methodVisitor, MethodDescription methodDescription) {
if (parameterIndex >= methodDescription.getParameters().size()) {
throw new IllegalArgumentException("Method " + methodDescription + " has less then " + parameterIndex + " parameters");
}
return new AnnotationAppender.Target.OnMethodParameter(methodVisitor, parameterIndex);
}
}
}
}
A method attribute appender that writes a receiver type.
/**
* A method attribute appender that writes a receiver type.
*/
@HashCodeAndEqualsPlugin.Enhance
class ForReceiverType implements MethodAttributeAppender, Factory {
The receiver type for which annotations are appended to the instrumented method.
/**
* The receiver type for which annotations are appended to the instrumented method.
*/
private final TypeDescription.Generic receiverType;
Creates a new attribute appender that writes a receiver type.
Params: - receiverType – The receiver type for which annotations are appended to the instrumented method.
/**
* Creates a new attribute appender that writes a receiver type.
*
* @param receiverType The receiver type for which annotations are appended to the instrumented method.
*/
public ForReceiverType(TypeDescription.Generic receiverType) {
this.receiverType = receiverType;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodAttributeAppender make(TypeDescription typeDescription) {
return this;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter) {
receiverType.accept(AnnotationAppender.ForTypeAnnotations.ofReceiverType(new AnnotationAppender.Default(new AnnotationAppender.Target.OnMethod(methodVisitor)), annotationValueFilter));
}
}
A method attribute appender that combines several method attribute appenders to be represented as a single
method attribute appender.
/**
* A method attribute appender that combines several method attribute appenders to be represented as a single
* method attribute appender.
*/
@HashCodeAndEqualsPlugin.Enhance
class Compound implements MethodAttributeAppender {
The method attribute appenders this compound appender represents in their application order.
/**
* The method attribute appenders this compound appender represents in their application order.
*/
private final List<MethodAttributeAppender> methodAttributeAppenders;
Creates a new compound method attribute appender.
Params: - methodAttributeAppender – The method attribute appenders that are to be combined by this compound appender
in the order of their application.
/**
* Creates a new compound method attribute appender.
*
* @param methodAttributeAppender The method attribute appenders that are to be combined by this compound appender
* in the order of their application.
*/
public Compound(MethodAttributeAppender... methodAttributeAppender) {
this(Arrays.asList(methodAttributeAppender));
}
Creates a new compound method attribute appender.
Params: - methodAttributeAppenders – The method attribute appenders that are to be combined by this compound appender
in the order of their application.
/**
* Creates a new compound method attribute appender.
*
* @param methodAttributeAppenders The method attribute appenders that are to be combined by this compound appender
* in the order of their application.
*/
public Compound(List<? extends MethodAttributeAppender> methodAttributeAppenders) {
this.methodAttributeAppenders = new ArrayList<MethodAttributeAppender>();
for (MethodAttributeAppender methodAttributeAppender : methodAttributeAppenders) {
if (methodAttributeAppender instanceof Compound) {
this.methodAttributeAppenders.addAll(((Compound) methodAttributeAppender).methodAttributeAppenders);
} else if (!(methodAttributeAppender instanceof NoOp)) {
this.methodAttributeAppenders.add(methodAttributeAppender);
}
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter) {
for (MethodAttributeAppender methodAttributeAppender : methodAttributeAppenders) {
methodAttributeAppender.apply(methodVisitor, methodDescription, annotationValueFilter);
}
}
}
}