8356548: Use ClassFile API instead of ASM to transform classes in tests

Reviewed-by: sspitsyn, lmesnik, coleenp, iklam
This commit is contained in:
Chen Liang 2025-10-16 19:45:57 +00:00
parent 9589a29d25
commit 3248aaf3c4
24 changed files with 306 additions and 516 deletions

View File

@ -23,150 +23,121 @@
package compiler.calls.common;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassTransform;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.CodeElement;
import java.lang.classfile.CodeTransform;
import java.lang.classfile.Label;
import java.lang.constant.ClassDesc;
import java.lang.constant.DirectMethodHandleDesc;
import java.lang.constant.DynamicCallSiteDesc;
import java.lang.constant.MethodHandleDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import static java.lang.constant.ConstantDescs.*;
/**
* A class which patch InvokeDynamic class bytecode with invokydynamic
instruction, rewriting "caller" method to call "callee" method using
invokedynamic
*/
public class InvokeDynamicPatcher extends ClassVisitor {
public final class InvokeDynamicPatcher {
private static final String CLASS = InvokeDynamic.class.getName()
.replace('.', '/');
private static final ClassDesc CLASS = InvokeDynamic.class.describeConstable().orElseThrow();
private static final String CALLER_METHOD_NAME = "caller";
private static final String CALLEE_METHOD_NAME = "callee";
private static final String NATIVE_CALLEE_METHOD_NAME = "calleeNative";
private static final String BOOTSTRAP_METHOD_NAME = "bootstrapMethod";
private static final String CALL_NATIVE_FIELD = "nativeCallee";
private static final String CALL_NATIVE_FIELD_DESC = "Z";
private static final String CALLEE_METHOD_DESC
= "(L" + CLASS + ";IJFDLjava/lang/String;)Z";
private static final String ASSERTTRUE_METHOD_DESC
= "(ZLjava/lang/String;)V";
private static final String ASSERTS_CLASS = "jdk/test/lib/Asserts";
private static final ClassDesc CALL_NATIVE_FIELD_DESC = CD_boolean;
private static final MethodTypeDesc CALLEE_METHOD_DESC = MethodTypeDesc.of(
CD_boolean, CLASS, CD_int, CD_long, CD_float, CD_double, CD_String);
private static final MethodTypeDesc ASSERTTRUE_METHOD_DESC = MethodTypeDesc.of(
CD_void, CD_boolean, CD_String);
private static final ClassDesc ASSERTS_CLASS = ClassDesc.ofInternalName("jdk/test/lib/Asserts");
private static final String ASSERTTRUE_METHOD_NAME = "assertTrue";
public static void main(String args[]) {
ClassReader cr;
Path filePath;
try {
filePath = Paths.get(InvokeDynamic.class.getProtectionDomain().getCodeSource()
.getLocation().toURI()).resolve(CLASS + ".class");
} catch (URISyntaxException ex) {
throw new Error("TESTBUG: Can't get code source" + ex, ex);
}
try (FileInputStream fis = new FileInputStream(filePath.toFile())) {
cr = new ClassReader(fis);
} catch (IOException e) {
throw new Error("Error reading file", e);
}
ClassWriter cw = new ClassWriter(cr,
ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
cr.accept(new InvokeDynamicPatcher(Opcodes.ASM5, cw), 0);
try {
Files.write(filePath, cw.toByteArray(),
StandardOpenOption.WRITE);
} catch (IOException e) {
throw new Error(e);
}
}
public static void main(String args[]) throws IOException, URISyntaxException {
Path filePath = Path.of(InvokeDynamic.class.getProtectionDomain().getCodeSource()
.getLocation().toURI()).resolve(InvokeDynamic.class.getName().replace('.', '/') +".class");
var bytes = ClassFile.of().transformClass(ClassFile.of().parse(filePath),
ClassTransform.transformingMethodBodies(m -> m.methodName().equalsString(CALLER_METHOD_NAME), new CodeTransform() {
@Override
public void accept(CodeBuilder builder, CodeElement element) {
// discard
}
public InvokeDynamicPatcher(int api, ClassWriter cw) {
super(api, cw);
}
@Override
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature,
final String[] exceptions) {
/* a code generate looks like
* 0: aload_0
* 1: ldc #125 // int 1
* 3: ldc2_w #126 // long 2l
* 6: ldc #128 // float 3.0f
* 8: ldc2_w #129 // double 4.0d
* 11: ldc #132 // String 5
* 13: aload_0
* 14: getfield #135 // Field nativeCallee:Z
* 17: ifeq 28
* 20: invokedynamic #181, 0 // InvokeDynamic #1:calleeNative:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z
* 25: goto 33
* 28: invokedynamic #183, 0 // InvokeDynamic #1:callee:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z
* 33: ldc #185 // String Call insuccessfull
* 35: invokestatic #191 // Method jdk/test/lib/Asserts.assertTrue:(ZLjava/lang/String;)V
* 38: return
*
* or, using java-like pseudo-code
* if (this.nativeCallee == false) {
* invokedynamic-call-return-value = invokedynamic-of-callee
* } else {
* invokedynamic-call-return-value = invokedynamic-of-nativeCallee
* }
* Asserts.assertTrue(invokedynamic-call-return-value, error-message);
* return;
*/
if (name.equals(CALLER_METHOD_NAME)) {
MethodVisitor mv = cv.visitMethod(access, name, desc,
signature, exceptions);
Label nonNativeLabel = new Label();
Label checkLabel = new Label();
MethodType mtype = MethodType.methodType(CallSite.class,
MethodHandles.Lookup.class, String.class, MethodType.class);
Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, CLASS,
BOOTSTRAP_METHOD_NAME, mtype.toMethodDescriptorString());
mv.visitCode();
// push callee parameters onto stack
mv.visitVarInsn(Opcodes.ALOAD, 0);//push "this"
mv.visitLdcInsn(1);
mv.visitLdcInsn(2L);
mv.visitLdcInsn(3.0f);
mv.visitLdcInsn(4.0d);
mv.visitLdcInsn("5");
// params loaded. let's decide what method to call
mv.visitVarInsn(Opcodes.ALOAD, 0); // push "this"
// get nativeCallee field
mv.visitFieldInsn(Opcodes.GETFIELD, CLASS, CALL_NATIVE_FIELD,
CALL_NATIVE_FIELD_DESC);
// if nativeCallee == false goto nonNativeLabel
mv.visitJumpInsn(Opcodes.IFEQ, nonNativeLabel);
// invokedynamic nativeCalleeMethod using bootstrap method
mv.visitInvokeDynamicInsn(NATIVE_CALLEE_METHOD_NAME,
CALLEE_METHOD_DESC, bootstrap);
// goto checkLabel
mv.visitJumpInsn(Opcodes.GOTO, checkLabel);
// label: nonNativeLabel
mv.visitLabel(nonNativeLabel);
// invokedynamic calleeMethod using bootstrap method
mv.visitInvokeDynamicInsn(CALLEE_METHOD_NAME, CALLEE_METHOD_DESC,
bootstrap);
mv.visitLabel(checkLabel);
mv.visitLdcInsn(CallsBase.CALL_ERR_MSG);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, ASSERTS_CLASS,
ASSERTTRUE_METHOD_NAME, ASSERTTRUE_METHOD_DESC, false);
// label: return
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
return null;
}
return super.visitMethod(access, name, desc, signature, exceptions);
/* the code generated looks like
* 0: aload_0
* 1: ldc #125 // int 1
* 3: ldc2_w #126 // long 2l
* 6: ldc #128 // float 3.0f
* 8: ldc2_w #129 // double 4.0d
* 11: ldc #132 // String 5
* 13: aload_0
* 14: getfield #135 // Field nativeCallee:Z
* 17: ifeq 28
* 20: invokedynamic #181, 0 // InvokeDynamic #1:calleeNative:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z
* 25: goto 33
* 28: invokedynamic #183, 0 // InvokeDynamic #1:callee:(Lcompiler/calls/common/InvokeDynamic;IJFDLjava/lang/String;)Z
* 33: ldc #185 // String Call insuccessfull
* 35: invokestatic #191 // Method jdk/test/lib/Asserts.assertTrue:(ZLjava/lang/String;)V
* 38: return
*
* or, using java-like pseudo-code
* if (this.nativeCallee == false) {
* invokedynamic-call-return-value = invokedynamic-of-callee
* } else {
* invokedynamic-call-return-value = invokedynamic-of-nativeCallee
* }
* Asserts.assertTrue(invokedynamic-call-return-value, error-message);
* return;
*/
@Override
public void atEnd(CodeBuilder builder) {
Label nonNativeLabel = builder.newLabel();
Label checkLabel = builder.newLabel();
MethodType mtype = MethodType.methodType(CallSite.class,
MethodHandles.Lookup.class, String.class, MethodType.class);
DirectMethodHandleDesc dmh = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC,
CLASS, BOOTSTRAP_METHOD_NAME, mtype.descriptorString());
// push callee parameters onto stack
builder.aload(builder.receiverSlot())
.ldc(1)
.ldc(2L)
.ldc(3.0f)
.ldc(4.0d)
.ldc("5")
// params loaded. let's decide what method to call
.aload(builder.receiverSlot())
// get nativeCallee field
.getfield(CLASS, CALL_NATIVE_FIELD, CALL_NATIVE_FIELD_DESC)
// if nativeCallee == false goto nonNativeLabel
.ifeq(nonNativeLabel)
// invokedynamic nativeCalleeMethod using bootstrap method
.invokedynamic(DynamicCallSiteDesc.of(dmh, NATIVE_CALLEE_METHOD_NAME, CALLEE_METHOD_DESC))
// goto checkLabel
.goto_(checkLabel)
// label: nonNativeLabel
.labelBinding(nonNativeLabel)
// invokedynamic calleeMethod using bootstrap method
.invokedynamic(DynamicCallSiteDesc.of(dmh, CALLEE_METHOD_NAME, CALLEE_METHOD_DESC))
.labelBinding(checkLabel)
.ldc(CallsBase.CALL_ERR_MSG)
.invokestatic(ASSERTS_CLASS, ASSERTTRUE_METHOD_NAME, ASSERTTRUE_METHOD_DESC)
// label: return
.return_();
}
}));
Files.write(filePath, bytes, StandardOpenOption.WRITE);
}
}

View File

@ -25,10 +25,10 @@
* @test
* @summary check calls from compiled to compiled using InvokeDynamic
* @library /test/lib /
* @library /testlibrary/asm
* @modules java.base/jdk.internal.misc
*
* @build jdk.test.whitebox.WhiteBox
* @build compiler.calls.common.InvokeDynamic
* @run driver compiler.calls.common.InvokeDynamicPatcher
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.

View File

@ -25,10 +25,10 @@
* @test
* @summary check calls from compiled to interpreted using InvokeDynamic
* @library /test/lib /
* @library /testlibrary/asm
* @modules java.base/jdk.internal.misc
*
* @build jdk.test.whitebox.WhiteBox
* @build compiler.calls.common.InvokeDynamic
* @run driver compiler.calls.common.InvokeDynamicPatcher
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.

View File

@ -25,10 +25,10 @@
* @test
* @summary check calls from compiled to native using InvokeDynamic
* @library /test/lib /
* @library /testlibrary/asm
* @modules java.base/jdk.internal.misc
*
* @build jdk.test.whitebox.WhiteBox
* @build compiler.calls.common.InvokeDynamic
* @run driver compiler.calls.common.InvokeDynamicPatcher
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.

View File

@ -25,10 +25,10 @@
* @test
* @summary check calls from interpreted to compiled using InvokeDynamic
* @library /test/lib /
* @library /testlibrary/asm
* @modules java.base/jdk.internal.misc
*
* @build jdk.test.whitebox.WhiteBox
* @build compiler.calls.common.InvokeDynamic
* @run driver compiler.calls.common.InvokeDynamicPatcher
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.

View File

@ -25,10 +25,10 @@
* @test
* @summary check calls from interpreted to interpreted using InvokeDynamic
* @library /test/lib /
* @library /testlibrary/asm
* @modules java.base/jdk.internal.misc
*
* @build jdk.test.whitebox.WhiteBox
* @build compiler.calls.common.InvokeDynamic
* @run driver compiler.calls.common.InvokeDynamicPatcher
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.

View File

@ -25,10 +25,10 @@
* @test
* @summary check calls from interpreted to native using InvokeDynamic
* @library /test/lib /
* @library /testlibrary/asm
* @modules java.base/jdk.internal.misc
*
* @build jdk.test.whitebox.WhiteBox
* @build compiler.calls.common.InvokeDynamic
* @run driver compiler.calls.common.InvokeDynamicPatcher
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.

View File

@ -21,12 +21,11 @@
* questions.
*/
/**
/*
* @test
* @bug 8042235
* @summary redefining method used by multiple MethodHandles crashes VM
* @library /
* @library /testlibrary/asm
* @modules java.compiler
* java.instrument
* jdk.attach
@ -37,15 +36,13 @@
package compiler.jsr292;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassHierarchyResolver;
import java.lang.classfile.ClassTransform;
import java.lang.classfile.instruction.ConstantInstruction;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
@ -159,28 +156,15 @@ public class RedefineMethodUsedByMultipleMethodHandles {
public byte[] transform(ClassLoader cl, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (Foo.class.equals(classBeingRedefined)) {
System.out.println("redefining " + classBeingRedefined);
ClassReader cr = new ClassReader(classfileBuffer);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
ClassVisitor adapter = new ClassVisitor(Opcodes.ASM5, cw) {
@Override
public MethodVisitor visitMethod(int access, String base, String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, base, desc, signature, exceptions);
if (mv != null) {
mv = new MethodVisitor(Opcodes.ASM5, mv) {
@Override
public void visitLdcInsn(Object cst) {
System.out.println("replacing \"" + cst + "\" with \"bar\"");
mv.visitLdcInsn("bar");
}
};
}
return mv;
var context = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofResourceParsing(cl)));
return context.transformClass(context.parse(classfileBuffer), ClassTransform.transformingMethodBodies((codeBuilder, codeElement) -> {
if (codeElement instanceof ConstantInstruction.LoadConstantInstruction ldc) {
System.out.println("replacing \"" + ldc.constantEntry().constantValue() + "\" with \"bar\"");
codeBuilder.ldc("bar");
} else {
codeBuilder.with(codeElement);
}
};
cr.accept(adapter, ClassReader.SKIP_FRAMES);
cw.visitEnd();
return cw.toByteArray();
}));
}
return classfileBuffer;
}

View File

@ -23,28 +23,26 @@
package compiler.jvmci.common;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import jdk.test.lib.Utils;
import jdk.vm.ci.code.InstalledCode;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.hotspot.CompilerToVMHelper;
import jdk.vm.ci.hotspot.HotSpotNmethod;
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
import java.io.IOException;
import java.lang.classfile.Attributes;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.lang.classfile.MethodModel;
import java.lang.constant.ClassDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@ -71,76 +69,55 @@ public class CTVMUtilities {
}
public static Map<Integer, Integer> getBciToLineNumber(Executable method) {
Map<Integer, Integer> lineNumbers = new TreeMap<>();
Class<?> aClass = method.getDeclaringClass();
ClassReader cr;
try {
Module aModule = aClass.getModule();
String name = aClass.getName();
cr = new ClassReader(aModule.getResourceAsStream(
name.replace('.', '/') + ".class"));
} catch (IOException e) {
throw new Error("TEST BUG: can read " + aClass.getName() + " : " + e, e);
}
ClassNode cn = new ClassNode();
cr.accept(cn, ClassReader.EXPAND_FRAMES);
ClassModel classModel = findClassBytes(method.getDeclaringClass());
MethodModel methodModel = findMethod(classModel, method);
if (methodModel == null)
return Map.of();
Map<Label, Integer> labels = new HashMap<>();
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassVisitor cv = new ClassVisitorForLabels(cw, labels, method);
cr.accept(cv, ClassReader.EXPAND_FRAMES);
labels.forEach((k, v) -> lineNumbers.put(k.getOffset(), v));
boolean isEmptyMethod = Modifier.isAbstract(method.getModifiers())
|| Modifier.isNative(method.getModifiers());
if (lineNumbers.isEmpty() && !isEmptyMethod) {
throw new Error(method + " doesn't contains the line numbers table "
+"(the method marked neither abstract nor native)");
var foundLineNumberTable = methodModel.code().flatMap(code ->
code.findAttribute(Attributes.lineNumberTable()));
if (foundLineNumberTable.isEmpty()) {
boolean isEmptyMethod = Modifier.isAbstract(method.getModifiers())
|| Modifier.isNative(method.getModifiers());
if (!isEmptyMethod) {
throw new Error(method + " doesn't contains the line numbers table "
+ "(the method marked neither abstract nor native)");
}
return Map.of();
}
Map<Integer, Integer> lineNumbers = new TreeMap<>();
foundLineNumberTable.get().lineNumbers().forEach(ln ->
lineNumbers.put(ln.startPc(), ln.lineNumber()));
return lineNumbers;
}
private static class ClassVisitorForLabels extends ClassVisitor {
private final Map<Label, Integer> lineNumbers;
private final String targetName;
private final String targetDesc;
public ClassVisitorForLabels(ClassWriter cw, Map<Label, Integer> lines,
Executable target) {
super(Opcodes.ASM7, cw);
this.lineNumbers = lines;
StringBuilder builder = new StringBuilder("(");
for (Parameter parameter : target.getParameters()) {
builder.append(Utils.toJVMTypeSignature(parameter.getType()));
}
builder.append(")");
if (target instanceof Constructor) {
targetName = "<init>";
builder.append("V");
} else {
targetName = target.getName();
builder.append(Utils.toJVMTypeSignature(
((Method) target).getReturnType()));
}
targetDesc = builder.toString();
// Finds the ClassFile API model of a given class, or fail with an Error.
public static ClassModel findClassBytes(Class<?> clazz) {
String binaryName = clazz.getName();
byte[] fileBytes;
try (var inputStream = clazz.getModule().getResourceAsStream(
binaryName.replace('.', '/') + ".class")) {
fileBytes = inputStream.readAllBytes();
} catch (IOException e) {
throw new Error("TEST BUG: cannot read " + binaryName, e);
}
return ClassFile.of().parse(fileBytes);
}
@Override
public final MethodVisitor visitMethod(int access, String name,
String desc, String signature,
String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
exceptions);
if (targetDesc.equals(desc) && targetName.equals(name)) {
return new MethodVisitor(Opcodes.ASM7, mv) {
@Override
public void visitLineNumber(int i, Label label) {
super.visitLineNumber(i, label);
lineNumbers.put(label, i);
}
};
// Finds a matching method in a class model, or null if none match.
public static MethodModel findMethod(ClassModel classModel, Executable method) {
MethodTypeDesc methodType = MethodType.methodType(
method instanceof Method m ? m.getReturnType() : void.class,
method.getParameterTypes()).describeConstable().orElseThrow();
String methodName = method instanceof Method m ? m.getName() : ConstantDescs.INIT_NAME;
for (var methodModel : classModel.methods()) {
if (methodModel.methodName().equalsString(methodName)
&& methodModel.methodType().isMethodType(methodType)) {
return methodModel;
}
return mv;
}
return null;
}
}

View File

@ -21,35 +21,28 @@
* questions.
*/
import org.objectweb.asm.*;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassTransform;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.CodeElement;
import java.lang.classfile.CodeTransform;
class Asmator {
static byte[] fixup(byte[] buf) throws java.io.IOException {
ClassReader cr = new ClassReader(buf);
ClassWriter cw = new ClassWriter(0);
ClassVisitor cv = new ClassVisitor(Opcodes.ASM4, cw) {
public MethodVisitor visitMethod(
final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions)
{
MethodVisitor mv = super.visitMethod(access,
name,
desc,
signature,
exceptions);
if (mv == null) return null;
if (name.equals("callme")) {
// make receiver go dead!
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitVarInsn(Opcodes.ASTORE, 0);
static byte[] fixup(byte[] buf) {
return ClassFile.of().transformClass(ClassFile.of().parse(buf), ClassTransform.transformingMethodBodies(
m -> m.methodName().equalsString("callme"),
new CodeTransform() {
@Override
public void atStart(CodeBuilder builder) {
// make receiver go dead!
builder.aconst_null().astore(0);
}
@Override
public void accept(CodeBuilder builder, CodeElement element) {
builder.with(element); // pass through
}
}
return mv;
}
};
cr.accept(cv, 0);
return cw.toByteArray();
));
}
}

View File

@ -25,7 +25,6 @@
* @test
* @bug 8003720
* @summary Method in interpreter stack frame can be deallocated
* @library /testlibrary/asm
* @modules java.base/jdk.internal.misc
* @compile -XDignore.symbol.file Victim.java
* @run main/othervm -Xverify:all -Xint Test8003720

View File

@ -27,17 +27,15 @@
* @bug 8228604
*
* @requires vm.jvmti
* @library /testlibrary/asm
* @library /test/lib
*
* @run main/othervm/native -agentlib:MissedStackMapFrames MissedStackMapFrames
*/
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.lang.classfile.Attributes;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.lang.classfile.MethodModel;
public class MissedStackMapFrames {
static {
@ -58,30 +56,19 @@ public class MissedStackMapFrames {
private static native byte[] retransformBytes(int idx);
private static int getStackMapFrameCount(byte[] classfileBuffer) {
ClassReader reader = new ClassReader(classfileBuffer);
final int[] frameCount = {0};
ClassVisitor cv = new ClassVisitor(Opcodes.ASM9) {
@Override
public MethodVisitor visitMethod(int access, String name,
String descriptor, String signature,
String[] exceptions) {
return new MethodVisitor(Opcodes.ASM9) {
private int methodFrames = 0;
@Override
public void visitFrame(int type, int numLocal, Object[] local,
int numStack, Object[] stack) {
methodFrames++;
}
@Override
public void visitEnd() {
log(" method " + name + " - " + methodFrames + " frames");
frameCount[0] += methodFrames;
}
};
ClassModel clazz = ClassFile.of().parse(classfileBuffer);
int count = 0;
for (MethodModel method : clazz.methods()) {
var foundStackMapTable = method.code().flatMap(code -> code.findAttribute(Attributes.stackMapTable()));
if (foundStackMapTable.isPresent()) {
int methodFrames = foundStackMapTable.get().entries().size();
log(" method " + method.methodName() + " - " + methodFrames + " frames");
count += methodFrames;
} else {
log(" method " + method.methodName() + " - No StackMapTable");
}
};
reader.accept(cv, 0);
return frameCount[0];
}
return count;
}
private static int checkStackMapFrames(String mode, byte[] classfileBuffer) {

View File

@ -24,7 +24,6 @@
/*
* @test
* @library /test/lib
* @library /testlibrary/asm
* @summary Test that type annotations are retained after a retransform
* @requires vm.jvmti
* @modules java.base/jdk.internal.misc
@ -46,6 +45,11 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.classfile.ClassBuilder;
import java.lang.classfile.ClassElement;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassTransform;
import java.lang.classfile.FieldModel;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
@ -55,17 +59,10 @@ import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.AnnotatedWildcardType;
import java.lang.reflect.Executable;
import java.lang.reflect.TypeVariable;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import static org.objectweb.asm.Opcodes.ASM7;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@ -86,53 +83,27 @@ public class RedefineAnnotations {
ProtectionDomain protectionDomain, byte[] classfileBuffer)
throws IllegalClassFormatException {
ClassWriter cw = new ClassWriter(0);
ClassVisitor cv = new ReAddDummyFieldsClassVisitor(ASM7, cw) { };
ClassReader cr = new ClassReader(classfileBuffer);
cr.accept(cv, 0);
return cw.toByteArray();
}
// Shuffle constant pool
ClassFile context = ClassFile.of(ClassFile.ConstantPoolSharingOption.NEW_POOL);
return context.transformClass(context.parse(classfileBuffer), new ClassTransform() {
final List<FieldModel> dummyFields = new ArrayList<>();
public class ReAddDummyFieldsClassVisitor extends ClassVisitor {
LinkedList<F> fields = new LinkedList<>();
public ReAddDummyFieldsClassVisitor(int api, ClassVisitor cv) {
super(api, cv);
}
@Override public FieldVisitor visitField(int access, String name,
String desc, String signature, Object value) {
if (name.startsWith("dummy")) {
// Remove dummy field
fields.addLast(new F(access, name, desc, signature, value));
return null;
@Override
public void accept(ClassBuilder builder, ClassElement element) {
if (element instanceof FieldModel field && field.fieldName().stringValue().startsWith("dummy")) {
// Hold on to the associated constant pool entries too
dummyFields.addLast(field);
} else {
builder.with(element);
}
}
return cv.visitField(access, name, desc, signature, value);
}
@Override public void visitEnd() {
F f;
while ((f = fields.pollFirst()) != null) {
// Re-add dummy fields
cv.visitField(f.access, f.name, f.desc, f.signature, f.value);
@Override
public void atEnd(ClassBuilder builder) {
// Add the associated constant pool entries to the end of the CP
dummyFields.forEach(builder);
}
}
private class F {
private int access;
private String name;
private String desc;
private String signature;
private Object value;
F(int access, String name, String desc, String signature, Object value) {
this.access = access;
this.name = name;
this.desc = desc;
this.signature = signature;
this.value = value;
}
}
});
}
@Override public byte[] transform(ClassLoader loader, String className,

View File

@ -35,6 +35,11 @@
import java.io.File;
import java.io.FileOutputStream;
import java.lang.classfile.ClassBuilder;
import java.lang.classfile.ClassElement;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassTransform;
import java.lang.classfile.attribute.SourceFileAttribute;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@ -141,27 +146,28 @@ public class RedefineGenericSignatureTest {
private byte[] getNewClassBytes() {
byte[] bytecode = InMemoryJavaCompiler.compile(GenericSignatureTarget.class.getName(), newTargetClassSource);
ClassWriter cw = new ClassWriter(0);
ClassReader cr = new ClassReader(bytecode);
cr.accept(new ClassVisitor(Opcodes.ASM7, cw) {
ClassFile context = ClassFile.of();
return context.transformClass(context.parse(bytecode), new ClassTransform() {
private boolean sourceSet = false;
@Override
public void visitSource(String source, String debug) {
sourceSet = true;
log("Changing source: \"" + source + "\" -> \"" + sourceFileNameNew + "\"");
super.visitSource(sourceFileNameNew, debug);
public void accept(ClassBuilder builder, ClassElement element) {
if (element instanceof SourceFileAttribute src) {
sourceSet = true;
log("Changing source: \"" + src.sourceFile() + "\" -> \"" + sourceFileNameNew + "\"");
builder.with(SourceFileAttribute.of(sourceFileNameNew));
} else {
builder.with(element);
}
}
@Override
public void visitEnd() {
public void atEnd(ClassBuilder builder) {
if (!sourceSet) {
log("Set source: \"" + sourceFileNameNew + "\"");
super.visitSource(sourceFileNameNew, null);
builder.with(SourceFileAttribute.of(sourceFileNameNew));
}
super.visitEnd();
}
}, 0);
return cw.toByteArray();
});
}
private void runTest() throws Throwable {

View File

@ -27,7 +27,6 @@
* @summary Ensure Object natives stay registered after redefinition
* @requires vm.jvmti
* @library /test/lib
* @library /testlibrary/asm
* @modules java.base/jdk.internal.misc
* java.compiler
* java.instrument
@ -41,19 +40,15 @@ import jdk.test.lib.helpers.ClassFileInstaller;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.lang.RuntimeException;
import java.lang.classfile.ClassBuilder;
import java.lang.classfile.ClassElement;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassFileVersion;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;
import java.util.Arrays;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import static org.objectweb.asm.Opcodes.ASM6;
import static org.objectweb.asm.Opcodes.V1_8;
public class RedefineObject {
@ -69,31 +64,14 @@ public class RedefineObject {
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer)
throws IllegalClassFormatException {
ClassWriter cw = new ClassWriter(0);
// Force an older ASM to force a bytecode update
ClassVisitor cv = new DummyClassVisitor(ASM6, cw) { };
ClassReader cr = new ClassReader(classfileBuffer);
cr.accept(cv, 0);
byte[] bytes = cw.toByteArray();
return bytes;
}
public class DummyClassVisitor extends ClassVisitor {
public DummyClassVisitor(int api, ClassVisitor cv) {
super(api, cv);
}
public void visit(
final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces) {
// Artificially lower to JDK 8 version to force a redefine
cv.visit(V1_8, access, name, signature, superName, interfaces);
}
return ClassFile.of().transformClass(ClassFile.of().parse(classfileBuffer), (classBuilder, classElement) -> {
if (classElement instanceof ClassFileVersion cfv) {
// Force a redefine with different class file versions
classBuilder.with(ClassFileVersion.of(cfv.majorVersion() - 1, 0));
} else {
classBuilder.with(classElement);
}
});
}
@Override public byte[] transform(ClassLoader loader, String className,

View File

@ -27,7 +27,6 @@
* @bug 7124710
*
* @requires vm.jvmti
* @library /testlibrary/asm
* @library /test/lib
*
* @comment main/othervm/native -Xlog:redefine*=trace -agentlib:RedefineRetransform RedefineRetransform
@ -42,13 +41,17 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.lang.classfile.Annotation;
import java.lang.classfile.AnnotationElement;
import java.lang.classfile.AnnotationValue;
import java.lang.classfile.Attributes;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.lang.classfile.ClassTransform;
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
import java.lang.constant.ClassDesc;
import java.util.List;
import java.util.NoSuchElementException;
/*
* The test verifies that after interleaved RedefineClasses/RetransformClasses calls
@ -81,67 +84,32 @@ public class RedefineRetransform {
// Class bytes for initial TestClass (ClassVersion == 0).
private static byte[] initialClassBytes;
private static class VersionScanner extends ClassVisitor {
private Integer detectedVersion;
private Integer versionToSet;
// to get version
public VersionScanner() {
super(Opcodes.ASM7);
}
// to set version
public VersionScanner(int verToSet, ClassVisitor classVisitor) {
super(Opcodes.ASM7, classVisitor);
versionToSet = verToSet;
}
public int detectedVersion() {
if (detectedVersion == null) {
throw new RuntimeException("Version not detected");
}
return detectedVersion;
}
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
//log("visitAnnotation: descr = '" + descriptor + "', visible = " + visible);
if (Type.getDescriptor(ClassVersion.class).equals(descriptor)) {
return new AnnotationVisitor(Opcodes.ASM7, super.visitAnnotation(descriptor, visible)) {
@Override
public void visit(String name, Object value) {
//log("visit: name = '" + name + "', value = " + value
// + " (" + (value == null ? "N/A" : value.getClass()) + ")");
if ("value".equals(name) && value instanceof Integer intValue) {
detectedVersion = intValue;
if (versionToSet != null) {
//log("replace with " + versionToSet);
value = versionToSet;
}
}
super.visit(name, value);
}
};
}
return super.visitAnnotation(descriptor, visible);
}
}
private static final ClassDesc CD_ClassVersion = ClassVersion.class.describeConstable().orElseThrow();
// Generates TestClass class bytes with the specified ClassVersion value.
private static byte[] getClassBytes(int ver) {
if (ver < 0) {
return null;
}
ClassWriter cw = new ClassWriter(0);
ClassReader cr = new ClassReader(initialClassBytes);
cr.accept(new VersionScanner(ver, cw), 0);
return cw.toByteArray();
return ClassFile.of().transformClass(ClassFile.of().parse(initialClassBytes),
// overwrites previously passed RVAA
ClassTransform.endHandler(classBuilder -> classBuilder.with(RuntimeVisibleAnnotationsAttribute
.of(Annotation.of(CD_ClassVersion, AnnotationElement.ofInt("value", ver))))));
}
// Extracts ClassVersion values from the provided class bytes.
private static int getClassBytesVersion(byte[] classBytes) {
ClassReader cr = new ClassReader(classBytes);
VersionScanner scanner = new VersionScanner();
cr.accept(scanner, 0);
return scanner.detectedVersion();
ClassModel classModel = ClassFile.of().parse(classBytes);
RuntimeVisibleAnnotationsAttribute rvaa = classModel.findAttribute(Attributes.runtimeVisibleAnnotations()).orElseThrow();
List<AnnotationElement> classVersionElementValuePairs = rvaa.annotations().stream()
.filter(anno -> anno.className().isFieldType(CD_ClassVersion))
.findFirst().orElseThrow().elements();
if (classVersionElementValuePairs.size() != 1)
throw new NoSuchElementException();
AnnotationElement elementValuePair = classVersionElementValuePairs.getFirst();
if (!elementValuePair.name().equalsString("value") || !(elementValuePair.value() instanceof AnnotationValue.OfInt intVal))
throw new NoSuchElementException();
return intVal.intValue();
}
static void init() {

View File

@ -26,6 +26,9 @@ package gc.g1.unloading;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassTransform;
import java.lang.constant.ClassDesc;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
@ -42,11 +45,6 @@ import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
/**
* Class that imitates shell script to produce jar file with many similar
* classes inside.
@ -261,28 +259,9 @@ public class GenClassPoolJar {
* @return new class file to write into class
*/
byte[] morphClass(byte[] classToMorph, String newName) {
ClassReader cr = new ClassReader(classToMorph);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new ClassRenamer(cw, newName);
cr.accept(cv, 0);
return cw.toByteArray();
var context = ClassFile.of();
return context.transformClass(context.parse(classToMorph),
ClassDesc.ofInternalName(newName),
ClassTransform.ACCEPT_ALL);
}
/**
* Visitor to rename class.
*/
static class ClassRenamer extends ClassVisitor implements Opcodes {
private final String newName;
public ClassRenamer(ClassVisitor cv, String newName) {
super(ASM4, cv);
this.newName = newName;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
cv.visit(version, access, newName, signature, superName, interfaces);
}
}
}

View File

@ -30,7 +30,6 @@
* VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, javac]
*
* @modules java.base/jdk.internal.misc
* @library /testlibrary/asm
* @library /vmTestbase
* /test/lib
*

View File

@ -30,7 +30,6 @@
* VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, javac]
*
* @modules java.base/jdk.internal.misc
* @library /testlibrary/asm
* @library /vmTestbase
* /test/lib
*

View File

@ -30,7 +30,6 @@
* VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, javac]
*
* @modules java.base/jdk.internal.misc
* @library /testlibrary/asm
* @library /vmTestbase
* /test/lib
*

View File

@ -30,7 +30,6 @@
* VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, javac]
*
* @modules java.base/jdk.internal.misc
* @library /testlibrary/asm
* @library /vmTestbase
* /test/lib
*

View File

@ -30,7 +30,6 @@
* VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, javac]
*
* @modules java.base/jdk.internal.misc
* @library /testlibrary/asm
* @library /vmTestbase
* /test/lib
*

View File

@ -30,7 +30,6 @@
* VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, javac]
*
* @modules java.base/jdk.internal.misc
* @library /testlibrary/asm
* @library /vmTestbase
* /test/lib
*

View File

@ -25,14 +25,12 @@ package nsk.jvmti.GetClassFields;
import java.io.PrintStream;
import java.io.InputStream;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.lang.classfile.FieldModel;
import java.util.List;
import java.util.ArrayList;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes;
public class getclfld007 {
@ -79,44 +77,29 @@ public class getclfld007 {
static void check(Class cls) throws Exception {
FieldExplorer explorer = new FieldExplorer(cls);
List<String> fields = explorer.get();
List<String> fields = getFields(cls);
check(cls, fields.toArray(new String[0]));
}
// helper class to get list of the class fields
// in the order they appear in the class file
static class FieldExplorer extends ClassVisitor {
private final Class cls;
private List<String> fieldNameAndSig = new ArrayList<>();
private FieldExplorer(Class cls) {
super(Opcodes.ASM7);
this.cls = cls;
}
private static InputStream getClassBytes(Class<?> cls) throws Exception {
String clsName = cls.getName();
String clsPath = clsName.replace('.', '/') + ".class";
return cls.getClassLoader().getResourceAsStream(clsPath);
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
System.out.println(" field '" + name + "', type = " + descriptor);
fieldNameAndSig.add(name);
fieldNameAndSig.add(descriptor);
return super.visitField(access, name, descriptor, signature, value);
}
private InputStream getClassBytes() throws Exception {
String clsName = cls.getName();
String clsPath = clsName.replace('.', '/') + ".class";
return cls.getClassLoader().getResourceAsStream(clsPath);
}
// each field is represented by 2 Strings in the list: name and type descriptor
public List<String> get() throws Exception {
System.out.println("Class " + cls.getName());
try (InputStream classBytes = getClassBytes()) {
ClassReader classReader = new ClassReader(classBytes);
classReader.accept(this, 0);
// get list of the class fields in the order they appear in the class file
// each field is represented by 2 Strings in the list: name and type descriptor
public static List<String> getFields(Class<?> cls) throws Exception {
System.out.println("Class " + cls.getName());
List<String> fieldNameAndSig = new ArrayList<>();
try (InputStream classBytes = getClassBytes(cls)) {
ClassModel classModel = ClassFile.of().parse(classBytes.readAllBytes());
for (FieldModel field : classModel.fields()) {
fieldNameAndSig.add(field.fieldName().stringValue());
fieldNameAndSig.add(field.fieldType().stringValue());
}
return fieldNameAndSig;
}
return fieldNameAndSig;
}
static class InnerClass1 {