Skip to content

Commit 666b4a0

Browse files
authored
Keep generic signature aligned when adding TaskWrapper interface (#9794)
* When adding `TaskWrapper` to the declared interfaces, make sure we also add it to the generic signature * Add test to verify `TaskWrapper` transformation keeps both declared and generic interfaces in sync
1 parent 5d03203 commit 666b4a0

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/profiling/UnwrappingVisitor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public ClassVisitor wrap(
5656
classVisitor, instrumentedType.getInternalName(), fieldName);
5757
}
5858

59-
private static class ImplementTaskWrapperClassVisitor extends ClassVisitor {
59+
static class ImplementTaskWrapperClassVisitor extends ClassVisitor {
6060

6161
private static final String TASK_WRAPPER =
6262
"datadog/trace/bootstrap/instrumentation/api/TaskWrapper";
@@ -83,6 +83,9 @@ public void visit(
8383
String[] interfaces) {
8484
if (interfaces == null || !Arrays.asList(interfaces).contains(TASK_WRAPPER)) {
8585
interfaces = append(interfaces, TASK_WRAPPER);
86+
if (signature != null) {
87+
signature += 'L' + TASK_WRAPPER + ';';
88+
}
8689
modify = true;
8790
}
8891
super.visit(version, access, name, signature, superName, interfaces);
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package datadog.trace.agent.tooling.bytebuddy.profiling
2+
3+
import static net.bytebuddy.utility.OpenedClassReader.ASM_API
4+
5+
import java.util.concurrent.FutureTask
6+
import net.bytebuddy.jar.asm.ClassReader
7+
import net.bytebuddy.jar.asm.ClassVisitor
8+
import net.bytebuddy.jar.asm.ClassWriter
9+
import net.bytebuddy.jar.asm.signature.SignatureReader
10+
import net.bytebuddy.jar.asm.signature.SignatureVisitor
11+
import org.apache.commons.io.IOUtils
12+
import spock.lang.Specification
13+
14+
class UnwrappingVisitorTest extends Specification {
15+
16+
void 'test generic signature is kept in sync'(){
17+
setup:
18+
def classFile = readClassBytes(FutureTask)
19+
def classReader = new ClassReader(classFile)
20+
def classWriter = new ClassWriter(classReader, 0)
21+
22+
def declaredInterfaces = []
23+
def genericInterfaces = []
24+
25+
// visitor that captures interface types defined inside a generic type signature
26+
def signatureVisitor = new SignatureVisitor(ASM_API) {
27+
@Override
28+
SignatureVisitor visitInterface() {
29+
return new SignatureVisitor(ASM_API) {
30+
@Override
31+
void visitClassType(String name) {
32+
genericInterfaces += name
33+
}
34+
}
35+
}
36+
}
37+
38+
// visitor that captures declared and generic interface types
39+
def classVisitor = new ClassVisitor(ASM_API, classWriter) {
40+
@Override
41+
void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
42+
declaredInterfaces = interfaces
43+
new SignatureReader(signature).accept(signatureVisitor)
44+
super.visit(version, access, name, signature, superName, interfaces)
45+
}
46+
}
47+
48+
// apply TaskWrapper transformation and check both declared and generic interfaces are updated
49+
def taskVisitor = new UnwrappingVisitor.ImplementTaskWrapperClassVisitor(
50+
classVisitor, 'java.util.concurrent.FutureTask', 'callable')
51+
52+
when:
53+
classReader.accept(taskVisitor, 0)
54+
55+
then:
56+
genericInterfaces == declaredInterfaces
57+
genericInterfaces.last() == 'datadog/trace/bootstrap/instrumentation/api/TaskWrapper'
58+
}
59+
60+
static byte [] readClassBytes(Class<?> clazz){
61+
final String classResourceName = '/' + clazz.getName().replace('.', '/') + '.class'
62+
try (InputStream is = clazz.getResourceAsStream(classResourceName)) {
63+
if(is == null) {
64+
throw new IllegalStateException("Could not find class resource: " + classResourceName)
65+
}
66+
return IOUtils.toByteArray(is)
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)