package net.bytebuddy.dynamic.scaffold.inline; import lombok.EqualsAndHashCode; import net.bytebuddy.ClassFileVersion; import net.bytebuddy.asm.AsmVisitorWrapper; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.MethodList; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.TypeResolutionStrategy; import net.bytebuddy.dynamic.scaffold.*; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.attribute.AnnotationRetention; import net.bytebuddy.implementation.attribute.AnnotationValueFilter; import net.bytebuddy.implementation.attribute.TypeAttributeAppender; import net.bytebuddy.implementation.auxiliary.AuxiliaryType; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.LatentMatcher; import net.bytebuddy.pool.TypePool; import java.util.HashSet; import java.util.Set; import static net.bytebuddy.matcher.ElementMatchers.is; /** * A type builder that rebases an instrumented type. * * @param <T> A loaded type that the dynamic type is guaranteed to be a subtype of. */ @EqualsAndHashCode(callSuper = true) public class RebaseDynamicTypeBuilder<T> extends AbstractInliningDynamicTypeBuilder<T> { /** * The method rebase resolver to use for determining the name of a rebased method. */ private final MethodNameTransformer methodNameTransformer; /** * Creates a rebase dynamic type builder. * * @param instrumentedType An instrumented type representing the subclass. * @param classFileVersion The class file version to use for types that are not based on an existing class file. * @param auxiliaryTypeNamingStrategy The naming strategy to use for naming auxiliary types. * @param annotationValueFilterFactory The annotation value filter factory to use. * @param annotationRetention The annotation retention strategy to use. * @param implementationContextFactory The implementation context factory to use. * @param methodGraphCompiler The method graph compiler to use. * @param typeValidation Determines if a type should be explicitly validated. * @param ignoredMethods A matcher for identifying methods that should be excluded from instrumentation. * @param originalType The original type that is being redefined or rebased. * @param classFileLocator The class file locator for locating the original type's class file. * @param methodNameTransformer The method rebase resolver to use for determining the name of a rebased method. */ public RebaseDynamicTypeBuilder(InstrumentedType.WithFlexibleName instrumentedType, ClassFileVersion classFileVersion, AuxiliaryType.NamingStrategy auxiliaryTypeNamingStrategy, AnnotationValueFilter.Factory annotationValueFilterFactory, AnnotationRetention annotationRetention, Implementation.Context.Factory implementationContextFactory, MethodGraph.Compiler methodGraphCompiler, TypeValidation typeValidation, LatentMatcher<? super MethodDescription> ignoredMethods, TypeDescription originalType, ClassFileLocator classFileLocator, MethodNameTransformer methodNameTransformer) { this(instrumentedType, new FieldRegistry.Default(), new MethodRegistry.Default(), annotationRetention.isEnabled() ? new TypeAttributeAppender.ForInstrumentedType.Differentiating(originalType) : TypeAttributeAppender.ForInstrumentedType.INSTANCE, AsmVisitorWrapper.NoOp.INSTANCE, classFileVersion, auxiliaryTypeNamingStrategy, annotationValueFilterFactory, annotationRetention, implementationContextFactory, methodGraphCompiler, typeValidation, ignoredMethods, originalType, classFileLocator, methodNameTransformer); } /** * Creates a rebase dynamic type builder. * * @param instrumentedType An instrumented type representing the subclass. * @param fieldRegistry The field pool to use. * @param methodRegistry The method pool to use. * @param typeAttributeAppender The type attribute appender to apply onto the instrumented type. * @param asmVisitorWrapper The ASM visitor wrapper to apply onto the class writer. * @param classFileVersion The class file version to use for types that are not based on an existing class file. * @param auxiliaryTypeNamingStrategy The naming strategy to use for naming auxiliary types. * @param annotationValueFilterFactory The annotation value filter factory to use. * @param annotationRetention The annotation retention strategy to use. * @param implementationContextFactory The implementation context factory to use. * @param methodGraphCompiler The method graph compiler to use. * @param typeValidation Determines if a type should be explicitly validated. * @param ignoredMethods A matcher for identifying methods that should be excluded from instrumentation. * @param originalType The original type that is being redefined or rebased. * @param classFileLocator The class file locator for locating the original type's class file. * @param methodNameTransformer The method rebase resolver to use for determining the name of a rebased method. */ protected RebaseDynamicTypeBuilder(InstrumentedType.WithFlexibleName instrumentedType, FieldRegistry fieldRegistry, MethodRegistry methodRegistry, TypeAttributeAppender typeAttributeAppender, AsmVisitorWrapper asmVisitorWrapper, ClassFileVersion classFileVersion, AuxiliaryType.NamingStrategy auxiliaryTypeNamingStrategy, AnnotationValueFilter.Factory annotationValueFilterFactory, AnnotationRetention annotationRetention, Implementation.Context.Factory implementationContextFactory, MethodGraph.Compiler methodGraphCompiler, TypeValidation typeValidation, LatentMatcher<? super MethodDescription> ignoredMethods, TypeDescription originalType, ClassFileLocator classFileLocator, MethodNameTransformer methodNameTransformer) { super(instrumentedType, fieldRegistry, methodRegistry, typeAttributeAppender, asmVisitorWrapper, classFileVersion, auxiliaryTypeNamingStrategy, annotationValueFilterFactory, annotationRetention, implementationContextFactory, methodGraphCompiler, typeValidation, ignoredMethods, originalType, classFileLocator); this.methodNameTransformer = methodNameTransformer; } @Override protected DynamicType.Builder<T> materialize(InstrumentedType.WithFlexibleName instrumentedType, FieldRegistry fieldRegistry, MethodRegistry methodRegistry, TypeAttributeAppender typeAttributeAppender, AsmVisitorWrapper asmVisitorWrapper, ClassFileVersion classFileVersion, AuxiliaryType.NamingStrategy auxiliaryTypeNamingStrategy, AnnotationValueFilter.Factory annotationValueFilterFactory, AnnotationRetention annotationRetention, Implementation.Context.Factory implementationContextFactory, MethodGraph.Compiler methodGraphCompiler, TypeValidation typeValidation, LatentMatcher<? super MethodDescription> ignoredMethods) { return new RebaseDynamicTypeBuilder<T>(instrumentedType, fieldRegistry, methodRegistry, typeAttributeAppender, asmVisitorWrapper, classFileVersion, auxiliaryTypeNamingStrategy, annotationValueFilterFactory, annotationRetention, implementationContextFactory, methodGraphCompiler, typeValidation, ignoredMethods, originalType, classFileLocator, methodNameTransformer); } @Override public DynamicType.Unloaded<T> make(TypeResolutionStrategy typeResolutionStrategy, TypePool typePool) { MethodRegistry.Prepared methodRegistry = this.methodRegistry.prepare(instrumentedType, methodGraphCompiler, typeValidation, InliningImplementationMatcher.of(ignoredMethods, originalType)); MethodRebaseResolver methodRebaseResolver = MethodRebaseResolver.Default.make(methodRegistry.getInstrumentedType(), new HashSet<MethodDescription.Token>(originalType.getDeclaredMethods() .asTokenList(is(originalType)) .filter(RebaseableMatcher.of(methodRegistry.getInstrumentedType(), methodRegistry.getInstrumentedMethods()))), classFileVersion, auxiliaryTypeNamingStrategy, methodNameTransformer); return TypeWriter.Default.<T>forRebasing(methodRegistry, fieldRegistry.compile(methodRegistry.getInstrumentedType()), typeAttributeAppender, asmVisitorWrapper, classFileVersion, annotationValueFilterFactory, annotationRetention, auxiliaryTypeNamingStrategy, implementationContextFactory, typeValidation, typePool, originalType, classFileLocator, methodRebaseResolver).make(typeResolutionStrategy.resolve()); } /** * A matcher that filters any method that should not be rebased, i.e. that is not already defined by the original type. */ @EqualsAndHashCode protected static class RebaseableMatcher implements ElementMatcher<MethodDescription.Token> { /** * A set of method tokens representing all instrumented methods. */ private final Set<MethodDescription.Token> tokens; /** * Creates a new matcher for identifying rebasable methods. * * @param tokens A set of method tokens representing all instrumented methods. */ protected RebaseableMatcher(Set<MethodDescription.Token> tokens) { this.tokens = tokens; } /** * Returns a matcher that filters any method that should not be rebased. * * @param instrumentedType The instrumented type. * @param instrumentedMethods All instrumented methods. * @return A suitable matcher that filters all methods that should not be rebased. */ protected static ElementMatcher<MethodDescription.Token> of(TypeDescription instrumentedType, MethodList<?> instrumentedMethods) { return new RebaseableMatcher(new HashSet<MethodDescription.Token>(instrumentedMethods.asTokenList(is(instrumentedType)))); } @Override public boolean matches(MethodDescription.Token target) { return tokens.contains(target); } } }