Replace UnloadedType with ByteBuddy TypeDescription
MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java
@@ -20,6 +20,7 @@ import net.bytebuddy.utility.JavaModule;
@Slf4j
public class AgentInstaller {
+ public static final DDLocationStrategy LOCATION_STRATEGY = new DDLocationStrategy();
private static volatile Instrumentation INSTRUMENTATION;
public static Instrumentation getInstrumentation() {
@@ -47,7 +48,7 @@ public class AgentInstaller {
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.DescriptionStrategy.Default.POOL_ONLY)
.with(new LoggingListener())
- .with(new DDLocationStrategy())
+ .with(LOCATION_STRATEGY)
.ignore(any(), skipClassLoader())
.or(nameStartsWith("datadog.trace."))
.or(nameStartsWith("datadog.opentracing."))

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/DDLocationStrategy.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/DDLocationStrategy.java
@@ -12,6 +12,10 @@ import net.bytebuddy.utility.JavaModule;
* reached.
*/
public class DDLocationStrategy implements AgentBuilder.LocationStrategy {
+ public ClassFileLocator classFileLocator(ClassLoader classLoader) {
+ return classFileLocator(classLoader, null);
+ }
+
@Override
public ClassFileLocator classFileLocator(ClassLoader classLoader, final JavaModule javaModule) {
final List<ClassFileLocator> locators = new ArrayList<>();

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/Reference.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/Reference.java
@@ -466,8 +466,11 @@ public class Reference {
public String toString() {
// <init>()V
// toString()Ljava/lang/String;
- return name
- + Type.getMethodType(returnType, parameterTypes.toArray(new Type[0])).getDescriptor();
+ return name + getDescriptor();
+ }
+
+ public String getDescriptor() {
+ return Type.getMethodType(returnType, parameterTypes.toArray(new Type[0])).getDescriptor();
}
@Override

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceMatcher.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceMatcher.java
@@ -4,6 +4,7 @@ import static datadog.trace.agent.tooling.ClassLoaderMatcher.BOOTSTRAP_CLASSLOAD
import static datadog.trace.bootstrap.WeakMap.Provider.newWeakMap;
import static net.bytebuddy.dynamic.loading.ClassLoadingStrategy.BOOTSTRAP_LOADER;
+import datadog.trace.agent.tooling.AgentInstaller;
import datadog.trace.agent.tooling.Utils;
import datadog.trace.agent.tooling.muzzle.Reference.Mismatch;
import datadog.trace.agent.tooling.muzzle.Reference.Source;
@@ -15,14 +16,11 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;
-import java.util.concurrent.ConcurrentHashMap;
import lombok.extern.slf4j.Slf4j;
-import net.bytebuddy.jar.asm.ClassReader;
-import net.bytebuddy.jar.asm.ClassVisitor;
-import net.bytebuddy.jar.asm.FieldVisitor;
-import net.bytebuddy.jar.asm.MethodVisitor;
-import net.bytebuddy.jar.asm.Opcodes;
-import net.bytebuddy.jar.asm.Type;
+import net.bytebuddy.description.field.FieldDescription;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.pool.TypePool;
/** Matches a set of references against a classloader. */
@Slf4j
@@ -82,335 +80,158 @@ public class ReferenceMatcher {
* @param loader
* @return A list of mismatched sources. A list of size 0 means the reference matches the class.
*/
- private static List<Reference.Mismatch> checkMatch(Reference reference, ClassLoader loader) {
- if (loader == BOOTSTRAP_CLASSLOADER) {
- throw new IllegalStateException("Cannot directly check against bootstrap classloader");
- }
- if (!onClasspath(reference.getClassName(), loader)) {
+ public static List<Reference.Mismatch> checkMatch(Reference reference, ClassLoader loader) {
+ final TypePool typePool =
+ TypePool.Default.of(AgentInstaller.LOCATION_STRATEGY.classFileLocator(loader));
+ final List<Mismatch> mismatches = new ArrayList<>(0);
+ try {
+ final TypePool.Resolution resolution =
+ typePool.describe(Utils.getClassName(reference.getClassName()));
+ if (!resolution.isResolved()) {
return Collections.<Mismatch>singletonList(
new Mismatch.MissingClass(
reference.getSources().toArray(new Source[0]), reference.getClassName()));
}
- final List<Mismatch> mismatches = new ArrayList<>(0);
- try {
- ReferenceMatcher.UnloadedType typeOnClasspath =
- ReferenceMatcher.UnloadedType.of(reference.getClassName(), loader);
- mismatches.addAll(typeOnClasspath.checkMatch(reference));
- for (Reference.Method requiredMethod : reference.getMethods()) {
- mismatches.addAll(typeOnClasspath.checkMatch(requiredMethod));
- }
+ return checkMatch(reference, resolution.resolve());
} catch (Exception e) {
+ if (e.getMessage().startsWith("Cannot resolve type description for ")) {
+ // bytebuddy throws an illegal state exception with this message if it cannot resolve types
+ // TODO: handle missing type resolutions without catching bytebuddy's exceptions
+ final String className = e.getMessage().replace("Cannot resolve type description for ", "");
+ mismatches.add(
+ new Mismatch.MissingClass(reference.getSources().toArray(new Source[0]), className));
+ } else {
// Shouldn't happen. Fail the reference check and add a mismatch for debug logging.
mismatches.add(new Mismatch.ReferenceCheckError(e, reference, loader));
}
- return mismatches;
- }
-
- private static boolean onClasspath(final String className, final ClassLoader loader) {
- final String resourceName = Utils.getResourceName(className);
- return loader.getResource(resourceName) != null
- // we can also reach bootstrap classes
- || Utils.getBootstrapProxy().getResource(resourceName) != null;
- }
-
- /**
- * A representation of a jvm class created from a byte array without loading the class in
- * question.
- *
- * <p>Used to compare an expected Reference with the actual runtime class without causing
- * classloads.
- */
- public static class UnloadedType extends ClassVisitor {
- private static final Map<ClassLoader, Map<String, UnloadedType>> typeCache =
- Collections.synchronizedMap(new WeakHashMap<ClassLoader, Map<String, UnloadedType>>());
-
- private String superName = null;
- private String className = null;
- private String[] interfaceNames = new String[0];
- private UnloadedType unloadedSuper = null;
- private final List<UnloadedType> unloadedInterfaces = new ArrayList<>();
- private int flags;
- private final List<Method> methods = new ArrayList<>();
- private final List<Field> fields = new ArrayList<>();
-
- public static UnloadedType of(String className, ClassLoader classLoader) throws Exception {
- if (classLoader != Utils.getBootstrapProxy()) {
- // getResource delegation won't see our bootstrap classes so do the delegation here.
- if (Utils.getBootstrapProxy().getResource(Utils.getResourceName(className)) != null) {
- return of(className, Utils.getBootstrapProxy());
- }
- }
- className = Utils.getInternalName(className);
- Map<String, UnloadedType> classLoaderCache = typeCache.get(classLoader);
- if (classLoaderCache == null) {
- synchronized (classLoader) {
- classLoaderCache = typeCache.get(classLoader);
- if (classLoaderCache == null) {
- classLoaderCache = new ConcurrentHashMap<>();
- typeCache.put(classLoader, classLoaderCache);
}
- }
- }
- UnloadedType unloadedType = classLoaderCache.get(className);
- if (unloadedType == null) {
- final InputStream in = classLoader.getResourceAsStream(Utils.getResourceName(className));
- unloadedType = new UnloadedType(null);
- final ClassReader reader = new ClassReader(in);
- reader.accept(unloadedType, ClassReader.SKIP_CODE);
- if (unloadedType.superName != null) {
- unloadedType.unloadedSuper = UnloadedType.of(unloadedType.superName, classLoader);
- }
- for (String interfaceName : unloadedType.interfaceNames) {
- unloadedType.unloadedInterfaces.add(UnloadedType.of(interfaceName, classLoader));
- }
- classLoaderCache.put(className, unloadedType);
- }
- return unloadedType;
- }
-
- private UnloadedType(ClassVisitor cv) {
- super(Opcodes.ASM6, cv);
- }
-
- public String getClassName() {
- return className;
- }
-
- public String getSuperName() {
- return superName;
+ return mismatches;
}
- public int getFlags() {
- return flags;
- }
+ public static List<Reference.Mismatch> checkMatch(
+ Reference reference, TypeDescription typeOnClasspath) {
+ final List<Mismatch> mismatches = new ArrayList<>(0);
- public List<Reference.Mismatch> checkMatch(Reference reference) {
- final List<Reference.Mismatch> mismatches = new ArrayList<>(0);
for (Reference.Flag flag : reference.getFlags()) {
- if (!flag.matches(getFlags())) {
- final String desc = this.getClassName();
+ if (!flag.matches(typeOnClasspath.getModifiers())) {
+ final String desc = reference.getClassName();
mismatches.add(
new Mismatch.MissingFlag(
- reference.getSources().toArray(new Source[0]), desc, flag, getFlags()));
- }
+ reference.getSources().toArray(new Source[0]),
+ desc,
+ flag,
+ typeOnClasspath.getModifiers()));
}
- return mismatches;
}
- public List<Reference.Mismatch> checkMatch(Reference.Field fieldRef) {
- final List<Reference.Mismatch> mismatches = new ArrayList<>(0);
- final Field unloadedField = findField(fieldRef, true);
- if (unloadedField == null) {
+ for (Reference.Field fieldRef : reference.getFields()) {
+ FieldDescription.InDefinedShape fieldDescription = findField(fieldRef, typeOnClasspath);
+ if (fieldDescription == null) {
mismatches.add(
new Reference.Mismatch.MissingField(
fieldRef.getSources().toArray(new Reference.Source[0]),
- className,
+ reference.getClassName(),
fieldRef.getName(),
fieldRef.getType().getInternalName()));
} else {
for (Reference.Flag flag : fieldRef.getFlags()) {
- if (!flag.matches(unloadedField.getFlags())) {
- final String desc = this.getClassName() + "#" + unloadedField.signature;
+ if (!flag.matches(fieldDescription.getModifiers())) {
+ final String desc =
+ reference.getClassName()
+ + "#"
+ + fieldRef.getName()
+ + fieldRef.getType().getInternalName();
mismatches.add(
new Mismatch.MissingFlag(
fieldRef.getSources().toArray(new Source[0]),
desc,
flag,
- unloadedField.getFlags()));
+ fieldDescription.getModifiers()));
}
}
}
- return mismatches;
}
- public List<Reference.Mismatch> checkMatch(Reference.Method methodRef) {
- final List<Reference.Mismatch> mismatches = new ArrayList<>(0);
- final Method unloadedMethod = findMethod(methodRef, true);
- if (unloadedMethod == null) {
+ for (Reference.Method methodRef : reference.getMethods()) {
+ final MethodDescription.InDefinedShape methodDescription =
+ findMethod(methodRef, typeOnClasspath);
+ if (methodDescription == null) {
mismatches.add(
new Reference.Mismatch.MissingMethod(
methodRef.getSources().toArray(new Reference.Source[0]),
- className,
- methodRef.toString()));
+ methodRef.getName(),
+ methodRef.getDescriptor()));
} else {
for (Reference.Flag flag : methodRef.getFlags()) {
- if (!flag.matches(unloadedMethod.getFlags())) {
- final String desc = this.getClassName() + "#" + unloadedMethod.signature;
+ if (!flag.matches(methodDescription.getModifiers())) {
+ final String desc =
+ reference.getClassName() + "#" + methodRef.getName() + methodRef.getDescriptor();
mismatches.add(
new Mismatch.MissingFlag(
methodRef.getSources().toArray(new Source[0]),
desc,
flag,
- unloadedMethod.getFlags()));
+ methodDescription.getModifiers()));
}
}
}
- return mismatches;
}
- private Method findMethod(Reference.Method method, boolean includePrivateMethods) {
- Method unloadedMethod =
- new Method(
- 0,
- method.getName(),
- Type.getMethodType(
- method.getReturnType(), method.getParameterTypes().toArray(new Type[0]))
- .getDescriptor());
- for (Method meth : methods) {
- if (meth.equals(unloadedMethod)) {
- if (meth.is(Opcodes.ACC_PRIVATE)) {
- return includePrivateMethods ? meth : null;
- } else {
- return meth;
- }
- }
- }
- if (null != unloadedSuper) {
- final Method meth = unloadedSuper.findMethod(method, false);
- if (null != meth) return meth;
- }
- for (UnloadedType unloadedInterface : unloadedInterfaces) {
- final Method meth = unloadedInterface.findMethod(method, false);
- if (null != meth) return meth;
- }
- return null;
+ return mismatches;
}
- private Field findField(Reference.Field fieldRef, boolean includePrivateFields) {
- final Field key = new Field(0, fieldRef.getName(), fieldRef.getType().getDescriptor());
- final int index = fields.indexOf(key);
- if (index != -1) {
- final Field foundField = fields.get(index);
- if (foundField.is(Opcodes.ACC_PRIVATE)) {
- return includePrivateFields ? foundField : null;
- } else {
- return foundField;
+ private static FieldDescription.InDefinedShape findField(
+ Reference.Field fieldRef, TypeDescription typeOnClasspath) {
+ for (FieldDescription.InDefinedShape fieldType : typeOnClasspath.getDeclaredFields()) {
+ if (fieldType.getName().equals(fieldRef.getName())
+ && fieldType
+ .getType()
+ .asErasure()
+ .getInternalName()
+ .equals(fieldRef.getType().getInternalName())) {
+ return fieldType;
}
- } else {
- Field superField = null;
- if (unloadedSuper != null) {
- superField = unloadedSuper.findField(fieldRef, false);
- if (superField != null) {
- return superField;
}
+ if (typeOnClasspath.getSuperClass() != null) {
+ FieldDescription.InDefinedShape fieldOnSupertype =
+ findField(fieldRef, typeOnClasspath.getSuperClass().asErasure());
+ if (fieldOnSupertype != null) {
+ return fieldOnSupertype;
}
- for (UnloadedType unloadedInterface : unloadedInterfaces) {
- superField = unloadedInterface.findField(fieldRef, false);
- if (superField != null) {
- return superField;
}
+ for (TypeDescription.Generic interfaceType : typeOnClasspath.getInterfaces()) {
+ FieldDescription.InDefinedShape fieldOnSupertype =
+ findField(fieldRef, interfaceType.asErasure());
+ if (fieldOnSupertype != null) {
+ return fieldOnSupertype;
}
}
return null;
}
- @Override
- public void visit(
- final int version,
- final int access,
- final String name,
- final String signature,
- final String superName,
- final String[] interfaces) {
- className = Utils.getClassName(name);
- if (null != superName) this.superName = Utils.getClassName(superName);
- if (null != interfaces) this.interfaceNames = interfaces;
- this.flags = access;
- super.visit(version, access, name, signature, superName, interfaces);
- }
-
- @Override
- public FieldVisitor visitField(
- int access, String name, String descriptor, String signature, Object value) {
- fields.add(new Field(access, name, descriptor));
- return super.visitField(access, name, descriptor, signature, value);
- }
-
- @Override
- public MethodVisitor visitMethod(
- final int access,
- final String name,
- final String descriptor,
- final String signature,
- final String[] exceptions) {
- methods.add(new Method(access, name, descriptor));
- return super.visitMethod(access, name, descriptor, signature, exceptions);
- }
-
- private static class Method {
- private final int flags;
- // name + descriptor
- private final String signature;
-
- public Method(int flags, String name, String desc) {
- this.flags = flags;
- this.signature = name + desc;
- }
-
- public boolean is(int flag) {
- boolean result = (flags & flag) != 0;
- return result;
- }
-
- public int getFlags() {
- return flags;
- }
-
- @Override
- public String toString() {
- return new StringBuilder("Unloaded: ").append(signature).toString();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof Method) {
- return signature.equals(((Method) o).signature);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return signature.hashCode();
- }
- }
-
- private static class Field {
- private final int flags;
- // name + typeDesc
- private final String signature;
-
- public Field(int flags, String name, String typeDesc) {
- this.flags = flags;
- this.signature = name + typeDesc;
- }
-
- private int getFlags() {
- return flags;
- }
-
- public boolean is(int flag) {
- boolean result = (flags & flag) != 0;
- return result;
+ private static MethodDescription.InDefinedShape findMethod(
+ Reference.Method methodRef, TypeDescription typeOnClasspath) {
+ for (MethodDescription.InDefinedShape methodDescription :
+ typeOnClasspath.getDeclaredMethods()) {
+ if (methodDescription.getInternalName().equals(methodRef.getName())
+ && methodDescription.getDescriptor().equals(methodRef.getDescriptor())) {
+ return methodDescription;
}
-
- @Override
- public String toString() {
- return new StringBuilder("Unloaded: ").append(signature).toString();
}
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof Field) {
- return signature.equals(((Field) o).signature);
+ if (typeOnClasspath.getSuperClass() != null) {
+ MethodDescription.InDefinedShape methodOnSupertype =
+ findMethod(methodRef, typeOnClasspath.getSuperClass().asErasure());
+ if (methodOnSupertype != null) {
+ return methodOnSupertype;
}
- return false;
}
-
- @Override
- public int hashCode() {
- return signature.hashCode();
+ for (TypeDescription.Generic interfaceType : typeOnClasspath.getInterfaces()) {
+ MethodDescription.InDefinedShape methodOnSupertype =
+ findMethod(methodRef, interfaceType.asErasure());
+ if (methodOnSupertype != null) {
+ return methodOnSupertype;
}
}
+ return null;
}
}

MODIFY
dd-java-agent/testing/src/test/groovy/muzzle/ReferenceMatcherTest.groovy
dd-java-agent/testing/src/test/groovy/muzzle/ReferenceMatcherTest.groovy
package muzzle
+import static datadog.trace.agent.tooling.muzzle.Reference.Flag.*
+import static datadog.trace.agent.tooling.muzzle.Reference.Mismatch.*
+
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.agent.test.TestUtils
-import datadog.trace.agent.tooling.Utils
import datadog.trace.agent.tooling.muzzle.Reference
import datadog.trace.agent.tooling.muzzle.Reference.Source
-import datadog.trace.agent.tooling.muzzle.Reference.Flag
import datadog.trace.agent.tooling.muzzle.ReferenceCreator
import datadog.trace.agent.tooling.muzzle.ReferenceMatcher
@@ -35,8 +36,8 @@ class ReferenceMatcherTest extends AgentTestRunner {
ReferenceMatcher refMatcher = new ReferenceMatcher(refs)
expect:
- refMatcher.getMismatchedReferenceSources(safeClasspath).size() == 0
- refMatcher.getMismatchedReferenceSources(unsafeClasspath).size() == 1
+ getMismatchClassSet(refMatcher.getMismatchedReferenceSources(safeClasspath)) == new HashSet<>()
+ getMismatchClassSet(refMatcher.getMismatchedReferenceSources(unsafeClasspath)) == new HashSet<>([MissingClass])
}
def "matching does not hold a strong reference to classloaders"() {
@@ -44,111 +45,68 @@ class ReferenceMatcherTest extends AgentTestRunner {
MuzzleWeakReferenceTest.classLoaderRefIsGarbageCollected()
}
- def "match classes"() {
- ReferenceMatcher.UnloadedType unloadedB = ReferenceMatcher.UnloadedType.of(MethodBodyAdvice.B.getName(), MethodBodyAdvice.B.getClassLoader())
- Reference ref
+ def "matching ref #referenceName #referenceFlags against #classToCheck produces #expectedMismatches"() {
+ setup:
+ Reference.Builder builder = new Reference.Builder(referenceName)
+ for (Reference.Flag refFlag : referenceFlags) {
+ builder = builder.withFlag(refFlag)
+ }
+ Reference ref = builder.build()
- when:
- ref = new Reference.Builder(Utils.getInternalName(MethodBodyAdvice.B.getName()))
- .withFlag(Flag.NON_INTERFACE)
- .build()
- then:
- unloadedB.checkMatch(ref).size() == 0
+ expect:
+ getMismatchClassSet(ReferenceMatcher.checkMatch(ref, this.getClass().getClassLoader())) == new HashSet<Object>(expectedMismatches)
+
+ where:
+ referenceName | referenceFlags | classToCheck | expectedMismatches
+ MethodBodyAdvice.B.getName() | [NON_INTERFACE] | MethodBodyAdvice.B | []
+ MethodBodyAdvice.B.getName() | [INTERFACE] | MethodBodyAdvice.B | [MissingFlag]
+ }
- when:
- ref = new Reference.Builder(Utils.getInternalName(MethodBodyAdvice.B.getName()))
- .withFlag(Flag.INTERFACE)
+ def "method match #methodTestDesc"() {
+ setup:
+ Type methodType = Type.getMethodType(methodDesc)
+ Reference reference = new Reference.Builder(classToCheck.getName())
+ .withMethod(new Source[0], methodFlags as Reference.Flag[], methodName, methodType.getReturnType(), methodType.getArgumentTypes())
.build()
- then:
- unloadedB.checkMatch(ref).size() == 1
+
+ expect:
+ getMismatchClassSet(ReferenceMatcher.checkMatch(reference, this.getClass().getClassLoader())) == new HashSet<Object>(expectedMismatches)
+
+ where:
+ methodName | methodDesc | methodFlags | classToCheck | expectedMismatches | methodTestDesc
+ "aMethod" | "(Ljava/lang/String;)Ljava/lang/String;" | [] | MethodBodyAdvice.B | [] | "match method declared in class"
+ "hashCode" | "()I" | [] | MethodBodyAdvice.B | [] | "match method declared in superclass"
+ "someMethod" | "()V" | [] | MethodBodyAdvice.SomeInterface | [] | "match method declared in interface"
+ "privateStuff" | "()V" | [PRIVATE_OR_HIGHER] | MethodBodyAdvice.B | [] | "match private method"
+ "privateStuff" | "()V" | [PROTECTED_OR_HIGHER] | MethodBodyAdvice.B2 | [MissingFlag] | "fail match private in supertype"
+ "aStaticMethod" | "()V" | [NON_STATIC] | MethodBodyAdvice.B | [MissingFlag] | "static method mismatch"
+ "missingMethod" | "()V" | [] | MethodBodyAdvice.B | [MissingMethod] | "missing method mismatch"
}
- def "match methods"() {
+ def "field match #fieldTestDesc"() {
setup:
- ReferenceMatcher.UnloadedType unloadedB = ReferenceMatcher.UnloadedType.of(MethodBodyAdvice.B.getName(), MethodBodyAdvice.B.getClassLoader())
- ReferenceMatcher.UnloadedType unloadedB2 = ReferenceMatcher.UnloadedType.of(MethodBodyAdvice.B2.getName(), MethodBodyAdvice.B2.getClassLoader())
- ReferenceMatcher.UnloadedType unloadedInterface = ReferenceMatcher.UnloadedType.of(MethodBodyAdvice.AnotherInterface.getName(), MethodBodyAdvice.AnotherInterface.getClassLoader())
- Reference.Method methodRef
-
- // match method declared in the class
- when:
- methodRef = new Reference.Method("aMethod", "(Ljava/lang/String;)Ljava/lang/String;")
- then:
- unloadedB.checkMatch(methodRef).size() == 0
-
- // match method declared in the supertype
- when:
- methodRef = new Reference.Method("hashCode", "()I")
- then:
- unloadedB.checkMatch(methodRef).size() == 0
-
- // match method declared in interface
- when:
- methodRef = new Reference.Method("someMethod", "()V")
- then:
- unloadedInterface.checkMatch(methodRef).size() == 0
-
- // match private method in the class
- when:
- methodRef = new Reference.Method("privateStuff", "()V")
- then:
- unloadedB.checkMatch(methodRef).size() == 0
-
- // fail to match private method in superclass
- when:
- methodRef = new Reference.Method("privateStuff", "()V")
- then:
- unloadedB2.checkMatch(methodRef).size() == 1
-
- // static method flag mismatch
- when:
- methodRef = new Reference.Method(new Source[0], [Flag.NON_STATIC] as Flag[], "aStaticMethod", Type.getType("V"))
- then:
- unloadedB2.checkMatch(methodRef).size() == 1
-
- // missing method mismatch
- when:
- methodRef = new Reference.Method(new Source[0], new Flag[0], "missingTestMethod", Type.VOID_TYPE, new Type[0])
- then:
- unloadedB.checkMatch(methodRef).size() == 1
+ Reference reference = new Reference.Builder(classToCheck.getName())
+ .withField(new Source[0], fieldFlags as Reference.Flag[], fieldName, Type.getType(fieldType))
+ .build()
+
+ expect:
+ getMismatchClassSet(ReferenceMatcher.checkMatch(reference, this.getClass().getClassLoader())) == new HashSet<Object>(expectedMismatches)
+
+ where:
+ fieldName | fieldType | fieldFlags | classToCheck | expectedMismatches | fieldTestDesc
+ "missingField" | "Ljava/lang/String;" | [] | MethodBodyAdvice.A | [MissingField] | "mismatch missing field"
+ "privateField" | "Ljava/lang/String;" | [] | MethodBodyAdvice.A | [MissingField] | "mismatch field type signature"
+ "privateField" | "Ljava/lang/Object;" | [PRIVATE_OR_HIGHER] | MethodBodyAdvice.A | [] | "match private field"
+ "privateField" | "Ljava/lang/Object;" | [PROTECTED_OR_HIGHER] | MethodBodyAdvice.A2 | [MissingFlag] | "mismatch private field in supertype"
+ "protectedField" | "Ljava/lang/Object;" | [STATIC] | MethodBodyAdvice.A | [MissingFlag] | "mismatch static field"
+ "staticB" | Type.getType(MethodBodyAdvice.B).getDescriptor() | [STATIC, PROTECTED_OR_HIGHER] | MethodBodyAdvice.A | [] | "match static field"
}
- def "match fields" () {
- ReferenceMatcher.UnloadedType unloadedA = ReferenceMatcher.UnloadedType.of(MethodBodyAdvice.A.getName(), MethodBodyAdvice.A.getClassLoader())
- ReferenceMatcher.UnloadedType unloadedA2 = ReferenceMatcher.UnloadedType.of(MethodBodyAdvice.A2.getName(), MethodBodyAdvice.A2.getClassLoader())
- Reference.Field fieldRef
-
- when:
- fieldRef = new Reference.Field(new Source[0], new Flag[0], "missingField", Type.getType("Ljava/lang/String;"))
- then:
- unloadedA.checkMatch(fieldRef).size() == 1
-
- when:
- // wrong field type sig should create a mismatch
- fieldRef = new Reference.Field(new Source[0], new Flag[0], "privateField", Type.getType("Ljava/lang/String;"))
- then:
- unloadedA.checkMatch(fieldRef).size() == 1
-
- when:
- fieldRef = new Reference.Field(new Source[0], new Flag[0], "privateField", Type.getType("Ljava/lang/Object;"))
- then:
- unloadedA.checkMatch(fieldRef).size() == 0
- unloadedA2.checkMatch(fieldRef).size() == 1
-
- when:
- fieldRef = new Reference.Field(new Source[0], [Flag.NON_STATIC, Flag.PROTECTED_OR_HIGHER] as Flag[], "protectedField", Type.getType("Ljava/lang/Object;"))
- then:
- unloadedA.checkMatch(fieldRef).size() == 0
- unloadedA2.checkMatch(fieldRef).size() == 0
-
- when:
- fieldRef = new Reference.Field(new Source[0], [Flag.STATIC] as Flag[], "protectedField", Type.getType("Ljava/lang/Object;"))
- then:
- unloadedA.checkMatch(fieldRef).size() == 1
-
- when:
- fieldRef = new Reference.Field(new Source[0], [Flag.PROTECTED_OR_HIGHER, Flag.STATIC] as Flag[], "staticB", Type.getType(MethodBodyAdvice.B))
- then:
- unloadedA.checkMatch(fieldRef).size() == 0
+ private static Set<Class> getMismatchClassSet(List<Reference.Mismatch> mismatches) {
+ final Set<Class> mismatchClasses = new HashSet<>(mismatches.size())
+ for (Reference.Mismatch mismatch : mismatches) {
+ mismatchClasses.add(mismatch.getClass())
+ }
+ return mismatchClasses
}
}

Replace UnloadedType with ByteBuddy TypeDescription