net.bytebuddy.asm.MemberRemoval Maven / Gradle / Ivy
/*
* Copyright 2014 - Present Rafael Winterhalter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.bytebuddy.asm;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;
import net.bytebuddy.utility.CompoundList;
import net.bytebuddy.utility.OpenedClassReader;
import net.bytebuddy.utility.nullability.AlwaysNull;
import net.bytebuddy.utility.nullability.MaybeNull;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import java.util.HashMap;
import java.util.Map;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.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 net.bytebuddy.dynamic.DynamicType}'s
* type description of the instrumented type.
*
*/
@HashCodeAndEqualsPlugin.Enhance
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. Note that this implementation will
* not strip bridge methods for virtual overrides of generic methods.
*
* @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. Note that this implementation will
* not strip bridge methods for virtual overrides of generic methods.
*
* @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));
}
/**
* {@inheritDoc}
*/
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.
*/
@javax.annotation.Nonnull(when = javax.annotation.meta.When.NEVER)
private static final FieldVisitor REMOVE_FIELD = null;
/**
* Indicates the removal of a method.
*/
@AlwaysNull
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(OpenedClassReader.ASM_API, classVisitor);
this.fieldMatcher = fieldMatcher;
this.methodMatcher = methodMatcher;
this.fields = fields;
this.methods = methods;
}
@Override
@MaybeNull
public FieldVisitor visitField(int modifiers, String internalName, String descriptor, @MaybeNull String signature, @MaybeNull 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
@MaybeNull
public MethodVisitor visitMethod(int modifiers, String internalName, String descriptor, @MaybeNull String signature, @MaybeNull String[] exception) {
MethodDescription methodDescription = methods.get(internalName + descriptor);
return methodDescription != null && methodMatcher.matches(methodDescription)
? REMOVE_METHOD
: super.visitMethod(modifiers, internalName, descriptor, signature, exception);
}
}
}