Skip to content

Commit 6ee44dd

Browse files
jpbempeldeejgregor
andauthored
Allow pre-Java 6 classes to be transformed in the debugger (#9591)
Allow pre-Java 6 classes to be transformed in the debugger Pre-Java 6 classes can contain JSR/RET instructions which are not supported by ASM when the COMPUTE_FRAMES is used. This leads to exceptions like this: 13:34:29.407 [Test worker] ERROR com.datadog.debugger.agent.DebuggerTransformer - Cannot write classfile for class: org.apache.felix.gogo.runtime.Pipe Exception: java.lang.IllegalArgumentException: JSR/RET are not supported with computeFrames option at org.objectweb.asm.Frame.execute(Frame.java:1028) at org.objectweb.asm.MethodWriter.visitJumpInsn(MethodWriter.java:1147) at org.objectweb.asm.tree.JumpInsnNode.accept(JumpInsnNode.java:79) at org.objectweb.asm.tree.InsnList.accept(InsnList.java:144) at org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:749) at org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:647) at org.objectweb.asm.tree.ClassNode.accept(ClassNode.java:468) at com.datadog.debugger.agent.DebuggerTransformer.writeClassFile(DebuggerTransformer.java:492) ... This uses ASM's JSRInlinerAdapter to rewrite the problematic JSR/RET instructions as the class file is written. --------- Co-authored-by: DJ Gregor <[email protected]>
1 parent aadd471 commit 6ee44dd

File tree

2 files changed

+43
-1
lines changed

2 files changed

+43
-1
lines changed

dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerTransformer.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,11 @@
5959
import net.bytebuddy.description.type.TypeDescription;
6060
import net.bytebuddy.pool.TypePool;
6161
import org.objectweb.asm.ClassReader;
62+
import org.objectweb.asm.ClassVisitor;
6263
import org.objectweb.asm.ClassWriter;
64+
import org.objectweb.asm.MethodVisitor;
6365
import org.objectweb.asm.Opcodes;
66+
import org.objectweb.asm.commons.JSRInlinerAdapter;
6467
import org.objectweb.asm.tree.ClassNode;
6568
import org.objectweb.asm.tree.MethodNode;
6669
import org.objectweb.asm.tree.analysis.Analyzer;
@@ -487,9 +490,10 @@ private byte[] writeClassFile(
487490
classNode.version = Opcodes.V1_8;
488491
}
489492
ClassWriter writer = new SafeClassWriter(loader);
493+
ClassVisitor visitor = new JsrInliningClassVisitor(writer);
490494
LOGGER.debug("Generating bytecode for class: {}", Strings.getClassName(classFilePath));
491495
try {
492-
classNode.accept(writer);
496+
classNode.accept(visitor);
493497
} catch (Throwable t) {
494498
LOGGER.error("Cannot write classfile for class: {} Exception: ", classFilePath, t);
495499
reportInstrumentationFails(definitions, Strings.getClassName(classFilePath));
@@ -929,6 +933,26 @@ private static Path dumpClassFile(String className, byte[] classfileBuffer) {
929933
}
930934
}
931935

936+
/**
937+
* A {@link org.objectweb.asm.ClassVisitor} that uses {@link
938+
* org.objectweb.asm.commons.JSRInlinerAdapter} to remove JSR instructions and inlines the
939+
* referenced subroutines. This allows pre-Java 6 classes with finally blocks to be successfully
940+
* transformed. Without this an IllegalArgumentException for "JSR/RET are not supported with
941+
* computeFrames option" would be thrown when writing the transformed class.
942+
*/
943+
static class JsrInliningClassVisitor extends ClassVisitor {
944+
protected JsrInliningClassVisitor(ClassVisitor parent) {
945+
super(Opcodes.ASM9, parent);
946+
}
947+
948+
@Override
949+
public MethodVisitor visitMethod(
950+
int access, String name, String descriptor, String signature, String[] exceptions) {
951+
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
952+
return new JSRInlinerAdapter(mv, access, name, descriptor, signature, exceptions);
953+
}
954+
}
955+
932956
static class SafeClassWriter extends ClassWriter {
933957
private final ClassLoader classLoader;
934958

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,24 @@ public void veryOldClassFile() throws Exception {
275275
assertOneSnapshot(listener);
276276
}
277277

278+
/**
279+
* Ensure older pre-Java 6 class files with JSR/RET can be rewritten without "JSR/RET are not
280+
* supported with computeFrames option" exceptions being thrown.
281+
*/
282+
@Test
283+
public void veryOldClassFileWithJsrRet() throws Exception {
284+
final String CLASS_NAME = "antlr.Tool"; // compiled with jdk 1.2
285+
TestSnapshotListener listener = installMethodProbe(CLASS_NAME, "copyFile", null);
286+
Class<?> testClass = Class.forName(CLASS_NAME);
287+
assertNotNull(testClass);
288+
try {
289+
Reflect.onClass(testClass).create().call("copyFile", null, null);
290+
} catch (Throwable t) {
291+
// ignore
292+
}
293+
assertOneSnapshot(listener);
294+
}
295+
278296
@Test
279297
public void oldClass1_1() throws Exception {
280298
final String CLASS_NAME = "org.apache.commons.lang.BooleanUtils"; // compiled with jdk 1.1

0 commit comments

Comments
 (0)