diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleVisitor.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleVisitor.java index 50966e040c..342a229f53 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleVisitor.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleVisitor.java @@ -229,29 +229,75 @@ public class MuzzleVisitor implements AsmVisitorWrapper { false); } for (Reference.Field field : references[i].getFields()) { - mv.visitLdcInsn(field.getName()); - mv.visitLdcInsn(field.getFlags().size()); - mv.visitTypeInsn( - Opcodes.ANEWARRAY, "datadog/trace/agent/tooling/muzzle/Reference$Flag"); + { // sources + mv.visitLdcInsn(field.getSources().size()); + mv.visitTypeInsn( + Opcodes.ANEWARRAY, "datadog/trace/agent/tooling/muzzle/Reference$Source"); - int j = 0; - for (Reference.Flag flag : field.getFlags()) { - mv.visitInsn(Opcodes.DUP); - mv.visitLdcInsn(j); - mv.visitFieldInsn( - Opcodes.GETSTATIC, - "datadog/trace/agent/tooling/muzzle/Reference$Flag", - flag.name(), - "Ldatadog/trace/agent/tooling/muzzle/Reference$Flag;"); - mv.visitInsn(Opcodes.AASTORE); - ++j; + int j = 0; + for (Reference.Source source : field.getSources()) { + mv.visitInsn(Opcodes.DUP); + mv.visitLdcInsn(j); + + mv.visitTypeInsn( + Opcodes.NEW, "datadog/trace/agent/tooling/muzzle/Reference$Source"); + mv.visitInsn(Opcodes.DUP); + mv.visitLdcInsn(source.getName()); + mv.visitLdcInsn(source.getLine()); + mv.visitMethodInsn( + Opcodes.INVOKESPECIAL, + "datadog/trace/agent/tooling/muzzle/Reference$Source", + "", + "(Ljava/lang/String;I)V", + false); + + mv.visitInsn(Opcodes.AASTORE); + ++j; + } + } + + { // flags + mv.visitLdcInsn(field.getFlags().size()); + mv.visitTypeInsn( + Opcodes.ANEWARRAY, "datadog/trace/agent/tooling/muzzle/Reference$Flag"); + + int j = 0; + for (Reference.Flag flag : field.getFlags()) { + mv.visitInsn(Opcodes.DUP); + mv.visitLdcInsn(j); + mv.visitFieldInsn( + Opcodes.GETSTATIC, + "datadog/trace/agent/tooling/muzzle/Reference$Flag", + flag.name(), + "Ldatadog/trace/agent/tooling/muzzle/Reference$Flag;"); + mv.visitInsn(Opcodes.AASTORE); + ++j; + } + } + + mv.visitLdcInsn(field.getName()); + + { // field type + mv.visitLdcInsn(field.getType().getDescriptor()); + mv.visitMethodInsn( + Opcodes.INVOKESTATIC, + Type.getInternalName(Type.class), + "getType", + Type.getMethodDescriptor(Type.class.getMethod("getType", String.class)), + false); } mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, "datadog/trace/agent/tooling/muzzle/Reference$Builder", "withField", - "(Ljava/lang/String;[Ldatadog/trace/agent/tooling/muzzle/Reference$Flag;)Ldatadog/trace/agent/tooling/muzzle/Reference$Builder;", + Type.getMethodDescriptor( + Reference.Builder.class.getMethod( + "withField", + Reference.Source[].class, + Reference.Flag[].class, + String.class, + Type.class)), false); } for (Reference.Method method : references[i].getMethods()) { diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/Reference.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/Reference.java index 83709e8edb..dd18d88d9e 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/Reference.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/Reference.java @@ -67,9 +67,7 @@ public class Reference { } /** - * Create a new reference which combines this reference with another reference. - * - *

Attempts to merge incompatible references will throw an IllegalStateException. + * Create a new reference which combines this reference with another reference of the same type. * * @param anotherReference A reference to the same class * @return a new Reference which merges the two references @@ -212,6 +210,24 @@ public class Reference { } } + public static class MissingField extends Mismatch { + private final String className; + private final String fieldName; + private final String fieldDesc; + + public MissingField(Source[] sources, String className, String fieldName, String fieldDesc) { + super(sources); + this.className = className; + this.fieldName = fieldName; + this.fieldDesc = fieldDesc; + } + + @Override + String getMismatchDetails() { + return "Missing field " + className + "#" + fieldName + fieldDesc; + } + } + public static class MissingMethod extends Mismatch { final String className; final String method; @@ -418,8 +434,7 @@ public class Reference { public Method merge(Method anotherMethod) { if (!this.equals(anotherMethod)) { - throw new IllegalStateException( - "Cannot merge incompatible methods " + this + " <> " + anotherMethod); + throw new IllegalStateException("illegal merge " + this + " != " + anotherMethod); } final Set mergedSources = new HashSet<>(); @@ -459,25 +474,45 @@ public class Reference { private final Set sources; private final Set flags; private final String name; + private final Type type; - public Field(Set sources, Set flags, String name) { - this.sources = sources; - this.flags = flags; + public Field(Source[] sources, Flag[] flags, String name, Type fieldType) { + this.sources = new HashSet<>(Arrays.asList(sources)); + this.flags = new HashSet<>(Arrays.asList(flags)); this.name = name; + this.type = fieldType; } public String getName() { return name; } + public Set getSources() { + return sources; + } + public Set getFlags() { return flags; } + public Type getType() { + return type; + } + public Field merge(Field anotherField) { - // TODO: implement - // also assert same class - return this; + if (!this.equals(anotherField) || (!type.equals(anotherField.type))) { + throw new IllegalStateException("illegal merge " + this + " != " + anotherField); + } + return new Field( + Reference.merge(sources, anotherField.sources).toArray(new Source[0]), + mergeFlags(flags, anotherField.flags).toArray(new Flag[0]), + name, + type); + } + + @Override + public String toString() { + return "FieldRef:" + name + type.getInternalName(); } @Override @@ -528,8 +563,15 @@ public class Reference { return this; } - public Builder withField(String fieldName, Flag... fieldFlags) { - // TODO + public Builder withField( + Source[] sources, Flag[] fieldFlags, String fieldName, Type fieldType) { + final Field field = new Field(sources, fieldFlags, fieldName, fieldType); + int existingIndex = fields.indexOf(field); + if (existingIndex == -1) { + fields.add(field); + } else { + fields.set(existingIndex, field.merge(fields.get(existingIndex))); + } return this; } diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceCreator.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceCreator.java index 016d09c0eb..f0d0172b23 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceCreator.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceCreator.java @@ -106,6 +106,24 @@ public class ReferenceCreator extends ClassVisitor { } } + /** + * Compute the minimum required access for FROM class to access a field on the TO class. + * + * @return A reference flag with the required level of access. + */ + private static Reference.Flag computeMinimumFieldAccess(Type from, Type to) { + if (from.getInternalName().equalsIgnoreCase(to.getInternalName())) { + return Reference.Flag.PRIVATE_OR_HIGHER; + } else if (internalPackageName(from.getInternalName()) + .equals(internalPackageName(to.getInternalName()))) { + return Reference.Flag.PACKAGE_OR_HIGHER; + } else { + // Additional references: check the type hierarchy of FROM to distinguish public from + // protected + return Reference.Flag.PROTECTED_OR_HIGHER; + } + } + /** * Compute the minimum required access for FROM class to access METHODTYPE on the TO class. * @@ -163,7 +181,10 @@ public class ReferenceCreator extends ClassVisitor { public FieldVisitor visitField( int access, String name, String descriptor, String signature, Object value) { // Additional references we could check - // - type of field + visible from this package + // - annotations on field + + // intentionally not creating refs to fields here. + // Will create refs in method instructions to include line numbers. return super.visitField(access, name, descriptor, signature, value); } @@ -197,25 +218,46 @@ public class ReferenceCreator extends ClassVisitor { public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { // Additional references we could check // * DONE owner class - // * owner class has a field (name) - // * field is static or non-static - // * field's visibility from this point (NON_PRIVATE?) - // * owner class's visibility from this point (NON_PRIVATE?) + // * DONE owner class has a field (name) + // * DONE field is static or non-static + // * DONE field's visibility from this point (NON_PRIVATE?) + // * DONE owner class's visibility from this point (NON_PRIVATE?) // // * DONE field-source class (descriptor) - // * field-source visibility from this point (PRIVATE?) + // * DONE field-source visibility from this point (PRIVATE?) + + final Type ownerType = Type.getType("L" + owner + ";"); + final Type fieldType = Type.getType(descriptor); + + List fieldFlags = new ArrayList<>(); + fieldFlags.add(computeMinimumFieldAccess(refSourceType, ownerType)); + fieldFlags.add( + opcode == Opcodes.GETSTATIC || opcode == Opcodes.PUTSTATIC + ? Reference.Flag.STATIC + : Reference.Flag.NON_STATIC); - // owning class has a field addReference( - new Reference.Builder(owner).withSource(refSourceClassName, currentLineNumber).build()); - Type fieldType = Type.getType(descriptor); - if (fieldType.getSort() == Type.ARRAY) { - fieldType = fieldType.getElementType(); + new Reference.Builder(ownerType.getInternalName()) + .withSource(refSourceClassName, currentLineNumber) + .withFlag(computeMinimumClassAccess(refSourceType, ownerType)) + .withField( + new Reference.Source[] { + new Reference.Source(refSourceClassName, currentLineNumber) + }, + fieldFlags.toArray(new Reference.Flag[0]), + name, + fieldType) + .build()); + + Type underlyingFieldType = fieldType; + while (underlyingFieldType.getSort() == Type.ARRAY) { + underlyingFieldType = underlyingFieldType.getElementType(); } - if (fieldType.getSort() == Type.OBJECT) { + if (underlyingFieldType.getSort() == Type.OBJECT) { addReference( - new Reference.Builder(fieldType.getInternalName()) + new Reference.Builder(underlyingFieldType.getInternalName()) .withSource(refSourceClassName, currentLineNumber) + .withFlag(computeMinimumClassAccess(refSourceType, underlyingFieldType)) .build()); } super.visitFieldInsn(opcode, owner, name, descriptor); @@ -241,7 +283,7 @@ public class ReferenceCreator extends ClassVisitor { { // ref for method return type Type returnType = methodType.getReturnType(); - if (returnType.getSort() == Type.ARRAY) { + while (returnType.getSort() == Type.ARRAY) { returnType = returnType.getElementType(); } if (returnType.getSort() == Type.OBJECT) { @@ -254,7 +296,7 @@ public class ReferenceCreator extends ClassVisitor { } // refs for method param types for (Type paramType : methodType.getArgumentTypes()) { - if (paramType.getSort() == Type.ARRAY) { + while (paramType.getSort() == Type.ARRAY) { paramType = paramType.getElementType(); } if (paramType.getSort() == Type.OBJECT) { @@ -267,9 +309,6 @@ public class ReferenceCreator extends ClassVisitor { } Type ownerType = Type.getType("L" + owner + ";"); - if (ownerType.getSort() == Type.ARRAY) { - ownerType = ownerType.getElementType(); - } final List methodFlags = new ArrayList<>(); methodFlags.add( diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceMatcher.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceMatcher.java index a53193d789..dcccbecd17 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceMatcher.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceMatcher.java @@ -19,6 +19,7 @@ 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; @@ -130,6 +131,7 @@ public class ReferenceMatcher { private final List unloadedInterfaces = new ArrayList<>(); private int flags; private final List methods = new ArrayList<>(); + private final List fields = new ArrayList<>(); public static UnloadedType of(String className, ClassLoader classLoader) throws Exception { className = Utils.getInternalName(className); @@ -189,23 +191,48 @@ public class ReferenceMatcher { return mismatches; } - public List checkMatch(Reference.Method method) { + public List checkMatch(Reference.Field fieldRef) { final List mismatches = new ArrayList<>(0); - // does the method exist? - Method unloadedMethod = findMethod(method, true); + final Field unloadedField = findField(fieldRef, true); + if (unloadedField == null) { + mismatches.add( + new Reference.Mismatch.MissingField( + fieldRef.getSources().toArray(new Reference.Source[0]), + className, + fieldRef.getName(), + fieldRef.getType().getInternalName())); + } else { + for (Reference.Flag flag : fieldRef.getFlags()) { + if (!flag.matches(unloadedField.getFlags())) { + final String desc = this.getClassName() + "#" + unloadedField.signature; + mismatches.add( + new Mismatch.MissingFlag( + fieldRef.getSources().toArray(new Source[0]), + desc, + flag, + unloadedField.getFlags())); + } + } + } + return mismatches; + } + + public List checkMatch(Reference.Method methodRef) { + final List mismatches = new ArrayList<>(0); + final Method unloadedMethod = findMethod(methodRef, true); if (unloadedMethod == null) { mismatches.add( new Reference.Mismatch.MissingMethod( - method.getSources().toArray(new Reference.Source[0]), + methodRef.getSources().toArray(new Reference.Source[0]), className, - method.toString())); + methodRef.toString())); } else { - for (Reference.Flag flag : method.getFlags()) { + for (Reference.Flag flag : methodRef.getFlags()) { if (!flag.matches(unloadedMethod.getFlags())) { final String desc = this.getClassName() + "#" + unloadedMethod.signature; mismatches.add( new Mismatch.MissingFlag( - method.getSources().toArray(new Source[0]), + methodRef.getSources().toArray(new Source[0]), desc, flag, unloadedMethod.getFlags())); @@ -243,10 +270,32 @@ public class ReferenceMatcher { return null; } - public boolean hasField(Reference.Field field) { - // TODO does the field exist? - // TODO are the expected field flags present (static, public, etc) - throw new RuntimeException("TODO"); + 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; + } + } else { + Field superField = null; + if (unloadedSuper != null) { + superField = unloadedSuper.findField(fieldRef, false); + if (superField != null) { + return superField; + } + } + for (UnloadedType unloadedInterface : unloadedInterfaces) { + superField = unloadedInterface.findField(fieldRef, false); + if (superField != null) { + return superField; + } + } + } + return null; } @Override @@ -264,6 +313,13 @@ public class ReferenceMatcher { 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, @@ -271,8 +327,6 @@ public class ReferenceMatcher { final String descriptor, final String signature, final String[] exceptions) { - // Additional references we could check - // - Classes in signature (return type, params) and visible from this package methods.add(new Method(access, name, descriptor)); return super.visitMethod(access, name, descriptor, signature, exceptions); } @@ -304,7 +358,45 @@ public class ReferenceMatcher { @Override public boolean equals(Object o) { if (o instanceof Method) { - return signature.toString().equals(((Method) o).signature); + 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; + } + + @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); } return false; } diff --git a/dd-java-agent/testing/src/test/groovy/muzzle/ReferenceCreatorTest.groovy b/dd-java-agent/testing/src/test/groovy/muzzle/ReferenceCreatorTest.groovy index d191f311a2..41a5fb5450 100644 --- a/dd-java-agent/testing/src/test/groovy/muzzle/ReferenceCreatorTest.groovy +++ b/dd-java-agent/testing/src/test/groovy/muzzle/ReferenceCreatorTest.groovy @@ -27,6 +27,7 @@ class ReferenceCreatorTest extends AgentTestRunner { references.get('java.lang.Object').getFlags().contains(Reference.Flag.PUBLIC) references.get('muzzle.TestClasses$MethodBodyAdvice$B').getFlags().contains(Reference.Flag.PACKAGE_OR_HIGHER) + // method refs Set bMethods = references.get('muzzle.TestClasses$MethodBodyAdvice$B').getMethods() findMethod(bMethods, "aMethod", "(Ljava/lang/String;)Ljava/lang/String;") != null findMethod(bMethods, "aMethodWithPrimitives", "(Z)V") != null @@ -35,6 +36,15 @@ class ReferenceCreatorTest extends AgentTestRunner { findMethod(bMethods, "aMethod", "(Ljava/lang/String;)Ljava/lang/String;").getFlags().contains(Reference.Flag.NON_STATIC) findMethod(bMethods, "aStaticMethod", "()V").getFlags().contains(Reference.Flag.STATIC) + + // field refs + references.get('muzzle.TestClasses$MethodBodyAdvice$B').getFields().isEmpty() + Set aFieldRefs = references.get('muzzle.TestClasses$MethodBodyAdvice$A').getFields() + findField(aFieldRefs, "b").getFlags().contains(Reference.Flag.PACKAGE_OR_HIGHER) + findField(aFieldRefs, "b").getFlags().contains(Reference.Flag.NON_STATIC) + findField(aFieldRefs, "staticB").getFlags().contains(Reference.Flag.PACKAGE_OR_HIGHER) + findField(aFieldRefs, "staticB").getFlags().contains(Reference.Flag.STATIC) + aFieldRefs.size() == 2 } def "protected ref test"() { @@ -47,7 +57,7 @@ class ReferenceCreatorTest extends AgentTestRunner { findMethod(bMethods, "protectedMethod", "()V").getFlags().contains(Reference.Flag.PROTECTED_OR_HIGHER) } - private static findMethod(Set methods, String methodName, String methodDesc) { + private static Reference.Method findMethod(Set methods, String methodName, String methodDesc) { for (Reference.Method method : methods) { if (method == new Reference.Method(methodName, methodDesc)) { return method @@ -55,4 +65,13 @@ class ReferenceCreatorTest extends AgentTestRunner { } return null } + + private static Reference.Field findField(Set fields, String fieldName) { + for (Reference.Field field : fields) { + if (field.getName().equals(fieldName)) { + return field + } + } + return null + } } diff --git a/dd-java-agent/testing/src/test/groovy/muzzle/ReferenceMatcherTest.groovy b/dd-java-agent/testing/src/test/groovy/muzzle/ReferenceMatcherTest.groovy index f89d4f7542..168554b35e 100644 --- a/dd-java-agent/testing/src/test/groovy/muzzle/ReferenceMatcherTest.groovy +++ b/dd-java-agent/testing/src/test/groovy/muzzle/ReferenceMatcherTest.groovy @@ -4,7 +4,6 @@ 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.Method import datadog.trace.agent.tooling.muzzle.Reference.Source import datadog.trace.agent.tooling.muzzle.Reference.Flag import datadog.trace.agent.tooling.muzzle.ReferenceCreator @@ -69,48 +68,87 @@ class ReferenceMatcherTest extends AgentTestRunner { 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()) - Method methodRef + Reference.Method methodRef // match method declared in the class when: - methodRef = new Method("aMethod", "(Ljava/lang/String;)Ljava/lang/String;") + 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 Method("hashCode", "()I") + methodRef = new Reference.Method("hashCode", "()I") then: unloadedB.checkMatch(methodRef).size() == 0 // match method declared in interface when: - methodRef = new Method("someMethod", "()V") + methodRef = new Reference.Method("someMethod", "()V") then: unloadedInterface.checkMatch(methodRef).size() == 0 // match private method in the class when: - methodRef = new Method("privateStuff", "()V") + methodRef = new Reference.Method("privateStuff", "()V") then: unloadedB.checkMatch(methodRef).size() == 0 // fail to match private method in superclass when: - methodRef = new Method("privateStuff", "()V") + methodRef = new Reference.Method("privateStuff", "()V") then: unloadedB2.checkMatch(methodRef).size() == 1 // static method flag mismatch when: - methodRef = new Method(new Source[0], [Flag.NON_STATIC] as Flag[], "aStaticMethod", Type.getType("V")) + 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 Method(new Source[0], new Flag[0], "missingTestMethod", Type.VOID_TYPE, new Type[0]) + methodRef = new Reference.Method(new Source[0], new Flag[0], "missingTestMethod", Type.VOID_TYPE, new Type[0]) then: unloadedB.checkMatch(methodRef).size() == 1 } + + 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 + } } diff --git a/dd-java-agent/testing/src/test/java/muzzle/TestClasses.java b/dd-java-agent/testing/src/test/java/muzzle/TestClasses.java index 41b19ba705..bb033be755 100644 --- a/dd-java-agent/testing/src/test/java/muzzle/TestClasses.java +++ b/dd-java-agent/testing/src/test/java/muzzle/TestClasses.java @@ -14,10 +14,14 @@ public class TestClasses { a.b.aMethodWithPrimitives(false); a.b.aMethodWithArrays(new String[0]); B.aStaticMethod(); + A.staticB.aMethod("bar"); } public static class A { public B b = new B(); + protected Object protectedField = null; + private Object privateField = null; + public static B staticB = new B(); } public static class B { @@ -45,6 +49,8 @@ public class TestClasses { } } + public static class A2 extends A {} + public interface SomeInterface { void someMethod(); }