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

net.bytebuddy.dynamic.scaffold.inline.MethodRebaseResolver Maven / Gradle / Ivy

package net.bytebuddy.dynamic.scaffold.inline;

import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.auxiliary.AuxiliaryType;
import net.bytebuddy.implementation.auxiliary.TrivialType;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.constant.NullConstant;
import net.bytebuddy.jar.asm.Opcodes;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.utility.ByteBuddyCommons.join;

/**
 * A method rebase resolver is responsible for mapping methods of an instrumented type to an alternative signature.
 * This way a method can exist in two versions within a class:
 * 
    *
  1. The rebased method which represents the original implementation as it is present in a class file.
  2. *
  3. An overriden method which implements user code which is still able to invoke the original, rebased method.
  4. *
*/ public interface MethodRebaseResolver { /** * The modifier that is used for rebased methods. */ int REBASED_METHOD_MODIFIER = Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC; /** * Checks if a method is eligible for rebasing and resolves this possibly rebased method. * * @param methodDescription A description of the method to resolve. * @return A resolution for the given method. */ Resolution resolve(MethodDescription methodDescription); /** * Returns a (potentially empty) list of auxiliary types that are required by this method rebase resolver. * * @return A list of auxiliary types that are required by this method rebase resolver. */ List getAuxiliaryTypes(); /** * A method rebase resolver that preserves any method in its original form. */ enum Disabled implements MethodRebaseResolver { /** * The singleton instance. */ INSTANCE; @Override public Resolution resolve(MethodDescription methodDescription) { return new Resolution.Preserved(methodDescription); } @Override public List getAuxiliaryTypes() { return Collections.emptyList(); } @Override public String toString() { return "MethodRebaseResolver.Disabled." + name(); } } /** * A method name transformer provides a unique mapping of a method's name to an alternative name. * * @see MethodRebaseResolver */ interface MethodNameTransformer { /** * Transforms a method's name to an alternative name. For a given argument, this mapper must always provide * the same return value. * * @param methodDescription The original method. * @return The alternative name. */ String transform(MethodDescription methodDescription); /** * A method name transformer that adds a fixed suffix to an original method name, separated by a {@code $}. */ class Suffixing implements MethodNameTransformer { /** * The default suffix to add to an original method name. */ private static final String DEFAULT_SUFFIX = "original"; /** * The suffix to append to a method name. */ private final String suffix; /** * Creates a new suffixing method name transformer which adds a default suffix. */ public Suffixing() { this(DEFAULT_SUFFIX); } /** * Creates a new suffixing method name transformer. * * @param suffix The suffix to add to the method name before the seed. */ public Suffixing(String suffix) { this.suffix = suffix; } @Override public String transform(MethodDescription methodDescription) { return String.format("%s$%s", methodDescription.getInternalName(), suffix); } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && suffix.equals(((Suffixing) other).suffix); } @Override public int hashCode() { return suffix.hashCode(); } @Override public String toString() { return "MethodRebaseResolver.MethodNameTransformer.Suffixing{" + "suffix='" + suffix + '\'' + '}'; } } /** * A method name transformer that adds a fixed prefix to an original method name. */ class Prefixing implements MethodNameTransformer { /** * The default prefix to add to an original method name. */ private static final String DEFAULT_PREFIX = "original"; /** * The prefix that is appended. */ private final String prefix; /** * Creates a new prefixing method name transformer using a default prefix. */ public Prefixing() { this(DEFAULT_PREFIX); } /** * Creates a new prefixing method name transformer. * * @param prefix The prefix being used. */ public Prefixing(String prefix) { this.prefix = prefix; } @Override public String transform(MethodDescription methodDescription) { return String.format("%s%s", prefix, methodDescription.getInternalName()); } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && prefix.equals(((Prefixing) other).prefix); } @Override public int hashCode() { return prefix.hashCode(); } @Override public String toString() { return "MethodRebaseResolver.MethodNameTransformer.Prefixing{" + "prefix='" + prefix + '\'' + '}'; } } } /** * A resolution for a method that was checked by a {@link MethodRebaseResolver}. */ interface Resolution { /** * Checks if this resolution represents a rebased method. * * @return {@code true} if this resolution requires to rebase a method. */ boolean isRebased(); /** * Returns the resolved method if this resolution represents a rebased method or the original method. * * @return The resolved method if this resolution represents a rebased method or the original method. */ MethodDescription getResolvedMethod(); /** * A rebased method might require additional arguments in order to create a distinct signature. The * stack manipulation that is returned from this method loads these arguments onto the operand stack. For * a non-rebased method, this method throws an {@link java.lang.IllegalArgumentException}. * * @return A stack manipulation that loaded the additional arguments onto the stack, if any. */ StackManipulation getAdditionalArguments(); /** * A {@link MethodRebaseResolver.Resolution} of a non-rebased method. */ class Preserved implements Resolution { /** * The preserved method. */ private final MethodDescription methodDescription; /** * Creates a new {@link MethodRebaseResolver.Resolution} for * a non-rebased method. * * @param methodDescription The preserved method. */ public Preserved(MethodDescription methodDescription) { this.methodDescription = methodDescription; } @Override public boolean isRebased() { return false; } @Override public MethodDescription getResolvedMethod() { return methodDescription; } @Override public StackManipulation getAdditionalArguments() { return StackManipulation.LegalTrivial.INSTANCE; } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && methodDescription.equals(((Preserved) other).methodDescription); } @Override public int hashCode() { return methodDescription.hashCode(); } @Override public String toString() { return "MethodRebaseResolver.Resolution.Preserved{methodDescription=" + methodDescription + '}'; } } /** * A {@link MethodRebaseResolver.Resolution} of a rebased method. */ class ForRebasedMethod implements Resolution { /** * The rebased method. */ private final MethodDescription methodDescription; /** * Creates a {@link MethodRebaseResolver.Resolution} for a * rebased method. * * @param methodDescription The original method that should be rebased. * @param methodNameTransformer A transformer for renaming a rebased method. */ public ForRebasedMethod(MethodDescription methodDescription, MethodNameTransformer methodNameTransformer) { this.methodDescription = new MethodDescription.Latent( methodNameTransformer.transform(methodDescription), methodDescription.getDeclaringType(), methodDescription.getReturnType(), methodDescription.getParameters().asTypeList(), REBASED_METHOD_MODIFIER | (methodDescription.isStatic() ? Opcodes.ACC_STATIC : 0) | (methodDescription.isNative() ? Opcodes.ACC_NATIVE : 0), methodDescription.getExceptionTypes()); } @Override public boolean isRebased() { return true; } @Override public MethodDescription getResolvedMethod() { return methodDescription; } @Override public StackManipulation getAdditionalArguments() { return StackManipulation.LegalTrivial.INSTANCE; } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && methodDescription.equals(((ForRebasedMethod) other).methodDescription); } @Override public int hashCode() { return methodDescription.hashCode(); } @Override public String toString() { return "MethodRebaseResolver.Resolution.ForRebasedMethod{methodDescription=" + methodDescription + '}'; } } /** * A {@link MethodRebaseResolver.Resolution} of a rebased constructor. */ class ForRebasedConstructor implements Resolution { /** * The rebased constructor. */ private final MethodDescription methodDescription; /** * Creates a {@link MethodRebaseResolver.Resolution} for a * rebased method. * * @param methodDescription The constructor to rebase. * @param placeholderType A placeholder type which is added to a rebased constructor. */ public ForRebasedConstructor(MethodDescription methodDescription, TypeDescription placeholderType) { this.methodDescription = new MethodDescription.Latent(methodDescription.getInternalName(), methodDescription.getDeclaringType(), methodDescription.getReturnType(), join(methodDescription.getParameters().asTypeList(), placeholderType), REBASED_METHOD_MODIFIER, methodDescription.getExceptionTypes()); } @Override public boolean isRebased() { return true; } @Override public MethodDescription getResolvedMethod() { return methodDescription; } @Override public StackManipulation getAdditionalArguments() { return NullConstant.INSTANCE; } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && methodDescription.equals(((ForRebasedConstructor) other).methodDescription); } @Override public int hashCode() { return methodDescription.hashCode(); } @Override public String toString() { return "MethodRebaseResolver.Resolution.ForRebasedConstructor{methodDescription=" + methodDescription + '}'; } } } /** * An abstract base implementation for a method rebase resolver. */ abstract class AbstractBase implements MethodRebaseResolver { /** * A set containing all instrumented methods. */ protected final Set instrumentedMethods; /** * Creates a method rebase resolver. * * @param instrumentedMethods A set containing all instrumented methods. */ protected AbstractBase(Set instrumentedMethods) { this.instrumentedMethods = instrumentedMethods; } @Override public Resolution resolve(MethodDescription methodDescription) { return instrumentedMethods.contains(methodDescription) ? rebase(methodDescription) : new Resolution.Preserved(methodDescription); } /** * Resolves a method that is instrumented. * * @param methodDescription The method to be rebased. * @return A resolution for the given method. */ protected abstract Resolution rebase(MethodDescription methodDescription); @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && instrumentedMethods.equals(((AbstractBase) other).instrumentedMethods); } @Override public int hashCode() { return instrumentedMethods.hashCode(); } } /** * A partly enabled method rebase resolver that is only capable of rebasing methods but not constructors. */ class MethodsOnly extends AbstractBase { /** * The method name transformer to use for rebasing method names. */ private final MethodNameTransformer methodNameTransformer; /** * Creates a new methods-only method rebase resolver. * * @param instrumentedMethods The instrumented methods that are to be rebased. * @param methodNameTransformer The method name transformer to use for rebasing methods. */ protected MethodsOnly(Set instrumentedMethods, MethodNameTransformer methodNameTransformer) { super(instrumentedMethods); this.methodNameTransformer = methodNameTransformer; } /** * Creates a new method rebase resolver that is only capable of rebasing methods but not constructors. * * @param instrumentedMethods The instrumented methods that should be rebased. * @param methodNameTransformer The method name transformer to use for rebasing method names. * @return A suitable method rebase resolver. */ protected static MethodRebaseResolver of(MethodList instrumentedMethods, MethodNameTransformer methodNameTransformer) { return new MethodsOnly(new HashSet(instrumentedMethods), methodNameTransformer); } @Override protected Resolution rebase(MethodDescription methodDescription) { if (methodDescription.isConstructor()) { throw new IllegalArgumentException(); } return new Resolution.ForRebasedMethod(methodDescription, methodNameTransformer); } @Override public List getAuxiliaryTypes() { return Collections.emptyList(); } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && super.equals(other) && methodNameTransformer.equals(((MethodsOnly) other).methodNameTransformer); } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + methodNameTransformer.hashCode(); return result; } @Override public String toString() { return "MethodRebaseResolver.MethodsOnly{" + "instrumentedMethods=" + instrumentedMethods + ", methodNameTransformer=" + methodNameTransformer + '}'; } } /** * A fully enabled method rebase resolver that is capable of rebasing both methods and constructors. */ class Enabled extends AbstractBase { /** * The placeholder type to use for rebasing constructors. */ private final DynamicType placeholderType; /** * The method name transformer to use. */ private final MethodNameTransformer methodNameTransformer; /** * Creates a new enabled method rebase resolver. * * @param instrumentedMethods A set of methods that are to be rebased. * @param placeholderType The placeholder type to use for rebasing constructors. * @param methodNameTransformer The method name transformer to use. */ protected Enabled(Set instrumentedMethods, DynamicType placeholderType, MethodNameTransformer methodNameTransformer) { super(instrumentedMethods); this.placeholderType = placeholderType; this.methodNameTransformer = methodNameTransformer; } /** * Creates a method rebase resolver which is only capable of rebasing constructors if the instrumented methods suggest this. * * @param instrumentedMethods The instrumented methods to consider for rebasement. * @param instrumentedType The instrumented type. * @param classFileVersion The class file version to define a potential auxiliary type in. * @param auxiliaryTypeNamingStrategy A naming strategy to apply when naming an auxiliary type. * @param methodNameTransformer The method name transformer to use. * @return Returns a method rebase resolver that is capable of rebasing any of the instrumented methods. */ public static MethodRebaseResolver make(MethodList instrumentedMethods, TypeDescription instrumentedType, ClassFileVersion classFileVersion, AuxiliaryType.NamingStrategy auxiliaryTypeNamingStrategy, MethodNameTransformer methodNameTransformer) { return instrumentedMethods.filter(isConstructor()).isEmpty() ? MethodsOnly.of(instrumentedMethods, methodNameTransformer) : of(instrumentedMethods, TrivialType.INSTANCE.make(auxiliaryTypeNamingStrategy.name(TrivialType.INSTANCE, instrumentedType), classFileVersion, AuxiliaryType.MethodAccessorFactory.Illegal.INSTANCE), methodNameTransformer); } /** * Creates a fully enabled method rebase resolver. * * @param instrumentedMethods The instrumented methods. * @param placeholderType The placeholder type to use as when rebasing constructors. * @param methodNameTransformer The method name transformer to use. * @return A suitable method rebase resolver. */ protected static MethodRebaseResolver of(MethodList instrumentedMethods, DynamicType placeholderType, MethodNameTransformer methodNameTransformer) { return new Enabled(new HashSet(instrumentedMethods), placeholderType, methodNameTransformer); } @Override protected Resolution rebase(MethodDescription methodDescription) { return methodDescription.isConstructor() ? new Resolution.ForRebasedConstructor(methodDescription, placeholderType.getTypeDescription()) : new Resolution.ForRebasedMethod(methodDescription, methodNameTransformer); } @Override public List getAuxiliaryTypes() { return Collections.singletonList(placeholderType); } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && super.equals(other) && placeholderType.equals(((Enabled) other).placeholderType) && methodNameTransformer.equals(((Enabled) other).methodNameTransformer); } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + placeholderType.hashCode(); result = 31 * result + methodNameTransformer.hashCode(); return result; } @Override public String toString() { return "MethodRebaseResolver.Enabled{" + "instrumentedMethods=" + instrumentedMethods + ", placeholderType=" + placeholderType + ", methodNameTransformer=" + methodNameTransformer + '}'; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy