8294974: Convert jdk.jshell/jdk.jshell.execution.LocalExecutionControl to use the Classfile API to instrument classes
Reviewed-by: jlahoda
This commit is contained in:
parent
f835aaafc7
commit
a95bc7acd0
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
maxOutputSize = 500000
|
maxOutputSize = 2500000
|
||||||
enablePreview = true
|
enablePreview = true
|
||||||
modules = \
|
modules = \
|
||||||
java.base/jdk.internal.classfile \
|
java.base/jdk.internal.classfile \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user