From 514c009a08f5d34c009e808dbda8a3ae11eee357 Mon Sep 17 00:00:00 2001 From: jean-philippe bempel Date: Mon, 30 Oct 2023 07:35:59 +0100 Subject: [PATCH 1/2] Add Language Specifics in Symbol Extraction LanguageSpecifics add specific information to symbol/scopes for Classes, methods and fields. It's relate to - access modifiers - annotations - super class - interfaces - return type --- .../debugger/symbol/LanguageSpecifics.java | 144 ++++++++++ .../com/datadog/debugger/symbol/Scope.java | 10 +- .../com/datadog/debugger/symbol/Symbol.java | 17 +- .../debugger/symbol/SymbolExtractor.java | 253 ++++++++++++++++-- .../SymbolExtractionTransformerTest.java | 160 +++++++++++ .../debugger/symbol/SymbolExtraction13.java | 39 +++ .../debugger/symbol/SymbolExtraction14.java | 36 +++ 7 files changed, 624 insertions(+), 35 deletions(-) create mode 100644 dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/LanguageSpecifics.java create mode 100644 dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/symbol/SymbolExtraction13.java create mode 100644 dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/symbol/SymbolExtraction14.java diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/LanguageSpecifics.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/LanguageSpecifics.java new file mode 100644 index 00000000000..78599d993d1 --- /dev/null +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/LanguageSpecifics.java @@ -0,0 +1,144 @@ +package com.datadog.debugger.symbol; + +import com.datadog.debugger.agent.Generated; +import com.squareup.moshi.Json; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +public class LanguageSpecifics { + @Json(name = "access_modifiers") + private final List accessModifiers; + + private final List annotations; + + @Json(name = "super_class") + private final String superClass; + + private final List interfaces; + + @Json(name = "return_type") + private final String returnType; + + public LanguageSpecifics( + List accessModifiers, + List annotations, + String superClass, + List interfaces, + String returnType) { + this.accessModifiers = accessModifiers; + this.annotations = annotations; + this.superClass = superClass; + this.interfaces = interfaces; + this.returnType = returnType; + } + + public List getAccessModifiers() { + return accessModifiers; + } + + public List getAnnotations() { + return annotations; + } + + public String getSuperClass() { + return superClass; + } + + public List getInterfaces() { + return interfaces; + } + + public String getReturnType() { + return returnType; + } + + @Generated + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LanguageSpecifics that = (LanguageSpecifics) o; + return Objects.equals(accessModifiers, that.accessModifiers) + && Objects.equals(annotations, that.annotations) + && Objects.equals(superClass, that.superClass) + && Objects.equals(interfaces, that.interfaces) + && Objects.equals(returnType, that.returnType); + } + + @Generated + @Override + public int hashCode() { + return Objects.hash(accessModifiers, annotations, superClass, interfaces, returnType); + } + + @Generated + @Override + public String toString() { + return "LanguageSpecifics{" + + "accessModifiers=" + + accessModifiers + + ", annotations=" + + annotations + + ", superClass='" + + superClass + + '\'' + + ", interfaces=" + + interfaces + + ", returnType='" + + returnType + + '\'' + + '}'; + } + + public static class Builder { + private List accessModifiers; + private List annotations; + private String superClass; + private List interfaces; + private String returnType; + + public Builder addModifiers(Collection modifiers) { + if (modifiers == null || modifiers.isEmpty()) { + this.accessModifiers = null; + return this; + } + accessModifiers = new ArrayList<>(modifiers); + return this; + } + + public Builder addAnnotations(Collection annotations) { + if (annotations == null || annotations.isEmpty()) { + this.annotations = null; + return this; + } + this.annotations = new ArrayList<>(annotations); + return this; + } + + public Builder superClass(String superClass) { + this.superClass = superClass; + return this; + } + + public Builder addInterfaces(Collection interfaces) { + if (interfaces == null || interfaces.isEmpty()) { + this.interfaces = null; + return this; + } + this.interfaces = new ArrayList<>(interfaces); + return this; + } + + public Builder returnType(String returnType) { + this.returnType = returnType; + return this; + } + + public LanguageSpecifics build() { + return new LanguageSpecifics( + accessModifiers, annotations, superClass, interfaces, returnType); + } + } +} diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/Scope.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/Scope.java index 4963e687959..bd0012bfb03 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/Scope.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/Scope.java @@ -19,7 +19,7 @@ public class Scope { private final String name; @Json(name = "language_specifics") - private final List languageSpecifics; + private final LanguageSpecifics languageSpecifics; private final List symbols; private final List scopes; @@ -30,7 +30,7 @@ public Scope( int startLine, int endLine, String name, - List languageSpecifics, + LanguageSpecifics languageSpecifics, List symbols, List scopes) { this.scopeType = scopeType; @@ -63,7 +63,7 @@ public String getName() { return name; } - public List getLanguageSpecifics() { + public LanguageSpecifics getLanguageSpecifics() { return languageSpecifics; } @@ -110,7 +110,7 @@ public static class Builder { private final int startLine; private final int endLine; private String name; - private List languageSpecifics; + private LanguageSpecifics languageSpecifics; private List symbols; private List scopes; @@ -126,7 +126,7 @@ public Builder name(String name) { return this; } - public Builder languageSpecifics(List languageSpecifics) { + public Builder languageSpecifics(LanguageSpecifics languageSpecifics) { this.languageSpecifics = languageSpecifics; return this; } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/Symbol.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/Symbol.java index 55380f6c916..66deef6ee9f 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/Symbol.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/Symbol.java @@ -10,11 +10,20 @@ public class Symbol { private final int line; private final String type; - public Symbol(SymbolType symbolType, String name, int line, String type) { + @Json(name = "language_specifics") + private final LanguageSpecifics languageSpecifics; + + public Symbol( + SymbolType symbolType, + String name, + int line, + String type, + LanguageSpecifics languageSpecifics) { this.symbolType = symbolType; this.name = name; this.line = line; this.type = type; + this.languageSpecifics = languageSpecifics; } public SymbolType getSymbolType() { @@ -33,6 +42,10 @@ public String getType() { return type; } + public LanguageSpecifics getLanguageSpecifics() { + return languageSpecifics; + } + @Override public String toString() { return "Symbol{" @@ -46,6 +59,8 @@ public String toString() { + ", type='" + type + '\'' + + ", languageSpecifics=" + + languageSpecifics + '}'; } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymbolExtractor.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymbolExtractor.java index 3498234820e..f5fadff6ce0 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymbolExtractor.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymbolExtractor.java @@ -7,16 +7,19 @@ import com.datadog.debugger.instrumentation.ASMHelper; import datadog.trace.util.Strings; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.LabelNode; @@ -35,44 +38,27 @@ public static Scope extract(byte[] classFileBuffer, String jarName) { private static Scope extractScopes(ClassNode classNode, String jarName) { try { String sourceFile = extractSourceFile(classNode); - List methodScopes = new ArrayList<>(); - for (MethodNode method : classNode.methods) { - MethodLineInfo methodLineInfo = extractMethodLineInfo(method); - List varScopes = new ArrayList<>(); - List methodSymbols = new ArrayList<>(); - int localVarBaseSlot = extractArgs(method, methodSymbols, methodLineInfo.start); - extractScopesFromVariables( - sourceFile, method, methodLineInfo.lineMap, varScopes, localVarBaseSlot); - ScopeType methodScopeType = ScopeType.METHOD; - if (method.name.startsWith("lambda$")) { - methodScopeType = ScopeType.CLOSURE; - } - Scope methodScope = - Scope.builder(methodScopeType, sourceFile, methodLineInfo.start, methodLineInfo.end) - .name(method.name) - .scopes(varScopes) - .symbols(methodSymbols) - .build(); - methodScopes.add(methodScope); - } + List methodScopes = extractMethods(classNode, sourceFile); int classStartLine = Integer.MAX_VALUE; int classEndLine = 0; for (Scope scope : methodScopes) { classStartLine = Math.min(classStartLine, scope.getStartLine()); classEndLine = Math.max(classEndLine, scope.getEndLine()); } - List fields = new ArrayList<>(); - for (FieldNode fieldNode : classNode.fields) { - SymbolType symbolType = - ASMHelper.isStaticField(fieldNode) ? SymbolType.STATIC_FIELD : SymbolType.FIELD; - fields.add( - new Symbol(symbolType, fieldNode.name, 0, Type.getType(fieldNode.desc).getClassName())); - } + List fields = extractFields(classNode); + LanguageSpecifics classSpecifics = + new LanguageSpecifics.Builder() + .addModifiers(extractClassModifiers(classNode.access)) + .addInterfaces(extractInterfaces(classNode)) + .addAnnotations(extractAnnotations(classNode.visibleAnnotations)) + .superClass(Strings.getClassName(classNode.superName)) + .build(); Scope classScope = Scope.builder(ScopeType.CLASS, sourceFile, classStartLine, classEndLine) .name(Strings.getClassName(classNode.name)) .scopes(methodScopes) .symbols(fields) + .languageSpecifics(classSpecifics) .build(); return Scope.builder(ScopeType.JAR, jarName, 0, 0) .name(jarName) @@ -84,6 +70,214 @@ private static Scope extractScopes(ClassNode classNode, String jarName) { } } + private static Collection extractInterfaces(ClassNode classNode) { + if (classNode.interfaces.isEmpty()) { + return Collections.emptyList(); + } + return classNode.interfaces.stream().map(Strings::getClassName).collect(Collectors.toList()); + } + + private static List extractFields(ClassNode classNode) { + List fields = new ArrayList<>(); + for (FieldNode fieldNode : classNode.fields) { + SymbolType symbolType = + ASMHelper.isStaticField(fieldNode) ? SymbolType.STATIC_FIELD : SymbolType.FIELD; + LanguageSpecifics fieldSpecifics = + new LanguageSpecifics.Builder() + .addModifiers(extractFieldModifiers(fieldNode.access)) + .addAnnotations(extractAnnotations(fieldNode.visibleAnnotations)) + .build(); + fields.add( + new Symbol( + symbolType, + fieldNode.name, + 0, + Type.getType(fieldNode.desc).getClassName(), + fieldSpecifics)); + } + return fields; + } + + private static List extractMethods(ClassNode classNode, String sourceFile) { + List methodScopes = new ArrayList<>(); + for (MethodNode method : classNode.methods) { + MethodLineInfo methodLineInfo = extractMethodLineInfo(method); + List varScopes = new ArrayList<>(); + List methodSymbols = new ArrayList<>(); + int localVarBaseSlot = extractArgs(method, methodSymbols, methodLineInfo.start); + extractScopesFromVariables( + sourceFile, method, methodLineInfo.lineMap, varScopes, localVarBaseSlot); + ScopeType methodScopeType = ScopeType.METHOD; + if (method.name.startsWith("lambda$")) { + methodScopeType = ScopeType.CLOSURE; + } + LanguageSpecifics methodSpecifics = + new LanguageSpecifics.Builder() + .addModifiers(extractMethodModifiers(classNode, method, method.access)) + .addAnnotations(extractAnnotations(method.visibleAnnotations)) + .returnType(Type.getType(method.desc).getReturnType().getClassName()) + .build(); + Scope methodScope = + Scope.builder(methodScopeType, sourceFile, methodLineInfo.start, methodLineInfo.end) + .name(method.name) + .scopes(varScopes) + .symbols(methodSymbols) + .languageSpecifics(methodSpecifics) + .build(); + methodScopes.add(methodScope); + } + return methodScopes; + } + + private static Collection extractClassModifiers(int access) { + List results = new ArrayList<>(); + for (int remaining = access, bit; remaining != 0; remaining -= bit) { + bit = Integer.lowestOneBit(remaining); + switch (bit) { + case Opcodes.ACC_PUBLIC: + results.add("public"); + break; + case Opcodes.ACC_PRIVATE: + results.add("private"); + break; + case Opcodes.ACC_PROTECTED: + results.add("protected"); + break; + case Opcodes.ACC_STATIC: + results.add("static"); + break; + case Opcodes.ACC_FINAL: + results.add("final"); + break; + case Opcodes.ACC_SUPER: + break; // not interesting + case Opcodes.ACC_INTERFACE: + results.add("interface"); + break; + case Opcodes.ACC_ABSTRACT: + results.add("abstract"); + break; + case Opcodes.ACC_SYNTHETIC: + results.add("synthetic"); + break; + case Opcodes.ACC_ANNOTATION: + results.add("annotation"); + break; + case Opcodes.ACC_ENUM: + results.add("enum"); + break; + default: + throw new IllegalArgumentException("Invalid access modifiers: " + bit); + } + } + return results; + } + + private static Collection extractMethodModifiers( + ClassNode classNode, MethodNode methodNode, int access) { + List results = new ArrayList<>(); + for (int remaining = access, bit; remaining != 0; remaining -= bit) { + bit = Integer.lowestOneBit(remaining); + switch (bit) { + case Opcodes.ACC_PUBLIC: + results.add("public"); + break; + case Opcodes.ACC_PRIVATE: + results.add("private"); + break; + case Opcodes.ACC_PROTECTED: + results.add("protected"); + break; + case Opcodes.ACC_STATIC: + results.add("static"); + break; + case Opcodes.ACC_FINAL: + results.add("final"); + break; + case Opcodes.ACC_SYNCHRONIZED: + results.add("synchronized"); + break; + case Opcodes.ACC_BRIDGE: + results.add("(bridge)"); + break; + case Opcodes.ACC_VARARGS: + results.add("(varargs)"); + break; + case Opcodes.ACC_NATIVE: + results.add("native"); + break; + case Opcodes.ACC_ABSTRACT: + results.add("abstract"); + break; + case Opcodes.ACC_STRICT: + results.add("strictfp"); + break; + case Opcodes.ACC_SYNTHETIC: + results.add("synthetic"); + break; + default: + throw new IllegalArgumentException("Invalid access modifiers: " + bit); + } + } + // if class is an interface && method as code this is a default method + if ((classNode.access & Opcodes.ACC_INTERFACE) > 0 && methodNode.instructions.size() > 0) { + results.add("default"); + } + return results; + } + + private static Collection extractFieldModifiers(int access) { + List results = new ArrayList<>(); + for (int remaining = access, bit; remaining != 0; remaining -= bit) { + bit = Integer.lowestOneBit(remaining); + switch (bit) { + case Opcodes.ACC_PUBLIC: + results.add("public"); + break; + case Opcodes.ACC_PRIVATE: + results.add("private"); + break; + case Opcodes.ACC_PROTECTED: + results.add("protected"); + break; + case Opcodes.ACC_STATIC: + results.add("static"); + break; + case Opcodes.ACC_FINAL: + results.add("final"); + break; + case Opcodes.ACC_VOLATILE: + results.add("volatile"); + break; + case Opcodes.ACC_TRANSIENT: + results.add("transient"); + break; + case Opcodes.ACC_SYNTHETIC: + results.add("synthetic"); + break; + case Opcodes.ACC_ENUM: + results.add("enum"); + break; + default: + throw new IllegalArgumentException("Invalid access modifiers: " + bit); + } + } + return results; + } + + private static Collection extractAnnotations(List annotationNodes) { + if (annotationNodes == null || annotationNodes.isEmpty()) { + return Collections.emptyList(); + } + List results = new ArrayList<>(); + for (AnnotationNode annotationNode : annotationNodes) { + StringBuilder sb = new StringBuilder("@"); + sb.append(Type.getType(annotationNode.desc).getClassName()); + results.add(sb.toString()); + } + return results; + } + private static String extractSourceFile(ClassNode classNode) { String packageName = classNode.name; int idx = packageName.lastIndexOf('/'); @@ -111,7 +305,7 @@ private static int extractArgs( } String argName = localVarsBySlot[slot] != null ? localVarsBySlot[slot].name : "p" + slot; methodSymbols.add( - new Symbol(SymbolType.ARG, argName, methodStartLine, argType.getClassName())); + new Symbol(SymbolType.ARG, argName, methodStartLine, argType.getClassName(), null)); slot += argType.getSize(); } return slot; @@ -149,7 +343,8 @@ private static void extractScopesFromVariables( int line = monotonicLineMap.get(var.start.getLabel()); minLine = Math.min(line, minLine); varSymbols.add( - new Symbol(SymbolType.LOCAL, var.name, line, Type.getType(var.desc).getClassName())); + new Symbol( + SymbolType.LOCAL, var.name, line, Type.getType(var.desc).getClassName(), null)); } int endLine = monotonicLineMap.get(entry.getKey().getLabel()); Scope varScope = diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/symbol/SymbolExtractionTransformerTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/symbol/SymbolExtractionTransformerTest.java index 990efce48e6..8a474c5c455 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/symbol/SymbolExtractionTransformerTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/symbol/SymbolExtractionTransformerTest.java @@ -1,7 +1,9 @@ package com.datadog.debugger.symbol; +import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.when; import static utils.InstrumentationTestHelper.compileAndLoadClass; @@ -683,6 +685,164 @@ public void symbolExtraction12() throws IOException, URISyntaxException { 11); } + @Test + public void symbolExtraction13() throws IOException, URISyntaxException { + final String CLASS_NAME = SYMBOL_PACKAGE + "SymbolExtraction13"; + final String SOURCE_FILE = SYMBOL_PACKAGE_DIR + "SymbolExtraction13.java"; + SymbolSinkMock symbolSinkMock = new SymbolSinkMock(config); + SymbolExtractionTransformer transformer = + new SymbolExtractionTransformer(symbolSinkMock, config); + instr.addTransformer(transformer); + Class testClass = compileAndLoadClass(CLASS_NAME); + Reflect.on(testClass).call("main", "1").get(); + Scope classScope = symbolSinkMock.jarScopes.get(0).getScopes().get(0); + assertLangSpecifics( + classScope.getLanguageSpecifics(), + asList("public"), + asList( + "@com.datadog.debugger.symbol.MyAnnotation", "@com.datadog.debugger.symbol.MyMarker"), + Object.class.getTypeName(), + null, + null); + Scope mainMethodScope = classScope.getScopes().get(1); + assertLangSpecifics( + mainMethodScope.getLanguageSpecifics(), + asList("public", "static"), + asList("@com.datadog.debugger.symbol.MyAnnotation"), + null, + null, + Integer.TYPE.getTypeName()); + assertEquals(3, classScope.getSymbols().size()); + Symbol intField = classScope.getSymbols().get(0); + assertLangSpecifics( + intField.getLanguageSpecifics(), + asList("private"), + asList("@com.datadog.debugger.symbol.MyAnnotation"), + null, + null, + null); + Scope myAnnotationClassScope = symbolSinkMock.jarScopes.get(1).getScopes().get(0); + assertLangSpecifics( + myAnnotationClassScope.getLanguageSpecifics(), + asList("interface", "abstract", "annotation"), + asList("@java.lang.annotation.Target", "@java.lang.annotation.Retention"), + Object.class.getTypeName(), + asList("java.lang.annotation.Annotation"), + null); + } + + @Test + public void symbolExtraction14() throws IOException, URISyntaxException { + final String CLASS_NAME = SYMBOL_PACKAGE + "SymbolExtraction14"; + final String SOURCE_FILE = SYMBOL_PACKAGE_DIR + "SymbolExtraction14.java"; + SymbolSinkMock symbolSinkMock = new SymbolSinkMock(config); + SymbolExtractionTransformer transformer = + new SymbolExtractionTransformer(symbolSinkMock, config); + instr.addTransformer(transformer); + Class testClass = compileAndLoadClass(CLASS_NAME); + Reflect.on(testClass).call("main", "1").get(); + Scope classScope = symbolSinkMock.jarScopes.get(0).getScopes().get(0); + assertLangSpecifics( + classScope.getLanguageSpecifics(), + asList("public", "abstract"), + null, + Object.class.getTypeName(), + asList("com.datadog.debugger.symbol.I1", "com.datadog.debugger.symbol.I2"), + null); + assertEquals(4, classScope.getScopes().size()); + Scope m1MethodScope = classScope.getScopes().get(2); + assertLangSpecifics( + m1MethodScope.getLanguageSpecifics(), + asList("protected", "abstract"), + null, + null, + null, + Void.TYPE.getTypeName()); + Scope m2MethodScope = classScope.getScopes().get(3); + assertLangSpecifics( + m2MethodScope.getLanguageSpecifics(), + asList("private", "final", "synchronized", "(varargs)", "strictfp"), + null, + null, + null, + String.class.getTypeName()); + Scope i1ClassScope = symbolSinkMock.jarScopes.get(1).getScopes().get(0); + assertLangSpecifics( + i1ClassScope.getLanguageSpecifics(), + asList("interface", "abstract"), + null, + Object.class.getTypeName(), + null, + null); + Scope m3MethodScope = i1ClassScope.getScopes().get(0); + assertLangSpecifics( + m3MethodScope.getLanguageSpecifics(), + asList("public", "default"), + null, + null, + null, + Void.TYPE.getTypeName()); + Scope myEnumClassScope = symbolSinkMock.jarScopes.get(3).getScopes().get(0); + assertLangSpecifics( + myEnumClassScope.getLanguageSpecifics(), + asList("final", "enum"), + null, + Enum.class.getTypeName(), + null, + null); + assertEquals(4, myEnumClassScope.getSymbols().size()); + Symbol oneField = myEnumClassScope.getSymbols().get(0); + assertLangSpecifics( + oneField.getLanguageSpecifics(), + asList("public", "static", "final", "enum"), + null, + null, + null, + null); + Symbol valuesField = myEnumClassScope.getSymbols().get(3); + assertLangSpecifics( + valuesField.getLanguageSpecifics(), + asList("private", "static", "final", "synthetic"), + null, + null, + null, + null); + } + + private void assertLangSpecifics( + LanguageSpecifics languageSpecifics, + List expectedModifiers, + List expectedAnnotations, + String expectedSuperClass, + List expectedInterfaces, + String expectedReturnType) { + if (expectedModifiers == null) { + assertNull(languageSpecifics.getAccessModifiers()); + } else { + assertEquals(expectedModifiers, languageSpecifics.getAccessModifiers()); + } + if (expectedAnnotations == null) { + assertNull(languageSpecifics.getAnnotations()); + } else { + assertEquals(expectedAnnotations, languageSpecifics.getAnnotations()); + } + if (expectedSuperClass == null) { + assertNull(languageSpecifics.getSuperClass()); + } else { + assertEquals(expectedSuperClass, languageSpecifics.getSuperClass()); + } + if (expectedInterfaces == null) { + assertNull(languageSpecifics.getInterfaces()); + } else { + assertEquals(expectedInterfaces, languageSpecifics.getInterfaces()); + } + if (expectedReturnType == null) { + assertNull(languageSpecifics.getReturnType()); + } else { + assertEquals(expectedReturnType, languageSpecifics.getReturnType()); + } + } + private static void assertScope( Scope scope, ScopeType scopeType, diff --git a/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/symbol/SymbolExtraction13.java b/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/symbol/SymbolExtraction13.java new file mode 100644 index 00000000000..6cd268066ef --- /dev/null +++ b/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/symbol/SymbolExtraction13.java @@ -0,0 +1,39 @@ +package com.datadog.debugger.symbol; + +import org.junit.jupiter.api.Test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@MyAnnotation("class") +@MyMarker +public class SymbolExtraction13 { + + @MyAnnotation("field") + private int intField; + public static volatile String strField; + protected final transient double doubleField = 3.14; + + @MyAnnotation("method") + public static int main(String arg) { + System.out.println(MyAnnotation.class); + return 42; + } + + private static class InnerClass { + + } +} + +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@interface MyAnnotation { + String value() default ""; +} + +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@interface MyMarker { +} diff --git a/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/symbol/SymbolExtraction14.java b/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/symbol/SymbolExtraction14.java new file mode 100644 index 00000000000..417867c3659 --- /dev/null +++ b/dd-java-agent/agent-debugger/src/test/resources/com/datadog/debugger/symbol/SymbolExtraction14.java @@ -0,0 +1,36 @@ +package com.datadog.debugger.symbol; + +import org.junit.jupiter.api.Test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public abstract class SymbolExtraction14 extends Object implements I1, I2{ + + public static int main(String arg) { + System.out.println(MyEnum.ONE); + return 42; + } + + protected abstract void m1(); + private strictfp synchronized final String m2(String... strVarArgs) { + return null; + } + +} + +interface I1 { + default void m3(){} +} + +interface I2 { + +} + +enum MyEnum { + ONE, + TWO, + THREE +} From f01ed6ffbb3205022b8d14f8c8c10ae2899448a2 Mon Sep 17 00:00:00 2001 From: jean-philippe bempel Date: Tue, 31 Oct 2023 15:26:43 +0100 Subject: [PATCH 2/2] add missing asserts --- .../SymbolExtractionTransformerTest.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/symbol/SymbolExtractionTransformerTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/symbol/SymbolExtractionTransformerTest.java index 8a474c5c455..b3fda99615f 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/symbol/SymbolExtractionTransformerTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/symbol/SymbolExtractionTransformerTest.java @@ -688,7 +688,6 @@ public void symbolExtraction12() throws IOException, URISyntaxException { @Test public void symbolExtraction13() throws IOException, URISyntaxException { final String CLASS_NAME = SYMBOL_PACKAGE + "SymbolExtraction13"; - final String SOURCE_FILE = SYMBOL_PACKAGE_DIR + "SymbolExtraction13.java"; SymbolSinkMock symbolSinkMock = new SymbolSinkMock(config); SymbolExtractionTransformer transformer = new SymbolExtractionTransformer(symbolSinkMock, config); @@ -729,12 +728,27 @@ public void symbolExtraction13() throws IOException, URISyntaxException { Object.class.getTypeName(), asList("java.lang.annotation.Annotation"), null); + Symbol strField = classScope.getSymbols().get(1); + assertLangSpecifics( + strField.getLanguageSpecifics(), + asList("public", "static", "volatile"), + null, + null, + null, + null); + Symbol doubleField = classScope.getSymbols().get(2); + assertLangSpecifics( + doubleField.getLanguageSpecifics(), + asList("protected", "final", "transient"), + null, + null, + null, + null); } @Test public void symbolExtraction14() throws IOException, URISyntaxException { final String CLASS_NAME = SYMBOL_PACKAGE + "SymbolExtraction14"; - final String SOURCE_FILE = SYMBOL_PACKAGE_DIR + "SymbolExtraction14.java"; SymbolSinkMock symbolSinkMock = new SymbolSinkMock(config); SymbolExtractionTransformer transformer = new SymbolExtractionTransformer(symbolSinkMock, config);