net.bytebuddy.dynamic.scaffold.MethodRegistry Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of byte-buddy Show documentation
Show all versions of byte-buddy Show documentation
Byte Buddy is a Java library for creating Java classes at run time.
This artifact is a build of Byte Buddy with all ASM dependencies repackaged into its own name space.
package net.bytebuddy.dynamic.scaffold;
import net.bytebuddy.instrumentation.Instrumentation;
import net.bytebuddy.instrumentation.LoadedTypeInitializer;
import net.bytebuddy.instrumentation.attribute.MethodAttributeAppender;
import net.bytebuddy.instrumentation.method.MethodDescription;
import net.bytebuddy.instrumentation.method.MethodList;
import net.bytebuddy.instrumentation.method.MethodLookupEngine;
import net.bytebuddy.instrumentation.method.bytecode.ByteCodeAppender;
import net.bytebuddy.instrumentation.method.matcher.MethodMatcher;
import net.bytebuddy.instrumentation.type.InstrumentedType;
import net.bytebuddy.instrumentation.type.TypeDescription;
import java.util.*;
import static net.bytebuddy.instrumentation.method.matcher.MethodMatchers.is;
import static net.bytebuddy.utility.ByteBuddyCommons.join;
/**
* A method registry is responsible for storing information on how a method is intercepted.
*/
public interface MethodRegistry {
/**
* Creates a new method registry with a new compilable entry representing the arguments of this method call. The new
* entry will be matched first, i.e. other registered entries will attempted to be matched before the new entry.
*
* @param latentMethodMatcher A latent method matcher that represents this method matching.
* @param instrumentation The instrumentation that is responsible for implementing this method.
* @param attributeAppenderFactory The attribute appender factory that is responsible for implementing this method.
* @return A new method registry with the new compilable entry prepended.
*/
MethodRegistry prepend(LatentMethodMatcher latentMethodMatcher,
Instrumentation instrumentation,
MethodAttributeAppender.Factory attributeAppenderFactory);
/**
* Creates a new method registry with a new compilable entry representing the arguments of this method call. The new
* entry will be matched last, i.e. other registered entries will attempted to be matched after the new entry.
*
* @param latentMethodMatcher A latent method matcher that represents this method matching.
* @param instrumentation The instrumentation that is responsible for implementing this method.
* @param attributeAppenderFactory The attribute appender factory that is responsible for implementing this method.
* @return A new method registry with the new compilable entry appended.
*/
MethodRegistry append(LatentMethodMatcher latentMethodMatcher,
Instrumentation instrumentation,
MethodAttributeAppender.Factory attributeAppenderFactory);
/**
* Prepares this method registry for a given instrumented type.
*
* @param instrumentedType The instrumented type that is to be prepared.
* @return A prepared method registry.
*/
Prepared prepare(InstrumentedType instrumentedType);
/**
* A {@link net.bytebuddy.dynamic.scaffold.MethodRegistry} that was prepared for a given
* {@link net.bytebuddy.instrumentation.type.InstrumentedType}.
*/
static interface Prepared {
/**
* The readily prepared instrumented type with all optional members registered as they are required
* by this instances {@link net.bytebuddy.instrumentation.Instrumentation}s.
*
* @return The final instrumented type.
*/
TypeDescription getInstrumentedType();
/**
* The type initializer as it is required by this instance's
* {@link net.bytebuddy.instrumentation.Instrumentation}s.
*
* @return The final loaded type initializer.
*/
LoadedTypeInitializer getLoadedTypeInitializer();
/**
* Compiles this prepared method registry.
*
* @param instrumentationTargetFactory The instrumentation target factory to use for compilation.
* @param methodLookupEngine The method lookup engine to use for compilation.
* @param fallback The fallback entry to use.
* @return A compiled method registry.
*/
Compiled compile(Instrumentation.Target.Factory instrumentationTargetFactory,
MethodLookupEngine methodLookupEngine,
TypeWriter.MethodPool.Entry.Factory fallback);
}
/**
* Represents a compiled {@link net.bytebuddy.dynamic.scaffold.MethodRegistry}.
*/
static interface Compiled extends TypeWriter.MethodPool {
/**
* The readily prepared instrumented type with all optional members registered as they are required
* by this instances {@link net.bytebuddy.instrumentation.Instrumentation}s.
*
* @return The final instrumented type.
*/
TypeDescription getInstrumentedType();
/**
* The type initializer as it is required by this instance's
* {@link net.bytebuddy.instrumentation.Instrumentation}s.
*
* @return The final loaded type initializer.
*/
LoadedTypeInitializer getLoadedTypeInitializer();
/**
* Returns a list of all methods that are invokable on the instrumented type.
*
* @return A list of all methods that are invokable on the instrumented type.
*/
MethodList getInvokableMethods();
}
/**
* A latent method matcher represents a method matcher that might not yet be assembled because it misses
* information on the actual instrumented type.
*/
static interface LatentMethodMatcher {
/**
* Manifests a latent method matcher.
*
* @param typeDescription The description of the type that is subject to instrumentation.
* @return A method matcher that represents the manifested version of this latent method matcher for the
* given instrumented type description.
*/
MethodMatcher manifest(TypeDescription typeDescription);
/**
* An wrapper implementation for an already assembled method matcher.
*/
static class Simple implements LatentMethodMatcher {
/**
* The method matcher that is represented by this instance.
*/
private final MethodMatcher methodMatcher;
/**
* Creates a new wrapper.
*
* @param methodMatcher The method matcher to be wrapped by this instance.
*/
public Simple(MethodMatcher methodMatcher) {
this.methodMatcher = methodMatcher;
}
@Override
public MethodMatcher manifest(TypeDescription instrumentedType) {
return methodMatcher;
}
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& methodMatcher.equals(((Simple) other).methodMatcher);
}
@Override
public int hashCode() {
return methodMatcher.hashCode();
}
@Override
public String toString() {
return "MethodRegistry.LatentMethodMatcher.Simple{methodMatcher=" + methodMatcher + '}';
}
}
}
/**
* A default implementation of a method registry.
*/
static class Default implements MethodRegistry {
/**
* A list of all entries in their registration order.
*/
private final List entries;
/**
* Creates a new empty method registry.
*/
public Default() {
entries = Collections.emptyList();
}
/**
* Creates a new default {@link net.bytebuddy.dynamic.scaffold.MethodRegistry} with a given list of
* registered entries.
*
* @param entries The entries of this method registry.
*/
private Default(List entries) {
this.entries = entries;
}
@Override
public MethodRegistry prepend(LatentMethodMatcher latentMethodMatcher,
Instrumentation instrumentation,
MethodAttributeAppender.Factory attributeAppenderFactory) {
return new Default(join(new Entry(latentMethodMatcher, instrumentation, attributeAppenderFactory), entries));
}
@Override
public MethodRegistry append(LatentMethodMatcher latentMethodMatcher,
Instrumentation instrumentation,
MethodAttributeAppender.Factory attributeAppenderFactory) {
return new Default(join(entries, new Entry(latentMethodMatcher, instrumentation, attributeAppenderFactory)));
}
@Override
public MethodRegistry.Prepared prepare(InstrumentedType instrumentedType) {
List additionalEntries = new LinkedList();
Set instrumentations = new HashSet(entries.size());
for (Entry entry : entries) {
// Only call the preparation method of an instrumentation if the instrumentation was not yet prepared.
if (instrumentations.add(entry.getInstrumentation())) {
MethodList beforePreparation = instrumentedType.getDeclaredMethods();
instrumentedType = entry.getInstrumentation().prepare(instrumentedType);
// If an instrumentation adds methods to the instrumented type, those methods should be
// handled by this instrumentation. Thus an additional matcher that matches these exact methods
// is registered, in case that the instrumentation actually added methods. These matcher must be
// prepended to any other entry such that they become of higher precedence to manually registered
// method interceptions. Otherwise, those user interceptions could match the methods that were
// added by the instrumentation.
if (beforePreparation.size() < instrumentedType.getDeclaredMethods().size()) {
additionalEntries.add(new Entry(
new ListDifferenceMethodMatcher(beforePreparation, instrumentedType.getDeclaredMethods()),
entry.getInstrumentation(),
MethodAttributeAppender.NoOp.INSTANCE));
}
}
}
return new Prepared(instrumentedType.detach(),
instrumentedType.getLoadedTypeInitializer(),
entries,
additionalEntries);
}
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& entries.equals(((Default) other).entries);
}
@Override
public int hashCode() {
return entries.hashCode();
}
@Override
public String toString() {
return "MethodRegistry.Default{entries=" + entries + '}';
}
/**
* A prepared default method registry.
*/
protected static class Prepared implements MethodRegistry.Prepared {
/**
* A convenience index pointing to the first element of an array to improve the readability of the code.
*/
private static final int AT_BEGINNING = 0;
/**
* The instrumented type this method registry was prepared for.
*/
private final TypeDescription instrumentedType;
/**
* The loaded type initializer this method registry was prepared for.
*/
private final LoadedTypeInitializer loadedTypeInitializer;
/**
* All entries in their application order.
*/
private final List entries;
/**
* All additional entries that were added during preparation.
*/
private final List additionalEntries;
/**
* Creates a new prepared default method registry.
*
* @param instrumentedType The instrumented type this method registry was prepared for.
* @param loadedTypeInitializer The loaded type initializer this method registry was prepared for.
* @param entries All entries in their application order.
* @param additionalEntries All additional entries that were added during preparation.
*/
protected Prepared(TypeDescription instrumentedType,
LoadedTypeInitializer loadedTypeInitializer,
List entries,
List additionalEntries) {
this.instrumentedType = instrumentedType;
this.loadedTypeInitializer = loadedTypeInitializer;
this.entries = entries;
this.additionalEntries = additionalEntries;
}
@Override
public TypeDescription getInstrumentedType() {
return instrumentedType;
}
@Override
public LoadedTypeInitializer getLoadedTypeInitializer() {
return loadedTypeInitializer;
}
@Override
public MethodRegistry.Compiled compile(Instrumentation.Target.Factory instrumentationTargetFactory,
MethodLookupEngine methodLookupEngine,
TypeWriter.MethodPool.Entry.Factory fallback) {
MethodLookupEngine.Finding finding = methodLookupEngine.process(instrumentedType);
Instrumentation.Target instrumentationTarget = instrumentationTargetFactory.make(finding);
Map byteCodeAppenders = new HashMap(entries.size());
List compiledEntries = new LinkedList();
for (Entry entry : entries) {
// Make sure that the instrumentation's byte code appender was not yet created.
if (!byteCodeAppenders.containsKey(entry.getInstrumentation())) {
byteCodeAppenders.put(entry.getInstrumentation(), entry.getInstrumentation().appender(instrumentationTarget));
}
compiledEntries.add(new Compiled.Entry(entry.getLatentMethodMatcher().manifest(instrumentationTarget.getTypeDescription()),
byteCodeAppenders.get(entry.getInstrumentation()),
entry.getAttributeAppenderFactory().make(instrumentationTarget.getTypeDescription())));
}
// All additional entries must belong to instrumentations that were already registered. The method
// matchers must be added at the beginning of the compiled entry queue.
for (Entry entry : additionalEntries) {
compiledEntries.add(AT_BEGINNING,
new Compiled.Entry(entry.getLatentMethodMatcher().manifest(instrumentationTarget.getTypeDescription()),
byteCodeAppenders.get(entry.getInstrumentation()),
entry.getAttributeAppenderFactory().make(instrumentationTarget.getTypeDescription()))
);
}
return new Compiled(instrumentedType,
loadedTypeInitializer,
finding.getInvokableMethods(),
new ArrayList(compiledEntries),
fallback.compile(instrumentationTarget));
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
Prepared prepared = (Prepared) other;
return additionalEntries.equals(prepared.additionalEntries)
&& entries.equals(prepared.entries)
&& instrumentedType.equals(prepared.instrumentedType)
&& loadedTypeInitializer.equals(prepared.loadedTypeInitializer);
}
@Override
public int hashCode() {
int result = instrumentedType.hashCode();
result = 31 * result + loadedTypeInitializer.hashCode();
result = 31 * result + entries.hashCode();
result = 31 * result + additionalEntries.hashCode();
return result;
}
@Override
public String toString() {
return "MethodRegistry.Default.Prepared{" +
"instrumentedType=" + instrumentedType +
", loadedTypeInitializer=" + loadedTypeInitializer +
", entries=" + entries +
", additionalEntries=" + additionalEntries +
'}';
}
}
/**
* A compiled default method registry.
*/
protected static class Compiled implements MethodRegistry.Compiled {
/**
* The instrumented type.
*/
private final TypeDescription instrumentedType;
/**
* The loaded type initializer.
*/
private final LoadedTypeInitializer loadedTypeInitializer;
/**
* A list of all methods that can be invoked on the instrumented type.
*/
private final MethodList invokableMethods;
/**
* The list of all compiled entries of this compiled method registry.
*/
private final List entries;
/**
* The fallback entry to apply for any method that is not matched by any of the registered compiled entries.
*/
private final MethodRegistry.Compiled.Entry fallback;
/**
* Creates a new compiled default method registry.
*
* @param instrumentedType The instrumented type.
* @param loadedTypeInitializer The loaded type initializer.
* @param invokableMethods A list of all methods that can be invoked on the instrumented type.
* @param entries The list of all compiled entries of this compiled method registry.
* @param fallback The fallback entry to apply for any method that is not matched by any of
* the registered compiled entries.
*/
protected Compiled(TypeDescription instrumentedType,
LoadedTypeInitializer loadedTypeInitializer,
MethodList invokableMethods,
List entries,
MethodRegistry.Compiled.Entry fallback) {
this.instrumentedType = instrumentedType;
this.loadedTypeInitializer = loadedTypeInitializer;
this.invokableMethods = invokableMethods;
this.entries = entries;
this.fallback = fallback;
}
@Override
public TypeDescription getInstrumentedType() {
return instrumentedType;
}
@Override
public LoadedTypeInitializer getLoadedTypeInitializer() {
return loadedTypeInitializer;
}
@Override
public MethodList getInvokableMethods() {
return invokableMethods;
}
@Override
public MethodRegistry.Compiled.Entry target(MethodDescription methodDescription) {
for (Entry entry : entries) {
if (entry.matches(methodDescription)) {
return entry;
}
}
return fallback;
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
Compiled compiled = (Compiled) other;
return entries.equals(compiled.entries)
&& fallback.equals(compiled.fallback)
&& instrumentedType.equals(compiled.instrumentedType)
&& invokableMethods.equals(compiled.invokableMethods)
&& loadedTypeInitializer.equals(compiled.loadedTypeInitializer);
}
@Override
public int hashCode() {
int result = instrumentedType.hashCode();
result = 31 * result + loadedTypeInitializer.hashCode();
result = 31 * result + invokableMethods.hashCode();
result = 31 * result + entries.hashCode();
result = 31 * result + fallback.hashCode();
return result;
}
@Override
public String toString() {
return "MethodRegistry.Default.Compiled{" +
"instrumentedType=" + instrumentedType +
", loadedTypeInitializer=" + loadedTypeInitializer +
", invokableMethods=" + invokableMethods +
", entries=" + entries +
", fallback=" + fallback +
'}';
}
/**
* An entry of a compiled default method registry.
*/
protected static class Entry extends TypeWriter.MethodPool.Entry.Simple implements MethodMatcher {
/**
* The method matcher that represents this compiled entry.
*/
private final MethodMatcher methodMatcher;
/**
* Creates an entry of a compiled default method registry.
*
* @param methodMatcher The method matcher to be wrapped by this instance.
* @param byteCodeAppender The byte code appender that represents this compiled entry.
* @param attributeAppender The method attribute appender that represents this compiled entry.
*/
protected Entry(MethodMatcher methodMatcher,
ByteCodeAppender byteCodeAppender,
MethodAttributeAppender attributeAppender) {
super(byteCodeAppender, attributeAppender);
this.methodMatcher = methodMatcher;
}
@Override
public boolean matches(MethodDescription methodDescription) {
return methodMatcher.matches(methodDescription);
}
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& super.equals(other)
&& methodMatcher.equals(((Entry) other).methodMatcher);
}
@Override
public int hashCode() {
return 31 * super.hashCode() + methodMatcher.hashCode();
}
@Override
public String toString() {
return "MethodRegistry.Default.Compiled.Entry{" +
"methodMatcher=" + methodMatcher +
", byteCodeAppender=" + getByteCodeAppender() +
", attributeAppender=" + getAttributeAppender() +
'}';
}
}
}
/**
* A method matcher that matches methods that are found in only one of two lists.
*/
private static class ListDifferenceMethodMatcher implements MethodMatcher, LatentMethodMatcher {
/**
* The methods that are matched by this instance.
*/
private final MethodList matchedMethods;
/**
* Creates a new list difference method matcher.
*
* @param beforeMethods A list of methods that should not be matched.
* @param afterMethods The same list after adding additional methods. The order of the methods in
* this list must not be altered.
*/
private ListDifferenceMethodMatcher(MethodList beforeMethods, MethodList afterMethods) {
matchedMethods = afterMethods.subList(beforeMethods.size(), afterMethods.size());
}
@Override
public boolean matches(MethodDescription methodDescription) {
return matchedMethods.filter(is(methodDescription)).size() == 1;
}
@Override
public MethodMatcher manifest(TypeDescription typeDescription) {
return this;
}
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& matchedMethods.equals(((ListDifferenceMethodMatcher) other).matchedMethods);
}
@Override
public int hashCode() {
return matchedMethods.hashCode();
}
@Override
public String toString() {
return "oneOf(" + matchedMethods + ')';
}
}
/**
* A registration within a method registry, consisting of a latent method matcher, an instrumentation that
* is to be applied on any method that is matched by the method matcher that is extracted from the latent
* matcher's manifestation and a method attribute appender factory that is applied to any intercepted method.
*/
private static class Entry {
/**
* The latent method matcher that is representing this entry.
*/
private final LatentMethodMatcher latentMethodMatcher;
/**
* The instrumentation that is representing this entry.
*/
private final Instrumentation instrumentation;
/**
* The method attribute appender factory that is representing this entry.
*/
private final MethodAttributeAppender.Factory attributeAppenderFactory;
/**
* Creates a new entry.
*
* @param latentMethodMatcher A latent method matcher that represents this method matching.
* @param instrumentation The instrumentation that is responsible for implementing this method.
* @param attributeAppenderFactory The attribute appender factory that is responsible for implementing
* this method.
*/
private Entry(LatentMethodMatcher latentMethodMatcher,
Instrumentation instrumentation,
MethodAttributeAppender.Factory attributeAppenderFactory) {
this.latentMethodMatcher = latentMethodMatcher;
this.instrumentation = instrumentation;
this.attributeAppenderFactory = attributeAppenderFactory;
}
/**
* Returns this entry's latent method matcher.
*
* @return This entry's latent method matcher.
*/
public LatentMethodMatcher getLatentMethodMatcher() {
return latentMethodMatcher;
}
/**
* Returns this entry's instrumentation.
*
* @return This entry's instrumentation.
*/
public Instrumentation getInstrumentation() {
return instrumentation;
}
/**
* Returns this entry's attribute appender factory.
*
* @return This entry's attribute appender factory.
*/
public MethodAttributeAppender.Factory getAttributeAppenderFactory() {
return attributeAppenderFactory;
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
Entry entry = (Entry) other;
return attributeAppenderFactory.equals(entry.attributeAppenderFactory)
&& instrumentation.equals(entry.instrumentation)
&& latentMethodMatcher.equals(entry.latentMethodMatcher);
}
@Override
public int hashCode() {
int result = latentMethodMatcher.hashCode();
result = 31 * result + instrumentation.hashCode();
result = 31 * result + attributeAppenderFactory.hashCode();
return result;
}
@Override
public String toString() {
return "MethodRegistry.Default.Entry{" +
"latentMethodMatcher=" + latentMethodMatcher +
", instrumentation=" + instrumentation +
", attributeAppenderFactory=" + attributeAppenderFactory +
'}';
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy