/*
* 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.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeList;
import net.bytebuddy.dynamic.scaffold.FieldLocator;
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.implementation.bytecode.collection.ArrayFactory;
import net.bytebuddy.implementation.bytecode.constant.MethodConstant;
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.RandomString;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.lang.reflect.InvocationHandler;
import java.util.ArrayList;
import java.util.List;
import static net.bytebuddy.matcher.ElementMatchers.*;
An adapter for adapting an InvocationHandler
. The adapter allows the invocation handler to also intercept method calls to non-interface methods. /**
* An adapter for adapting an {@link java.lang.reflect.InvocationHandler}. The adapter allows the invocation handler
* to also intercept method calls to non-interface methods.
*/
@HashCodeAndEqualsPlugin.Enhance
public abstract class InvocationHandlerAdapter implements Implementation {
A type description of the InvocationHandler
. /**
* A type description of the {@link InvocationHandler}.
*/
private static final TypeDescription.Generic INVOCATION_HANDLER_TYPE = TypeDescription.Generic.OfNonGenericType.ForLoadedType.of(InvocationHandler.class);
Indicates that a value should not be cached.
/**
* Indicates that a value should not be cached.
*/
private static final boolean UNCACHED = false;
Indicates that a Method
should be cached. /**
* Indicates that a {@link java.lang.reflect.Method} should be cached.
*/
private static final boolean CACHED = true;
Indicates that a lookup of a method constant should not be looked up using an AccessController
. /**
* Indicates that a lookup of a method constant should not be looked up using an {@link java.security.AccessController}.
*/
private static final boolean UNPRIVILEGED = false;
Indicates that a lookup of a method constant should be looked up using an AccessController
. /**
* Indicates that a lookup of a method constant should be looked up using an {@link java.security.AccessController}.
*/
private static final boolean PRIVILEGED = true;
The name of the field for storing an invocation handler.
/**
* The name of the field for storing an invocation handler.
*/
protected final String fieldName;
The assigner that is used for assigning the return invocation handler's return value to the
intercepted method's return value.
/**
* The assigner that is used for assigning the return invocation handler's return value to the
* intercepted method's return value.
*/
protected final Assigner assigner;
Determines if the Method
instances that are handed to the intercepted methods are cached in static
fields. /**
* Determines if the {@link java.lang.reflect.Method} instances that are handed to the intercepted methods are
* cached in {@code static} fields.
*/
protected final boolean cached;
Determines if the Method
instances are retrieved by using an AccessController
. /**
* Determines if the {@link java.lang.reflect.Method} instances are retrieved by using an {@link java.security.AccessController}.
*/
protected final boolean privileged;
Creates a new invocation handler for a given field.
Params: - fieldName – The name of the field.
- cached – Determines if the
Method
instances that are handed to the intercepted methods are cached in static
fields. - privileged – Determines if the
Method
instances are retrieved by using an AccessController
. - assigner – The assigner to apply when defining this implementation.
/**
* Creates a new invocation handler for a given field.
*
* @param fieldName The name of the field.
* @param cached Determines if the {@link java.lang.reflect.Method} instances that are handed to the
* intercepted methods are cached in {@code static} fields.
* @param privileged Determines if the {@link java.lang.reflect.Method} instances are retrieved by using an {@link java.security.AccessController}.
* @param assigner The assigner to apply when defining this implementation.
*/
protected InvocationHandlerAdapter(String fieldName, boolean cached, boolean privileged, Assigner assigner) {
this.fieldName = fieldName;
this.cached = cached;
this.privileged = privileged;
this.assigner = assigner;
}
Creates an implementation for any instance of an InvocationHandler
that delegates all method interceptions to the given instance which will be stored in a static
field. Params: - invocationHandler – The invocation handler to which all method calls are delegated.
Returns: An implementation that delegates all method interceptions to the given invocation handler.
/**
* Creates an implementation for any instance of an {@link java.lang.reflect.InvocationHandler} that delegates
* all method interceptions to the given instance which will be stored in a {@code static} field.
*
* @param invocationHandler The invocation handler to which all method calls are delegated.
* @return An implementation that delegates all method interceptions to the given invocation handler.
*/
public static InvocationHandlerAdapter of(InvocationHandler invocationHandler) {
return of(invocationHandler, ForInstance.PREFIX + "$" + RandomString.hashOf(invocationHandler.hashCode()));
}
Creates an implementation for any instance of an InvocationHandler
that delegates all method interceptions to the given instance which will be stored in a static
field. Params: - invocationHandler – The invocation handler to which all method calls are delegated.
- fieldName – The name of the field.
Returns: An implementation that delegates all method interceptions to the given invocation handler.
/**
* Creates an implementation for any instance of an {@link java.lang.reflect.InvocationHandler} that delegates
* all method interceptions to the given instance which will be stored in a {@code static} field.
*
* @param invocationHandler The invocation handler to which all method calls are delegated.
* @param fieldName The name of the field.
* @return An implementation that delegates all method interceptions to the given invocation handler.
*/
public static InvocationHandlerAdapter of(InvocationHandler invocationHandler, String fieldName) {
return new ForInstance(fieldName, CACHED, UNPRIVILEGED, Assigner.DEFAULT, invocationHandler);
}
Creates an implementation for any InvocationHandler
that delegates all method interceptions to a field with the given name. This field has to be of a subtype of invocation handler and needs to be set before any invocations are intercepted. Otherwise, a NullPointerException
will be thrown. Params: - name – The name of the field.
Returns: An implementation that delegates all method interceptions to an instance field of the given name.
/**
* Creates an implementation for any {@link java.lang.reflect.InvocationHandler} that delegates
* all method interceptions to a field with the given name. This field has to be of a subtype of invocation
* handler and needs to be set before any invocations are intercepted. Otherwise, a {@link java.lang.NullPointerException}
* will be thrown.
*
* @param name The name of the field.
* @return An implementation that delegates all method interceptions to an instance field of the given name.
*/
public static InvocationHandlerAdapter toField(String name) {
return toField(name, FieldLocator.ForClassHierarchy.Factory.INSTANCE);
}
Creates an implementation for any InvocationHandler
that delegates all method interceptions to a field with the given name. This field has to be of a subtype of invocation handler and needs to be set before any invocations are intercepted. Otherwise, a NullPointerException
will be thrown. Params: - name – The name of the field.
- fieldLocatorFactory – The field locator factory
Returns: An implementation that delegates all method interceptions to an instance field of the given name.
/**
* Creates an implementation for any {@link java.lang.reflect.InvocationHandler} that delegates
* all method interceptions to a field with the given name. This field has to be of a subtype of invocation
* handler and needs to be set before any invocations are intercepted. Otherwise, a {@link java.lang.NullPointerException}
* will be thrown.
*
* @param name The name of the field.
* @param fieldLocatorFactory The field locator factory
* @return An implementation that delegates all method interceptions to an instance field of the given name.
*/
public static InvocationHandlerAdapter toField(String name, FieldLocator.Factory fieldLocatorFactory) {
return new ForField(name, CACHED, UNPRIVILEGED, Assigner.DEFAULT, fieldLocatorFactory);
}
Returns a list of stack manipulations that loads all arguments of an instrumented method.
Params: - instrumentedMethod – The method that is instrumented.
Returns: A list of stack manipulation that loads all arguments of an instrumented method.
/**
* Returns a list of stack manipulations that loads all arguments of an instrumented method.
*
* @param instrumentedMethod The method that is instrumented.
* @return A list of stack manipulation that loads all arguments of an instrumented method.
*/
private List<StackManipulation> argumentValuesOf(MethodDescription instrumentedMethod) {
TypeList.Generic parameterTypes = instrumentedMethod.getParameters().asTypeList();
List<StackManipulation> instruction = new ArrayList<StackManipulation>(parameterTypes.size());
int currentIndex = 1;
for (TypeDescription.Generic parameterType : parameterTypes) {
instruction.add(new StackManipulation.Compound(
MethodVariableAccess.of(parameterType).loadFrom(currentIndex),
assigner.assign(parameterType, TypeDescription.Generic.OBJECT, Assigner.Typing.STATIC)));
currentIndex += parameterType.getStackSize().getSize();
}
return instruction;
}
By default, any Method
instance that is handed over to an InvocationHandler
is cached in a static field. By invoking this method, this feature can be disabled. Returns: A similar invocation handler adapter that applies caching.
/**
* By default, any {@link java.lang.reflect.Method} instance that is handed over to an
* {@link java.lang.reflect.InvocationHandler} is cached in a static field. By invoking this method,
* this feature can be disabled.
*
* @return A similar invocation handler adapter that applies caching.
*/
public abstract WithoutPrivilegeConfiguration withoutMethodCache();
Configures an assigner to use with this invocation handler adapter.
Params: - assigner – The assigner to apply when defining this implementation.
Returns: This instrumentation with the given assigner
configured.
/**
* Configures an assigner to use with this invocation handler adapter.
*
* @param assigner The assigner to apply when defining this implementation.
* @return This instrumentation with the given {@code assigner} configured.
*/
public abstract Implementation withAssigner(Assigner assigner);
Configures that the method constants supplied to the invocation handler adapter are resolved using an AccessController
. Returns: This instrumentation with a privileged lookup configured.
/**
* Configures that the method constants supplied to the invocation handler adapter are resolved using an {@link java.security.AccessController}.
*
* @return This instrumentation with a privileged lookup configured.
*/
public abstract AssignerConfigurable withPrivilegedLookup();
Applies an implementation that delegates to a invocation handler.
Params: - methodVisitor – The method visitor for writing the byte code to.
- implementationContext – The implementation context for the current implementation.
- instrumentedMethod – The method that is instrumented.
- preparingManipulation – A stack manipulation that applies any preparation to the operand stack.
- fieldDescription – The field that contains the value for the invocation handler.
Returns: The size of the applied assignment.
/**
* Applies an implementation that delegates to a invocation handler.
*
* @param methodVisitor The method visitor for writing the byte code to.
* @param implementationContext The implementation context for the current implementation.
* @param instrumentedMethod The method that is instrumented.
* @param preparingManipulation A stack manipulation that applies any preparation to the operand stack.
* @param fieldDescription The field that contains the value for the invocation handler.
* @return The size of the applied assignment.
*/
protected ByteCodeAppender.Size apply(MethodVisitor methodVisitor,
Context implementationContext,
MethodDescription instrumentedMethod,
StackManipulation preparingManipulation,
FieldDescription fieldDescription) {
if (instrumentedMethod.isStatic()) {
throw new IllegalStateException("It is not possible to apply an invocation handler onto the static method " + instrumentedMethod);
}
MethodConstant.CanCache methodConstant = privileged
? MethodConstant.ofPrivileged(instrumentedMethod.asDefined())
: MethodConstant.of(instrumentedMethod.asDefined());
StackManipulation.Size stackSize = new StackManipulation.Compound(
preparingManipulation,
FieldAccess.forField(fieldDescription).read(),
MethodVariableAccess.loadThis(),
cached ? methodConstant.cached() : methodConstant,
ArrayFactory.forType(TypeDescription.Generic.OBJECT).withValues(argumentValuesOf(instrumentedMethod)),
MethodInvocation.invoke(INVOCATION_HANDLER_TYPE.getDeclaredMethods().filter(isAbstract()).getOnly()),
assigner.assign(TypeDescription.Generic.OBJECT, instrumentedMethod.getReturnType(), Assigner.Typing.DYNAMIC),
MethodReturn.of(instrumentedMethod.getReturnType())
).apply(methodVisitor, implementationContext);
return new ByteCodeAppender.Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
}
Allows for the configuration of an Assigner
of an InvocationHandlerAdapter
. /**
* Allows for the configuration of an {@link net.bytebuddy.implementation.bytecode.assign.Assigner}
* of an {@link net.bytebuddy.implementation.InvocationHandlerAdapter}.
*/
public interface AssignerConfigurable extends Implementation {
Configures an assigner to use with this invocation handler adapter.
Params: - assigner – The assigner to apply when defining this implementation.
Returns: This instrumentation with the given assigner
configured.
/**
* Configures an assigner to use with this invocation handler adapter.
*
* @param assigner The assigner to apply when defining this implementation.
* @return This instrumentation with the given {@code assigner} configured.
*/
Implementation withAssigner(Assigner assigner);
}
Allows the configuration of privileged lookup for the resolution of Method
constants that are provided to the invocation handler. /**
* Allows the configuration of privileged lookup for the resolution of {@link java.lang.reflect.Method}
* constants that are provided to the invocation handler.
*/
public interface WithoutPrivilegeConfiguration extends AssignerConfigurable {
Configures that the method constants supplied to the invocation handler adapter are resolved using an AccessController
. Returns: This instrumentation with a privileged lookup configured.
/**
* Configures that the method constants supplied to the invocation handler adapter are resolved using an {@link java.security.AccessController}.
*
* @return This instrumentation with a privileged lookup configured.
*/
AssignerConfigurable withPrivilegedLookup();
}
An implementation of an InvocationHandlerAdapter
that delegates method invocations to an adapter that is stored in a static field. /**
* An implementation of an {@link net.bytebuddy.implementation.InvocationHandlerAdapter} that delegates method
* invocations to an adapter that is stored in a static field.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class ForInstance extends InvocationHandlerAdapter implements WithoutPrivilegeConfiguration {
The prefix for field that are created for storing the instrumented value.
/**
* The prefix for field that are created for storing the instrumented value.
*/
private static final String PREFIX = "invocationHandler";
The invocation handler to which method interceptions are to be delegated.
/**
* The invocation handler to which method interceptions are to be delegated.
*/
protected final InvocationHandler invocationHandler;
Creates a new invocation handler adapter for delegating invocations to an invocation handler that is stored
in a static field.
Params: - fieldName – The name of the field.
- cached – Determines if the
Method
instances that are handed to the intercepted methods are cached in static
fields. - privileged – Determines if the
Method
instances are retrieved by using an AccessController
. - assigner – The assigner to apply when defining this implementation.
- invocationHandler – The invocation handler to which all method calls are delegated.
/**
* Creates a new invocation handler adapter for delegating invocations to an invocation handler that is stored
* in a static field.
*
* @param fieldName The name of the field.
* @param cached Determines if the {@link java.lang.reflect.Method} instances that are handed to the
* intercepted methods are cached in {@code static} fields.
* @param privileged Determines if the {@link java.lang.reflect.Method} instances are retrieved by
* using an {@link java.security.AccessController}.
* @param assigner The assigner to apply when defining this implementation.
* @param invocationHandler The invocation handler to which all method calls are delegated.
*/
protected ForInstance(String fieldName, boolean cached, boolean privileged, Assigner assigner, InvocationHandler invocationHandler) {
super(fieldName, cached, privileged, assigner);
this.invocationHandler = invocationHandler;
}
@Override
public WithoutPrivilegeConfiguration withoutMethodCache() {
return new ForInstance(fieldName, UNCACHED, privileged, assigner, invocationHandler);
}
@Override
public Implementation withAssigner(Assigner assigner) {
return new ForInstance(fieldName, cached, privileged, assigner, invocationHandler);
}
@Override
public AssignerConfigurable withPrivilegedLookup() {
return new ForInstance(fieldName, cached, PRIVILEGED, assigner, invocationHandler);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType
.withField(new FieldDescription.Token(fieldName,
Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_VOLATILE | Opcodes.ACC_SYNTHETIC,
INVOCATION_HANDLER_TYPE))
.withInitializer(new LoadedTypeInitializer.ForStaticField(fieldName, invocationHandler));
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ByteCodeAppender appender(Target implementationTarget) {
return new Appender(implementationTarget.getInstrumentedType());
}
An appender for implementing the ForInstance
. /**
* An appender for implementing the {@link ForInstance}.
*/
@HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
protected class Appender implements ByteCodeAppender {
The instrumented type for which the methods are being intercepted.
/**
* The instrumented type for which the methods are being intercepted.
*/
private final TypeDescription instrumentedType;
Creates a new appender.
Params: - instrumentedType – The type that is instrumented.
/**
* Creates a new appender.
*
* @param instrumentedType The type that is instrumented.
*/
protected Appender(TypeDescription instrumentedType) {
this.instrumentedType = instrumentedType;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
return ForInstance.this.apply(methodVisitor,
implementationContext,
instrumentedMethod,
StackManipulation.Trivial.INSTANCE,
instrumentedType.getDeclaredFields().filter(named(fieldName).and(genericFieldType(INVOCATION_HANDLER_TYPE))).getOnly());
}
}
}
An implementation of an InvocationHandlerAdapter
that delegates method invocations to an adapter that is stored in an instance field. /**
* An implementation of an {@link net.bytebuddy.implementation.InvocationHandlerAdapter} that delegates method
* invocations to an adapter that is stored in an instance field.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class ForField extends InvocationHandlerAdapter implements WithoutPrivilegeConfiguration {
The field locator factory to use.
/**
* The field locator factory to use.
*/
private final FieldLocator.Factory fieldLocatorFactory;
Creates a new invocation handler adapter that loads its value from a field.
Params: - fieldName – The name of the field.
- cached – Determines if the
Method
instances that are handed to the intercepted methods are cached in static
fields. - privileged – Determines if the
Method
instances are retrieved by using an AccessController
. - assigner – The assigner to apply when defining this implementation.
- fieldLocatorFactory – The field locator factory to use.
/**
* Creates a new invocation handler adapter that loads its value from a field.
*
* @param fieldName The name of the field.
* @param cached Determines if the {@link java.lang.reflect.Method} instances that are handed to the
* intercepted methods are cached in {@code static} fields.
* @param privileged Determines if the {@link java.lang.reflect.Method} instances are retrieved by using
* an {@link java.security.AccessController}.
* @param assigner The assigner to apply when defining this implementation.
* @param fieldLocatorFactory The field locator factory to use.
*/
protected ForField(String fieldName, boolean cached, boolean privileged, Assigner assigner, FieldLocator.Factory fieldLocatorFactory) {
super(fieldName, cached, privileged, assigner);
this.fieldLocatorFactory = fieldLocatorFactory;
}
@Override
public WithoutPrivilegeConfiguration withoutMethodCache() {
return new ForField(fieldName, UNCACHED, privileged, assigner, fieldLocatorFactory);
}
@Override
public Implementation withAssigner(Assigner assigner) {
return new ForField(fieldName, cached, privileged, assigner, fieldLocatorFactory);
}
@Override
public AssignerConfigurable withPrivilegedLookup() {
return new ForField(fieldName, cached, PRIVILEGED, assigner, fieldLocatorFactory);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ByteCodeAppender appender(Target implementationTarget) {
FieldLocator.Resolution resolution = fieldLocatorFactory.make(implementationTarget.getInstrumentedType()).locate(fieldName);
if (!resolution.isResolved()) {
throw new IllegalStateException("Could not find a field named '" + fieldName + "' for " + implementationTarget.getInstrumentedType());
} else if (!resolution.getField().getType().asErasure().isAssignableTo(InvocationHandler.class)) {
throw new IllegalStateException("Field " + resolution.getField() + " does not declare a type that is assignable to invocation handler");
}
return new Appender(resolution.getField());
}
An appender for implementing the ForField
. /**
* An appender for implementing the {@link ForField}.
*/
@HashCodeAndEqualsPlugin.Enhance(includeSyntheticFields = true)
protected class Appender implements ByteCodeAppender {
The field that contains the invocation handler.
/**
* The field that contains the invocation handler.
*/
private final FieldDescription fieldDescription;
Creates a new appender.
Params: - fieldDescription – The field that contains the invocation handler.
/**
* Creates a new appender.
*
* @param fieldDescription The field that contains the invocation handler.
*/
protected Appender(FieldDescription fieldDescription) {
this.fieldDescription = fieldDescription;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
return ForField.this.apply(methodVisitor,
implementationContext,
instrumentedMethod,
fieldDescription.isStatic()
? StackManipulation.Trivial.INSTANCE
: MethodVariableAccess.loadThis(),
fieldDescription);
}
}
}
}