All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fitbur.bytebuddy.implementation.bind.annotation.Super Maven / Gradle / Ivy

There is a newer version: 1.0.0
Show newest version
package com.fitbur.bytebuddy.implementation.bind.annotation;

import com.fitbur.bytebuddy.description.annotation.AnnotationDescription;
import com.fitbur.bytebuddy.description.enumeration.EnumerationDescription;
import com.fitbur.bytebuddy.description.method.MethodDescription;
import com.fitbur.bytebuddy.description.method.MethodList;
import com.fitbur.bytebuddy.description.method.ParameterDescription;
import com.fitbur.bytebuddy.description.type.TypeDescription;
import com.fitbur.bytebuddy.dynamic.TargetType;
import com.fitbur.bytebuddy.implementation.Implementation;
import com.fitbur.bytebuddy.implementation.auxiliary.TypeProxy;
import com.fitbur.bytebuddy.implementation.bind.MethodDelegationBinder;
import com.fitbur.bytebuddy.implementation.bytecode.StackManipulation;
import com.fitbur.bytebuddy.implementation.bytecode.assign.Assigner;

import java.lang.annotation.*;
import java.util.Arrays;

import static com.fitbur.bytebuddy.matcher.ElementMatchers.named;

/**
 * Parameters that are annotated with this annotation are assigned an instance of an auxiliary proxy type that allows calling
 * any {@code super} methods of the instrumented type where the parameter type must be a super type of the instrumented type.
 * The proxy type will be a direct subclass of the parameter's type such as for example a specific interface.
 * 

 

* Obviously, the proxy type must be instantiated before it is assigned to the intercepting method's parameter. For this * purpose, two strategies are available which can be specified by setting the {@link Super#strategy()} parameter which can * be assigned: *
    *
  1. {@link com.fitbur.bytebuddy.implementation.bind.annotation.Super.Instantiation#CONSTRUCTOR}: * A constructor call is made where {@link Super#constructorParameters()} determines the constructor's signature. Any constructor * parameter is assigned the parameter's default value when the constructor is called. Calling the default constructor is the * preconfigured strategy.
  2. *
  3. {@link com.fitbur.bytebuddy.implementation.bind.annotation.Super.Instantiation#UNSAFE}: * The proxy is created by making use of Java's {@link sun.reflect.ReflectionFactory} which is however not a public API which * is why it should be used with care. No constructor is called when this strategy is used. If this option is set, the * {@link Super#constructorParameters()} parameter is ignored.
  4. *
* Note that when for example intercepting a type {@code Foo} that implements some interface {@code Bar}, the proxy type * will only implement {@code Bar} and therefore extend {@link java.lang.Object} what allows for calling the default * constructor on the proxy. This implies that an interception by some method {@code qux(@Super Baz baz, @Super Bar bar)} * would cause the creation of two super call proxies, one extending {@code Baz}, the other extending {@code Bar}, give * that both types are super types of {@code Foo}. *

 

* As an exception, no method calls to {@link Object#finalize()} are delegated by calling this method on the {@code super}-call * proxy by default. If this is absolutely necessary, this can however be enabled by setting {@link Super#ignoreFinalizer()} * to {@code false}. *

 

* If a method parameter is not a super type of the instrumented type, the method with the parameter that is annoted by * #{@code Super} is not considered a possible delegation target. * * @see com.fitbur.bytebuddy.implementation.MethodDelegation * @see com.fitbur.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface Super { /** * Determines how the {@code super}call proxy type is instantiated. * * @return The instantiation strategy for this proxy. */ Instantiation strategy() default Instantiation.CONSTRUCTOR; /** * If {@code true}, the proxy type will not implement {@code super} calls to {@link Object#finalize()} or any overridden methods. * * @return {@code false} if finalizer methods should be considered for {@code super}-call proxy type delegation. */ boolean ignoreFinalizer() default true; /** * Determines if the generated proxy should be {@link java.io.Serializable}. If the annotated type * already is serializable, such an explicit specification is not required. * * @return {@code true} if the generated proxy should be {@link java.io.Serializable}. */ boolean serializableProxy() default false; /** * Defines the parameter types of the constructor to be called for the created {@code super}-call proxy type. * * @return The parameter types of the constructor to be called. */ Class[] constructorParameters() default {}; /** * Determines the type that is implemented by the proxy. When this value is set to its default value * {@code void}, the proxy is created as an instance of the parameter's type. When it is set to * {@link TargetType}, it is created as an instance of the generated class. Otherwise, the proxy type * is set to the given value. * * @return The type of the proxy or an indicator type, i.e. {@code void} or {@link TargetType}. */ Class proxyType() default void.class; /** * Determines the instantiation of the proxy type. * * @see com.fitbur.bytebuddy.implementation.bind.annotation.Super */ enum Instantiation { /** * A proxy instance is instantiated by its constructor. For the constructor's arguments, the parameters default * values are used. The constructor can be identified by setting {@link Super#constructorParameters()}. */ CONSTRUCTOR { @Override protected StackManipulation proxyFor(TypeDescription parameterType, Implementation.Target implementationTarget, AnnotationDescription.Loadable annotation) { return new TypeProxy.ForSuperMethodByConstructor(parameterType, implementationTarget, Arrays.asList(annotation.getValue(CONSTRUCTOR_PARAMETERS, TypeDescription[].class)), annotation.getValue(IGNORE_FINALIZER, Boolean.class), annotation.getValue(SERIALIZABLE_PROXY, Boolean.class)); } }, /** * A proxy is instantiated by calling JVM internal methods and without calling a constructor. This strategy * might fail on exotic JVM implementations. */ UNSAFE { @Override protected StackManipulation proxyFor(TypeDescription parameterType, Implementation.Target implementationTarget, AnnotationDescription.Loadable annotation) { return new TypeProxy.ForSuperMethodByReflectionFactory(parameterType, implementationTarget, annotation.getValue(IGNORE_FINALIZER, Boolean.class), annotation.getValue(SERIALIZABLE_PROXY, Boolean.class)); } }; /** * A reference to the ignore finalizer method. */ private static final MethodDescription.InDefinedShape IGNORE_FINALIZER; /** * A reference to the serializable proxy method. */ private static final MethodDescription.InDefinedShape SERIALIZABLE_PROXY; /** * A reference to the constructor parameters method. */ private static final MethodDescription.InDefinedShape CONSTRUCTOR_PARAMETERS; /* * Extracts method references to the annotation methods. */ static { MethodList annotationProperties = new TypeDescription.ForLoadedType(Super.class).getDeclaredMethods(); IGNORE_FINALIZER = annotationProperties.filter(named("ignoreFinalizer")).getOnly(); SERIALIZABLE_PROXY = annotationProperties.filter(named("serializableProxy")).getOnly(); CONSTRUCTOR_PARAMETERS = annotationProperties.filter(named("constructorParameters")).getOnly(); } /** * Creates a stack manipulation which loads a {@code super}-call proxy onto the stack. * * @param parameterType The type of the parameter that was annotated with * {@link com.fitbur.bytebuddy.implementation.bind.annotation.Super} * @param implementationTarget The implementation target for the currently created type. * @param annotation The annotation that caused this method call. * @return A stack manipulation representing this instance's instantiation strategy. */ protected abstract StackManipulation proxyFor(TypeDescription parameterType, Implementation.Target implementationTarget, AnnotationDescription.Loadable annotation); @Override public String toString() { return "Super.Instantiation." + name(); } } /** * A binder for handling the * {@link com.fitbur.bytebuddy.implementation.bind.annotation.Super} * annotation. * * @see TargetMethodAnnotationDrivenBinder */ enum Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder { /** * The singleton instance. */ INSTANCE; /** * A method reference to the strategy property. */ private static final MethodDescription.InDefinedShape STRATEGY; /** * A reference to the proxy type property. */ private static final MethodDescription.InDefinedShape PROXY_TYPE; /* * Extracts method references of the super annotation. */ static { MethodList annotationProperties = new TypeDescription.ForLoadedType(Super.class).getDeclaredMethods(); STRATEGY = annotationProperties.filter(named("strategy")).getOnly(); PROXY_TYPE = annotationProperties.filter(named("proxyType")).getOnly(); } @Override public Class getHandledType() { return Super.class; } @Override public MethodDelegationBinder.ParameterBinding bind(AnnotationDescription.Loadable annotation, MethodDescription source, ParameterDescription target, Implementation.Target implementationTarget, Assigner assigner) { if (target.getType().isPrimitive() || target.getType().isArray()) { throw new IllegalStateException(target + " uses the @Super annotation on an invalid type"); } TypeDescription proxyType = TypeLocator.ForType .of(annotation.getValue(PROXY_TYPE, TypeDescription.class)) .resolve(implementationTarget.getInstrumentedType(), target.getType()); if (proxyType.isFinal()) { throw new IllegalStateException("Cannot extend final type as @Super proxy: " + proxyType); } else if (source.isStatic() || !implementationTarget.getInstrumentedType().isAssignableTo(proxyType)) { return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE; } else { return new MethodDelegationBinder.ParameterBinding.Anonymous(annotation .getValue(STRATEGY, EnumerationDescription.class).load(Instantiation.class) .proxyFor(proxyType, implementationTarget, annotation)); } } @Override public String toString() { return "Super.Binder." + name(); } /** * Locates the type which should be the base type of the created proxy. */ protected interface TypeLocator { /** * Resolves the target type. * * @param instrumentedType The instrumented type. * @param parameterType The type of the target parameter. * @return The proxy type. */ TypeDescription resolve(TypeDescription instrumentedType, TypeDescription.Generic parameterType); /** * A type locator that yields the instrumented type. */ enum ForInstrumentedType implements TypeLocator { /** * The singleton instance. */ INSTANCE; @Override public TypeDescription resolve(TypeDescription instrumentedType, TypeDescription.Generic parameterType) { return instrumentedType; } @Override public String toString() { return "Super.Binder.TypeLocator.ForInstrumentedType." + name(); } } /** * A type locator that yields the target parameter's type. */ enum ForParameterType implements TypeLocator { /** * The singleton instance. */ INSTANCE; @Override public TypeDescription resolve(TypeDescription instrumentedType, TypeDescription.Generic parameterType) { TypeDescription erasure = parameterType.asErasure(); return erasure.equals(instrumentedType) ? instrumentedType : erasure; } @Override public String toString() { return "Super.Binder.TypeLocator.ForParameterType." + name(); } } /** * A type locator that returns a given type. */ class ForType implements TypeLocator { /** * The type to be returned upon resolution. */ private final TypeDescription typeDescription; /** * Creates a new type locator for a given type. * * @param typeDescription The type to be returned upon resolution. */ protected ForType(TypeDescription typeDescription) { this.typeDescription = typeDescription; } /** * Resolves a type locator based upon an annotation value. * * @param typeDescription The annotation's value. * @return The appropriate type locator. */ protected static TypeLocator of(TypeDescription typeDescription) { if (typeDescription.represents(void.class)) { return ForParameterType.INSTANCE; } else if (typeDescription.represents(TargetType.class)) { return ForInstrumentedType.INSTANCE; } else if (typeDescription.isPrimitive() || typeDescription.isArray()) { throw new IllegalStateException("Cannot assign proxy to " + typeDescription); } else { return new ForType(typeDescription); } } @Override public TypeDescription resolve(TypeDescription instrumentedType, TypeDescription.Generic parameterType) { if (!typeDescription.isAssignableTo(parameterType.asErasure())) { throw new IllegalStateException("Impossible to assign " + typeDescription + " to parameter of type " + parameterType); } return typeDescription; } @Override public boolean equals(Object other) { if (this == other) return true; if (other == null || getClass() != other.getClass()) return false; ForType forType = (ForType) other; return typeDescription.equals(forType.typeDescription); } @Override public int hashCode() { return typeDescription.hashCode(); } @Override public String toString() { return "Super.Binder.TypeLocator.ForType{" + "typeDescription=" + typeDescription + '}'; } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy