mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-11 14:11:36 +00:00
8356548: Use ClassFile API instead of ASM to transform classes in tests
Reviewed-by: sspitsyn, lmesnik, coleenp, iklam
This commit is contained in:
parent
9589a29d25
commit
3248aaf3c4
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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:.
|
||||
|
||||
@ -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:.
|
||||
|
||||
@ -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:.
|
||||
|
||||
@ -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:.
|
||||
|
||||
@ -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:.
|
||||
|
||||
@ -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:.
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user