/*
* Copyright 2014 - 2020 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.dynamic.scaffold;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.FilterableList;
import org.objectweb.asm.Opcodes;
import java.util.*;
import static net.bytebuddy.matcher.ElementMatchers.*;
A method graph represents a view on a set of methods as they are seen from a given type. Any method is represented as a node that represents
a method, its bridge methods, its resolution state and information on if it was made visible by a visibility bridge.
/**
* A method graph represents a view on a set of methods as they are seen from a given type. Any method is represented as a node that represents
* a method, its bridge methods, its resolution state and information on if it was made visible by a visibility bridge.
*/
public interface MethodGraph {
Locates a node in this graph which represents the provided method token.
Params: - token – A method token that represents the method to be located.
Returns: The node representing the given token.
/**
* Locates a node in this graph which represents the provided method token.
*
* @param token A method token that represents the method to be located.
* @return The node representing the given token.
*/
Node locate(MethodDescription.SignatureToken token);
Lists all nodes of this method graph.
Returns: A list of all nodes of this method graph.
/**
* Lists all nodes of this method graph.
*
* @return A list of all nodes of this method graph.
*/
NodeList listNodes();
A canonical implementation of an empty method graph.
/**
* A canonical implementation of an empty method graph.
*/
enum Empty implements MethodGraph.Linked, MethodGraph.Compiler {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Node locate(MethodDescription.SignatureToken token) {
return Node.Unresolved.INSTANCE;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public NodeList listNodes() {
return new NodeList(Collections.<Node>emptyList());
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodGraph getSuperClassGraph() {
return this;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodGraph getInterfaceGraph(TypeDescription typeDescription) {
return this;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Linked compile(TypeDescription typeDescription) {
return this;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint) {
return this;
}
}
A linked method graph represents a view that additionally exposes information of a given type's super type view and a
view on this graph's directly implemented interfaces.
/**
* A linked method graph represents a view that additionally exposes information of a given type's super type view and a
* view on this graph's directly implemented interfaces.
*/
interface Linked extends MethodGraph {
Returns a graph representing the view on this represented type's super type.
Returns: A graph representing the view on this represented type's super type.
/**
* Returns a graph representing the view on this represented type's super type.
*
* @return A graph representing the view on this represented type's super type.
*/
MethodGraph getSuperClassGraph();
Returns a graph representing the view on this represented type's directly implemented interface type.
Params: - typeDescription – The interface type for which a view is to be returned.
Returns: A graph representing the view on this represented type's directly implemented interface type.
/**
* Returns a graph representing the view on this represented type's directly implemented interface type.
*
* @param typeDescription The interface type for which a view is to be returned.
* @return A graph representing the view on this represented type's directly implemented interface type.
*/
MethodGraph getInterfaceGraph(TypeDescription typeDescription);
A simple implementation of a linked method graph that exposes views by delegation to given method graphs.
/**
* A simple implementation of a linked method graph that exposes views by delegation to given method graphs.
*/
@HashCodeAndEqualsPlugin.Enhance
class Delegation implements Linked {
The represented type's method graph.
/**
* The represented type's method graph.
*/
private final MethodGraph methodGraph;
The super class's method graph.
/**
* The super class's method graph.
*/
private final MethodGraph superClassGraph;
A mapping of method graphs of the represented type's directly implemented interfaces to their graph representatives.
/**
* A mapping of method graphs of the represented type's directly implemented interfaces to their graph representatives.
*/
private final Map<TypeDescription, MethodGraph> interfaceGraphs;
Creates a new delegation method graph.
Params: - methodGraph – The represented type's method graph.
- superClassGraph – The super class's method graph.
- interfaceGraphs – A mapping of method graphs of the represented type's directly implemented interfaces to their graph representatives.
/**
* Creates a new delegation method graph.
*
* @param methodGraph The represented type's method graph.
* @param superClassGraph The super class's method graph.
* @param interfaceGraphs A mapping of method graphs of the represented type's directly implemented interfaces to their graph representatives.
*/
public Delegation(MethodGraph methodGraph, MethodGraph superClassGraph, Map<TypeDescription, MethodGraph> interfaceGraphs) {
this.methodGraph = methodGraph;
this.superClassGraph = superClassGraph;
this.interfaceGraphs = interfaceGraphs;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodGraph getSuperClassGraph() {
return superClassGraph;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodGraph getInterfaceGraph(TypeDescription typeDescription) {
MethodGraph interfaceGraph = interfaceGraphs.get(typeDescription);
return interfaceGraph == null
? Empty.INSTANCE
: interfaceGraph;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Node locate(MethodDescription.SignatureToken token) {
return methodGraph.locate(token);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public NodeList listNodes() {
return methodGraph.listNodes();
}
}
}
Represents a node within a method graph.
/**
* Represents a node within a method graph.
*/
interface Node {
Returns the sort of this node.
Returns: The sort of this node.
/**
* Returns the sort of this node.
*
* @return The sort of this node.
*/
Sort getSort();
Returns the method that is represented by this node.
Returns: The method that is represented by this node.
/**
* Returns the method that is represented by this node.
*
* @return The method that is represented by this node.
*/
MethodDescription getRepresentative();
Returns a set of type tokens that this method represents. This set contains the actual method's type including the
types of all bridge methods.
Returns: A set of type tokens that this method represents.
/**
* Returns a set of type tokens that this method represents. This set contains the actual method's type including the
* types of all bridge methods.
*
* @return A set of type tokens that this method represents.
*/
Set<MethodDescription.TypeToken> getMethodTypes();
Returns the minimal method visibility of all methods that are represented by this node.
Returns: The minimal method visibility of all methods that are represented by this node.
/**
* Returns the minimal method visibility of all methods that are represented by this node.
*
* @return The minimal method visibility of all methods that are represented by this node.
*/
Visibility getVisibility();
Represents a Node
's state. /**
* Represents a {@link net.bytebuddy.dynamic.scaffold.MethodGraph.Node}'s state.
*/
enum Sort {
Represents a resolved node that was made visible by a visibility bridge.
/**
* Represents a resolved node that was made visible by a visibility bridge.
*/
VISIBLE(true, true, true),
Represents a resolved node that was not made visible by a visibility bridge.
/**
* Represents a resolved node that was not made visible by a visibility bridge.
*/
RESOLVED(true, true, false),
Represents an ambiguous node, i.e. a node that might refer to several methods.
/**
* Represents an ambiguous node, i.e. a node that might refer to several methods.
*/
AMBIGUOUS(true, false, false),
Represents an unresolved node.
/**
* Represents an unresolved node.
*/
UNRESOLVED(false, false, false);
true
if this sort represents a resolved node. /**
* {@code true} if this sort represents a resolved node.
*/
private final boolean resolved;
true
if this sort represents a non-ambiguous node. /**
* {@code true} if this sort represents a non-ambiguous node.
*/
private final boolean unique;
true
if this sort represents a node that was made by a visibility bridge. /**
* {@code true} if this sort represents a node that was made by a visibility bridge.
*/
private final boolean madeVisible;
Creates a new sort.
Params: - resolved –
true
if this sort represents a resolved node. - unique –
true
if this sort represents a non-ambiguous node. - madeVisible –
true
if this sort represents a node that was made by a visibility bridge.
/**
* Creates a new sort.
*
* @param resolved {@code true} if this sort represents a resolved node.
* @param unique {@code true} if this sort represents a non-ambiguous node.
* @param madeVisible {@code true} if this sort represents a node that was made by a visibility bridge.
*/
Sort(boolean resolved, boolean unique, boolean madeVisible) {
this.resolved = resolved;
this.unique = unique;
this.madeVisible = madeVisible;
}
Verifies if this sort represents a resolved node.
Returns: true
if this sort represents a resolved node.
/**
* Verifies if this sort represents a resolved node.
*
* @return {@code true} if this sort represents a resolved node.
*/
public boolean isResolved() {
return resolved;
}
Verifies if this sort represents a non-ambiguous node.
Returns: true
if this sort represents a non-ambiguous node.
/**
* Verifies if this sort represents a non-ambiguous node.
*
* @return {@code true} if this sort represents a non-ambiguous node.
*/
public boolean isUnique() {
return unique;
}
Verifies if this sort represents a node that was made visible by a visibility bridge.
Returns: true
if this sort represents a node that was made visible by a visibility bridge.
/**
* Verifies if this sort represents a node that was made visible by a visibility bridge.
*
* @return {@code true} if this sort represents a node that was made visible by a visibility bridge.
*/
public boolean isMadeVisible() {
return madeVisible;
}
}
A canonical implementation of an unresolved node.
/**
* A canonical implementation of an unresolved node.
*/
enum Unresolved implements Node {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Sort getSort() {
return Sort.UNRESOLVED;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription getRepresentative() {
throw new IllegalStateException("Cannot resolve the method of an illegal node");
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Set<MethodDescription.TypeToken> getMethodTypes() {
throw new IllegalStateException("Cannot resolve bridge method of an illegal node");
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Visibility getVisibility() {
throw new IllegalStateException("Cannot resolve visibility of an illegal node");
}
}
A simple implementation of a resolved node of a method without bridges.
/**
* A simple implementation of a resolved node of a method without bridges.
*/
@HashCodeAndEqualsPlugin.Enhance
class Simple implements Node {
The represented method.
/**
* The represented method.
*/
private final MethodDescription methodDescription;
Creates a simple node.
Params: - methodDescription – The represented method.
/**
* Creates a simple node.
*
* @param methodDescription The represented method.
*/
public Simple(MethodDescription methodDescription) {
this.methodDescription = methodDescription;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Sort getSort() {
return Sort.RESOLVED;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription getRepresentative() {
return methodDescription;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Set<MethodDescription.TypeToken> getMethodTypes() {
return Collections.emptySet();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Visibility getVisibility() {
return methodDescription.getVisibility();
}
}
}
A compiler to produce a MethodGraph
from a given type. /**
* A compiler to produce a {@link MethodGraph} from a given type.
*/
@SuppressFBWarnings(value = "IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION", justification = "Safe initialization is implied")
interface Compiler {
The default compiler for compiling Java methods.
/**
* The default compiler for compiling Java methods.
*/
Compiler DEFAULT = MethodGraph.Compiler.Default.forJavaHierarchy();
Compiles the given type into a method graph considering the type to be the viewpoint.
Params: - typeDescription – The type to be compiled.
Returns: A linked method graph representing the given type.
/**
* Compiles the given type into a method graph considering the type to be the viewpoint.
*
* @param typeDescription The type to be compiled.
* @return A linked method graph representing the given type.
*/
MethodGraph.Linked compile(TypeDescription typeDescription);
Compiles the given type into a method graph.
Params: - typeDefinition – The type to be compiled.
- viewPoint – The view point that determines the method's visibility.
Returns: A linked method graph representing the given type.
/**
* Compiles the given type into a method graph.
*
* @param typeDefinition The type to be compiled.
* @param viewPoint The view point that determines the method's visibility.
* @return A linked method graph representing the given type.
*/
MethodGraph.Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint);
A flat compiler that simply returns the methods that are declared by the instrumented type.
/**
* A flat compiler that simply returns the methods that are declared by the instrumented type.
*/
enum ForDeclaredMethods implements Compiler {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Linked compile(TypeDescription typeDescription) {
return compile(typeDescription, typeDescription);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint) {
LinkedHashMap<MethodDescription.SignatureToken, Node> nodes = new LinkedHashMap<MethodDescription.SignatureToken, Node>();
for (MethodDescription methodDescription : typeDefinition.getDeclaredMethods().filter(isVirtual().and(not(isBridge())).and(isVisibleTo(viewPoint)))) {
nodes.put(methodDescription.asSignatureToken(), new Node.Simple(methodDescription));
}
return new Linked.Delegation(new MethodGraph.Simple(nodes), Empty.INSTANCE, Collections.<TypeDescription, MethodGraph>emptyMap());
}
}
An abstract base implementation of a method graph compiler.
/**
* An abstract base implementation of a method graph compiler.
*/
abstract class AbstractBase implements Compiler {
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Linked compile(TypeDescription typeDescription) {
return compile(typeDescription, typeDescription);
}
}
A default implementation of a method graph.
Type parameters: - <T> – The type of the harmonizer token to be used for linking methods of different types.
/**
* A default implementation of a method graph.
*
* @param <T> The type of the harmonizer token to be used for linking methods of different types.
*/
@HashCodeAndEqualsPlugin.Enhance
class Default<T> extends AbstractBase {
The harmonizer to be used.
/**
* The harmonizer to be used.
*/
private final Harmonizer<T> harmonizer;
The merger to be used.
/**
* The merger to be used.
*/
private final Merger merger;
A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
/**
* A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
*/
private final TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor;
Creates a new default method graph compiler.
Params: - harmonizer – The harmonizer to be used.
- merger – The merger to be used.
- visitor – A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
/**
* Creates a new default method graph compiler.
*
* @param harmonizer The harmonizer to be used.
* @param merger The merger to be used.
* @param visitor A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
*/
protected Default(Harmonizer<T> harmonizer, Merger merger, TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) {
this.harmonizer = harmonizer;
this.merger = merger;
this.visitor = visitor;
}
Creates a default compiler using the given harmonizer and merger. All raw types are reified before analyzing their properties.
Params: - harmonizer – The harmonizer to be used for creating tokens that uniquely identify a method hierarchy.
- merger – The merger to be used for identifying a method to represent an ambiguous method resolution.
Type parameters: - <S> – The type of the harmonizer token.
Returns: A default compiler for the given harmonizer and merger.
/**
* Creates a default compiler using the given harmonizer and merger. All raw types are reified before analyzing their properties.
*
* @param harmonizer The harmonizer to be used for creating tokens that uniquely identify a method hierarchy.
* @param merger The merger to be used for identifying a method to represent an ambiguous method resolution.
* @param <S> The type of the harmonizer token.
* @return A default compiler for the given harmonizer and merger.
*/
public static <S> Compiler of(Harmonizer<S> harmonizer, Merger merger) {
return new Default<S>(harmonizer, merger, TypeDescription.Generic.Visitor.Reifying.INITIATING);
}
Creates a default compiler using the given harmonizer and merger.
Params: - harmonizer – The harmonizer to be used for creating tokens that uniquely identify a method hierarchy.
- merger – The merger to be used for identifying a method to represent an ambiguous method resolution.
- visitor – A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
Type parameters: - <S> – The type of the harmonizer token.
Returns: A default compiler for the given harmonizer and merger.
/**
* Creates a default compiler using the given harmonizer and merger.
*
* @param harmonizer The harmonizer to be used for creating tokens that uniquely identify a method hierarchy.
* @param merger The merger to be used for identifying a method to represent an ambiguous method resolution.
* @param visitor A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
* @param <S> The type of the harmonizer token.
* @return A default compiler for the given harmonizer and merger.
*/
public static <S> Compiler of(Harmonizer<S> harmonizer, Merger merger, TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) {
return new Default<S>(harmonizer, merger, visitor);
}
Creates a default compiler for a method hierarchy following the rules of the Java programming language. According
to these rules, two methods of the same name are only different if their parameter types represent different raw
types. The return type is not considered as a part of the signature.
Ambiguous methods are merged by considering the method that was discovered first.
Returns: A compiler for resolving a method hierarchy following the rules of the Java programming language.
/**
* <p>
* Creates a default compiler for a method hierarchy following the rules of the Java programming language. According
* to these rules, two methods of the same name are only different if their parameter types represent different raw
* types. The return type is not considered as a part of the signature.
* </p>
* <p>
* Ambiguous methods are merged by considering the method that was discovered first.
* </p>
*
* @return A compiler for resolving a method hierarchy following the rules of the Java programming language.
*/
public static Compiler forJavaHierarchy() {
return of(Harmonizer.ForJavaMethod.INSTANCE, Merger.Directional.LEFT);
}
Creates a default compiler for a method hierarchy following the rules of the Java virtual machine. According
to these rules, two methods of the same name are different if their parameter types and return types represent
different type erasures.
Ambiguous methods are merged by considering the method that was discovered first.
Returns: A compiler for resolving a method hierarchy following the rules of the Java programming language.
/**
* <p>
* Creates a default compiler for a method hierarchy following the rules of the Java virtual machine. According
* to these rules, two methods of the same name are different if their parameter types and return types represent
* different type erasures.
* </p>
* <p>
* Ambiguous methods are merged by considering the method that was discovered first.
* </p>
*
* @return A compiler for resolving a method hierarchy following the rules of the Java programming language.
*/
public static Compiler forJVMHierarchy() {
return of(Harmonizer.ForJVMMethod.INSTANCE, Merger.Directional.LEFT);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodGraph.Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint) {
Map<TypeDefinition, Key.Store<T>> snapshots = new HashMap<TypeDefinition, Key.Store<T>>();
Key.Store<?> rootStore = doAnalyze(typeDefinition, snapshots, isVirtual().and(isVisibleTo(viewPoint)));
TypeDescription.Generic superClass = typeDefinition.getSuperClass();
List<TypeDescription.Generic> interfaceTypes = typeDefinition.getInterfaces();
Map<TypeDescription, MethodGraph> interfaceGraphs = new HashMap<TypeDescription, MethodGraph>();
for (TypeDescription.Generic interfaceType : interfaceTypes) {
interfaceGraphs.put(interfaceType.asErasure(), snapshots.get(interfaceType).asGraph(merger));
}
return new Linked.Delegation(rootStore.asGraph(merger),
superClass == null
? Empty.INSTANCE
: snapshots.get(superClass).asGraph(merger),
interfaceGraphs);
}
Analyzes the given type description without checking if the end of the type hierarchy was reached.
Params: - typeDefinition – The type to analyze.
- key – The type in its original form before applying the visitor.
- snapshots – A map containing snapshots of key stores for previously analyzed types.
- relevanceMatcher – A matcher for filtering methods that should be included in the graph.
Returns: A key store describing the provided type.
/**
* Analyzes the given type description without checking if the end of the type hierarchy was reached.
*
* @param typeDefinition The type to analyze.
* @param key The type in its original form before applying the visitor.
* @param snapshots A map containing snapshots of key stores for previously analyzed types.
* @param relevanceMatcher A matcher for filtering methods that should be included in the graph.
* @return A key store describing the provided type.
*/
protected Key.Store<T> analyze(TypeDefinition typeDefinition,
TypeDefinition key,
Map<TypeDefinition, Key.Store<T>> snapshots,
ElementMatcher<? super MethodDescription> relevanceMatcher) {
Key.Store<T> store = snapshots.get(key);
if (store == null) {
store = doAnalyze(typeDefinition, snapshots, relevanceMatcher);
snapshots.put(key, store);
}
return store;
}
Analyzes the given type description.
Params: - typeDescription – The type to analyze.
- snapshots – A map containing snapshots of key stores for previously analyzed types.
- relevanceMatcher – A matcher for filtering methods that should be included in the graph.
Returns: A key store describing the provided type.
/**
* Analyzes the given type description.
*
* @param typeDescription The type to analyze.
* @param snapshots A map containing snapshots of key stores for previously analyzed types.
* @param relevanceMatcher A matcher for filtering methods that should be included in the graph.
* @return A key store describing the provided type.
*/
protected Key.Store<T> analyzeNullable(TypeDescription.Generic typeDescription,
Map<TypeDefinition, Key.Store<T>> snapshots,
ElementMatcher<? super MethodDescription> relevanceMatcher) {
return typeDescription == null
? new Key.Store<T>()
: analyze(typeDescription.accept(visitor), typeDescription, snapshots, relevanceMatcher);
}
Analyzes the given type description without checking if it is already presented in the key store.
Params: - typeDefinition – The type to analyze.
- snapshots – A map containing snapshots of key stores for previously analyzed types.
- relevanceMatcher – A matcher for filtering methods that should be included in the graph.
Returns: A key store describing the provided type.
/**
* Analyzes the given type description without checking if it is already presented in the key store.
*
* @param typeDefinition The type to analyze.
* @param snapshots A map containing snapshots of key stores for previously analyzed types.
* @param relevanceMatcher A matcher for filtering methods that should be included in the graph.
* @return A key store describing the provided type.
*/
protected Key.Store<T> doAnalyze(TypeDefinition typeDefinition,
Map<TypeDefinition, Key.Store<T>> snapshots,
ElementMatcher<? super MethodDescription> relevanceMatcher) {
Key.Store<T> store = analyzeNullable(typeDefinition.getSuperClass(), snapshots, relevanceMatcher);
Key.Store<T> interfaceStore = new Key.Store<T>();
for (TypeDescription.Generic interfaceType : typeDefinition.getInterfaces()) {
interfaceStore = interfaceStore.combineWith(analyze(interfaceType.accept(visitor), interfaceType, snapshots, relevanceMatcher));
}
return store.inject(interfaceStore).registerTopLevel(typeDefinition.getDeclaredMethods().filter(relevanceMatcher), harmonizer);
}
A harmonizer is responsible for creating a token that identifies a method's relevant attributes for considering
two methods of being equal or not.
Type parameters: - <S> – The type of the token that is created by the implementing harmonizer.
/**
* A harmonizer is responsible for creating a token that identifies a method's relevant attributes for considering
* two methods of being equal or not.
*
* @param <S> The type of the token that is created by the implementing harmonizer.
*/
public interface Harmonizer<S> {
Harmonizes the given type token.
Params: - typeToken – The type token to harmonize.
Returns: A token representing the given type token.
/**
* Harmonizes the given type token.
*
* @param typeToken The type token to harmonize.
* @return A token representing the given type token.
*/
S harmonize(MethodDescription.TypeToken typeToken);
A harmonizer for the Java programming language that identifies a method by its parameter types only.
/**
* A harmonizer for the Java programming language that identifies a method by its parameter types only.
*/
enum ForJavaMethod implements Harmonizer<ForJavaMethod.Token> {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Token harmonize(MethodDescription.TypeToken typeToken) {
return new Token(typeToken);
}
A token that identifies a Java method's type by its parameter types only.
/**
* A token that identifies a Java method's type by its parameter types only.
*/
protected static class Token {
The represented type token.
/**
* The represented type token.
*/
private final MethodDescription.TypeToken typeToken;
The hash code of this token which is precomputed for to improve performance.
/**
* The hash code of this token which is precomputed for to improve performance.
*/
private final int hashCode;
Creates a new type token for a Java method.
Params: - typeToken – The represented type token.
/**
* Creates a new type token for a Java method.
*
* @param typeToken The represented type token.
*/
protected Token(MethodDescription.TypeToken typeToken) {
this.typeToken = typeToken;
hashCode = typeToken.getParameterTypes().hashCode();
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object other) {
return this == other || other instanceof Token && typeToken.getParameterTypes().equals(((Token) other).typeToken.getParameterTypes());
}
@Override
public String toString() {
return typeToken.getParameterTypes().toString();
}
}
}
A harmonizer for the Java virtual machine's method dispatching rules that identifies a method by its parameter types and return type.
/**
* A harmonizer for the Java virtual machine's method dispatching rules that identifies a method by its parameter types and return type.
*/
enum ForJVMMethod implements Harmonizer<ForJVMMethod.Token> {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Token harmonize(MethodDescription.TypeToken typeToken) {
return new Token(typeToken);
}
A token that identifies a Java method's type by its parameter types and return type.
/**
* A token that identifies a Java method's type by its parameter types and return type.
*/
protected static class Token {
The represented type token.
/**
* The represented type token.
*/
private final MethodDescription.TypeToken typeToken;
The hash code of this token which is precomputed for to improve performance.
/**
* The hash code of this token which is precomputed for to improve performance.
*/
private final int hashCode;
Creates a new type token for a JVM method.
Params: - typeToken – The represented type token.
/**
* Creates a new type token for a JVM method.
*
* @param typeToken The represented type token.
*/
public Token(MethodDescription.TypeToken typeToken) {
this.typeToken = typeToken;
hashCode = typeToken.getReturnType().hashCode() + 31 * typeToken.getParameterTypes().hashCode();
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (!(other instanceof Token)) {
return false;
}
Token token = (Token) other;
return typeToken.getReturnType().equals(token.typeToken.getReturnType())
&& typeToken.getParameterTypes().equals(token.typeToken.getParameterTypes());
}
@Override
public String toString() {
return typeToken.toString();
}
}
}
}
Implementations are responsible for identifying a representative method for a Node
between several ambiguously resolved methods. /**
* Implementations are responsible for identifying a representative method for a {@link net.bytebuddy.dynamic.scaffold.MethodGraph.Node}
* between several ambiguously resolved methods.
*/
public interface Merger {
Merges two ambiguously resolved methods to yield a single representative.
Params: - left – The left method description, i.e. the method that was discovered first or was previously merged.
- right – The right method description, i.e. the method that was discovered last.
Returns: A method description compatible to both method's types that is used as a representative.
/**
* Merges two ambiguously resolved methods to yield a single representative.
*
* @param left The left method description, i.e. the method that was discovered first or was previously merged.
* @param right The right method description, i.e. the method that was discovered last.
* @return A method description compatible to both method's types that is used as a representative.
*/
MethodDescription merge(MethodDescription left, MethodDescription right);
A directional merger that always returns either the left or right method description.
/**
* A directional merger that always returns either the left or right method description.
*/
enum Directional implements Merger {
A merger that always returns the left method, i.e. the method that was discovered first or was previously merged.
/**
* A merger that always returns the left method, i.e. the method that was discovered first or was previously merged.
*/
LEFT(true),
A merger that always returns the right method, i.e. the method that was discovered last.
/**
* A merger that always returns the right method, i.e. the method that was discovered last.
*/
RIGHT(false);
true
if the left method should be returned when merging methods. /**
* {@code true} if the left method should be returned when merging methods.
*/
private final boolean left;
Creates a directional merger.
Params: - left –
true
if the left method should be returned when merging methods.
/**
* Creates a directional merger.
*
* @param left {@code true} if the left method should be returned when merging methods.
*/
Directional(boolean left) {
this.left = left;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription merge(MethodDescription left, MethodDescription right) {
return this.left
? left
: right;
}
}
}
A key represents a collection of methods within a method graph to later yield a node representing a collection of methods,
i.e. a method representative including information on the required method bridges.
Type parameters: - <S> – The type of the token used for deciding on method equality.
/**
* A key represents a collection of methods within a method graph to later yield a node representing a collection of methods,
* i.e. a method representative including information on the required method bridges.
*
* @param <S> The type of the token used for deciding on method equality.
*/
protected abstract static class Key<S> {
The internal name of the method this key identifies.
/**
* The internal name of the method this key identifies.
*/
protected final String internalName;
The number of method parameters of the method this key identifies.
/**
* The number of method parameters of the method this key identifies.
*/
protected final int parameterCount;
Creates a new key.
Params: - internalName – The internal name of the method this key identifies.
- parameterCount – The number of method parameters of the method this key identifies.
/**
* Creates a new key.
*
* @param internalName The internal name of the method this key identifies.
* @param parameterCount The number of method parameters of the method this key identifies.
*/
protected Key(String internalName, int parameterCount) {
this.internalName = internalName;
this.parameterCount = parameterCount;
}
Returns a set of all identifiers of this key.
Returns: A set of all identifiers of this key.
/**
* Returns a set of all identifiers of this key.
*
* @return A set of all identifiers of this key.
*/
protected abstract Set<S> getIdentifiers();
@Override
public int hashCode() {
return internalName.hashCode() + 31 * parameterCount;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (!(other instanceof Key)) {
return false;
}
Key key = (Key) other;
return internalName.equals(key.internalName)
&& parameterCount == key.parameterCount
&& !Collections.disjoint(getIdentifiers(), key.getIdentifiers());
}
A harmonized key represents a key where equality is decided based on tokens that are returned by a Harmonizer
. Type parameters: - <V> – The type of the tokens yielded by a harmonizer.
/**
* A harmonized key represents a key where equality is decided based on tokens that are returned by a
* {@link net.bytebuddy.dynamic.scaffold.MethodGraph.Compiler.Default.Harmonizer}.
*
* @param <V> The type of the tokens yielded by a harmonizer.
*/
protected static class Harmonized<V> extends Key<V> {
A mapping of identifiers to the type tokens they represent.
/**
* A mapping of identifiers to the type tokens they represent.
*/
private final Map<V, Set<MethodDescription.TypeToken>> identifiers;
Creates a new harmonized key.
Params: - internalName – The internal name of the method this key identifies.
- parameterCount – The number of method parameters of the method this key identifies.
- identifiers – A mapping of identifiers to the type tokens they represent.
/**
* Creates a new harmonized key.
*
* @param internalName The internal name of the method this key identifies.
* @param parameterCount The number of method parameters of the method this key identifies.
* @param identifiers A mapping of identifiers to the type tokens they represent.
*/
protected Harmonized(String internalName, int parameterCount, Map<V, Set<MethodDescription.TypeToken>> identifiers) {
super(internalName, parameterCount);
this.identifiers = identifiers;
}
Creates a new harmonized key for the given method description.
Params: - methodDescription – The method description to represent as a harmonized key.
- harmonizer – The harmonizer to use.
Type parameters: - <Q> – The type of the token yielded by a harmonizer.
Returns: A harmonized key representing the provided method.
/**
* Creates a new harmonized key for the given method description.
*
* @param methodDescription The method description to represent as a harmonized key.
* @param harmonizer The harmonizer to use.
* @param <Q> The type of the token yielded by a harmonizer.
* @return A harmonized key representing the provided method.
*/
protected static <Q> Harmonized<Q> of(MethodDescription methodDescription, Harmonizer<Q> harmonizer) {
MethodDescription.TypeToken typeToken = methodDescription.asTypeToken();
return new Harmonized<Q>(methodDescription.getInternalName(),
methodDescription.getParameters().size(),
Collections.singletonMap(harmonizer.harmonize(typeToken), Collections.<MethodDescription.TypeToken>emptySet()));
}
Creates a detached version of this key.
Params: - typeToken – The type token of the representative method.
Returns: The detached version of this key.
/**
* Creates a detached version of this key.
*
* @param typeToken The type token of the representative method.
* @return The detached version of this key.
*/
protected Detached detach(MethodDescription.TypeToken typeToken) {
Set<MethodDescription.TypeToken> identifiers = new HashSet<MethodDescription.TypeToken>();
for (Set<MethodDescription.TypeToken> typeTokens : this.identifiers.values()) {
identifiers.addAll(typeTokens);
}
identifiers.add(typeToken);
return new Detached(internalName, parameterCount, identifiers);
}
Combines this key with the given key.
Params: - key – The key to be merged with this key.
Returns: A harmonized key representing the merger of this key and the given key.
/**
* Combines this key with the given key.
*
* @param key The key to be merged with this key.
* @return A harmonized key representing the merger of this key and the given key.
*/
protected Harmonized<V> combineWith(Harmonized<V> key) {
Map<V, Set<MethodDescription.TypeToken>> identifiers = new HashMap<V, Set<MethodDescription.TypeToken>>(this.identifiers);
for (Map.Entry<V, Set<MethodDescription.TypeToken>> entry : key.identifiers.entrySet()) {
Set<MethodDescription.TypeToken> typeTokens = identifiers.get(entry.getKey());
if (typeTokens == null) {
identifiers.put(entry.getKey(), entry.getValue());
} else {
typeTokens = new HashSet<MethodDescription.TypeToken>(typeTokens);
typeTokens.addAll(entry.getValue());
identifiers.put(entry.getKey(), typeTokens);
}
}
return new Harmonized<V>(internalName, parameterCount, identifiers);
}
Extends this key by the given method description.
Params: - methodDescription – The method to extend this key with.
- harmonizer – The harmonizer to use for determining method equality.
Returns: The harmonized key representing the extension of this key with the provided method.
/**
* Extends this key by the given method description.
*
* @param methodDescription The method to extend this key with.
* @param harmonizer The harmonizer to use for determining method equality.
* @return The harmonized key representing the extension of this key with the provided method.
*/
protected Harmonized<V> extend(MethodDescription.InDefinedShape methodDescription, Harmonizer<V> harmonizer) {
Map<V, Set<MethodDescription.TypeToken>> identifiers = new HashMap<V, Set<MethodDescription.TypeToken>>(this.identifiers);
MethodDescription.TypeToken typeToken = methodDescription.asTypeToken();
V identifier = harmonizer.harmonize(typeToken);
Set<MethodDescription.TypeToken> typeTokens = identifiers.get(identifier);
if (typeTokens == null) {
identifiers.put(identifier, Collections.singleton(typeToken));
} else {
typeTokens = new HashSet<MethodDescription.TypeToken>(typeTokens);
typeTokens.add(typeToken);
identifiers.put(identifier, typeTokens);
}
return new Harmonized<V>(internalName, parameterCount, identifiers);
}
@Override
protected Set<V> getIdentifiers() {
return identifiers.keySet();
}
}
A detached version of a key that identifies methods by their JVM signature, i.e. parameter types and return type.
/**
* A detached version of a key that identifies methods by their JVM signature, i.e. parameter types and return type.
*/
protected static class Detached extends Key<MethodDescription.TypeToken> {
The type tokens represented by this key.
/**
* The type tokens represented by this key.
*/
private final Set<MethodDescription.TypeToken> identifiers;
Creates a new detached key.
Params: - internalName – The internal name of the method this key identifies.
- parameterCount – The number of method parameters of the method this key identifies.
- identifiers – The type tokens represented by this key.
/**
* Creates a new detached key.
*
* @param internalName The internal name of the method this key identifies.
* @param parameterCount The number of method parameters of the method this key identifies.
* @param identifiers The type tokens represented by this key.
*/
protected Detached(String internalName, int parameterCount, Set<MethodDescription.TypeToken> identifiers) {
super(internalName, parameterCount);
this.identifiers = identifiers;
}
Creates a new detached key of the given method token.
Params: - token – The method token to represent as a key.
Returns: A detached key representing the given method token..
/**
* Creates a new detached key of the given method token.
*
* @param token The method token to represent as a key.
* @return A detached key representing the given method token..
*/
protected static Detached of(MethodDescription.SignatureToken token) {
return new Detached(token.getName(), token.getParameterTypes().size(), Collections.singleton(token.asTypeToken()));
}
@Override
protected Set<MethodDescription.TypeToken> getIdentifiers() {
return identifiers;
}
}
A store for collected methods that are identified by keys.
Type parameters: - <V> – The type of the token used for deciding on method equality.
/**
* A store for collected methods that are identified by keys.
*
* @param <V> The type of the token used for deciding on method equality.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class Store<V> {
A mapping of harmonized keys to their represented entry.
/**
* A mapping of harmonized keys to their represented entry.
*/
private final LinkedHashMap<Harmonized<V>, Entry<V>> entries;
Creates an empty store.
/**
* Creates an empty store.
*/
protected Store() {
this(new LinkedHashMap<Harmonized<V>, Entry<V>>());
}
Creates a new store representing the given entries.
Params: - entries – A mapping of harmonized keys to their represented entry.
/**
* Creates a new store representing the given entries.
*
* @param entries A mapping of harmonized keys to their represented entry.
*/
private Store(LinkedHashMap<Harmonized<V>, Entry<V>> entries) {
this.entries = entries;
}
Combines the two given stores.
Params: - left – The left store to be combined.
- right – The right store to be combined.
Type parameters: - <W> – The type of the harmonized key of both stores.
Returns: An entry representing the combination of both stores.
/**
* Combines the two given stores.
*
* @param left The left store to be combined.
* @param right The right store to be combined.
* @param <W> The type of the harmonized key of both stores.
* @return An entry representing the combination of both stores.
*/
private static <W> Entry<W> combine(Entry<W> left, Entry<W> right) {
Set<MethodDescription> leftMethods = left.getCandidates(), rightMethods = right.getCandidates();
LinkedHashSet<MethodDescription> combined = new LinkedHashSet<MethodDescription>();
combined.addAll(leftMethods);
combined.addAll(rightMethods);
for (MethodDescription leftMethod : leftMethods) {
TypeDescription leftType = leftMethod.getDeclaringType().asErasure();
for (MethodDescription rightMethod : rightMethods) {
TypeDescription rightType = rightMethod.getDeclaringType().asErasure();
if (leftType.equals(rightType)) {
break;
} else if (leftType.isAssignableTo(rightType)) {
combined.remove(rightMethod);
break;
} else if (leftType.isAssignableFrom(rightType)) {
combined.remove(leftMethod);
break;
}
}
}
Key.Harmonized<W> key = left.getKey().combineWith(right.getKey());
Visibility visibility = left.getVisibility().expandTo(right.getVisibility());
return combined.size() == 1
? new Entry.Resolved<W>(key, combined.iterator().next(), visibility, Entry.Resolved.NOT_MADE_VISIBLE)
: new Entry.Ambiguous<W>(key, combined, visibility);
}
Registers a new top level method within this store.
Params: - methodDescriptions – The methods to register.
- harmonizer – The harmonizer to use for determining method equality.
Returns: A store with the given method registered as a top-level method.
/**
* Registers a new top level method within this store.
*
* @param methodDescriptions The methods to register.
* @param harmonizer The harmonizer to use for determining method equality.
* @return A store with the given method registered as a top-level method.
*/
protected Store<V> registerTopLevel(List<? extends MethodDescription> methodDescriptions, Harmonizer<V> harmonizer) {
if (methodDescriptions.isEmpty()) {
return this;
}
LinkedHashMap<Harmonized<V>, Entry<V>> entries = new LinkedHashMap<Harmonized<V>, Entry<V>>(this.entries);
for (MethodDescription methodDescription : methodDescriptions) {
Harmonized<V> key = Harmonized.of(methodDescription, harmonizer);
Entry<V> currentEntry = entries.remove(key), extendedEntry = (currentEntry == null
? new Entry.Initial<V>(key)
: currentEntry).extendBy(methodDescription, harmonizer);
entries.put(extendedEntry.getKey(), extendedEntry);
}
return new Store<V>(entries);
}
Combines this store with the given store.
Params: - store – The store to combine with this store.
Returns: A store representing a combination of this store and the given store.
/**
* Combines this store with the given store.
*
* @param store The store to combine with this store.
* @return A store representing a combination of this store and the given store.
*/
protected Store<V> combineWith(Store<V> store) {
if (entries.isEmpty()) {
return store;
} else if (store.entries.isEmpty()) {
return this;
}
LinkedHashMap<Harmonized<V>, Entry<V>> entries = new LinkedHashMap<Harmonized<V>, Entry<V>>(this.entries);
for (Entry<V> entry : store.entries.values()) {
Entry<V> previousEntry = entries.remove(entry.getKey()), injectedEntry = previousEntry == null
? entry
: combine(previousEntry, entry);
entries.put(injectedEntry.getKey(), injectedEntry);
}
return new Store<V>(entries);
}
Injects the given store into this store.
Params: - store – The key store to inject into this store.
Returns: A store that represents this store with the given store injected.
/**
* Injects the given store into this store.
*
* @param store The key store to inject into this store.
* @return A store that represents this store with the given store injected.
*/
protected Store<V> inject(Store<V> store) {
if (entries.isEmpty()) {
return store;
} else if (store.entries.isEmpty()) {
return this;
}
LinkedHashMap<Harmonized<V>, Entry<V>> entries = new LinkedHashMap<Harmonized<V>, Entry<V>>(this.entries);
for (Entry<V> entry : store.entries.values()) {
Entry<V> dominantEntry = entries.remove(entry.getKey()), injectedEntry = dominantEntry == null
? entry
: dominantEntry.inject(entry.getKey(), entry.getVisibility());
entries.put(injectedEntry.getKey(), injectedEntry);
}
return new Store<V>(entries);
}
Transforms this store into a method graph by applying the given merger.
Params: - merger – The merger to apply for resolving the representative for ambiguous resolutions.
Returns: The method graph that represents this key store.
/**
* Transforms this store into a method graph by applying the given merger.
*
* @param merger The merger to apply for resolving the representative for ambiguous resolutions.
* @return The method graph that represents this key store.
*/
protected MethodGraph asGraph(Merger merger) {
LinkedHashMap<Key<MethodDescription.TypeToken>, Node> entries = new LinkedHashMap<Key<MethodDescription.TypeToken>, Node>();
for (Entry<V> entry : this.entries.values()) {
Node node = entry.asNode(merger);
entries.put(entry.getKey().detach(node.getRepresentative().asTypeToken()), node);
}
return new Graph(entries);
}
An entry of a key store.
Type parameters: - <W> – The type of the harmonized token used for determining method equality.
/**
* An entry of a key store.
*
* @param <W> The type of the harmonized token used for determining method equality.
*/
protected interface Entry<W> {
Returns the harmonized key of this entry.
Returns: The harmonized key of this entry.
/**
* Returns the harmonized key of this entry.
*
* @return The harmonized key of this entry.
*/
Harmonized<W> getKey();
Returns all candidate methods represented by this entry.
Returns: All candidate methods represented by this entry.
/**
* Returns all candidate methods represented by this entry.
*
* @return All candidate methods represented by this entry.
*/
Set<MethodDescription> getCandidates();
Returns the minimal visibility of this entry.
Returns: The minimal visibility of this entry.
/**
* Returns the minimal visibility of this entry.
*
* @return The minimal visibility of this entry.
*/
Visibility getVisibility();
Extends this entry by the given method.
Params: - methodDescription – The method description to extend this entry with.
- harmonizer – The harmonizer to use for determining method equality.
Returns: This key extended by the given method.
/**
* Extends this entry by the given method.
*
* @param methodDescription The method description to extend this entry with.
* @param harmonizer The harmonizer to use for determining method equality.
* @return This key extended by the given method.
*/
Entry<W> extendBy(MethodDescription methodDescription, Harmonizer<W> harmonizer);
Injects the given key into this entry.
Params: - key – The key to inject into this entry.
- visibility – The entry's minimal visibility.
Returns: This entry extended with the given key.
/**
* Injects the given key into this entry.
*
* @param key The key to inject into this entry.
* @param visibility The entry's minimal visibility.
* @return This entry extended with the given key.
*/
Entry<W> inject(Harmonized<W> key, Visibility visibility);
Transforms this entry into a node.
Params: - merger – The merger to use for determining the representative method of an ambiguous node.
Returns: The resolved node.
/**
* Transforms this entry into a node.
*
* @param merger The merger to use for determining the representative method of an ambiguous node.
* @return The resolved node.
*/
Node asNode(Merger merger);
An entry in its initial state before registering any method as a representative.
Type parameters: - <U> – The type of the harmonized key to determine method equality.
/**
* An entry in its initial state before registering any method as a representative.
*
* @param <U> The type of the harmonized key to determine method equality.
*/
class Initial<U> implements Entry<U> {
The harmonized key this entry represents.
/**
* The harmonized key this entry represents.
*/
private final Harmonized<U> key;
Creates a new initial key.
Params: - key – The harmonized key this entry represents.
/**
* Creates a new initial key.
*
* @param key The harmonized key this entry represents.
*/
protected Initial(Harmonized<U> key) {
this.key = key;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Harmonized<U> getKey() {
throw new IllegalStateException("Cannot extract key from initial entry:" + this);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Set<MethodDescription> getCandidates() {
throw new IllegalStateException("Cannot extract method from initial entry:" + this);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Visibility getVisibility() {
throw new IllegalStateException("Cannot extract visibility from initial entry:" + this);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Entry<U> extendBy(MethodDescription methodDescription, Harmonizer<U> harmonizer) {
return new Resolved<U>(key.extend(methodDescription.asDefined(), harmonizer),
methodDescription,
methodDescription.getVisibility(),
Resolved.NOT_MADE_VISIBLE);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Entry<U> inject(Harmonized<U> key, Visibility visibility) {
throw new IllegalStateException("Cannot inject into initial entry without a registered method: " + this);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Node asNode(Merger merger) {
throw new IllegalStateException("Cannot transform initial entry without a registered method: " + this);
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (other == null || getClass() != other.getClass()) {
return false;
}
Initial<?> initial = (Initial<?>) other;
return key.equals(initial.key);
}
}
An entry representing a non-ambiguous node resolution.
Type parameters: - <U> – The type of the harmonized key to determine method equality.
/**
* An entry representing a non-ambiguous node resolution.
*
* @param <U> The type of the harmonized key to determine method equality.
*/
@HashCodeAndEqualsPlugin.Enhance
class Resolved<U> implements Entry<U> {
Indicates that a type's methods are already globally visible, meaning that a bridge method is not added
with the intend of creating a visibility bridge.
/**
* Indicates that a type's methods are already globally visible, meaning that a bridge method is not added
* with the intend of creating a visibility bridge.
*/
private static final int MADE_VISIBLE = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED;
Indicates that the entry was not made visible.
/**
* Indicates that the entry was not made visible.
*/
private static final boolean NOT_MADE_VISIBLE = false;
The harmonized key this entry represents.
/**
* The harmonized key this entry represents.
*/
private final Harmonized<U> key;
The non-ambiguous, representative method of this entry.
/**
* The non-ambiguous, representative method of this entry.
*/
private final MethodDescription methodDescription;
The minimal required visibility for this method.
/**
* The minimal required visibility for this method.
*/
private final Visibility visibility;
true
if this entry's representative was made visible by a visibility bridge. /**
* {@code true} if this entry's representative was made visible by a visibility bridge.
*/
private final boolean madeVisible;
Creates a new resolved entry.
Params: - key – The harmonized key this entry represents.
- methodDescription – The non-ambiguous, representative method of this entry.
- visibility – The minimal required visibility for this method.
- madeVisible –
true
if this entry's representative was made visible by a visibility bridge.
/**
* Creates a new resolved entry.
*
* @param key The harmonized key this entry represents.
* @param methodDescription The non-ambiguous, representative method of this entry.
* @param visibility The minimal required visibility for this method.
* @param madeVisible {@code true} if this entry's representative was made visible by a visibility bridge.
*/
protected Resolved(Harmonized<U> key, MethodDescription methodDescription, Visibility visibility, boolean madeVisible) {
this.key = key;
this.methodDescription = methodDescription;
this.visibility = visibility;
this.madeVisible = madeVisible;
}
Creates an entry for an override where a method overrides another method within a super class.
Params: - key – The merged key for both methods.
- override – The method declared by the extending type, potentially a bridge method.
- original – The method that is overridden by the extending type.
- visibility – The minimal required visibility for this entry.
Type parameters: - <V> – The type of the harmonized key to determine method equality.
Returns: An entry representing the merger of both methods.
/**
* Creates an entry for an override where a method overrides another method within a super class.
*
* @param key The merged key for both methods.
* @param override The method declared by the extending type, potentially a bridge method.
* @param original The method that is overridden by the extending type.
* @param visibility The minimal required visibility for this entry.
* @param <V> The type of the harmonized key to determine method equality.
* @return An entry representing the merger of both methods.
*/
private static <V> Entry<V> of(Harmonized<V> key, MethodDescription override, MethodDescription original, Visibility visibility) {
visibility = visibility.expandTo(original.getVisibility()).expandTo(override.getVisibility());
return override.isBridge()
? new Resolved<V>(key, original, visibility, (original.getDeclaringType().getModifiers() & MADE_VISIBLE) == 0)
: new Resolved<V>(key, override, visibility, NOT_MADE_VISIBLE);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Harmonized<U> getKey() {
return key;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Set<MethodDescription> getCandidates() {
return Collections.singleton(methodDescription);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Visibility getVisibility() {
return visibility;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Entry<U> extendBy(MethodDescription methodDescription, Harmonizer<U> harmonizer) {
Harmonized<U> key = this.key.extend(methodDescription.asDefined(), harmonizer);
Visibility visibility = this.visibility.expandTo(methodDescription.getVisibility());
return methodDescription.getDeclaringType().equals(this.methodDescription.getDeclaringType())
? Ambiguous.of(key, methodDescription, this.methodDescription, visibility)
: Resolved.of(key, methodDescription, this.methodDescription, visibility);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Entry<U> inject(Harmonized<U> key, Visibility visibility) {
return new Resolved<U>(this.key.combineWith(key), methodDescription, this.visibility.expandTo(visibility), madeVisible);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodGraph.Node asNode(Merger merger) {
return new Node(key.detach(methodDescription.asTypeToken()), methodDescription, visibility, madeVisible);
}
A node implementation representing a non-ambiguous method.
/**
* A node implementation representing a non-ambiguous method.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class Node implements MethodGraph.Node {
The detached key representing this node.
/**
* The detached key representing this node.
*/
private final Detached key;
The representative method of this node.
/**
* The representative method of this node.
*/
private final MethodDescription methodDescription;
The node's minimal visibility.
/**
* The node's minimal visibility.
*/
private final Visibility visibility;
true
if the represented method was made explicitly visible by a visibility bridge. /**
* {@code true} if the represented method was made explicitly visible by a visibility bridge.
*/
private final boolean visible;
Creates a new node.
Params: - key – The detached key representing this node.
- methodDescription – The representative method of this node.
- visibility – The node's minimal visibility.
- visible –
true
if the represented method was made explicitly visible by a visibility bridge.
/**
* Creates a new node.
*
* @param key The detached key representing this node.
* @param methodDescription The representative method of this node.
* @param visibility The node's minimal visibility.
* @param visible {@code true} if the represented method was made explicitly visible by a visibility bridge.
*/
protected Node(Detached key, MethodDescription methodDescription, Visibility visibility, boolean visible) {
this.key = key;
this.methodDescription = methodDescription;
this.visibility = visibility;
this.visible = visible;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Sort getSort() {
return visible
? Sort.VISIBLE
: Sort.RESOLVED;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription getRepresentative() {
return methodDescription;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Set<MethodDescription.TypeToken> getMethodTypes() {
return key.getIdentifiers();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Visibility getVisibility() {
return visibility;
}
}
}
An entry representing an ambiguous node resolution.
Type parameters: - <U> – The type of the harmonized key to determine method equality.
/**
* An entry representing an ambiguous node resolution.
*
* @param <U> The type of the harmonized key to determine method equality.
*/
@HashCodeAndEqualsPlugin.Enhance
class Ambiguous<U> implements Entry<U> {
The harmonized key this entry represents.
/**
* The harmonized key this entry represents.
*/
private final Harmonized<U> key;
A set of ambiguous methods that this entry represents.
/**
* A set of ambiguous methods that this entry represents.
*/
private final LinkedHashSet<MethodDescription> methodDescriptions;
The minimal required visibility for this method.
/**
* The minimal required visibility for this method.
*/
private final Visibility visibility;
Creates a new ambiguous entry.
Params: - key – The harmonized key this entry represents.
- methodDescriptions – A set of ambiguous methods that this entry represents.
- visibility – The minimal required visibility for this method.
/**
* Creates a new ambiguous entry.
*
* @param key The harmonized key this entry represents.
* @param methodDescriptions A set of ambiguous methods that this entry represents.
* @param visibility The minimal required visibility for this method.
*/
protected Ambiguous(Harmonized<U> key, LinkedHashSet<MethodDescription> methodDescriptions, Visibility visibility) {
this.key = key;
this.methodDescriptions = methodDescriptions;
this.visibility = visibility;
}
Creates a new ambiguous entry if both provided entries are not considered to be a bridge of one another.
Params: - key – The key of the entry to be created.
- left – The left method to be considered.
- right – The right method to be considered.
- visibility – The entry's minimal visibility.
Type parameters: - <Q> – The type of the token of the harmonized key to determine method equality.
Returns: The entry representing both methods.
/**
* Creates a new ambiguous entry if both provided entries are not considered to be a bridge of one another.
*
* @param key The key of the entry to be created.
* @param left The left method to be considered.
* @param right The right method to be considered.
* @param visibility The entry's minimal visibility.
* @param <Q> The type of the token of the harmonized key to determine method equality.
* @return The entry representing both methods.
*/
protected static <Q> Entry<Q> of(Harmonized<Q> key, MethodDescription left, MethodDescription right, Visibility visibility) {
visibility = visibility.expandTo(left.getVisibility()).expandTo(right.getVisibility());
return left.isBridge() ^ right.isBridge()
? new Resolved<Q>(key, left.isBridge() ? right : left, visibility, Resolved.NOT_MADE_VISIBLE)
: new Ambiguous<Q>(key, new LinkedHashSet<MethodDescription>(Arrays.asList(left, right)), visibility);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Harmonized<U> getKey() {
return key;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Set<MethodDescription> getCandidates() {
return methodDescriptions;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Visibility getVisibility() {
return visibility;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Entry<U> extendBy(MethodDescription methodDescription, Harmonizer<U> harmonizer) {
Harmonized<U> key = this.key.extend(methodDescription.asDefined(), harmonizer);
LinkedHashSet<MethodDescription> methodDescriptions = new LinkedHashSet<MethodDescription>();
TypeDescription declaringType = methodDescription.getDeclaringType().asErasure();
boolean bridge = methodDescription.isBridge();
Visibility visibility = this.visibility;
for (MethodDescription extendedMethod : this.methodDescriptions) {
if (extendedMethod.getDeclaringType().asErasure().equals(declaringType)) {
if (extendedMethod.isBridge() ^ bridge) {
methodDescriptions.add(bridge ? extendedMethod : methodDescription);
} else {
methodDescriptions.add(methodDescription);
methodDescriptions.add(extendedMethod);
}
}
visibility = visibility.expandTo(extendedMethod.getVisibility());
}
if (methodDescriptions.isEmpty()) {
return new Resolved<U>(key, methodDescription, visibility, bridge);
} else if (methodDescriptions.size() == 1) {
return new Resolved<U>(key, methodDescriptions.iterator().next(), visibility, Resolved.NOT_MADE_VISIBLE);
} else {
return new Ambiguous<U>(key, methodDescriptions, visibility);
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Entry<U> inject(Harmonized<U> key, Visibility visibility) {
return new Ambiguous<U>(this.key.combineWith(key), methodDescriptions, this.visibility.expandTo(visibility));
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodGraph.Node asNode(Merger merger) {
Iterator<MethodDescription> iterator = methodDescriptions.iterator();
MethodDescription methodDescription = iterator.next();
while (iterator.hasNext()) {
methodDescription = merger.merge(methodDescription, iterator.next());
}
return new Node(key.detach(methodDescription.asTypeToken()), methodDescription, visibility);
}
A node implementation representing an ambiguous method resolution.
/**
* A node implementation representing an ambiguous method resolution.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class Node implements MethodGraph.Node {
The detached key representing this node.
/**
* The detached key representing this node.
*/
private final Detached key;
The representative method of this node.
/**
* The representative method of this node.
*/
private final MethodDescription methodDescription;
The node's minimal visibility.
/**
* The node's minimal visibility.
*/
private final Visibility visibility;
Params: - key – The detached key representing this node.
- methodDescription – The representative method of this node.
- visibility – The node's minimal visibility.
/**
* @param key The detached key representing this node.
* @param methodDescription The representative method of this node.
* @param visibility The node's minimal visibility.
*/
protected Node(Detached key, MethodDescription methodDescription, Visibility visibility) {
this.key = key;
this.methodDescription = methodDescription;
this.visibility = visibility;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Sort getSort() {
return Sort.AMBIGUOUS;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public MethodDescription getRepresentative() {
return methodDescription;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Set<MethodDescription.TypeToken> getMethodTypes() {
return key.getIdentifiers();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Visibility getVisibility() {
return visibility;
}
}
}
}
A graph implementation based on a key store.
/**
* A graph implementation based on a key store.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class Graph implements MethodGraph {
A mapping of a node's type tokens to the represented node.
/**
* A mapping of a node's type tokens to the represented node.
*/
private final LinkedHashMap<Key<MethodDescription.TypeToken>, Node> entries;
Creates a new graph.
Params: - entries – A mapping of a node's type tokens to the represented node.
/**
* Creates a new graph.
*
* @param entries A mapping of a node's type tokens to the represented node.
*/
protected Graph(LinkedHashMap<Key<MethodDescription.TypeToken>, Node> entries) {
this.entries = entries;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Node locate(MethodDescription.SignatureToken token) {
Node node = entries.get(Detached.of(token));
return node == null
? Node.Unresolved.INSTANCE
: node;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public NodeList listNodes() {
return new NodeList(new ArrayList<Node>(entries.values()));
}
}
}
}
}
}
A list of nodes.
/**
* A list of nodes.
*/
class NodeList extends FilterableList.AbstractBase<Node, NodeList> {
The represented nodes.
/**
* The represented nodes.
*/
private final List<? extends Node> nodes;
Creates a list of nodes.
Params: - nodes – The represented nodes.
/**
* Creates a list of nodes.
*
* @param nodes The represented nodes.
*/
public NodeList(List<? extends Node> nodes) {
this.nodes = nodes;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Node get(int index) {
return nodes.get(index);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public int size() {
return nodes.size();
}
@Override
protected NodeList wrap(List<Node> values) {
return new NodeList(values);
}
Transforms this list of nodes into a list of the node's representatives.
Returns: A list of these node's representatives.
/**
* Transforms this list of nodes into a list of the node's representatives.
*
* @return A list of these node's representatives.
*/
public MethodList<?> asMethodList() {
List<MethodDescription> methodDescriptions = new ArrayList<MethodDescription>(size());
for (Node node : nodes) {
methodDescriptions.add(node.getRepresentative());
}
return new MethodList.Explicit<MethodDescription>(methodDescriptions);
}
}
A simple implementation of a method graph.
/**
* A simple implementation of a method graph.
*/
@HashCodeAndEqualsPlugin.Enhance
class Simple implements MethodGraph {
The nodes represented by this method graph.
/**
* The nodes represented by this method graph.
*/
private final LinkedHashMap<MethodDescription.SignatureToken, Node> nodes;
Creates a new simple method graph.
Params: - nodes – The nodes represented by this method graph.
/**
* Creates a new simple method graph.
*
* @param nodes The nodes represented by this method graph.
*/
public Simple(LinkedHashMap<MethodDescription.SignatureToken, Node> nodes) {
this.nodes = nodes;
}
Returns a method graph that contains all of the provided methods as simple nodes.
Params: - methodDescriptions – A list of method descriptions to be represented as simple nodes.
Returns: A method graph that represents all of the provided methods as simple nodes.
/**
* Returns a method graph that contains all of the provided methods as simple nodes.
*
* @param methodDescriptions A list of method descriptions to be represented as simple nodes.
* @return A method graph that represents all of the provided methods as simple nodes.
*/
public static MethodGraph of(List<? extends MethodDescription> methodDescriptions) {
LinkedHashMap<MethodDescription.SignatureToken, Node> nodes = new LinkedHashMap<MethodDescription.SignatureToken, Node>();
for (MethodDescription methodDescription : methodDescriptions) {
nodes.put(methodDescription.asSignatureToken(), new Node.Simple(methodDescription));
}
return new Simple(nodes);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Node locate(MethodDescription.SignatureToken token) {
Node node = nodes.get(token);
return node == null
? Node.Unresolved.INSTANCE
: node;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public NodeList listNodes() {
return new NodeList(new ArrayList<Node>(nodes.values()));
}
}
}