8294974: Convert jdk.jshell/jdk.jshell.execution.LocalExecutionControl to use the Classfile API to instrument classes

Reviewed-by: jlahoda
This commit is contained in:
Adam Sotona 2023-03-13 15:53:01 +00:00
parent f835aaafc7
commit a95bc7acd0
3 changed files with 34 additions and 44 deletions

View File

@ -193,15 +193,17 @@ module java.base {
java.logging; java.logging;
exports jdk.internal.classfile to exports jdk.internal.classfile to
jdk.jartool, jdk.jartool,
jdk.jlink; jdk.jlink,
jdk.jshell;
exports jdk.internal.classfile.attribute to exports jdk.internal.classfile.attribute to
jdk.jartool; jdk.jartool;
exports jdk.internal.classfile.constantpool to exports jdk.internal.classfile.constantpool to
jdk.jartool; jdk.jartool;
exports jdk.internal.classfile.instruction to
jdk.jshell;
exports jdk.internal.org.objectweb.asm to exports jdk.internal.org.objectweb.asm to
jdk.jfr, jdk.jfr,
jdk.jlink, jdk.jlink;
jdk.jshell;
exports jdk.internal.org.objectweb.asm.tree to exports jdk.internal.org.objectweb.asm.tree to
jdk.jfr, jdk.jfr,
jdk.jlink; jdk.jlink;

View File

@ -24,17 +24,17 @@
*/ */
package jdk.jshell.execution; package jdk.jshell.execution;
import java.lang.constant.ClassDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.MethodTypeDesc;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream; import java.util.stream.Stream;
import jdk.internal.org.objectweb.asm.ClassReader; import jdk.internal.classfile.Classfile;
import jdk.internal.org.objectweb.asm.ClassVisitor; import jdk.internal.classfile.ClassTransform;
import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.classfile.instruction.BranchInstruction;
import jdk.internal.org.objectweb.asm.Label;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
/** /**
* An implementation of {@link jdk.jshell.spi.ExecutionControl} which executes * An implementation of {@link jdk.jshell.spi.ExecutionControl} which executes
@ -74,43 +74,31 @@ public class LocalExecutionControl extends DirectExecutionControl {
.toArray(ClassBytecodes[]::new)); .toArray(ClassBytecodes[]::new));
} }
private static final String CANCEL_CLASS = "REPL.$Cancel$";
private static final ClassDesc CD_Cancel = ClassDesc.of(CANCEL_CLASS);
private static final ClassDesc CD_ThreadDeath = ClassDesc.of("java.lang.ThreadDeath");
private static final MethodTypeDesc MTD_void = MethodTypeDesc.of(ConstantDescs.CD_void);
private static byte[] instrument(byte[] classFile) { private static byte[] instrument(byte[] classFile) {
var reader = new ClassReader(classFile); return Classfile.parse(classFile)
var writer = new ClassWriter(reader, 0); .transform(ClassTransform.transformingMethodBodies((cob, coe) -> {
reader.accept(new ClassVisitor(Opcodes.ASM9, writer) { if (coe instanceof BranchInstruction)
@Override cob.invokestatic(CD_Cancel, "stopCheck", MTD_void);
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { cob.with(coe);
return new MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) { }));
@Override
public void visitJumpInsn(int opcode, Label label) {
visitMethodInsn(Opcodes.INVOKESTATIC, "REPL/$Cancel$", "stopCheck", "()V", false);
super.visitJumpInsn(opcode, label);
}
};
}
}, 0);
return writer.toByteArray();
} }
private static ClassBytecodes genCancelClass() { private static ClassBytecodes genCancelClass() {
var cancelWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); return new ClassBytecodes(CANCEL_CLASS, Classfile.build(CD_Cancel, clb ->
cancelWriter.visit(Opcodes.V19, Opcodes.ACC_PUBLIC, "REPL/$Cancel$", null, "java/lang/Object", null); clb.withFlags(Classfile.ACC_PUBLIC)
cancelWriter.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_VOLATILE, "allStop", "Z", null, null); .withField("allStop", ConstantDescs.CD_boolean, Classfile.ACC_PUBLIC | Classfile.ACC_STATIC | Classfile.ACC_VOLATILE)
var checkVisitor = cancelWriter.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "stopCheck", "()V", null, null); .withMethodBody("stopCheck", MTD_void, Classfile.ACC_PUBLIC | Classfile.ACC_STATIC, cob ->
checkVisitor.visitCode(); cob.getstatic(CD_Cancel, "allStop", ConstantDescs.CD_boolean)
checkVisitor.visitFieldInsn(Opcodes.GETSTATIC, "REPL/$Cancel$", "allStop", "Z"); .ifThenElse(tb -> tb.new_(CD_ThreadDeath)
var skip = new Label(); .dup()
checkVisitor.visitJumpInsn(Opcodes.IFEQ, skip); .invokespecial(CD_ThreadDeath, "<init>", MTD_void)
checkVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/ThreadDeath"); .athrow(),
checkVisitor.visitInsn(Opcodes.DUP); eb -> eb.return_()))));
checkVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/ThreadDeath", "<init>", "()V", false);
checkVisitor.visitInsn(Opcodes.ATHROW);
checkVisitor.visitLabel(skip);
checkVisitor.visitInsn(Opcodes.RETURN);
checkVisitor.visitMaxs(0, 0);
checkVisitor.visitEnd();
cancelWriter.visitEnd();
return new ClassBytecodes("REPL.$Cancel$", cancelWriter.toByteArray());
} }
@Override @Override
@ -118,7 +106,7 @@ public class LocalExecutionControl extends DirectExecutionControl {
protected String invoke(Method doitMethod) throws Exception { protected String invoke(Method doitMethod) throws Exception {
if (allStop == null) { if (allStop == null) {
super.load(new ClassBytecodes[]{ genCancelClass() }); super.load(new ClassBytecodes[]{ genCancelClass() });
allStop = findClass("REPL.$Cancel$").getDeclaredField("allStop"); allStop = findClass(CANCEL_CLASS).getDeclaredField("allStop");
} }
allStop.set(null, false); allStop.set(null, false);

View File

@ -1,4 +1,4 @@
maxOutputSize = 500000 maxOutputSize = 2500000
enablePreview = true enablePreview = true
modules = \ modules = \
java.base/jdk.internal.classfile \ java.base/jdk.internal.classfile \