com.ui4j.bytebuddy.dynamic.scaffold.inline.RebaseInstrumentationTarget Maven / Gradle / Ivy
The newest version!
package com.ui4j.bytebuddy.dynamic.scaffold.inline;
import com.ui4j.bytebuddy.dynamic.scaffold.BridgeMethodResolver;
import com.ui4j.bytebuddy.instrumentation.Instrumentation;
import com.ui4j.bytebuddy.instrumentation.method.MethodDescription;
import com.ui4j.bytebuddy.instrumentation.method.MethodLookupEngine;
import com.ui4j.bytebuddy.instrumentation.method.bytecode.stack.StackManipulation;
import com.ui4j.bytebuddy.instrumentation.method.bytecode.stack.member.MethodInvocation;
import com.ui4j.bytebuddy.instrumentation.type.TypeDescription;
import com.ui4j.bytebuddy.jar.asm.MethodVisitor;
/**
* An instrumentation target for redefining a given type while preserving the original methods within the
* instrumented type.
*
* Super method calls are merely emulated by this {@link com.ui4j.bytebuddy.instrumentation.Instrumentation.Target} in order
* to preserve Java's super call semantics a user would expect when invoking a {@code super}-prefixed method. This
* means that original methods are either moved to renamed {@code private} methods which are never dispatched
* virtually or they are invoked directly via the {@code INVOKESPECIAL} invocation to explicitly forbid a virtual
* dispatch.
*/
public class RebaseInstrumentationTarget extends Instrumentation.Target.AbstractBase {
/**
* A method rebase resolver to be used when calling a rebased method.
*/
protected final MethodRebaseResolver methodRebaseResolver;
/**
* Creates a rebase instrumentation target.
*
* @param finding The lookup of the instrumented type this instance should represent.
* @param bridgeMethodResolverFactory A factory for creating a bridge method resolver.
* @param methodRebaseResolver A method rebase resolver to be used when calling a rebased method.
*/
protected RebaseInstrumentationTarget(MethodLookupEngine.Finding finding,
BridgeMethodResolver.Factory bridgeMethodResolverFactory,
MethodRebaseResolver methodRebaseResolver) {
super(finding, bridgeMethodResolverFactory);
this.methodRebaseResolver = methodRebaseResolver;
}
@Override
protected Instrumentation.SpecialMethodInvocation invokeSuper(MethodDescription methodDescription) {
return methodDescription.getDeclaringType().equals(typeDescription)
? invokeSuper(methodRebaseResolver.resolve(methodDescription))
: Instrumentation.SpecialMethodInvocation.Simple.of(methodDescription, typeDescription.getSupertype());
}
/**
* Defines a special method invocation on type level. This means that invoke super instructions are not explicitly
* dispatched on the super type but on the instrumented type. This allows to call methods non-virtually even though
* they are not defined on the super type. Redefined constructors are not renamed by are added an additional
* parameter of a type which is only used for this purpose. Additionally, a {@code null} value is loaded onto the
* stack when the special method invocation is applied in order to fill the operand stack with an additional caller
* argument. Non-constructor methods are renamed.
*
* @param resolution A proxied super method invocation on the instrumented type.
* @return A special method invocation on this proxied super method.
*/
private Instrumentation.SpecialMethodInvocation invokeSuper(MethodRebaseResolver.Resolution resolution) {
return resolution.isRebased()
? RebasedMethodSpecialMethodInvocation.of(resolution, typeDescription)
: Instrumentation.SpecialMethodInvocation.Simple.of(resolution.getResolvedMethod(), typeDescription);
}
@Override
public TypeDescription getOriginType() {
return typeDescription;
}
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& super.equals(other)
&& methodRebaseResolver.equals(((RebaseInstrumentationTarget) other).methodRebaseResolver);
}
@Override
public int hashCode() {
return 31 * super.hashCode() + methodRebaseResolver.hashCode();
}
@Override
public String toString() {
return "RebaseInstrumentationTarget{" +
"typeDescription=" + typeDescription +
", defaultMethods=" + defaultMethods +
", bridgeMethodResolver=" + bridgeMethodResolver +
", methodRebaseResolver=" + methodRebaseResolver +
'}';
}
/**
* A {@link com.ui4j.bytebuddy.instrumentation.Instrumentation.SpecialMethodInvocation} which invokes a rebased method
* as given by a {@link MethodRebaseResolver}.
*/
protected static class RebasedMethodSpecialMethodInvocation implements Instrumentation.SpecialMethodInvocation {
/**
* The method to invoke via a special method invocation.
*/
private final MethodDescription methodDescription;
/**
* The instrumented type on which the method should be invoked on.
*/
private final TypeDescription instrumentedType;
/**
* The stack manipulation to execute in order to invoke the rebased method.
*/
private final StackManipulation stackManipulation;
/**
* Creates a special method invocation for a rebased method.
*
* @param resolution The resolution of the rebased method.
* @param instrumentedType The instrumented type on which this method is to be invoked.
*/
protected RebasedMethodSpecialMethodInvocation(MethodRebaseResolver.Resolution resolution,
TypeDescription instrumentedType) {
this.instrumentedType = instrumentedType;
methodDescription = resolution.getResolvedMethod();
stackManipulation = new Compound(resolution.getAdditionalArguments(), resolution.getResolvedMethod().isStatic()
? MethodInvocation.invoke(resolution.getResolvedMethod())
: MethodInvocation.invoke(resolution.getResolvedMethod()).special(instrumentedType));
}
/**
* Creates a special method invocation for a rebased method if such an invocation is possible or otherwise
* returns an illegal special method invocation.
*
* @param resolution The resolution of the rebased method.
* @param instrumentedType The instrumented type on which this method is to be invoked.
* @return A special method invocation for the given method.
*/
public static Instrumentation.SpecialMethodInvocation of(MethodRebaseResolver.Resolution resolution,
TypeDescription instrumentedType) {
return resolution.getResolvedMethod().isAbstract()
? Illegal.INSTANCE
: new RebasedMethodSpecialMethodInvocation(resolution, instrumentedType);
}
@Override
public MethodDescription getMethodDescription() {
return methodDescription;
}
@Override
public TypeDescription getTypeDescription() {
return instrumentedType;
}
@Override
public boolean isValid() {
return stackManipulation.isValid();
}
@Override
public Size apply(MethodVisitor methodVisitor, Instrumentation.Context instrumentationContext) {
return stackManipulation.apply(methodVisitor, instrumentationContext);
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (!(other instanceof Instrumentation.SpecialMethodInvocation)) return false;
Instrumentation.SpecialMethodInvocation specialMethodInvocation = (Instrumentation.SpecialMethodInvocation) other;
return isValid() == specialMethodInvocation.isValid()
&& instrumentedType.equals(specialMethodInvocation.getTypeDescription())
&& methodDescription.getInternalName().equals(specialMethodInvocation.getMethodDescription().getInternalName())
&& methodDescription.getParameters().asTypeList().equals(specialMethodInvocation.getMethodDescription().getParameters().asTypeList())
&& methodDescription.getReturnType().equals(specialMethodInvocation.getMethodDescription().getReturnType());
}
@Override
public int hashCode() {
int result = methodDescription.getInternalName().hashCode();
result = 31 * result + methodDescription.getParameters().asTypeList().hashCode();
result = 31 * result + methodDescription.getReturnType().hashCode();
result = 31 * result + instrumentedType.hashCode();
return result;
}
@Override
public String toString() {
return "RebaseInstrumentationTarget.RedefinedConstructorInvocation{" +
"instrumentedType=" + instrumentedType +
", methodDescription=" + methodDescription +
'}';
}
}
/**
* A factory for creating a {@link com.ui4j.bytebuddy.dynamic.scaffold.inline.RebaseInstrumentationTarget}.
*/
public static class Factory implements Instrumentation.Target.Factory {
/**
* A factory for creating a bridge method resolver.
*/
private final BridgeMethodResolver.Factory bridgeMethodResolverFactory;
/**
* A method rebase resolver to be used when calling a rebased method.
*/
private final MethodRebaseResolver methodRebaseResolver;
/**
* Creates a new factory for creating a {@link com.ui4j.bytebuddy.dynamic.scaffold.inline.RebaseInstrumentationTarget}.
*
* @param bridgeMethodResolverFactory A factory for creating a bridge method resolver.
* @param methodRebaseResolver A method rebase resolver to be used when calling a rebased method.
*/
public Factory(BridgeMethodResolver.Factory bridgeMethodResolverFactory,
MethodRebaseResolver methodRebaseResolver) {
this.bridgeMethodResolverFactory = bridgeMethodResolverFactory;
this.methodRebaseResolver = methodRebaseResolver;
}
@Override
public Instrumentation.Target make(MethodLookupEngine.Finding finding) {
return new RebaseInstrumentationTarget(finding,
bridgeMethodResolverFactory,
methodRebaseResolver);
}
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& bridgeMethodResolverFactory.equals(((Factory) other).bridgeMethodResolverFactory)
&& methodRebaseResolver.equals(((Factory) other).methodRebaseResolver);
}
@Override
public int hashCode() {
return 31 * bridgeMethodResolverFactory.hashCode() + methodRebaseResolver.hashCode();
}
@Override
public String toString() {
return "RebaseInstrumentationTarget.Factory{" +
"bridgeMethodResolverFactory=" + bridgeMethodResolverFactory +
", methodRebaseResolver=" + methodRebaseResolver +
'}';
}
}
}