package net.bytebuddy.dynamic.scaffold.subclass; import lombok.EqualsAndHashCode; import net.bytebuddy.ClassFileVersion; import net.bytebuddy.asm.AsmVisitorWrapper; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; 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 static net.bytebuddy.matcher.ElementMatchers.*; /** * A type builder that creates an instrumented type as a subclass, i.e. a type that is not based on an existing class file. * * @param <T> A loaded type that the dynamic type is guaranteed to be a subtype of. */ @EqualsAndHashCode(callSuper = true) public class SubclassDynamicTypeBuilder<T> extends DynamicType.Builder.AbstractBase.Adapter<T> { /** * The constructor strategy to apply onto the instrumented type. */ private final ConstructorStrategy constructorStrategy; /** * Creates a new type builder for creating a subclass. * * @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 constructorStrategy The constructor strategy to apply onto the instrumented type. */ public SubclassDynamicTypeBuilder(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, ConstructorStrategy constructorStrategy) { this(instrumentedType, new FieldRegistry.Default(), new MethodRegistry.Default(), TypeAttributeAppender.ForInstrumentedType.INSTANCE, AsmVisitorWrapper.NoOp.INSTANCE, classFileVersion, auxiliaryTypeNamingStrategy, annotationValueFilterFactory, annotationRetention, implementationContextFactory, methodGraphCompiler, typeValidation, ignoredMethods, constructorStrategy); } /** * Creates a new type builder for creating a subclass. * * @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 constructorStrategy The constructor strategy to apply onto the instrumented type. */ protected SubclassDynamicTypeBuilder(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, ConstructorStrategy constructorStrategy) { super(instrumentedType, fieldRegistry, methodRegistry, typeAttributeAppender, asmVisitorWrapper, classFileVersion, auxiliaryTypeNamingStrategy, annotationValueFilterFactory, annotationRetention, implementationContextFactory, methodGraphCompiler, typeValidation, ignoredMethods); this.constructorStrategy = constructorStrategy; } @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 SubclassDynamicTypeBuilder<T>(instrumentedType, fieldRegistry, methodRegistry, typeAttributeAppender, asmVisitorWrapper, classFileVersion, auxiliaryTypeNamingStrategy, annotationValueFilterFactory, annotationRetention, implementationContextFactory, methodGraphCompiler, typeValidation, ignoredMethods, constructorStrategy); } @Override public DynamicType.Unloaded<T> make(TypeResolutionStrategy typeResolutionStrategy) { return make(typeResolutionStrategy, TypePool.ClassLoading.ofClassPath()); // Mimics the default behavior of ASM for least surprise. } @Override public DynamicType.Unloaded<T> make(TypeResolutionStrategy typeResolutionStrategy, TypePool typePool) { MethodRegistry.Compiled methodRegistry = constructorStrategy .inject(this.methodRegistry) .prepare(applyConstructorStrategy(instrumentedType), methodGraphCompiler, typeValidation, new InstrumentableMatcher(ignoredMethods)) .compile(SubclassImplementationTarget.Factory.SUPER_CLASS, classFileVersion); return TypeWriter.Default.<T>forCreation(methodRegistry, fieldRegistry.compile(methodRegistry.getInstrumentedType()), typeAttributeAppender, asmVisitorWrapper, classFileVersion, annotationValueFilterFactory, annotationRetention, auxiliaryTypeNamingStrategy, implementationContextFactory, typeValidation, typePool).make(typeResolutionStrategy.resolve()); } /** * Applies this builder's constructor strategy to the given instrumented type. * * @param instrumentedType The instrumented type to apply the constructor onto. * @return The instrumented type with the constructor strategy applied onto. */ private InstrumentedType applyConstructorStrategy(InstrumentedType instrumentedType) { if (!instrumentedType.isInterface()) { for (MethodDescription.Token token : constructorStrategy.extractConstructors(instrumentedType)) { instrumentedType = instrumentedType.withMethod(token); } } return instrumentedType; } /** * A matcher that locates all methods that are overridable and not ignored or that are directly defined on the instrumented type. */ @EqualsAndHashCode protected static class InstrumentableMatcher implements LatentMatcher<MethodDescription> { /** * A matcher for the ignored methods. */ private final LatentMatcher<? super MethodDescription> ignoredMethods; /** * Creates a latent method matcher that matches all methods that are to be instrumented by a {@link SubclassDynamicTypeBuilder}. * * @param ignoredMethods A matcher for the ignored methods. */ protected InstrumentableMatcher(LatentMatcher<? super MethodDescription> ignoredMethods) { this.ignoredMethods = ignoredMethods; } @Override public ElementMatcher<? super MethodDescription> resolve(TypeDescription typeDescription) { // Casting is required by JDK 6. return (ElementMatcher<? super MethodDescription>) isVirtual().and(not(isFinal())) .and(isVisibleTo(typeDescription)) .and(not(ignoredMethods.resolve(typeDescription))) .or(isDeclaredBy(typeDescription)); } } }