org.testifyproject.bytebuddy.asm.MemberRemoval Maven / Gradle / Ivy
package org.testifyproject.bytebuddy.asm;
import lombok.EqualsAndHashCode;
import org.testifyproject.bytebuddy.description.field.FieldDescription;
import org.testifyproject.bytebuddy.description.field.FieldList;
import org.testifyproject.bytebuddy.description.method.MethodDescription;
import org.testifyproject.bytebuddy.description.method.MethodList;
import org.testifyproject.bytebuddy.description.type.TypeDescription;
import org.testifyproject.bytebuddy.implementation.Implementation;
import org.testifyproject.bytebuddy.matcher.ElementMatcher;
import org.testifyproject.bytebuddy.matcher.ElementMatchers;
import org.testifyproject.bytebuddy.pool.TypePool;
import org.testifyproject.bytebuddy.utility.CompoundList;
import org.testifyproject.bytebuddy.jar.asm.ClassVisitor;
import org.testifyproject.bytebuddy.jar.asm.FieldVisitor;
import org.testifyproject.bytebuddy.jar.asm.MethodVisitor;
import org.testifyproject.bytebuddy.jar.asm.Opcodes;
import java.util.HashMap;
import java.util.Map;
import static org.testifyproject.bytebuddy.matcher.ElementMatchers.isConstructor;
import static org.testifyproject.bytebuddy.matcher.ElementMatchers.isMethod;
/**
*
* A visitor wrapper that removes fields or methods that match a given {@link ElementMatcher}.
*
*
* Important: This matcher is not capable of removing synthetic bridge methods which will be retained if they are
* declared by the same class. As bridge methods only invoke an overridden method, the dispatch should however not be
* influenced by their retention.
*
*
* Important: The removal of the method is not reflected in the created {@link org.testifyproject.bytebuddy.dynamic.DynamicType}'s
* type description of the instrumented type.
*
*/
@EqualsAndHashCode(callSuper = false)
public class MemberRemoval extends AsmVisitorWrapper.AbstractBase {
/**
* The matcher that decides upon field removal.
*/
private final ElementMatcher.Junction fieldMatcher;
/**
* The matcher that decides upon method removal.
*/
private final ElementMatcher.Junction methodMatcher;
/**
* Creates a new member removal instance that does not specify the removal of any methods.
*/
public MemberRemoval() {
this(ElementMatchers.none(), ElementMatchers.none());
}
/**
* Creates a new member removal instance.
*
* @param fieldMatcher The matcher that decides upon field removal.
* @param methodMatcher The matcher that decides upon field removal.
*/
protected MemberRemoval(ElementMatcher.Junction fieldMatcher,
ElementMatcher.Junction methodMatcher) {
this.fieldMatcher = fieldMatcher;
this.methodMatcher = methodMatcher;
}
/**
* Specifies that any field that matches the specified matcher should be removed.
*
* @param matcher The matcher that decides upon field removal.
* @return A new member removal instance that removes all previously specified members and any fields that match the specified matcher.
*/
public MemberRemoval stripFields(ElementMatcher super FieldDescription.InDefinedShape> matcher) {
return new MemberRemoval(fieldMatcher.or(matcher), methodMatcher);
}
/**
* Specifies that any method that matches the specified matcher should be removed.
*
* @param matcher The matcher that decides upon method removal.
* @return A new member removal instance that removes all previously specified members and any method that matches the specified matcher.
*/
public MemberRemoval stripMethods(ElementMatcher super MethodDescription> matcher) {
return stripInvokables(isMethod().and(matcher));
}
/**
* Specifies that any constructor that matches the specified matcher should be removed.
*
* @param matcher The matcher that decides upon constructor removal.
* @return A new member removal instance that removes all previously specified members and any constructor that matches the specified matcher.
*/
public MemberRemoval stripConstructors(ElementMatcher super MethodDescription> matcher) {
return stripInvokables(isConstructor().and(matcher));
}
/**
* Specifies that any method or constructor that matches the specified matcher should be removed.
*
* @param matcher The matcher that decides upon method and constructor removal.
* @return A new member removal instance that removes all previously specified members and any method or constructor that matches the specified matcher.
*/
public MemberRemoval stripInvokables(ElementMatcher super MethodDescription> matcher) {
return new MemberRemoval(fieldMatcher, methodMatcher.or(matcher));
}
@Override
public ClassVisitor wrap(TypeDescription instrumentedType,
ClassVisitor classVisitor,
Implementation.Context implementationContext,
TypePool typePool,
FieldList fields,
MethodList> methods,
int writerFlags,
int readerFlags) {
Map mappedFields = new HashMap();
for (FieldDescription.InDefinedShape fieldDescription : fields) {
mappedFields.put(fieldDescription.getInternalName() + fieldDescription.getDescriptor(), fieldDescription);
}
Map mappedMethods = new HashMap();
for (MethodDescription methodDescription : CompoundList.of(methods, new MethodDescription.Latent.TypeInitializer(instrumentedType))) {
mappedMethods.put(methodDescription.getInternalName() + methodDescription.getDescriptor(), methodDescription);
}
return new MemberRemovingClassVisitor(classVisitor, fieldMatcher, methodMatcher, mappedFields, mappedMethods);
}
/**
* A class visitor that removes members based on element matchers.
*/
protected static class MemberRemovingClassVisitor extends ClassVisitor {
/**
* Indicates the removal of a field.
*/
private static final FieldVisitor REMOVE_FIELD = null;
/**
* Indicates the removal of a method.
*/
private static final MethodVisitor REMOVE_METHOD = null;
/**
* The matcher that determines field removal.
*/
private final ElementMatcher.Junction fieldMatcher;
/**
* The matcher that determines method removal.
*/
private final ElementMatcher.Junction methodMatcher;
/**
* A mapping of field names and descriptors to their description.
*/
private final Map fields;
/**
* A mapping of method names and descriptors to their description.
*/
private final Map methods;
/**
* Creates a new member removing class visitor.
*
* @param classVisitor The class visitor to delegate to.
* @param fieldMatcher The matcher that determines field removal.
* @param methodMatcher The matcher that determines method removal.
* @param fields A mapping of field names and descriptors to their description.
* @param methods A mapping of method names and descriptors to their description.
*/
protected MemberRemovingClassVisitor(ClassVisitor classVisitor,
ElementMatcher.Junction fieldMatcher,
ElementMatcher.Junction methodMatcher,
Map fields,
Map methods) {
super(Opcodes.ASM6, classVisitor);
this.fieldMatcher = fieldMatcher;
this.methodMatcher = methodMatcher;
this.fields = fields;
this.methods = methods;
}
@Override
public FieldVisitor visitField(int modifiers, String internalName, String descriptor, String signature, Object value) {
FieldDescription.InDefinedShape fieldDescription = fields.get(internalName + descriptor);
return fieldDescription != null && fieldMatcher.matches(fieldDescription)
? REMOVE_FIELD
: super.visitField(modifiers, internalName, descriptor, signature, value);
}
@Override
public MethodVisitor visitMethod(int modifiers, String internalName, String descriptor, String signature, String[] exception) {
MethodDescription methodDescription = methods.get(internalName + descriptor);
return methodDescription != null && methodMatcher.matches(methodDescription)
? REMOVE_METHOD
: super.visitMethod(modifiers, internalName, descriptor, signature, exception);
}
}
}