/*
* 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;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.build.CachedReturnPlugin;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.annotation.AnnotationList;
import net.bytebuddy.description.annotation.AnnotationValue;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.ParameterDescription;
import net.bytebuddy.description.method.ParameterList;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeList;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.dynamic.scaffold.MethodGraph;
import net.bytebuddy.dynamic.scaffold.TypeInitializer;
import net.bytebuddy.dynamic.scaffold.TypeWriter;
import net.bytebuddy.implementation.attribute.AnnotationValueFilter;
import net.bytebuddy.implementation.auxiliary.AuxiliaryType;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
import net.bytebuddy.utility.CompoundList;
import net.bytebuddy.utility.RandomString;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.*;
An implementation is responsible for implementing methods of a dynamically created type as byte code. An
implementation is applied in two stages:
- The implementation is able to prepare an instrumented type by adding fields and/or helper methods that are required for the methods implemented by this implementation. Furthermore,
LoadedTypeInitializer
s and byte code for the type initializer can be registered for the instrumented type.
- Any implementation is required to supply a byte code appender that is responsible for providing the byte code
to the instrumented methods that were delegated to this implementation. This byte code appender is also
be responsible for providing implementations for the methods added in step 1.
An implementation should provide meaningful implementations of both Object.equals(Object)
and Object.hashCode()
if it wants to avoid to be used twice within the creation of a dynamic type. For two equal implementations only one will be applied on the creation of a dynamic type. /**
* An implementation is responsible for implementing methods of a dynamically created type as byte code. An
* implementation is applied in two stages:
* <ol>
* <li>The implementation is able to prepare an instrumented type by adding fields and/or helper methods that are
* required for the methods implemented by this implementation. Furthermore,
* {@link LoadedTypeInitializer}s and byte code for the type initializer can be registered for the instrumented
* type.</li>
* <li>Any implementation is required to supply a byte code appender that is responsible for providing the byte code
* to the instrumented methods that were delegated to this implementation. This byte code appender is also
* be responsible for providing implementations for the methods added in step <i>1</i>.</li>
* </ol>
* <p> </p>
* An implementation should provide meaningful implementations of both {@link java.lang.Object#equals(Object)}
* and {@link Object#hashCode()} if it wants to avoid to be used twice within the creation of a dynamic type. For two
* equal implementations only one will be applied on the creation of a dynamic type.
*/
public interface Implementation extends InstrumentedType.Prepareable {
Creates a byte code appender that determines the implementation of the instrumented type's methods.
Params: - implementationTarget – The target of the current implementation.
Returns: A byte code appender for implementing methods delegated to this implementation. This byte code appender is also responsible for handling methods that were added by this implementation on the call to Prepareable.prepare(InstrumentedType)
.
/**
* Creates a byte code appender that determines the implementation of the instrumented type's methods.
*
* @param implementationTarget The target of the current implementation.
* @return A byte code appender for implementing methods delegated to this implementation. This byte code appender
* is also responsible for handling methods that were added by this implementation on the call to
* {@link Implementation#prepare(InstrumentedType)}.
*/
ByteCodeAppender appender(Target implementationTarget);
Represents an implementation that can be chained together with another implementation.
/**
* Represents an implementation that can be chained together with another implementation.
*/
interface Composable extends Implementation {
Appends the supplied implementation to this implementation.
Params: - implementation – The subsequent implementation.
Returns: An implementation that combines this implementation with the provided one.
/**
* Appends the supplied implementation to this implementation.
*
* @param implementation The subsequent implementation.
* @return An implementation that combines this implementation with the provided one.
*/
Implementation andThen(Implementation implementation);
Appends the supplied composable implementation to this implementation.
Params: - implementation – The subsequent composable implementation.
Returns: A composable implementation that combines this implementation with the provided one.
/**
* Appends the supplied composable implementation to this implementation.
*
* @param implementation The subsequent composable implementation.
* @return A composable implementation that combines this implementation with the provided one.
*/
Composable andThen(Composable implementation);
}
Represents an type-specific method invocation on the current instrumented type which is not legal from outside
the type such as a super method or default method invocation. Legal instances of special method invocations must
be equal to one another if they represent the same invocation target.
/**
* Represents an type-specific method invocation on the current instrumented type which is not legal from outside
* the type such as a super method or default method invocation. Legal instances of special method invocations must
* be equal to one another if they represent the same invocation target.
*/
interface SpecialMethodInvocation extends StackManipulation {
Returns the method that represents this special method invocation. This method can be different even for equal special method invocations, dependant on the method that was used to request such an invocation by the means of a Target
. Returns: The method description that describes this instances invocation target.
/**
* Returns the method that represents this special method invocation. This method can be different even for
* equal special method invocations, dependant on the method that was used to request such an invocation by the
* means of a {@link Implementation.Target}.
*
* @return The method description that describes this instances invocation target.
*/
MethodDescription getMethodDescription();
Returns the target type the represented method is invoked on.
Returns: The type the represented method is invoked on.
/**
* Returns the target type the represented method is invoked on.
*
* @return The type the represented method is invoked on.
*/
TypeDescription getTypeDescription();
Checks that this special method invocation is compatible with the supplied type representation.
Params: - token – The type token to check against.
Returns: This special method invocation or an illegal invocation if the method invocation is not applicable.
/**
* Checks that this special method invocation is compatible with the supplied type representation.
*
* @param token The type token to check against.
* @return This special method invocation or an illegal invocation if the method invocation is not applicable.
*/
SpecialMethodInvocation withCheckedCompatibilityTo(MethodDescription.TypeToken token);
A canonical implementation of an illegal SpecialMethodInvocation
. /**
* A canonical implementation of an illegal {@link Implementation.SpecialMethodInvocation}.
*/
enum Illegal implements SpecialMethodInvocation {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isValid() {
return false;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
throw new IllegalStateException("Cannot implement an undefined method");
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription getMethodDescription() {
throw new IllegalStateException("An illegal special method invocation must not be applied");
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription getTypeDescription() {
throw new IllegalStateException("An illegal special method invocation must not be applied");
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public SpecialMethodInvocation withCheckedCompatibilityTo(MethodDescription.TypeToken token) {
return this;
}
}
An abstract base implementation of a valid special method invocation.
/**
* An abstract base implementation of a valid special method invocation.
*/
abstract class AbstractBase implements SpecialMethodInvocation {
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isValid() {
return true;
}
@Override
@CachedReturnPlugin.Enhance("hashCode")
public int hashCode() {
return 31 * getMethodDescription().asSignatureToken().hashCode() + getTypeDescription().hashCode();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (!(other instanceof SpecialMethodInvocation)) {
return false;
}
SpecialMethodInvocation specialMethodInvocation = (SpecialMethodInvocation) other;
return getMethodDescription().asSignatureToken().equals(specialMethodInvocation.getMethodDescription().asSignatureToken())
&& getTypeDescription().equals(specialMethodInvocation.getTypeDescription());
}
}
A canonical implementation of a SpecialMethodInvocation
. /**
* A canonical implementation of a {@link SpecialMethodInvocation}.
*/
class Simple extends SpecialMethodInvocation.AbstractBase {
The method description that is represented by this legal special method invocation.
/**
* The method description that is represented by this legal special method invocation.
*/
private final MethodDescription methodDescription;
The type description that is represented by this legal special method invocation.
/**
* The type description that is represented by this legal special method invocation.
*/
private final TypeDescription typeDescription;
A stack manipulation representing the method's invocation on the type description.
/**
* A stack manipulation representing the method's invocation on the type description.
*/
private final StackManipulation stackManipulation;
Creates a new legal special method invocation.
Params: - methodDescription – The method that represents the special method invocation.
- typeDescription – The type on which the method should be invoked on by an
INVOKESPECIAL
invocation. - stackManipulation – The stack manipulation that represents this special method invocation.
/**
* Creates a new legal special method invocation.
*
* @param methodDescription The method that represents the special method invocation.
* @param typeDescription The type on which the method should be invoked on by an {@code INVOKESPECIAL}
* invocation.
* @param stackManipulation The stack manipulation that represents this special method invocation.
*/
protected Simple(MethodDescription methodDescription, TypeDescription typeDescription, StackManipulation stackManipulation) {
this.methodDescription = methodDescription;
this.typeDescription = typeDescription;
this.stackManipulation = stackManipulation;
}
Creates a special method invocation for a given invocation target.
Params: - methodDescription – The method that represents the special method invocation.
- typeDescription – The type on which the method should be invoked on by an
INVOKESPECIAL
invocation.
Returns: A special method invocation representing a legal invocation if the method can be invoked
specially on the target type or an illegal invocation if this is not possible.
/**
* Creates a special method invocation for a given invocation target.
*
* @param methodDescription The method that represents the special method invocation.
* @param typeDescription The type on which the method should be invoked on by an {@code INVOKESPECIAL}
* invocation.
* @return A special method invocation representing a legal invocation if the method can be invoked
* specially on the target type or an illegal invocation if this is not possible.
*/
public static SpecialMethodInvocation of(MethodDescription methodDescription, TypeDescription typeDescription) {
StackManipulation stackManipulation = MethodInvocation.invoke(methodDescription).special(typeDescription);
return stackManipulation.isValid()
? new Simple(methodDescription, typeDescription, stackManipulation)
: SpecialMethodInvocation.Illegal.INSTANCE;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription getMethodDescription() {
return methodDescription;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription getTypeDescription() {
return typeDescription;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
return stackManipulation.apply(methodVisitor, implementationContext);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public SpecialMethodInvocation withCheckedCompatibilityTo(MethodDescription.TypeToken token) {
if (methodDescription.asTypeToken().equals(token)) {
return this;
} else {
return SpecialMethodInvocation.Illegal.INSTANCE;
}
}
}
}
The target of an implementation. Implementation targets must be immutable and can be queried without altering
the implementation result. An implementation target provides information on the type that is to be created
where it is the implementation's responsibility to cache expensive computations, especially such computations
that require reflective look-up.
/**
* The target of an implementation. Implementation targets must be immutable and can be queried without altering
* the implementation result. An implementation target provides information on the type that is to be created
* where it is the implementation's responsibility to cache expensive computations, especially such computations
* that require reflective look-up.
*/
interface Target {
Returns a description of the instrumented type.
Returns: A description of the instrumented type.
/**
* Returns a description of the instrumented type.
*
* @return A description of the instrumented type.
*/
TypeDescription getInstrumentedType();
Identifies the origin type of an implementation. The origin type describes the type that is subject to
any form of enhancement. If a subclass of a given type is generated, the base type of this subclass
describes the origin type. If a given type is redefined or rebased, the origin type is described by the
instrumented type itself.
Returns: The origin type of this implementation.
/**
* Identifies the origin type of an implementation. The origin type describes the type that is subject to
* any form of enhancement. If a subclass of a given type is generated, the base type of this subclass
* describes the origin type. If a given type is redefined or rebased, the origin type is described by the
* instrumented type itself.
*
* @return The origin type of this implementation.
*/
TypeDefinition getOriginType();
Creates a special method invocation for invoking the super method of the given method.
Params: - token – A token of the method that is to be invoked as a super method.
Returns: The corresponding special method invocation which might be illegal if the requested invocation is not legal.
/**
* Creates a special method invocation for invoking the super method of the given method.
*
* @param token A token of the method that is to be invoked as a super method.
* @return The corresponding special method invocation which might be illegal if the requested invocation is not legal.
*/
SpecialMethodInvocation invokeSuper(MethodDescription.SignatureToken token);
Creates a special method invocation for invoking a default method with the given token. The default method call must
not be ambiguous or an illegal special method invocation is returned.
Params: - token – A token of the method that is to be invoked as a default method.
Returns: The corresponding default method invocation which might be illegal if the requested invocation is not legal or ambiguous.
/**
* Creates a special method invocation for invoking a default method with the given token. The default method call must
* not be ambiguous or an illegal special method invocation is returned.
*
* @param token A token of the method that is to be invoked as a default method.
* @return The corresponding default method invocation which might be illegal if the requested invocation is not legal or ambiguous.
*/
SpecialMethodInvocation invokeDefault(MethodDescription.SignatureToken token);
Creates a special method invocation for invoking a default method.
Params: - targetType – The interface on which the default method is to be invoked.
- token – A token that uniquely describes the method to invoke.
Returns: The corresponding special method invocation which might be illegal if the requested invocation is
not legal.
/**
* Creates a special method invocation for invoking a default method.
*
* @param targetType The interface on which the default method is to be invoked.
* @param token A token that uniquely describes the method to invoke.
* @return The corresponding special method invocation which might be illegal if the requested invocation is
* not legal.
*/
SpecialMethodInvocation invokeDefault(MethodDescription.SignatureToken token, TypeDescription targetType);
Invokes a dominant method, i.e. if the method token can be invoked as a super method invocation, this invocation is considered dominant.
Alternatively, a method invocation is attempted on an interface type as a default method invocation only if this invocation is not ambiguous
for several interfaces.
Params: - token – The method token representing the method to be invoked.
Returns: A special method invocation for a method representing the method token.
/**
* Invokes a dominant method, i.e. if the method token can be invoked as a super method invocation, this invocation is considered dominant.
* Alternatively, a method invocation is attempted on an interface type as a default method invocation only if this invocation is not ambiguous
* for several interfaces.
*
* @param token The method token representing the method to be invoked.
* @return A special method invocation for a method representing the method token.
*/
SpecialMethodInvocation invokeDominant(MethodDescription.SignatureToken token);
A factory for creating an Target
. /**
* A factory for creating an {@link Implementation.Target}.
*/
interface Factory {
Creates an implementation target.
Params: - instrumentedType – The instrumented type.
- methodGraph – A method graph of the instrumented type.
- classFileVersion – The type's class file version.
Returns: An implementation target for the instrumented type.
/**
* Creates an implementation target.
*
* @param instrumentedType The instrumented type.
* @param methodGraph A method graph of the instrumented type.
* @param classFileVersion The type's class file version.
* @return An implementation target for the instrumented type.
*/
Target make(TypeDescription instrumentedType, MethodGraph.Linked methodGraph, ClassFileVersion classFileVersion);
}
An abstract base implementation for an Target
. /**
* An abstract base implementation for an {@link Implementation.Target}.
*/
@HashCodeAndEqualsPlugin.Enhance
abstract class AbstractBase implements Target {
The instrumented type.
/**
* The instrumented type.
*/
protected final TypeDescription instrumentedType;
The instrumented type's method graph.
/**
* The instrumented type's method graph.
*/
protected final MethodGraph.Linked methodGraph;
The default method invocation mode to apply.
/**
* The default method invocation mode to apply.
*/
protected final DefaultMethodInvocation defaultMethodInvocation;
Creates a new implementation target.
Params: - instrumentedType – The instrumented type.
- methodGraph – The instrumented type's method graph.
- defaultMethodInvocation – The default method invocation mode to apply.
/**
* Creates a new implementation target.
*
* @param instrumentedType The instrumented type.
* @param methodGraph The instrumented type's method graph.
* @param defaultMethodInvocation The default method invocation mode to apply.
*/
protected AbstractBase(TypeDescription instrumentedType, MethodGraph.Linked methodGraph, DefaultMethodInvocation defaultMethodInvocation) {
this.instrumentedType = instrumentedType;
this.methodGraph = methodGraph;
this.defaultMethodInvocation = defaultMethodInvocation;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription getInstrumentedType() {
return instrumentedType;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public SpecialMethodInvocation invokeDefault(MethodDescription.SignatureToken token) {
SpecialMethodInvocation specialMethodInvocation = SpecialMethodInvocation.Illegal.INSTANCE;
for (TypeDescription interfaceType : instrumentedType.getInterfaces().asErasures()) {
SpecialMethodInvocation invocation = invokeDefault(token, interfaceType).withCheckedCompatibilityTo(token.asTypeToken());
if (invocation.isValid()) {
if (specialMethodInvocation.isValid()) {
return SpecialMethodInvocation.Illegal.INSTANCE;
} else {
specialMethodInvocation = invocation;
}
}
}
return specialMethodInvocation;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public SpecialMethodInvocation invokeDefault(MethodDescription.SignatureToken token, TypeDescription targetType) {
return defaultMethodInvocation.apply(methodGraph.getInterfaceGraph(targetType).locate(token), targetType);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public SpecialMethodInvocation invokeDominant(MethodDescription.SignatureToken token) {
SpecialMethodInvocation specialMethodInvocation = invokeSuper(token);
return specialMethodInvocation.isValid()
? specialMethodInvocation
: invokeDefault(token);
}
Determines if default method invocations are possible.
/**
* Determines if default method invocations are possible.
*/
protected enum DefaultMethodInvocation {
Permits default method invocations, if an interface declaring a default method is possible.
/**
* Permits default method invocations, if an interface declaring a default method is possible.
*/
ENABLED {
@Override
protected SpecialMethodInvocation apply(MethodGraph.Node node, TypeDescription targetType) {
return node.getSort().isUnique()
? SpecialMethodInvocation.Simple.of(node.getRepresentative(), targetType)
: SpecialMethodInvocation.Illegal.INSTANCE;
}
},
Does not permit default method invocations.
/**
* Does not permit default method invocations.
*/
DISABLED {
@Override
protected SpecialMethodInvocation apply(MethodGraph.Node node, TypeDescription targetType) {
return SpecialMethodInvocation.Illegal.INSTANCE;
}
};
Resolves a default method invocation depending on the class file version permitting such calls.
Params: - classFileVersion – The class file version to resolve for.
Returns: A suitable default method invocation mode.
/**
* Resolves a default method invocation depending on the class file version permitting such calls.
*
* @param classFileVersion The class file version to resolve for.
* @return A suitable default method invocation mode.
*/
public static DefaultMethodInvocation of(ClassFileVersion classFileVersion) {
return classFileVersion.isAtLeast(ClassFileVersion.JAVA_V8)
? ENABLED
: DISABLED;
}
Resolves a default method invocation for a given node.
Params: - node – The node representing the default method call.
- targetType – The target type defining the default method.
Returns: A suitable special method invocation.
/**
* Resolves a default method invocation for a given node.
*
* @param node The node representing the default method call.
* @param targetType The target type defining the default method.
* @return A suitable special method invocation.
*/
protected abstract SpecialMethodInvocation apply(MethodGraph.Node node, TypeDescription targetType);
}
}
}
The context for an implementation application. An implementation context represents a mutable data structure where any registration is irrevocable. Calling methods on an implementation context should be considered equally sensitive as calling a MethodVisitor
. As such, an implementation context and a MethodVisitor
are complementary for creating an new Java type. /**
* The context for an implementation application. An implementation context represents a mutable data structure
* where any registration is irrevocable. Calling methods on an implementation context should be considered equally
* sensitive as calling a {@link org.objectweb.asm.MethodVisitor}. As such, an implementation context and a
* {@link org.objectweb.asm.MethodVisitor} are complementary for creating an new Java type.
*/
interface Context extends MethodAccessorFactory {
Registers an auxiliary type as required for the current implementation. Registering a type will cause the
creation of this type even if this type is not effectively used for the current implementation.
Params: - auxiliaryType – The auxiliary type that is required for the current implementation.
Returns: A description of the registered auxiliary type.
/**
* Registers an auxiliary type as required for the current implementation. Registering a type will cause the
* creation of this type even if this type is not effectively used for the current implementation.
*
* @param auxiliaryType The auxiliary type that is required for the current implementation.
* @return A description of the registered auxiliary type.
*/
TypeDescription register(AuxiliaryType auxiliaryType);
Caches a single value by storing it in form of a private
, final
and static
field. By caching values, expensive instance creations can be avoided and object identity can be preserved. The field is initiated in a generated class's static initializer. Params: - fieldValue – A stack manipulation for creating the value that is to be cached in a
static
field. After executing the stack manipulation, exactly one value must be put onto the operand stack which is assignable to the given fieldType
. - fieldType – The type of the field for storing the cached value. This field's type determines the value
that is put onto the operand stack by this method's returned stack manipulation.
Returns: A description of a field that was defined on the instrumented type which contains the given value.
/**
* Caches a single value by storing it in form of a {@code private}, {@code final} and {@code static} field.
* By caching values, expensive instance creations can be avoided and object identity can be preserved.
* The field is initiated in a generated class's static initializer.
*
* @param fieldValue A stack manipulation for creating the value that is to be cached in a {@code static} field.
* After executing the stack manipulation, exactly one value must be put onto the operand
* stack which is assignable to the given {@code fieldType}.
* @param fieldType The type of the field for storing the cached value. This field's type determines the value
* that is put onto the operand stack by this method's returned stack manipulation.
* @return A description of a field that was defined on the instrumented type which contains the given value.
*/
FieldDescription.InDefinedShape cache(StackManipulation fieldValue, TypeDescription fieldType);
Returns the instrumented type of the current implementation. The instrumented type is exposed with the intend of allowing optimal byte code generation and not for implementing checks or changing the behavior of a StackManipulation
. Returns: The instrumented type of the current implementation.
/**
* Returns the instrumented type of the current implementation. The instrumented type is exposed with the intend of allowing optimal
* byte code generation and not for implementing checks or changing the behavior of a {@link StackManipulation}.
*
* @return The instrumented type of the current implementation.
*/
TypeDescription getInstrumentedType();
Returns the class file version of the currently created dynamic type.
Returns: The class file version of the currently created dynamic type.
/**
* Returns the class file version of the currently created dynamic type.
*
* @return The class file version of the currently created dynamic type.
*/
ClassFileVersion getClassFileVersion();
Represents an extractable view of an Context
which allows the retrieval of any registered auxiliary type. /**
* Represents an extractable view of an {@link Implementation.Context} which
* allows the retrieval of any registered auxiliary type.
*/
interface ExtractableView extends Context {
Returns true
if this implementation context permits the registration of any implicit type initializers. Returns: true
if this implementation context permits the registration of any implicit type initializers.
/**
* Returns {@code true} if this implementation context permits the registration of any implicit type initializers.
*
* @return {@code true} if this implementation context permits the registration of any implicit type initializers.
*/
boolean isEnabled();
Returns any AuxiliaryType
that was registered with this Context
. Returns: A list of all manifested registered auxiliary types.
/**
* Returns any {@link net.bytebuddy.implementation.auxiliary.AuxiliaryType} that was registered
* with this {@link Implementation.Context}.
*
* @return A list of all manifested registered auxiliary types.
*/
List<DynamicType> getAuxiliaryTypes();
Writes any information that was registered with an Context
to the provided class visitor. This contains any fields for value caching, any accessor method and it writes the type initializer. The type initializer must therefore never be written manually. Params: - drain – The drain to write the type initializer to.
- classVisitor – The class visitor to which the extractable view is to be written.
- annotationValueFilterFactory – The annotation value filter factory to apply when writing annotation.
/**
* Writes any information that was registered with an {@link Implementation.Context}
* to the provided class visitor. This contains any fields for value caching, any accessor method and it
* writes the type initializer. The type initializer must therefore never be written manually.
*
* @param drain The drain to write the type initializer to.
* @param classVisitor The class visitor to which the extractable view is to be written.
* @param annotationValueFilterFactory The annotation value filter factory to apply when writing annotation.
*/
void drain(TypeInitializer.Drain drain, ClassVisitor classVisitor, AnnotationValueFilter.Factory annotationValueFilterFactory);
An abstract base implementation of an extractable view of an implementation context.
/**
* An abstract base implementation of an extractable view of an implementation context.
*/
@HashCodeAndEqualsPlugin.Enhance
abstract class AbstractBase implements ExtractableView {
The instrumented type.
/**
* The instrumented type.
*/
protected final TypeDescription instrumentedType;
The class file version of the dynamic type.
/**
* The class file version of the dynamic type.
*/
protected final ClassFileVersion classFileVersion;
Create a new extractable view.
Params: - instrumentedType – The instrumented type.
- classFileVersion – The class file version of the dynamic type.
/**
* Create a new extractable view.
*
* @param instrumentedType The instrumented type.
* @param classFileVersion The class file version of the dynamic type.
*/
protected AbstractBase(TypeDescription instrumentedType, ClassFileVersion classFileVersion) {
this.instrumentedType = instrumentedType;
this.classFileVersion = classFileVersion;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription getInstrumentedType() {
return instrumentedType;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ClassFileVersion getClassFileVersion() {
return classFileVersion;
}
}
}
A factory for creating a new implementation context.
/**
* A factory for creating a new implementation context.
*/
interface Factory {
Creates a new implementation context.
Params: - instrumentedType – The description of the type that is currently subject of creation.
- auxiliaryTypeNamingStrategy – The naming strategy for naming an auxiliary type.
- typeInitializer – The type initializer of the created instrumented type.
- classFileVersion – The class file version of the created class.
- auxiliaryClassFileVersion – The class file version of any auxiliary classes.
Returns: An implementation context in its extractable view.
/**
* Creates a new implementation context.
*
* @param instrumentedType The description of the type that is currently subject of creation.
* @param auxiliaryTypeNamingStrategy The naming strategy for naming an auxiliary type.
* @param typeInitializer The type initializer of the created instrumented type.
* @param classFileVersion The class file version of the created class.
* @param auxiliaryClassFileVersion The class file version of any auxiliary classes.
* @return An implementation context in its extractable view.
*/
ExtractableView make(TypeDescription instrumentedType,
AuxiliaryType.NamingStrategy auxiliaryTypeNamingStrategy,
TypeInitializer typeInitializer,
ClassFileVersion classFileVersion,
ClassFileVersion auxiliaryClassFileVersion);
}
An implementation context that does not allow for any injections into the static initializer block. This can be useful when
redefining a class when it is not allowed to add methods to a class what is an implicit requirement when copying the static
initializer block into another method.
/**
* An implementation context that does not allow for any injections into the static initializer block. This can be useful when
* redefining a class when it is not allowed to add methods to a class what is an implicit requirement when copying the static
* initializer block into another method.
*/
class Disabled extends ExtractableView.AbstractBase {
Creates a new disabled implementation context.
Params: - instrumentedType – The instrumented type.
- classFileVersion – The class file version to create the class in.
/**
* Creates a new disabled implementation context.
*
* @param instrumentedType The instrumented type.
* @param classFileVersion The class file version to create the class in.
*/
protected Disabled(TypeDescription instrumentedType, ClassFileVersion classFileVersion) {
super(instrumentedType, classFileVersion);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isEnabled() {
return false;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public List<DynamicType> getAuxiliaryTypes() {
return Collections.emptyList();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void drain(TypeInitializer.Drain drain, ClassVisitor classVisitor, AnnotationValueFilter.Factory annotationValueFilterFactory) {
drain.apply(classVisitor, TypeInitializer.None.INSTANCE, this);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription register(AuxiliaryType auxiliaryType) {
throw new IllegalStateException("Registration of auxiliary types was disabled: " + auxiliaryType);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription.InDefinedShape registerAccessorFor(SpecialMethodInvocation specialMethodInvocation, AccessType accessType) {
throw new IllegalStateException("Registration of method accessors was disabled: " + specialMethodInvocation.getMethodDescription());
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription.InDefinedShape registerGetterFor(FieldDescription fieldDescription, AccessType accessType) {
throw new IllegalStateException("Registration of field accessor was disabled: " + fieldDescription);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription.InDefinedShape registerSetterFor(FieldDescription fieldDescription, AccessType accessType) {
throw new IllegalStateException("Registration of field accessor was disabled: " + fieldDescription);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public FieldDescription.InDefinedShape cache(StackManipulation fieldValue, TypeDescription fieldType) {
throw new IllegalStateException("Field values caching was disabled: " + fieldType);
}
A factory for creating a Disabled
. /**
* A factory for creating a {@link net.bytebuddy.implementation.Implementation.Context.Disabled}.
*/
public enum Factory implements Context.Factory {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ExtractableView make(TypeDescription instrumentedType,
AuxiliaryType.NamingStrategy auxiliaryTypeNamingStrategy,
TypeInitializer typeInitializer,
ClassFileVersion classFileVersion,
ClassFileVersion auxiliaryClassFileVersion) {
if (typeInitializer.isDefined()) {
throw new IllegalStateException("Cannot define type initializer which was explicitly disabled: " + typeInitializer);
}
return new Disabled(instrumentedType, classFileVersion);
}
}
}
A default implementation of an ExtractableView
which serves as its own MethodAccessorFactory
. /**
* A default implementation of an {@link Implementation.Context.ExtractableView}
* which serves as its own {@link MethodAccessorFactory}.
*/
class Default extends ExtractableView.AbstractBase {
The name suffix to be appended to an accessor method.
/**
* The name suffix to be appended to an accessor method.
*/
public static final String ACCESSOR_METHOD_SUFFIX = "accessor";
The name prefix to be prepended to a field storing a cached value.
/**
* The name prefix to be prepended to a field storing a cached value.
*/
public static final String FIELD_CACHE_PREFIX = "cachedValue";
The naming strategy for naming auxiliary types that are registered.
/**
* The naming strategy for naming auxiliary types that are registered.
*/
private final AuxiliaryType.NamingStrategy auxiliaryTypeNamingStrategy;
The type initializer of the created instrumented type.
/**
* The type initializer of the created instrumented type.
*/
private final TypeInitializer typeInitializer;
The class file version to use for auxiliary classes.
/**
* The class file version to use for auxiliary classes.
*/
private final ClassFileVersion auxiliaryClassFileVersion;
A mapping of special method invocations to their accessor methods that each invoke their mapped invocation.
/**
* A mapping of special method invocations to their accessor methods that each invoke their mapped invocation.
*/
private final Map<SpecialMethodInvocation, DelegationRecord> registeredAccessorMethods;
The registered getters.
/**
* The registered getters.
*/
private final Map<FieldDescription, DelegationRecord> registeredGetters;
The registered setters.
/**
* The registered setters.
*/
private final Map<FieldDescription, DelegationRecord> registeredSetters;
A map of registered auxiliary types to their dynamic type representation.
/**
* A map of registered auxiliary types to their dynamic type representation.
*/
private final Map<AuxiliaryType, DynamicType> auxiliaryTypes;
A map of already registered field caches to their field representation.
/**
* A map of already registered field caches to their field representation.
*/
private final Map<FieldCacheEntry, FieldDescription.InDefinedShape> registeredFieldCacheEntries;
A set of registered field cache entries.
/**
* A set of registered field cache entries.
*/
private final Set<FieldDescription.InDefinedShape> registeredFieldCacheFields;
A random suffix to append to the names of accessor methods.
/**
* A random suffix to append to the names of accessor methods.
*/
private final String suffix;
If false
, the type initializer for this instance was already drained what prohibits the registration of additional cached field values. /**
* If {@code false}, the type initializer for this instance was already drained what prohibits the registration of additional cached field values.
*/
private boolean fieldCacheCanAppendEntries;
Creates a new default implementation context.
Params: - instrumentedType – The description of the type that is currently subject of creation.
- classFileVersion – The class file version of the created class.
- auxiliaryTypeNamingStrategy – The naming strategy for naming an auxiliary type.
- typeInitializer – The type initializer of the created instrumented type.
- auxiliaryClassFileVersion – The class file version to use for auxiliary classes.
/**
* Creates a new default implementation context.
*
* @param instrumentedType The description of the type that is currently subject of creation.
* @param classFileVersion The class file version of the created class.
* @param auxiliaryTypeNamingStrategy The naming strategy for naming an auxiliary type.
* @param typeInitializer The type initializer of the created instrumented type.
* @param auxiliaryClassFileVersion The class file version to use for auxiliary classes.
*/
protected Default(TypeDescription instrumentedType,
ClassFileVersion classFileVersion,
AuxiliaryType.NamingStrategy auxiliaryTypeNamingStrategy,
TypeInitializer typeInitializer,
ClassFileVersion auxiliaryClassFileVersion) {
super(instrumentedType, classFileVersion);
this.auxiliaryTypeNamingStrategy = auxiliaryTypeNamingStrategy;
this.typeInitializer = typeInitializer;
this.auxiliaryClassFileVersion = auxiliaryClassFileVersion;
registeredAccessorMethods = new HashMap<SpecialMethodInvocation, DelegationRecord>();
registeredGetters = new HashMap<FieldDescription, DelegationRecord>();
registeredSetters = new HashMap<FieldDescription, DelegationRecord>();
auxiliaryTypes = new HashMap<AuxiliaryType, DynamicType>();
registeredFieldCacheEntries = new HashMap<FieldCacheEntry, FieldDescription.InDefinedShape>();
registeredFieldCacheFields = new HashSet<FieldDescription.InDefinedShape>();
suffix = RandomString.make();
fieldCacheCanAppendEntries = true;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isEnabled() {
return true;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription.InDefinedShape registerAccessorFor(SpecialMethodInvocation specialMethodInvocation, AccessType accessType) {
DelegationRecord record = registeredAccessorMethods.get(specialMethodInvocation);
record = record == null
? new AccessorMethodDelegation(instrumentedType, suffix, accessType, specialMethodInvocation)
: record.with(accessType);
registeredAccessorMethods.put(specialMethodInvocation, record);
return record.getMethod();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription.InDefinedShape registerGetterFor(FieldDescription fieldDescription, AccessType accessType) {
DelegationRecord record = registeredGetters.get(fieldDescription);
record = record == null
? new FieldGetterDelegation(instrumentedType, suffix, accessType, fieldDescription)
: record.with(accessType);
registeredGetters.put(fieldDescription, record);
return record.getMethod();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription.InDefinedShape registerSetterFor(FieldDescription fieldDescription, AccessType accessType) {
DelegationRecord record = registeredSetters.get(fieldDescription);
record = record == null
? new FieldSetterDelegation(instrumentedType, suffix, accessType, fieldDescription)
: record.with(accessType);
registeredSetters.put(fieldDescription, record);
return record.getMethod();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription register(AuxiliaryType auxiliaryType) {
DynamicType dynamicType = auxiliaryTypes.get(auxiliaryType);
if (dynamicType == null) {
dynamicType = auxiliaryType.make(auxiliaryTypeNamingStrategy.name(instrumentedType), auxiliaryClassFileVersion, this);
auxiliaryTypes.put(auxiliaryType, dynamicType);
}
return dynamicType.getTypeDescription();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public List<DynamicType> getAuxiliaryTypes() {
return new ArrayList<DynamicType>(auxiliaryTypes.values());
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public FieldDescription.InDefinedShape cache(StackManipulation fieldValue, TypeDescription fieldType) {
FieldCacheEntry fieldCacheEntry = new FieldCacheEntry(fieldValue, fieldType);
FieldDescription.InDefinedShape fieldCache = registeredFieldCacheEntries.get(fieldCacheEntry);
if (fieldCache != null) {
return fieldCache;
}
if (!fieldCacheCanAppendEntries) {
throw new IllegalStateException("Cached values cannot be registered after defining the type initializer for " + instrumentedType);
}
int hashCode = fieldValue.hashCode();
do {
fieldCache = new CacheValueField(instrumentedType, fieldType.asGenericType(), suffix, hashCode++);
} while (!registeredFieldCacheFields.add(fieldCache));
registeredFieldCacheEntries.put(fieldCacheEntry, fieldCache);
return fieldCache;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void drain(TypeInitializer.Drain drain,
ClassVisitor classVisitor,
AnnotationValueFilter.Factory annotationValueFilterFactory) {
fieldCacheCanAppendEntries = false;
TypeInitializer typeInitializer = this.typeInitializer;
for (Map.Entry<FieldCacheEntry, FieldDescription.InDefinedShape> entry : registeredFieldCacheEntries.entrySet()) {
FieldVisitor fieldVisitor = classVisitor.visitField(entry.getValue().getModifiers(),
entry.getValue().getInternalName(),
entry.getValue().getDescriptor(),
entry.getValue().getGenericSignature(),
FieldDescription.NO_DEFAULT_VALUE);
if (fieldVisitor != null) {
fieldVisitor.visitEnd();
typeInitializer = typeInitializer.expandWith(entry.getKey().storeIn(entry.getValue()));
}
}
drain.apply(classVisitor, typeInitializer, this);
for (TypeWriter.MethodPool.Record record : registeredAccessorMethods.values()) {
record.apply(classVisitor, this, annotationValueFilterFactory);
}
for (TypeWriter.MethodPool.Record record : registeredGetters.values()) {
record.apply(classVisitor, this, annotationValueFilterFactory);
}
for (TypeWriter.MethodPool.Record record : registeredSetters.values()) {
record.apply(classVisitor, this, annotationValueFilterFactory);
}
}
A description of a field that stores a cached value.
/**
* A description of a field that stores a cached value.
*/
protected static class CacheValueField extends FieldDescription.InDefinedShape.AbstractBase {
The instrumented type.
/**
* The instrumented type.
*/
private final TypeDescription instrumentedType;
The type of the cache's field.
/**
* The type of the cache's field.
*/
private final TypeDescription.Generic fieldType;
The name of the field.
/**
* The name of the field.
*/
private final String name;
Creates a new cache value field.
Params: - instrumentedType – The instrumented type.
- fieldType – The type of the cache's field.
- suffix – The suffix to use for the cache field's name.
- hashCode – The hash value of the field's value for creating a unique field name.
/**
* Creates a new cache value field.
*
* @param instrumentedType The instrumented type.
* @param fieldType The type of the cache's field.
* @param suffix The suffix to use for the cache field's name.
* @param hashCode The hash value of the field's value for creating a unique field name.
*/
protected CacheValueField(TypeDescription instrumentedType, TypeDescription.Generic fieldType, String suffix, int hashCode) {
this.instrumentedType = instrumentedType;
this.fieldType = fieldType;
name = FIELD_CACHE_PREFIX + "$" + suffix + "$" + RandomString.hashOf(hashCode);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription.Generic getType() {
return fieldType;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public AnnotationList getDeclaredAnnotations() {
return new AnnotationList.Empty();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription getDeclaringType() {
return instrumentedType;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public int getModifiers() {
return Opcodes.ACC_SYNTHETIC | Opcodes.ACC_FINAL | Opcodes.ACC_STATIC | (instrumentedType.isInterface()
? Opcodes.ACC_PUBLIC
: Opcodes.ACC_PRIVATE);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public String getName() {
return name;
}
}
A field cache entry for uniquely identifying a cached field. A cached field is described by the stack
manipulation that loads the field's value onto the operand stack and the type of the field.
/**
* A field cache entry for uniquely identifying a cached field. A cached field is described by the stack
* manipulation that loads the field's value onto the operand stack and the type of the field.
*/
protected static class FieldCacheEntry implements StackManipulation {
The field value that is represented by this field cache entry.
/**
* The field value that is represented by this field cache entry.
*/
private final StackManipulation fieldValue;
The field type that is represented by this field cache entry.
/**
* The field type that is represented by this field cache entry.
*/
private final TypeDescription fieldType;
Creates a new field cache entry.
Params: - fieldValue – The field value that is represented by this field cache entry.
- fieldType – The field type that is represented by this field cache entry.
/**
* Creates a new field cache entry.
*
* @param fieldValue The field value that is represented by this field cache entry.
* @param fieldType The field type that is represented by this field cache entry.
*/
protected FieldCacheEntry(StackManipulation fieldValue, TypeDescription fieldType) {
this.fieldValue = fieldValue;
this.fieldType = fieldType;
}
Returns a stack manipulation where the represented value is stored in the given field.
Params: - fieldDescription – A static field in which the value is to be stored.
Returns: A byte code appender that represents this storage.
/**
* Returns a stack manipulation where the represented value is stored in the given field.
*
* @param fieldDescription A static field in which the value is to be stored.
* @return A byte code appender that represents this storage.
*/
protected ByteCodeAppender storeIn(FieldDescription fieldDescription) {
return new ByteCodeAppender.Simple(this, FieldAccess.forField(fieldDescription).write());
}
Returns the field type that is represented by this field cache entry.
Returns: The field type that is represented by this field cache entry.
/**
* Returns the field type that is represented by this field cache entry.
*
* @return The field type that is represented by this field cache entry.
*/
protected TypeDescription getFieldType() {
return fieldType;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isValid() {
return fieldValue.isValid();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
return fieldValue.apply(methodVisitor, implementationContext);
}
@Override
public int hashCode() {
int result = fieldValue.hashCode();
result = 31 * result + fieldType.hashCode();
return result;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (other == null || getClass() != other.getClass()) {
return false;
}
FieldCacheEntry fieldCacheEntry = (FieldCacheEntry) other;
return fieldValue.equals(fieldCacheEntry.fieldValue) && fieldType.equals(fieldCacheEntry.fieldType);
}
}
A base implementation of a method that accesses a property of an instrumented type.
/**
* A base implementation of a method that accesses a property of an instrumented type.
*/
protected abstract static class AbstractPropertyAccessorMethod extends MethodDescription.InDefinedShape.AbstractBase {
{@inheritDoc}
/**
* {@inheritDoc}
*/
public int getModifiers() {
return Opcodes.ACC_SYNTHETIC | getBaseModifiers() | (getDeclaringType().isInterface()
? Opcodes.ACC_PUBLIC
: Opcodes.ACC_FINAL);
}
Returns the base modifiers, i.e. the modifiers that define the accessed property's features.
Returns: Returns the base modifiers of the represented methods.
/**
* Returns the base modifiers, i.e. the modifiers that define the accessed property's features.
*
* @return Returns the base modifiers of the represented methods.
*/
protected abstract int getBaseModifiers();
}
A description of an accessor method to access another method from outside the instrumented type.
/**
* A description of an accessor method to access another method from outside the instrumented type.
*/
protected static class AccessorMethod extends AbstractPropertyAccessorMethod {
The instrumented type.
/**
* The instrumented type.
*/
private final TypeDescription instrumentedType;
The method that is being accessed.
/**
* The method that is being accessed.
*/
private final MethodDescription methodDescription;
The name of the method.
/**
* The name of the method.
*/
private final String name;
Creates a new accessor method.
Params: - instrumentedType – The instrumented type.
- methodDescription – The method that is being accessed.
- suffix – The suffix to append to the accessor method's name.
/**
* Creates a new accessor method.
*
* @param instrumentedType The instrumented type.
* @param methodDescription The method that is being accessed.
* @param suffix The suffix to append to the accessor method's name.
*/
protected AccessorMethod(TypeDescription instrumentedType, MethodDescription methodDescription, String suffix) {
this.instrumentedType = instrumentedType;
this.methodDescription = methodDescription;
name = methodDescription.getInternalName() + "$" + ACCESSOR_METHOD_SUFFIX + "$" + suffix;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription.Generic getReturnType() {
return methodDescription.getReturnType().asRawType();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ParameterList<ParameterDescription.InDefinedShape> getParameters() {
return new ParameterList.Explicit.ForTypes(this, methodDescription.getParameters().asTypeList().asRawTypes());
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeList.Generic getExceptionTypes() {
return methodDescription.getExceptionTypes().asRawTypes();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public AnnotationValue<?, ?> getDefaultValue() {
return AnnotationValue.UNDEFINED;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeList.Generic getTypeVariables() {
return new TypeList.Generic.Empty();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public AnnotationList getDeclaredAnnotations() {
return new AnnotationList.Empty();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription getDeclaringType() {
return instrumentedType;
}
@Override
protected int getBaseModifiers() {
return methodDescription.isStatic()
? Opcodes.ACC_STATIC
: EMPTY_MASK;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public String getInternalName() {
return name;
}
}
A description of a field getter method.
/**
* A description of a field getter method.
*/
protected static class FieldGetter extends AbstractPropertyAccessorMethod {
The instrumented type.
/**
* The instrumented type.
*/
private final TypeDescription instrumentedType;
The field for which a getter is described.
/**
* The field for which a getter is described.
*/
private final FieldDescription fieldDescription;
The name of the getter method.
/**
* The name of the getter method.
*/
private final String name;
Creates a new field getter.
Params: - instrumentedType – The instrumented type.
- fieldDescription – The field for which a getter is described.
- suffix – The name suffix for the field getter method.
/**
* Creates a new field getter.
*
* @param instrumentedType The instrumented type.
* @param fieldDescription The field for which a getter is described.
* @param suffix The name suffix for the field getter method.
*/
protected FieldGetter(TypeDescription instrumentedType, FieldDescription fieldDescription, String suffix) {
this.instrumentedType = instrumentedType;
this.fieldDescription = fieldDescription;
name = fieldDescription.getName() + "$" + ACCESSOR_METHOD_SUFFIX + "$" + suffix;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription.Generic getReturnType() {
return fieldDescription.getType().asRawType();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ParameterList<ParameterDescription.InDefinedShape> getParameters() {
return new ParameterList.Empty<ParameterDescription.InDefinedShape>();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeList.Generic getExceptionTypes() {
return new TypeList.Generic.Empty();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public AnnotationValue<?, ?> getDefaultValue() {
return AnnotationValue.UNDEFINED;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeList.Generic getTypeVariables() {
return new TypeList.Generic.Empty();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public AnnotationList getDeclaredAnnotations() {
return new AnnotationList.Empty();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription getDeclaringType() {
return instrumentedType;
}
@Override
protected int getBaseModifiers() {
return fieldDescription.isStatic()
? Opcodes.ACC_STATIC
: EMPTY_MASK;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public String getInternalName() {
return name;
}
}
A description of a field setter method.
/**
* A description of a field setter method.
*/
protected static class FieldSetter extends AbstractPropertyAccessorMethod {
The instrumented type.
/**
* The instrumented type.
*/
private final TypeDescription instrumentedType;
The field for which a setter is described.
/**
* The field for which a setter is described.
*/
private final FieldDescription fieldDescription;
The name of the field setter.
/**
* The name of the field setter.
*/
private final String name;
Creates a new field setter.
Params: - instrumentedType – The instrumented type.
- fieldDescription – The field for which a setter is described.
- suffix – The name suffix for the field setter method.
/**
* Creates a new field setter.
*
* @param instrumentedType The instrumented type.
* @param fieldDescription The field for which a setter is described.
* @param suffix The name suffix for the field setter method.
*/
protected FieldSetter(TypeDescription instrumentedType, FieldDescription fieldDescription, String suffix) {
this.instrumentedType = instrumentedType;
this.fieldDescription = fieldDescription;
name = fieldDescription.getName() + "$" + ACCESSOR_METHOD_SUFFIX + "$" + suffix;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription.Generic getReturnType() {
return TypeDescription.Generic.VOID;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ParameterList<ParameterDescription.InDefinedShape> getParameters() {
return new ParameterList.Explicit.ForTypes(this, Collections.singletonList(fieldDescription.getType().asRawType()));
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeList.Generic getExceptionTypes() {
return new TypeList.Generic.Empty();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public AnnotationValue<?, ?> getDefaultValue() {
return AnnotationValue.UNDEFINED;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeList.Generic getTypeVariables() {
return new TypeList.Generic.Empty();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public AnnotationList getDeclaredAnnotations() {
return new AnnotationList.Empty();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeDescription getDeclaringType() {
return instrumentedType;
}
@Override
protected int getBaseModifiers() {
return fieldDescription.isStatic()
? Opcodes.ACC_STATIC
: EMPTY_MASK;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public String getInternalName() {
return name;
}
}
An abstract method pool entry that delegates the implementation of a method to itself.
/**
* An abstract method pool entry that delegates the implementation of a method to itself.
*/
@HashCodeAndEqualsPlugin.Enhance
protected abstract static class DelegationRecord extends TypeWriter.MethodPool.Record.ForDefinedMethod implements ByteCodeAppender {
The delegation method.
/**
* The delegation method.
*/
protected final MethodDescription.InDefinedShape methodDescription;
The record's visibility.
/**
* The record's visibility.
*/
protected final Visibility visibility;
Creates a new delegation record.
Params: - methodDescription – The delegation method.
- visibility – The method's actual visibility.
/**
* Creates a new delegation record.
*
* @param methodDescription The delegation method.
* @param visibility The method's actual visibility.
*/
protected DelegationRecord(MethodDescription.InDefinedShape methodDescription, Visibility visibility) {
this.methodDescription = methodDescription;
this.visibility = visibility;
}
Returns this delegation record with the minimal visibility represented by the supplied access type.
Params: - accessType – The access type to enforce.
Returns: A new version of this delegation record with the minimal implied visibility.
/**
* Returns this delegation record with the minimal visibility represented by the supplied access type.
*
* @param accessType The access type to enforce.
* @return A new version of this delegation record with the minimal implied visibility.
*/
protected abstract DelegationRecord with(AccessType accessType);
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription.InDefinedShape getMethod() {
return methodDescription;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Sort getSort() {
return Sort.IMPLEMENTED;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Visibility getVisibility() {
return visibility;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void applyHead(MethodVisitor methodVisitor) {
/* do nothing */
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void applyBody(MethodVisitor methodVisitor, Context implementationContext, AnnotationValueFilter.Factory annotationValueFilterFactory) {
methodVisitor.visitCode();
Size size = applyCode(methodVisitor, implementationContext);
methodVisitor.visitMaxs(size.getOperandStackSize(), size.getLocalVariableSize());
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void applyAttributes(MethodVisitor methodVisitor, AnnotationValueFilter.Factory annotationValueFilterFactory) {
/* do nothing */
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Size applyCode(MethodVisitor methodVisitor, Context implementationContext) {
return apply(methodVisitor, implementationContext, getMethod());
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public TypeWriter.MethodPool.Record prepend(ByteCodeAppender byteCodeAppender) {
throw new UnsupportedOperationException("Cannot prepend code to a delegation for " + methodDescription);
}
}
An implementation of a Record
for implementing an accessor method. /**
* An implementation of a {@link TypeWriter.MethodPool.Record} for implementing
* an accessor method.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class AccessorMethodDelegation extends DelegationRecord {
The stack manipulation that represents the requested special method invocation.
/**
* The stack manipulation that represents the requested special method invocation.
*/
private final StackManipulation accessorMethodInvocation;
Creates a delegation to an accessor method.
Params: - instrumentedType – The instrumented type.
- suffix – The suffix to append to the method.
- accessType – The access type.
- specialMethodInvocation – The actual method's invocation.
/**
* Creates a delegation to an accessor method.
*
* @param instrumentedType The instrumented type.
* @param suffix The suffix to append to the method.
* @param accessType The access type.
* @param specialMethodInvocation The actual method's invocation.
*/
protected AccessorMethodDelegation(TypeDescription instrumentedType,
String suffix,
AccessType accessType,
SpecialMethodInvocation specialMethodInvocation) {
this(new AccessorMethod(instrumentedType, specialMethodInvocation.getMethodDescription(), suffix),
accessType.getVisibility(),
specialMethodInvocation);
}
Creates a delegation to an accessor method.
Params: - methodDescription – The accessor method.
- visibility – The method's visibility.
- accessorMethodInvocation – The actual method's invocation.
/**
* Creates a delegation to an accessor method.
*
* @param methodDescription The accessor method.
* @param visibility The method's visibility.
* @param accessorMethodInvocation The actual method's invocation.
*/
private AccessorMethodDelegation(MethodDescription.InDefinedShape methodDescription,
Visibility visibility,
StackManipulation accessorMethodInvocation) {
super(methodDescription, visibility);
this.accessorMethodInvocation = accessorMethodInvocation;
}
@Override
protected DelegationRecord with(AccessType accessType) {
return new AccessorMethodDelegation(methodDescription, visibility.expandTo(accessType.getVisibility()), accessorMethodInvocation);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext, MethodDescription instrumentedMethod) {
StackManipulation.Size stackSize = new StackManipulation.Compound(
MethodVariableAccess.allArgumentsOf(instrumentedMethod).prependThisReference(),
accessorMethodInvocation,
MethodReturn.of(instrumentedMethod.getReturnType())
).apply(methodVisitor, implementationContext);
return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
}
}
An implementation for a field getter.
/**
* An implementation for a field getter.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class FieldGetterDelegation extends DelegationRecord {
The field to read from.
/**
* The field to read from.
*/
private final FieldDescription fieldDescription;
Creates a new field getter implementation.
Params: - instrumentedType – The instrumented type.
- suffix – The suffix to use for the setter method.
- accessType – The method's access type.
- fieldDescription – The field to write to.
/**
* Creates a new field getter implementation.
*
* @param instrumentedType The instrumented type.
* @param suffix The suffix to use for the setter method.
* @param accessType The method's access type.
* @param fieldDescription The field to write to.
*/
protected FieldGetterDelegation(TypeDescription instrumentedType, String suffix, AccessType accessType, FieldDescription fieldDescription) {
this(new FieldGetter(instrumentedType, fieldDescription, suffix), accessType.getVisibility(), fieldDescription);
}
Creates a new field getter implementation.
Params: - methodDescription – The delegation method.
- visibility – The delegation method's visibility.
- fieldDescription – The field to read.
/**
* Creates a new field getter implementation.
*
* @param methodDescription The delegation method.
* @param visibility The delegation method's visibility.
* @param fieldDescription The field to read.
*/
private FieldGetterDelegation(MethodDescription.InDefinedShape methodDescription, Visibility visibility, FieldDescription fieldDescription) {
super(methodDescription, visibility);
this.fieldDescription = fieldDescription;
}
@Override
protected DelegationRecord with(AccessType accessType) {
return new FieldGetterDelegation(methodDescription, visibility.expandTo(accessType.getVisibility()), fieldDescription);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
StackManipulation.Size stackSize = new StackManipulation.Compound(
fieldDescription.isStatic()
? StackManipulation.Trivial.INSTANCE
: MethodVariableAccess.loadThis(),
FieldAccess.forField(fieldDescription).read(),
MethodReturn.of(fieldDescription.getType())
).apply(methodVisitor, implementationContext);
return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
}
}
An implementation for a field setter.
/**
* An implementation for a field setter.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class FieldSetterDelegation extends DelegationRecord {
The field to write to.
/**
* The field to write to.
*/
private final FieldDescription fieldDescription;
Creates a new field setter implementation.
Params: - instrumentedType – The instrumented type.
- suffix – The suffix to use for the setter method.
- accessType – The method's access type.
- fieldDescription – The field to write to.
/**
* Creates a new field setter implementation.
*
* @param instrumentedType The instrumented type.
* @param suffix The suffix to use for the setter method.
* @param accessType The method's access type.
* @param fieldDescription The field to write to.
*/
protected FieldSetterDelegation(TypeDescription instrumentedType, String suffix, AccessType accessType, FieldDescription fieldDescription) {
this(new FieldSetter(instrumentedType, fieldDescription, suffix), accessType.getVisibility(), fieldDescription);
}
Creates a new field setter.
Params: - methodDescription – The field accessor method.
- visibility – The delegation method's visibility.
- fieldDescription – The field to write to.
/**
* Creates a new field setter.
*
* @param methodDescription The field accessor method.
* @param visibility The delegation method's visibility.
* @param fieldDescription The field to write to.
*/
private FieldSetterDelegation(MethodDescription.InDefinedShape methodDescription, Visibility visibility, FieldDescription fieldDescription) {
super(methodDescription, visibility);
this.fieldDescription = fieldDescription;
}
@Override
protected DelegationRecord with(AccessType accessType) {
return new FieldSetterDelegation(methodDescription, visibility.expandTo(accessType.getVisibility()), fieldDescription);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
StackManipulation.Size stackSize = new StackManipulation.Compound(
MethodVariableAccess.allArgumentsOf(instrumentedMethod).prependThisReference(),
FieldAccess.forField(fieldDescription).write(),
MethodReturn.VOID
).apply(methodVisitor, implementationContext);
return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
}
}
A factory for creating a Default
. /**
* A factory for creating a {@link net.bytebuddy.implementation.Implementation.Context.Default}.
*/
public enum Factory implements ExtractableView.Factory {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ExtractableView make(TypeDescription instrumentedType,
AuxiliaryType.NamingStrategy auxiliaryTypeNamingStrategy,
TypeInitializer typeInitializer,
ClassFileVersion classFileVersion,
ClassFileVersion auxiliaryClassFileVersion) {
return new Default(instrumentedType, classFileVersion, auxiliaryTypeNamingStrategy, typeInitializer, auxiliaryClassFileVersion);
}
}
}
}
A compound implementation that allows to combine several implementations.
Note that the combination of two implementation might break the contract for implementing Object.equals(Object)
and Object.hashCode()
as described for Implementation
. See Also:
/**
* A compound implementation that allows to combine several implementations.
* <p> </p>
* Note that the combination of two implementation might break the contract for implementing
* {@link java.lang.Object#equals(Object)} and {@link Object#hashCode()} as described for
* {@link Implementation}.
*
* @see Implementation
*/
@HashCodeAndEqualsPlugin.Enhance
class Compound implements Implementation {
All implementation that are represented by this compound implementation.
/**
* All implementation that are represented by this compound implementation.
*/
private final List<Implementation> implementations;
Creates a new immutable compound implementation.
Params: - implementation – The implementations to combine in their order.
/**
* Creates a new immutable compound implementation.
*
* @param implementation The implementations to combine in their order.
*/
public Compound(Implementation... implementation) {
this(Arrays.asList(implementation));
}
Creates a new immutable compound implementation.
Params: - implementations – The implementations to combine in their order.
/**
* Creates a new immutable compound implementation.
*
* @param implementations The implementations to combine in their order.
*/
public Compound(List<? extends Implementation> implementations) {
this.implementations = new ArrayList<Implementation>();
for (Implementation implementation : implementations) {
if (implementation instanceof Compound.Composable) {
this.implementations.addAll(((Compound.Composable) implementation).implementations);
this.implementations.add(((Compound.Composable) implementation).composable);
} else if (implementation instanceof Compound) {
this.implementations.addAll(((Compound) implementation).implementations);
} else {
this.implementations.add(implementation);
}
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public InstrumentedType prepare(InstrumentedType instrumentedType) {
for (Implementation implementation : implementations) {
instrumentedType = implementation.prepare(instrumentedType);
}
return instrumentedType;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ByteCodeAppender appender(Target implementationTarget) {
ByteCodeAppender[] byteCodeAppender = new ByteCodeAppender[implementations.size()];
int index = 0;
for (Implementation implementation : implementations) {
byteCodeAppender[index++] = implementation.appender(implementationTarget);
}
return new ByteCodeAppender.Compound(byteCodeAppender);
}
A compound implementation that allows to combine several implementations and that is Composable
.
Note that the combination of two implementation might break the contract for implementing Object.equals(Object)
and Object.hashCode()
as described for Implementation
. See Also:
/**
* A compound implementation that allows to combine several implementations and that is {@link Implementation.Composable}.
* <p> </p>
* Note that the combination of two implementation might break the contract for implementing
* {@link java.lang.Object#equals(Object)} and {@link Object#hashCode()} as described for
* {@link Implementation}.
*
* @see Implementation
*/
@HashCodeAndEqualsPlugin.Enhance
public static class Composable implements Implementation.Composable {
The composable implementation that is applied last.
/**
* The composable implementation that is applied last.
*/
private final Implementation.Composable composable;
All implementation that are represented by this compound implementation.
/**
* All implementation that are represented by this compound implementation.
*/
private final List<Implementation> implementations;
Creates a new compound composable.
Params: - implementation – An implementation that is represented by this compound implementation prior to the composable.
- composable – The composable implementation that is applied last.
/**
* Creates a new compound composable.
*
* @param implementation An implementation that is represented by this compound implementation prior to the composable.
* @param composable The composable implementation that is applied last.
*/
public Composable(Implementation implementation, Implementation.Composable composable) {
this(Collections.singletonList(implementation), composable);
}
Creates a new compound composable.
Params: - implementations – All implementation that are represented by this compound implementation excluding the composable.
- composable – The composable implementation that is applied last.
/**
* Creates a new compound composable.
*
* @param implementations All implementation that are represented by this compound implementation excluding the composable.
* @param composable The composable implementation that is applied last.
*/
public Composable(List<? extends Implementation> implementations, Implementation.Composable composable) {
this.implementations = new ArrayList<Implementation>();
for (Implementation implementation : implementations) {
if (implementation instanceof Compound.Composable) {
this.implementations.addAll(((Compound.Composable) implementation).implementations);
this.implementations.add(((Compound.Composable) implementation).composable);
} else if (implementation instanceof Compound) {
this.implementations.addAll(((Compound) implementation).implementations);
} else {
this.implementations.add(implementation);
}
}
if (composable instanceof Compound.Composable) {
this.implementations.addAll(((Compound.Composable) composable).implementations);
this.composable = ((Compound.Composable) composable).composable;
} else {
this.composable = composable;
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public InstrumentedType prepare(InstrumentedType instrumentedType) {
for (Implementation implementation : implementations) {
instrumentedType = implementation.prepare(instrumentedType);
}
return composable.prepare(instrumentedType);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ByteCodeAppender appender(Target implementationTarget) {
ByteCodeAppender[] byteCodeAppender = new ByteCodeAppender[implementations.size() + 1];
int index = 0;
for (Implementation implementation : implementations) {
byteCodeAppender[index++] = implementation.appender(implementationTarget);
}
byteCodeAppender[index] = composable.appender(implementationTarget);
return new ByteCodeAppender.Compound(byteCodeAppender);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Implementation andThen(Implementation implementation) {
return new Compound(CompoundList.of(implementations, composable.andThen(implementation)));
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Implementation.Composable andThen(Implementation.Composable implementation) {
return new Compound.Composable(implementations, composable.andThen(implementation));
}
}
}
A simple implementation that does not register any members with the instrumented type.
/**
* A simple implementation that does not register any members with the instrumented type.
*/
@HashCodeAndEqualsPlugin.Enhance
class Simple implements Implementation {
The byte code appender to emmit.
/**
* The byte code appender to emmit.
*/
private final ByteCodeAppender byteCodeAppender;
Creates a new simple implementation for the given byte code appenders.
Params: - byteCodeAppender – The byte code appenders to apply in their order of application.
/**
* Creates a new simple implementation for the given byte code appenders.
*
* @param byteCodeAppender The byte code appenders to apply in their order of application.
*/
public Simple(ByteCodeAppender... byteCodeAppender) {
this.byteCodeAppender = new ByteCodeAppender.Compound(byteCodeAppender);
}
Creates a new simple instrumentation for the given stack manipulations which are summarized in a
byte code appender that defines any requested method by these manipulations.
Params: - stackManipulation – The stack manipulation to apply in their order of application.
/**
* Creates a new simple instrumentation for the given stack manipulations which are summarized in a
* byte code appender that defines any requested method by these manipulations.
*
* @param stackManipulation The stack manipulation to apply in their order of application.
*/
public Simple(StackManipulation... stackManipulation) {
byteCodeAppender = new ByteCodeAppender.Simple(stackManipulation);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ByteCodeAppender appender(Target implementationTarget) {
return byteCodeAppender;
}
}
}