// Generated by delombok at Sun Feb 26 12:31:38 KST 2017
package scouter.bytebuddy.implementation.auxiliary;
import scouter.bytebuddy.ByteBuddy;
import scouter.bytebuddy.ClassFileVersion;
import scouter.bytebuddy.description.annotation.AnnotationDescription;
import scouter.bytebuddy.description.annotation.AnnotationValue;
import scouter.bytebuddy.description.field.FieldDescription;
import scouter.bytebuddy.description.field.FieldList;
import scouter.bytebuddy.description.method.MethodDescription;
import scouter.bytebuddy.description.method.ParameterDescription;
import scouter.bytebuddy.description.modifier.Visibility;
import scouter.bytebuddy.description.type.TypeDefinition;
import scouter.bytebuddy.description.type.TypeDescription;
import scouter.bytebuddy.description.type.TypeVariableToken;
import scouter.bytebuddy.dynamic.DynamicType;
import scouter.bytebuddy.dynamic.scaffold.InstrumentedType;
import scouter.bytebuddy.dynamic.scaffold.MethodGraph;
import scouter.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import scouter.bytebuddy.implementation.Implementation;
import scouter.bytebuddy.implementation.MethodAccessorFactory;
import scouter.bytebuddy.implementation.bytecode.ByteCodeAppender;
import scouter.bytebuddy.implementation.bytecode.Duplication;
import scouter.bytebuddy.implementation.bytecode.StackManipulation;
import scouter.bytebuddy.implementation.bytecode.TypeCreation;
import scouter.bytebuddy.implementation.bytecode.assign.Assigner;
import scouter.bytebuddy.implementation.bytecode.member.FieldAccess;
import scouter.bytebuddy.implementation.bytecode.member.MethodInvocation;
import scouter.bytebuddy.implementation.bytecode.member.MethodReturn;
import scouter.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
import scouter.bytebuddy.jar.asm.MethodVisitor;
import scouter.bytebuddy.jar.asm.Opcodes;
import scouter.bytebuddy.matcher.ElementMatchers;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.Callable;
/**
* A method call proxy represents a class that is compiled against a particular method which can then be called whenever
* either its {@link java.util.concurrent.Callable#call()} or {@link Runnable#run()} method is called where the method
* call proxy implements both interfaces.
* <p> </p>
* In order to do so, the method call proxy instances are constructed by providing all the necessary information for
* calling a particular method:
* <ol>
* <li>If the target method is not {@code static}, the first argument should be an instance on which the method is called.</li>
* <li>All arguments for the called method in the order in which they are required.</li>
* </ol>
*/
public class MethodCallProxy implements AuxiliaryType {
/**
* The prefix of the fields holding the original method invocation's arguments.
*/
private static final String FIELD_NAME_PREFIX = "argument";
/**
* The special method invocation to invoke from the auxiliary type.
*/
private final Implementation.SpecialMethodInvocation specialMethodInvocation;
/**
* Determines if the generated proxy should be serializableProxy.
*/
private final boolean serializableProxy;
/**
* The assigner to use for invoking a bridge method target where the parameter and return types need to be
* assigned.
*/
private final Assigner assigner;
/**
* Creates a new method call proxy for a given method and uses a default assigner for assigning the method's return
* value to either the {@link java.util.concurrent.Callable#call()} or {@link Runnable#run()} method returns.
*
* @param specialMethodInvocation The special method invocation which should be invoked by this method call proxy.
* @param serializableProxy Determines if the generated proxy should be serializableProxy.
*/
public MethodCallProxy(Implementation.SpecialMethodInvocation specialMethodInvocation, boolean serializableProxy) {
this(specialMethodInvocation, serializableProxy, Assigner.DEFAULT);
}
/**
* Creates a new method call proxy for a given method.
*
* @param specialMethodInvocation The special method invocation which should be invoked by this method call proxy.
* @param serializableProxy Determines if the generated proxy should be serializableProxy.
* @param assigner An assigner for assigning the target method's return value to either the
* {@link java.util.concurrent.Callable#call()} or {@link Runnable#run()}} methods'
* return values.
*/
public MethodCallProxy(Implementation.SpecialMethodInvocation specialMethodInvocation, boolean serializableProxy, Assigner assigner) {
this.specialMethodInvocation = specialMethodInvocation;
this.serializableProxy = serializableProxy;
this.assigner = assigner;
}
/**
* Creates a linked hash map of field names to their types where each field represents a parameter of the method.
*
* @param methodDescription The method to extract into fields.
* @return A map of fields in the order they need to be loaded onto the operand stack for invoking the original
* method, including a reference to the instance of the instrumented type that is invoked if applicable.
*/
private static LinkedHashMap<String, TypeDescription> extractFields(MethodDescription methodDescription) {
LinkedHashMap<String, TypeDescription> typeDescriptions = new LinkedHashMap<String, TypeDescription>();
int currentIndex = 0;
if (!methodDescription.isStatic()) {
typeDescriptions.put(fieldName(currentIndex++), methodDescription.getDeclaringType().asErasure());
}
for (ParameterDescription parameterDescription : methodDescription.getParameters()) {
typeDescriptions.put(fieldName(currentIndex++), parameterDescription.getType().asErasure());
}
return typeDescriptions;
}
/**
* Creates a field name for a method parameter of a given index.
*
* @param index The index for which the field name is to be created.
* @return The name for the given parameter.
*/
private static String fieldName(int index) {
return String.format("%s%d", FIELD_NAME_PREFIX, index);
}
@Override
public DynamicType make(String auxiliaryTypeName, ClassFileVersion classFileVersion, MethodAccessorFactory methodAccessorFactory) {
MethodDescription accessorMethod = methodAccessorFactory.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT);
LinkedHashMap<String, TypeDescription> parameterFields = extractFields(accessorMethod);
DynamicType.Builder<?> builder = new ByteBuddy(classFileVersion).with(PrecomputedMethodGraph.INSTANCE).subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS).name(auxiliaryTypeName).modifiers(DEFAULT_TYPE_MODIFIER).implement(Runnable.class, Callable.class).intercept(new MethodCall(accessorMethod, assigner)).implement(serializableProxy ? new Class<?>[] {Serializable.class} : new Class<?>[0]).defineConstructor().withParameters(parameterFields.values()).intercept(ConstructorCall.INSTANCE);
for (Map.Entry<String, TypeDescription> field : parameterFields.entrySet()) {
builder = builder.defineField(field.getKey(), field.getValue(), Visibility.PRIVATE);
}
return builder.make();
}
/**
* A precomputed method graph that only displays the methods that are relevant for creating a method call proxy.
*/
protected enum PrecomputedMethodGraph implements MethodGraph.Compiler {
/**
* The singleton instance.
*/
INSTANCE;
/**
* The precomputed method graph.
*/
private final MethodGraph.Linked methodGraph;
/**
* Creates the precomputed method graph.
*/
PrecomputedMethodGraph() {
LinkedHashMap<MethodDescription.SignatureToken, MethodGraph.Node> nodes = new LinkedHashMap<MethodDescription.SignatureToken, MethodGraph.Node>();
MethodDescription callMethod = new MethodDescription.Latent(new TypeDescription.ForLoadedType(Callable.class), "call", Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, Collections.<TypeVariableToken>emptyList(), TypeDescription.Generic.OBJECT, Collections.<ParameterDescription.Token>emptyList(), Collections.singletonList(new TypeDescription.Generic.OfNonGenericType.ForLoadedType(Exception.class)), Collections.<AnnotationDescription>emptyList(), AnnotationValue.UNDEFINED, TypeDescription.Generic.UNDEFINED);
nodes.put(callMethod.asSignatureToken(), new MethodGraph.Node.Simple(callMethod));
MethodDescription runMethod = new MethodDescription.Latent(new TypeDescription.ForLoadedType(Runnable.class), "run", Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, Collections.<TypeVariableToken>emptyList(), TypeDescription.Generic.VOID, Collections.<ParameterDescription.Token>emptyList(), Collections.<TypeDescription.Generic>emptyList(), Collections.<AnnotationDescription>emptyList(), AnnotationValue.UNDEFINED, TypeDescription.Generic.UNDEFINED);
nodes.put(runMethod.asSignatureToken(), new MethodGraph.Node.Simple(runMethod));
MethodGraph methodGraph = new MethodGraph.Simple(nodes);
this.methodGraph = new MethodGraph.Linked.Delegation(methodGraph, methodGraph, Collections.<TypeDescription, MethodGraph>emptyMap());
}
@Override
public MethodGraph.Linked compile(TypeDescription typeDescription) {
return compile(typeDescription, typeDescription);
}
@Override
public MethodGraph.Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint) {
return methodGraph;
}
}
/**
* An implementation for a constructor of a {@link MethodCallProxy}.
*/
protected enum ConstructorCall implements Implementation {
/**
* The singleton instance.
*/
INSTANCE;
/**
* A reference of the {@link Object} type default constructor.
*/
private final MethodDescription objectTypeDefaultConstructor;
/**
* Creates the constructor call singleton.
*/
ConstructorCall() {
objectTypeDefaultConstructor = TypeDescription.OBJECT.getDeclaredMethods().filter(ElementMatchers.isConstructor()).getOnly();
}
@Override
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType;
}
@Override
public ByteCodeAppender appender(Target implementationTarget) {
return new Appender(implementationTarget.getInstrumentedType());
}
/**
* The appender for implementing the {@link MethodCallProxy.ConstructorCall}.
*/
protected static class Appender implements ByteCodeAppender {
/**
* The instrumented type being created.
*/
private final TypeDescription instrumentedType;
/**
* Creates a new appender.
*
* @param instrumentedType The instrumented type that is being created.
*/
private Appender(TypeDescription instrumentedType) {
this.instrumentedType = instrumentedType;
}
@Override
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
FieldList<?> fieldList = instrumentedType.getDeclaredFields();
StackManipulation[] fieldLoading = new StackManipulation[fieldList.size()];
int index = 0;
for (FieldDescription fieldDescription : fieldList) {
fieldLoading[index] = new StackManipulation.Compound(MethodVariableAccess.loadThis(), MethodVariableAccess.load(instrumentedMethod.getParameters().get(index)), FieldAccess.forField(fieldDescription).write());
index++;
}
StackManipulation.Size stackSize = new StackManipulation.Compound(MethodVariableAccess.loadThis(), MethodInvocation.invoke(ConstructorCall.INSTANCE.objectTypeDefaultConstructor), new StackManipulation.Compound(fieldLoading), MethodReturn.VOID).apply(methodVisitor, implementationContext);
return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodCallProxy.ConstructorCall.Appender)) return false;
final MethodCallProxy.ConstructorCall.Appender other = (MethodCallProxy.ConstructorCall.Appender) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$instrumentedType = this.instrumentedType;
final java.lang.Object other$instrumentedType = other.instrumentedType;
if (this$instrumentedType == null ? other$instrumentedType != null : !this$instrumentedType.equals(other$instrumentedType)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodCallProxy.ConstructorCall.Appender;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $instrumentedType = this.instrumentedType;
result = result * PRIME + ($instrumentedType == null ? 43 : $instrumentedType.hashCode());
return result;
}
}
}
/**
* An implementation for a method of a {@link MethodCallProxy}.
*/
protected static class MethodCall implements Implementation {
/**
* The method that is accessed by the implemented method.
*/
private final MethodDescription accessorMethod;
/**
* The assigner to be used for invoking the accessor method.
*/
private final Assigner assigner;
/**
* Creates a new method call implementation.
*
* @param accessorMethod The method that is accessed by the implemented method.
* @param assigner The assigner to be used for invoking the accessor method.
*/
protected MethodCall(MethodDescription accessorMethod, Assigner assigner) {
this.accessorMethod = accessorMethod;
this.assigner = assigner;
}
@Override
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType;
}
@Override
public ByteCodeAppender appender(Target implementationTarget) {
return new Appender(implementationTarget.getInstrumentedType());
}
/**
* The appender for implementing the {@link MethodCallProxy.MethodCall}.
*/
protected class Appender implements ByteCodeAppender {
/**
* The instrumented type that is implemented.
*/
private final TypeDescription instrumentedType;
/**
* Creates a new appender.
*
* @param instrumentedType The instrumented type to be implemented.
*/
private Appender(TypeDescription instrumentedType) {
this.instrumentedType = instrumentedType;
}
@Override
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
FieldList<?> fieldList = instrumentedType.getDeclaredFields();
List<StackManipulation> fieldLoadings = new ArrayList<StackManipulation>(fieldList.size());
for (FieldDescription fieldDescription : fieldList) {
fieldLoadings.add(new StackManipulation.Compound(MethodVariableAccess.loadThis(), FieldAccess.forField(fieldDescription).read()));
}
StackManipulation.Size stackSize = new StackManipulation.Compound(new StackManipulation.Compound(fieldLoadings), MethodInvocation.invoke(accessorMethod), assigner.assign(accessorMethod.getReturnType(), instrumentedMethod.getReturnType(), Assigner.Typing.DYNAMIC), MethodReturn.of(instrumentedMethod.getReturnType())).apply(methodVisitor, implementationContext);
return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
}
private MethodCall getMethodCall() {
return MethodCall.this;
}
// HE: Remove when Lombok support for getOuter is added.
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass()) && instrumentedType.equals(((Appender) other).instrumentedType) && MethodCall.this.equals(((Appender) other).getMethodCall());
}
// HE: Remove when Lombok support for getOuter is added.
/**
* Returns the outer instance.
*
* @return The outer instance.
*/
@Override
public int hashCode() {
return 31 * MethodCall.this.hashCode() + instrumentedType.hashCode();
}
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodCallProxy.MethodCall)) return false;
final MethodCallProxy.MethodCall other = (MethodCallProxy.MethodCall) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$accessorMethod = this.accessorMethod;
final java.lang.Object other$accessorMethod = other.accessorMethod;
if (this$accessorMethod == null ? other$accessorMethod != null : !this$accessorMethod.equals(other$accessorMethod)) return false;
final java.lang.Object this$assigner = this.assigner;
final java.lang.Object other$assigner = other.assigner;
if (this$assigner == null ? other$assigner != null : !this$assigner.equals(other$assigner)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodCallProxy.MethodCall;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $accessorMethod = this.accessorMethod;
result = result * PRIME + ($accessorMethod == null ? 43 : $accessorMethod.hashCode());
final java.lang.Object $assigner = this.assigner;
result = result * PRIME + ($assigner == null ? 43 : $assigner.hashCode());
return result;
}
}
/**
* A stack manipulation that creates a {@link MethodCallProxy}
* for a given method an pushes such an object onto the call stack. For this purpose, all arguments of the proxied method
* are loaded onto the stack what is only possible if this instance is used from a method with an identical signature such
* as the target method itself.
*/
public static class AssignableSignatureCall implements StackManipulation {
/**
* The special method invocation to be proxied by this stack manipulation.
*/
private final Implementation.SpecialMethodInvocation specialMethodInvocation;
/**
* Determines if the generated proxy should be serializableProxy.
*/
private final boolean serializable;
/**
* Creates an operand stack assignment that creates a
* {@link MethodCallProxy} for the
* {@code targetMethod} and pushes this proxy object onto the stack.
*
* @param specialMethodInvocation The special method invocation which should be invoked by the created method
* call proxy.
* @param serializable Determines if the generated proxy should be serializableProxy.
*/
public AssignableSignatureCall(Implementation.SpecialMethodInvocation specialMethodInvocation, boolean serializable) {
this.specialMethodInvocation = specialMethodInvocation;
this.serializable = serializable;
}
@Override
public boolean isValid() {
return true;
}
@Override
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
TypeDescription auxiliaryType = implementationContext.register(new MethodCallProxy(specialMethodInvocation, serializable));
return new Compound(TypeCreation.of(auxiliaryType), Duplication.SINGLE, MethodVariableAccess.allArgumentsOf(specialMethodInvocation.getMethodDescription()).prependThisReference(), MethodInvocation.invoke(auxiliaryType.getDeclaredMethods().filter(ElementMatchers.isConstructor()).getOnly())).apply(methodVisitor, implementationContext);
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodCallProxy.AssignableSignatureCall)) return false;
final MethodCallProxy.AssignableSignatureCall other = (MethodCallProxy.AssignableSignatureCall) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$specialMethodInvocation = this.specialMethodInvocation;
final java.lang.Object other$specialMethodInvocation = other.specialMethodInvocation;
if (this$specialMethodInvocation == null ? other$specialMethodInvocation != null : !this$specialMethodInvocation.equals(other$specialMethodInvocation)) return false;
if (this.serializable != other.serializable) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodCallProxy.AssignableSignatureCall;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $specialMethodInvocation = this.specialMethodInvocation;
result = result * PRIME + ($specialMethodInvocation == null ? 43 : $specialMethodInvocation.hashCode());
result = result * PRIME + (this.serializable ? 79 : 97);
return result;
}
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof MethodCallProxy)) return false;
final MethodCallProxy other = (MethodCallProxy) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$specialMethodInvocation = this.specialMethodInvocation;
final java.lang.Object other$specialMethodInvocation = other.specialMethodInvocation;
if (this$specialMethodInvocation == null ? other$specialMethodInvocation != null : !this$specialMethodInvocation.equals(other$specialMethodInvocation)) return false;
if (this.serializableProxy != other.serializableProxy) return false;
final java.lang.Object this$assigner = this.assigner;
final java.lang.Object other$assigner = other.assigner;
if (this$assigner == null ? other$assigner != null : !this$assigner.equals(other$assigner)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof MethodCallProxy;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $specialMethodInvocation = this.specialMethodInvocation;
result = result * PRIME + ($specialMethodInvocation == null ? 43 : $specialMethodInvocation.hashCode());
result = result * PRIME + (this.serializableProxy ? 79 : 97);
final java.lang.Object $assigner = this.assigner;
result = result * PRIME + ($assigner == null ? 43 : $assigner.hashCode());
return result;
}
}