8294977: Convert test/jdk/java tests from ASM library to Classfile API
Reviewed-by: asotona
This commit is contained in:
parent
82609b1ebc
commit
f6d7e30b84
test/jdk/java
io/Serializable/records
lang
Class/getSimpleName
ModuleTests
annotation
AnnotationTypeMismatchException
AnnotationTypeMismatchTest.javaArityTypeMismatchTest.javaArrayTypeMismatchTest.javaEnumTypeMismatchTest.java
AnnotationVerifier.javaClassFileGenerator.javainstrument
invoke
reflect
util/ServiceLoader
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,7 +27,7 @@
|
||||
* @summary InvalidClassException is thrown when the canonical constructor
|
||||
* cannot be found during deserialization.
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @run testng BadCanonicalCtrTest
|
||||
*/
|
||||
|
||||
@ -38,19 +38,22 @@ import java.io.InvalidClassException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamClass;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import java.lang.classfile.ClassTransform;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.MethodModel;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
|
||||
import jdk.test.lib.compiler.InMemoryJavaCompiler;
|
||||
import jdk.test.lib.ByteCodeLoader;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import static java.lang.System.out;
|
||||
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.constant.ConstantDescs.CD_void;
|
||||
import static java.lang.constant.ConstantDescs.INIT_NAME;
|
||||
import static java.lang.constant.ConstantDescs.MTD_void;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.Assert.expectThrows;
|
||||
|
||||
@ -203,33 +206,9 @@ public class BadCanonicalCtrTest {
|
||||
* Assumes just a single, canonical, constructor.
|
||||
*/
|
||||
static byte[] removeConstructor(byte[] classBytes) {
|
||||
ClassReader reader = new ClassReader(classBytes);
|
||||
ClassWriter writer = new ClassWriter(reader, COMPUTE_MAXS | COMPUTE_FRAMES);
|
||||
reader.accept(new RemoveCanonicalCtrVisitor(writer), 0);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
|
||||
/** Removes the <init> method. */
|
||||
static class RemoveCanonicalCtrVisitor extends ClassVisitor {
|
||||
static final String CTR_NAME = "<init>";
|
||||
RemoveCanonicalCtrVisitor(ClassVisitor cv) {
|
||||
super(ASM8, cv);
|
||||
}
|
||||
volatile boolean foundCanonicalCtr;
|
||||
@Override
|
||||
public MethodVisitor visitMethod(final int access,
|
||||
final String name,
|
||||
final String descriptor,
|
||||
final String signature,
|
||||
final String[] exceptions) {
|
||||
if (name.equals(CTR_NAME)) { // assume just a single constructor
|
||||
assert foundCanonicalCtr == false;
|
||||
foundCanonicalCtr = true;
|
||||
return null;
|
||||
} else {
|
||||
return cv.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
}
|
||||
}
|
||||
var cf = ClassFile.of();
|
||||
return cf.transform(cf.parse(classBytes), ClassTransform.dropping(ce ->
|
||||
ce instanceof MethodModel mm && mm.methodName().equalsString(INIT_NAME)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,55 +216,15 @@ public class BadCanonicalCtrTest {
|
||||
* Assumes just a single, canonical, constructor.
|
||||
*/
|
||||
static byte[] modifyConstructor(byte[] classBytes) {
|
||||
ClassReader reader = new ClassReader(classBytes);
|
||||
ClassWriter writer = new ClassWriter(reader, COMPUTE_MAXS | COMPUTE_FRAMES);
|
||||
reader.accept(new ModifyCanonicalCtrVisitor(writer), 0);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
|
||||
/** Replaces whatever <init> method it finds with <init>(Ljava/lang/Object;)V. */
|
||||
static class ModifyCanonicalCtrVisitor extends ClassVisitor {
|
||||
ModifyCanonicalCtrVisitor(ClassVisitor cv) {
|
||||
super(ASM8, cv);
|
||||
}
|
||||
boolean foundCanonicalCtr;
|
||||
String className;
|
||||
@Override
|
||||
public void visit(final int version,
|
||||
final int access,
|
||||
final String name,
|
||||
final String signature,
|
||||
final String superName,
|
||||
final String[] interfaces) {
|
||||
this.className = name;
|
||||
cv.visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
@Override
|
||||
public MethodVisitor visitMethod(final int access,
|
||||
final String name,
|
||||
final String descriptor,
|
||||
final String signature,
|
||||
final String[] exceptions) {
|
||||
if (name.equals("<init>")) { // assume just a single constructor
|
||||
assert foundCanonicalCtr == false;
|
||||
foundCanonicalCtr = true;
|
||||
return null;
|
||||
} else {
|
||||
return cv.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
// must have a signature that is not the same as the test record constructor
|
||||
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Object;)V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Record", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
|
||||
cv.visitEnd();
|
||||
}
|
||||
var cf = ClassFile.of();
|
||||
return cf.transform(cf.parse(classBytes), ClassTransform.dropping(ce ->
|
||||
ce instanceof MethodModel mm && mm.methodName().equalsString(INIT_NAME))
|
||||
.andThen(ClassTransform.endHandler(clb -> clb.withMethodBody(INIT_NAME,
|
||||
MethodTypeDesc.of(CD_void, CD_Object), ACC_PUBLIC, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(Record.class.describeConstable().orElseThrow(),
|
||||
INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
}))));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,7 +26,7 @@
|
||||
* @bug 8246774
|
||||
* @summary Basic tests for prohibited magic serialization methods
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @run testng ProhibitedMethods
|
||||
*/
|
||||
|
||||
@ -41,23 +41,23 @@ import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamClass;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.classfile.ClassTransform;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.function.Function;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import jdk.test.lib.compiler.InMemoryJavaCompiler;
|
||||
import jdk.test.lib.ByteCodeLoader;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import static java.lang.System.out;
|
||||
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import static java.lang.classfile.ClassFile.ACC_PRIVATE;
|
||||
import static java.lang.constant.ConstantDescs.CD_String;
|
||||
import static java.lang.constant.ConstantDescs.CD_void;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.Assert.expectThrows;
|
||||
@ -219,103 +219,38 @@ public class ProhibitedMethods {
|
||||
|
||||
// -- machinery for augmenting record classes with prohibited serial methods --
|
||||
|
||||
static final String WRITE_OBJECT_NAME = "writeObject";
|
||||
static final MethodTypeDesc WRITE_OBJECT_DESC = MethodTypeDesc.ofDescriptor("(Ljava/io/ObjectOutputStream;)V");
|
||||
|
||||
static byte[] addWriteObject(byte[] classBytes) {
|
||||
return addMethod(classBytes, cv -> new WriteObjectVisitor(cv));
|
||||
return addMethod(classBytes, WRITE_OBJECT_NAME, WRITE_OBJECT_DESC);
|
||||
}
|
||||
|
||||
static final String READ_OBJECT_NAME = "readObject";
|
||||
static final MethodTypeDesc READ_OBJECT_DESC = MethodTypeDesc.ofDescriptor("(Ljava/io/ObjectInputStream;)V");
|
||||
|
||||
static byte[] addReadObject(byte[] classBytes) {
|
||||
return addMethod(classBytes, cv -> new ReadObjectVisitor(cv));
|
||||
return addMethod(classBytes, READ_OBJECT_NAME, READ_OBJECT_DESC);
|
||||
}
|
||||
|
||||
static final String READ_OBJECT_NO_DATA_NAME = "readObjectNoData";
|
||||
static final MethodTypeDesc READ_OBJECT_NO_DATA_DESC = MethodTypeDesc.of(CD_void);
|
||||
|
||||
static byte[] addReadObjectNoData(byte[] classBytes) {
|
||||
return addMethod(classBytes, cv -> new ReadObjectNoDataVisitor(cv));
|
||||
return addMethod(classBytes, READ_OBJECT_NO_DATA_NAME, READ_OBJECT_NO_DATA_DESC);
|
||||
}
|
||||
|
||||
static byte[] addMethod(byte[] classBytes,
|
||||
Function<ClassVisitor,ClassVisitor> classVisitorCreator) {
|
||||
ClassReader reader = new ClassReader(classBytes);
|
||||
ClassWriter writer = new ClassWriter(reader, COMPUTE_MAXS | COMPUTE_FRAMES);
|
||||
reader.accept(classVisitorCreator.apply(writer), 0);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
|
||||
static abstract class AbstractVisitor extends ClassVisitor {
|
||||
final String nameOfMethodToAdd;
|
||||
AbstractVisitor(ClassVisitor cv, String nameOfMethodToAdd) {
|
||||
super(ASM8, cv);
|
||||
this.nameOfMethodToAdd = nameOfMethodToAdd;
|
||||
}
|
||||
@Override
|
||||
public MethodVisitor visitMethod(final int access,
|
||||
final String name,
|
||||
final String descriptor,
|
||||
final String signature,
|
||||
final String[] exceptions) {
|
||||
// the method-to-be-added should not already exist
|
||||
assert !name.equals(nameOfMethodToAdd) : "Unexpected " + name + " method";
|
||||
return cv.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
}
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
throw new UnsupportedOperationException("implement me");
|
||||
}
|
||||
}
|
||||
|
||||
/** A visitor that generates and adds a writeObject method. */
|
||||
static final class WriteObjectVisitor extends AbstractVisitor {
|
||||
static final String WRITE_OBJECT_NAME = "writeObject";
|
||||
static final String WRITE_OBJECT_DESC = "(Ljava/io/ObjectOutputStream;)V";
|
||||
WriteObjectVisitor(ClassVisitor cv) { super(cv, WRITE_OBJECT_NAME); }
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
MethodVisitor mv = cv.visitMethod(ACC_PRIVATE, WRITE_OBJECT_NAME, WRITE_OBJECT_DESC, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(WRITE_OBJECT_NAME + " should not be invoked");
|
||||
mv.visitMethodInsn(INVOKESTATIC, "org/testng/Assert", "fail", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cv.visitEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/** A visitor that generates and adds a readObject method. */
|
||||
static final class ReadObjectVisitor extends AbstractVisitor {
|
||||
static final String READ_OBJECT_NAME = "readObject";
|
||||
static final String READ_OBJECT_DESC = "(Ljava/io/ObjectInputStream;)V";
|
||||
ReadObjectVisitor(ClassVisitor cv) { super(cv, READ_OBJECT_NAME); }
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
MethodVisitor mv = cv.visitMethod(ACC_PRIVATE, READ_OBJECT_NAME, READ_OBJECT_DESC, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(READ_OBJECT_NAME + " should not be invoked");
|
||||
mv.visitMethodInsn(INVOKESTATIC, "org/testng/Assert", "fail", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cv.visitEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/** A visitor that generates and adds a readObjectNoData method. */
|
||||
static final class ReadObjectNoDataVisitor extends AbstractVisitor {
|
||||
static final String READ_OBJECT_NO_DATA_NAME = "readObjectNoData";
|
||||
static final String READ_OBJECT_NO_DATA_DESC = "()V";
|
||||
ReadObjectNoDataVisitor(ClassVisitor cv) { super(cv, READ_OBJECT_NO_DATA_NAME); }
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
MethodVisitor mv = cv.visitMethod(ACC_PRIVATE, READ_OBJECT_NO_DATA_NAME, READ_OBJECT_NO_DATA_DESC, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(READ_OBJECT_NO_DATA_NAME + " should not be invoked");
|
||||
mv.visitMethodInsn(INVOKESTATIC, "org/testng/Assert", "fail", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cv.visitEnd();
|
||||
}
|
||||
String name, MethodTypeDesc desc) {
|
||||
var cf = ClassFile.of();
|
||||
return cf.transform(cf.parse(classBytes), ClassTransform.endHandler(clb -> {
|
||||
clb.withMethodBody(name, desc, ACC_PRIVATE, cob -> {
|
||||
cob.constantInstruction(name + " should not be invoked");
|
||||
cob.invokestatic(Assert.class.describeConstable().orElseThrow(), "fail",
|
||||
MethodTypeDesc.of(CD_void, CD_String));
|
||||
cob.return_();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
// -- infra sanity --
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,7 +26,7 @@
|
||||
* @bug 8246774
|
||||
* @summary Basic tests for prohibited magic serialPersistentFields
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @run testng SerialPersistentFieldsTest
|
||||
*/
|
||||
|
||||
@ -38,23 +38,34 @@ import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamClass;
|
||||
import java.io.ObjectStreamField;
|
||||
import java.io.Serializable;
|
||||
import java.lang.classfile.ClassBuilder;
|
||||
import java.lang.classfile.ClassElement;
|
||||
import java.lang.classfile.ClassTransform;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.FieldModel;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.math.BigDecimal;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.FieldVisitor;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
import jdk.test.lib.ByteCodeLoader;
|
||||
import jdk.test.lib.compiler.InMemoryJavaCompiler;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import static java.lang.System.out;
|
||||
import static jdk.internal.org.objectweb.asm.ClassWriter.*;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import static java.lang.classfile.ClassFile.ACC_FINAL;
|
||||
import static java.lang.classfile.ClassFile.ACC_PRIVATE;
|
||||
import static java.lang.classfile.ClassFile.ACC_STATIC;
|
||||
import static java.lang.constant.ConstantDescs.CD_Class;
|
||||
import static java.lang.constant.ConstantDescs.CD_String;
|
||||
import static java.lang.constant.ConstantDescs.CD_void;
|
||||
import static java.lang.constant.ConstantDescs.CLASS_INIT_NAME;
|
||||
import static java.lang.constant.ConstantDescs.INIT_NAME;
|
||||
import static java.lang.constant.ConstantDescs.MTD_void;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
@ -218,105 +229,62 @@ public class SerialPersistentFieldsTest {
|
||||
|
||||
static byte[] addSerialPersistentFields(byte[] classBytes,
|
||||
ObjectStreamField[] spf) {
|
||||
ClassReader reader = new ClassReader(classBytes);
|
||||
ClassWriter writer = new ClassWriter(reader, COMPUTE_MAXS | COMPUTE_FRAMES);
|
||||
reader.accept(new SerialPersistentFieldsVisitor(writer, spf), 0);
|
||||
return writer.toByteArray();
|
||||
var cf = ClassFile.of();
|
||||
var model = cf.parse(classBytes);
|
||||
return cf.transform(model, new SerialPersistentFieldsVisitor(model.thisClass().asSymbol(), spf));
|
||||
}
|
||||
|
||||
/** A visitor that adds a serialPersistentFields field, and assigns it in clinit. */
|
||||
static final class SerialPersistentFieldsVisitor extends ClassVisitor {
|
||||
static final class SerialPersistentFieldsVisitor implements ClassTransform {
|
||||
static final String FIELD_NAME = "serialPersistentFields";
|
||||
static final String FIELD_DESC = "[Ljava/io/ObjectStreamField;";
|
||||
static final ClassDesc CD_ObjectStreamField = ObjectStreamField.class.describeConstable().orElseThrow();
|
||||
static final ClassDesc FIELD_DESC = CD_ObjectStreamField.arrayType();
|
||||
final ObjectStreamField[] spf;
|
||||
String className;
|
||||
SerialPersistentFieldsVisitor(ClassVisitor cv, ObjectStreamField[] spf) {
|
||||
super(ASM8, cv);
|
||||
final ClassDesc className;
|
||||
SerialPersistentFieldsVisitor(ClassDesc className, ObjectStreamField[] spf) {
|
||||
this.className = className;
|
||||
this.spf = spf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(final int version,
|
||||
final int access,
|
||||
final String name,
|
||||
final String signature,
|
||||
final String superName,
|
||||
final String[] interfaces) {
|
||||
this.className = name;
|
||||
cv.visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
@Override
|
||||
public FieldVisitor visitField(final int access,
|
||||
final String name,
|
||||
final String descriptor,
|
||||
final String signature,
|
||||
final Object value) {
|
||||
// the field-to-be-added should not already exist
|
||||
assert !name.equals("serialPersistentFields") : "Unexpected " + name + " field";
|
||||
return cv.visitField(access, name, descriptor, signature, value);
|
||||
}
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
{
|
||||
FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL,
|
||||
FIELD_NAME,
|
||||
FIELD_DESC,
|
||||
null,
|
||||
null);
|
||||
fv.visitEnd();
|
||||
public void accept(ClassBuilder builder, ClassElement element) {
|
||||
if (element instanceof FieldModel fieldModel) {
|
||||
var name = fieldModel.fieldName().stringValue();
|
||||
assert !name.equals(FIELD_NAME) : "Unexpected " + FIELD_NAME + " field";
|
||||
builder.accept(fieldModel);
|
||||
} else {
|
||||
builder.accept(element);
|
||||
}
|
||||
{
|
||||
MethodVisitor mv = cv.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitIntInsn(BIPUSH, spf.length);
|
||||
mv.visitTypeInsn(ANEWARRAY, "java/io/ObjectStreamField");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void atEnd(ClassBuilder builder) {
|
||||
builder.withField(FIELD_NAME, FIELD_DESC, ACC_PRIVATE | ACC_STATIC | ACC_FINAL);
|
||||
builder.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> {
|
||||
cob.bipush(spf.length);
|
||||
cob.anewarray(CD_ObjectStreamField);
|
||||
|
||||
for (int i = 0; i < spf.length; i++) {
|
||||
ObjectStreamField osf = spf[i];
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitIntInsn(BIPUSH, i);
|
||||
mv.visitTypeInsn(NEW, "java/io/ObjectStreamField");
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitLdcInsn(osf.getName());
|
||||
if (osf.getType().isPrimitive()) {
|
||||
mv.visitFieldInsn(GETSTATIC, getPrimitiveBoxClass(osf.getType()), "TYPE", "Ljava/lang/Class;");
|
||||
cob.dup();
|
||||
cob.bipush(i);
|
||||
cob.new_(CD_ObjectStreamField);
|
||||
cob.dup();
|
||||
cob.constantInstruction(osf.getName());
|
||||
if (osf.isPrimitive()) {
|
||||
cob.constantInstruction(DynamicConstantDesc.ofNamed(
|
||||
ConstantDescs.BSM_PRIMITIVE_CLASS, String.valueOf(osf.getTypeCode()), CD_Class));
|
||||
} else {
|
||||
mv.visitLdcInsn(Type.getType(osf.getType()));
|
||||
// Currently Classfile API cannot encode primitive classdescs as condy
|
||||
cob.constantInstruction(osf.getType().describeConstable().orElseThrow());
|
||||
}
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/io/ObjectStreamField", "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V", false);
|
||||
mv.visitInsn(AASTORE);
|
||||
cob.invokespecial(CD_ObjectStreamField, INIT_NAME, MethodTypeDesc.of(CD_void, CD_String, CD_Class));
|
||||
cob.aastore();
|
||||
}
|
||||
|
||||
mv.visitFieldInsn(PUTSTATIC, className, "serialPersistentFields", "[Ljava/io/ObjectStreamField;");
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
cv.visitEnd();
|
||||
}
|
||||
|
||||
static String getPrimitiveBoxClass(final Class<?> clazz) {
|
||||
if (!clazz.isPrimitive())
|
||||
throw new AssertionError("unexpected non-primitive:" + clazz);
|
||||
|
||||
if (clazz == Integer.TYPE) {
|
||||
return "java/lang/Integer";
|
||||
} else if (clazz == Boolean.TYPE) {
|
||||
return "java/lang/Boolean";
|
||||
} else if (clazz == Byte.TYPE) {
|
||||
return "java/lang/Byte";
|
||||
} else if (clazz == Character.TYPE) {
|
||||
return "java/lang/Character";
|
||||
} else if (clazz == Short.TYPE) {
|
||||
return "java/lang/Short";
|
||||
} else if (clazz == Double.TYPE) {
|
||||
return "java/lang/Double";
|
||||
} else if (clazz == Float.TYPE) {
|
||||
return "java/lang/Float";
|
||||
} else if (clazz == Long.TYPE) {
|
||||
return "java/lang/Long";
|
||||
} else {
|
||||
throw new AssertionError("unknown:" + clazz);
|
||||
}
|
||||
cob.putstatic(className, FIELD_NAME, FIELD_DESC);
|
||||
cob.return_();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,10 +24,24 @@
|
||||
/* @test
|
||||
* @bug 8057919
|
||||
* @summary Class.getSimpleName() should work for non-JLS compliant class names
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
*/
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import java.lang.classfile.ClassBuilder;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.CodeBuilder;
|
||||
import java.lang.classfile.attribute.EnclosingMethodAttribute;
|
||||
import java.lang.classfile.attribute.InnerClassInfo;
|
||||
import java.lang.classfile.attribute.InnerClassesAttribute;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
import static java.lang.classfile.ClassFile.ACC_STATIC;
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.constant.ConstantDescs.INIT_NAME;
|
||||
import static java.lang.constant.ConstantDescs.MTD_void;
|
||||
|
||||
public class GetSimpleNameTest {
|
||||
static class NestedClass {}
|
||||
@ -121,88 +135,89 @@ public class GetSimpleNameTest {
|
||||
}
|
||||
|
||||
static class BytecodeGenerator {
|
||||
final String innerName;
|
||||
final String outerName;
|
||||
final ClassDesc innerName;
|
||||
final ClassDesc outerName;
|
||||
final String simpleName;
|
||||
|
||||
BytecodeGenerator(String innerName, String outerName, String simpleName) {
|
||||
this.innerName = intl(innerName);
|
||||
this.outerName = intl(outerName);
|
||||
this.innerName = ClassDesc.of(innerName);
|
||||
this.outerName = ClassDesc.of(outerName);
|
||||
this.simpleName = simpleName;
|
||||
}
|
||||
|
||||
static String intl(String name) { return name.replace('.', '/'); }
|
||||
|
||||
static void makeDefaultCtor(ClassWriter cw) {
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
static void makeDefaultCtor(ClassBuilder clb) {
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cb -> {
|
||||
cb.aload(0);
|
||||
cb.invokespecial(CD_Object, INIT_NAME, MTD_void);
|
||||
cb.return_();
|
||||
});
|
||||
}
|
||||
|
||||
void makeCtxk(ClassWriter cw, boolean isInner) {
|
||||
void makeCtxk(ClassBuilder clb, boolean isInner) {
|
||||
if (isInner) {
|
||||
cw.visitOuterClass(outerName, "f", "()V");
|
||||
clb.with(EnclosingMethodAttribute.of(outerName,
|
||||
Optional.of("f"), Optional.of(MTD_void)));
|
||||
} else {
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "f", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
clb.withMethodBody("f", MTD_void, ACC_PUBLIC | ACC_STATIC,
|
||||
CodeBuilder::return_);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] getNestedClasses(boolean isInner) {
|
||||
String name = (isInner ? innerName : outerName);
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, name, null, "java/lang/Object", null);
|
||||
|
||||
cw.visitInnerClass(innerName, outerName, simpleName, ACC_PUBLIC | ACC_STATIC);
|
||||
|
||||
makeDefaultCtor(cw);
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
var name = (isInner ? innerName : outerName);
|
||||
return ClassFile.of().build(name, clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
clb.with(InnerClassesAttribute.of(
|
||||
InnerClassInfo.of(innerName,
|
||||
Optional.of(outerName),
|
||||
Optional.of(simpleName))));
|
||||
makeDefaultCtor(clb);
|
||||
});
|
||||
}
|
||||
|
||||
byte[] getInnerClasses(boolean isInner) {
|
||||
String name = (isInner ? innerName : outerName);
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, name, null, "java/lang/Object", null);
|
||||
|
||||
cw.visitInnerClass(innerName, outerName, simpleName, ACC_PUBLIC);
|
||||
|
||||
makeDefaultCtor(cw);
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
var name = (isInner ? innerName : outerName);
|
||||
return ClassFile.of().build(name, clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
clb.with(InnerClassesAttribute.of(
|
||||
InnerClassInfo.of(innerName,
|
||||
Optional.of(outerName),
|
||||
Optional.of(simpleName),
|
||||
AccessFlag.PUBLIC)));
|
||||
makeDefaultCtor(clb);
|
||||
});
|
||||
}
|
||||
|
||||
byte[] getLocalClasses(boolean isInner) {
|
||||
String name = (isInner ? innerName : outerName);
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, name, null, "java/lang/Object", null);
|
||||
|
||||
cw.visitInnerClass(innerName, null, simpleName, ACC_PUBLIC | ACC_STATIC);
|
||||
makeCtxk(cw, isInner);
|
||||
|
||||
makeDefaultCtor(cw);
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
var name = (isInner ? innerName : outerName);
|
||||
return ClassFile.of().build(name, clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
clb.with(InnerClassesAttribute.of(
|
||||
InnerClassInfo.of(innerName,
|
||||
Optional.empty(),
|
||||
Optional.of(simpleName),
|
||||
AccessFlag.PUBLIC, AccessFlag.STATIC)));
|
||||
makeDefaultCtor(clb);
|
||||
makeCtxk(clb, isInner);
|
||||
});
|
||||
}
|
||||
|
||||
byte[] getAnonymousClasses(boolean isInner) {
|
||||
String name = (isInner ? innerName : outerName);
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, name, null, "java/lang/Object", null);
|
||||
|
||||
cw.visitInnerClass(innerName, null, null, ACC_PUBLIC | ACC_STATIC);
|
||||
makeCtxk(cw, isInner);
|
||||
|
||||
makeDefaultCtor(cw);
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
var name = (isInner ? innerName : outerName);
|
||||
return ClassFile.of().build(name, clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
clb.with(InnerClassesAttribute.of(
|
||||
InnerClassInfo.of(innerName,
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
AccessFlag.PUBLIC, AccessFlag.STATIC)));
|
||||
makeDefaultCtor(clb);
|
||||
makeCtxk(clb, isInner);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,15 @@
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.classfile.AnnotationElement;
|
||||
import java.lang.classfile.AnnotationValue;
|
||||
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.RuntimeVisibleAnnotationsAttribute;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
@ -36,13 +42,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Attribute;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.commons.ModuleTargetAttribute;
|
||||
import jdk.test.lib.util.ModuleInfoWriter;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
@ -51,9 +50,7 @@ import static org.testng.Assert.*;
|
||||
/**
|
||||
* @test
|
||||
* @enablePreview
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* java.base/jdk.internal.org.objectweb.asm.commons
|
||||
* java.base/jdk.internal.module
|
||||
* @modules java.base/jdk.internal.module
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.util.ModuleInfoWriter
|
||||
* @run testng AnnotationsTest
|
||||
@ -149,23 +146,39 @@ public class AnnotationsTest {
|
||||
* Adds the Deprecated annotation to the given module-info class file.
|
||||
*/
|
||||
static byte[] addDeprecated(byte[] bytes, boolean forRemoval, String since) {
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
var cf = ClassFile.of();
|
||||
var oldModel = cf.parse(bytes);
|
||||
return cf.transform(oldModel, new ClassTransform() {
|
||||
boolean rvaaFound = false;
|
||||
|
||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM6, cw) { };
|
||||
@Override
|
||||
public void accept(ClassBuilder builder, ClassElement element) {
|
||||
if (!rvaaFound && element instanceof RuntimeVisibleAnnotationsAttribute rvaa) {
|
||||
rvaaFound = true;
|
||||
var res = new ArrayList<java.lang.classfile.Annotation>(rvaa.annotations().size() + 1);
|
||||
res.addAll(rvaa.annotations());
|
||||
res.add(createDeprecated());
|
||||
builder.accept(RuntimeVisibleAnnotationsAttribute.of(res));
|
||||
return;
|
||||
}
|
||||
builder.accept(element);
|
||||
}
|
||||
|
||||
ClassReader cr = new ClassReader(bytes);
|
||||
List<Attribute> attrs = new ArrayList<>();
|
||||
attrs.add(new ModuleTargetAttribute());
|
||||
cr.accept(cv, attrs.toArray(new Attribute[0]), 0);
|
||||
@Override
|
||||
public void atEnd(ClassBuilder builder) {
|
||||
if (!rvaaFound) {
|
||||
builder.accept(RuntimeVisibleAnnotationsAttribute.of(List.of(createDeprecated())));
|
||||
}
|
||||
}
|
||||
|
||||
AnnotationVisitor annotationVisitor
|
||||
= cv.visitAnnotation("Ljava/lang/Deprecated;", true);
|
||||
annotationVisitor.visit("forRemoval", forRemoval);
|
||||
annotationVisitor.visit("since", since);
|
||||
annotationVisitor.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
private java.lang.classfile.Annotation createDeprecated() {
|
||||
return java.lang.classfile.Annotation.of(
|
||||
Deprecated.class.describeConstable().orElseThrow(),
|
||||
AnnotationElement.of("forRemoval", AnnotationValue.ofBoolean(forRemoval)),
|
||||
AnnotationElement.of("since", AnnotationValue.ofString(since))
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,18 +26,21 @@
|
||||
* @bug 8228988 8266598
|
||||
* @summary An annotation-typed property of an annotation that is represented as an
|
||||
* incompatible property of another type should yield an AnnotationTypeMismatchException.
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @run main AnnotationTypeMismatchTest
|
||||
*/
|
||||
|
||||
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.annotation.AnnotationTypeMismatchException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.classfile.Annotation;
|
||||
import java.lang.classfile.AnnotationElement;
|
||||
import java.lang.classfile.AnnotationValue;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
|
||||
import java.lang.constant.ClassDesc;
|
||||
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
|
||||
public class AnnotationTypeMismatchTest {
|
||||
|
||||
@ -46,12 +49,15 @@ public class AnnotationTypeMismatchTest {
|
||||
* @AnAnnotation(value = AnEnum.VALUE) // would now be: value = @Value
|
||||
* class Carrier { }
|
||||
*/
|
||||
ClassWriter writer = new ClassWriter(0);
|
||||
writer.visit(Opcodes.V1_8, 0, "sample/Carrier", null, Type.getInternalName(Object.class), null);
|
||||
AnnotationVisitor v = writer.visitAnnotation(Type.getDescriptor(AnAnnotation.class), true);
|
||||
v.visitEnum("value", Type.getDescriptor(AnEnum.class), "VALUE");
|
||||
writer.visitEnd();
|
||||
byte[] b = writer.toByteArray();
|
||||
byte[] b = ClassFile.of().build(ClassDesc.of("sample", "Carrier"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
Annotation.of(
|
||||
AnAnnotation.class.describeConstable().orElseThrow(),
|
||||
AnnotationElement.of("value", AnnotationValue.of(AnEnum.VALUE))
|
||||
)
|
||||
));
|
||||
});
|
||||
ByteArrayClassLoader cl = new ByteArrayClassLoader(AnnotationTypeMismatchTest.class.getClassLoader());
|
||||
cl.init(b);
|
||||
AnAnnotation sample = cl.loadClass("sample.Carrier").getAnnotation(AnAnnotation.class);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,18 +27,21 @@
|
||||
* @summary Annotation property which is compiled as an array property but
|
||||
* changed observed as a singular element should throw an
|
||||
* AnnotationTypeMismatchException
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @run main ArityTypeMismatchTest
|
||||
*/
|
||||
|
||||
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.annotation.AnnotationTypeMismatchException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.classfile.Annotation;
|
||||
import java.lang.classfile.AnnotationElement;
|
||||
import java.lang.classfile.AnnotationValue;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
|
||||
import java.lang.constant.ClassDesc;
|
||||
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
|
||||
public class ArityTypeMismatchTest {
|
||||
|
||||
@ -54,15 +57,15 @@ public class ArityTypeMismatchTest {
|
||||
*
|
||||
* where @AnAnnotation expects a singular value.
|
||||
*/
|
||||
ClassWriter writer = new ClassWriter(0);
|
||||
writer.visit(Opcodes.V1_8, 0, "sample/Carrier", null, Type.getInternalName(Object.class), null);
|
||||
AnnotationVisitor v = writer.visitAnnotation(Type.getDescriptor(AnAnnotation.class), true);
|
||||
AnnotationVisitor v2 = v.visitArray("value");
|
||||
v2.visit(null, "v");
|
||||
v2.visitEnd();
|
||||
v.visitEnd();
|
||||
writer.visitEnd();
|
||||
byte[] b = writer.toByteArray();
|
||||
byte[] b = ClassFile.of().build(ClassDesc.of("sample", "Carrier"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
Annotation.of(
|
||||
AnAnnotation.class.describeConstable().orElseThrow(),
|
||||
AnnotationElement.of("value", AnnotationValue.of(new String[] {"v"}))
|
||||
)
|
||||
));
|
||||
});
|
||||
ByteArrayClassLoader cl = new ByteArrayClassLoader(ArityTypeMismatchTest.class.getClassLoader());
|
||||
cl.init(b);
|
||||
AnAnnotation sample = cl.loadClass("sample.Carrier").getAnnotation(AnAnnotation.class);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,21 +26,27 @@
|
||||
* @bug 8266766
|
||||
* @summary An array property of a type that is no longer of a type that is a legal member of an
|
||||
* annotation should throw an AnnotationTypeMismatchException.
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @run main ArrayTypeMismatchTest
|
||||
*/
|
||||
|
||||
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.AnnotationTypeMismatchException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.classfile.AnnotationElement;
|
||||
import java.lang.classfile.AnnotationValue;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import static java.lang.classfile.ClassFile.ACC_ABSTRACT;
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
|
||||
public class ArrayTypeMismatchTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
@ -67,8 +73,7 @@ public class ArrayTypeMismatchTest {
|
||||
throw new IllegalStateException("Found value: " + value);
|
||||
} catch (InvocationTargetException ite) {
|
||||
Throwable cause = ite.getCause();
|
||||
if (cause instanceof AnnotationTypeMismatchException) {
|
||||
AnnotationTypeMismatchException e = ((AnnotationTypeMismatchException) cause);
|
||||
if (cause instanceof AnnotationTypeMismatchException e) {
|
||||
if (!e.element().getName().equals("value")) {
|
||||
throw new IllegalStateException("Unexpected element: " + e.element());
|
||||
} else if (!e.foundType().equals("Array with component tag: @")) {
|
||||
@ -81,34 +86,35 @@ public class ArrayTypeMismatchTest {
|
||||
}
|
||||
|
||||
private static byte[] carrierType() {
|
||||
ClassWriter writer = new ClassWriter(0);
|
||||
writer.visit(Opcodes.V1_8, 0, "sample/Carrier", null, Type.getInternalName(Object.class), null);
|
||||
AnnotationVisitor v = writer.visitAnnotation("Lsample/Host;", true);
|
||||
AnnotationVisitor a = v.visitArray("value");
|
||||
a.visitAnnotation(null, Type.getDescriptor(NoAnnotation.class)).visitEnd();
|
||||
a.visitEnd();
|
||||
v.visitEnd();
|
||||
writer.visitEnd();
|
||||
return writer.toByteArray();
|
||||
return ClassFile.of().build(ClassDesc.of("sample", "Carrier"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
var badAnnotationArray = AnnotationValue.ofArray(AnnotationValue.ofAnnotation(
|
||||
java.lang.classfile.Annotation.of(
|
||||
NoAnnotation.class.describeConstable().orElseThrow()
|
||||
)));
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
java.lang.classfile.Annotation.of(ClassDesc.of("sample", "Host"),
|
||||
AnnotationElement.of("value", badAnnotationArray)
|
||||
)
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
private static byte[] annotationType() {
|
||||
ClassWriter writer = new ClassWriter(0);
|
||||
writer.visit(Opcodes.V1_8,
|
||||
Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE | Opcodes.ACC_ANNOTATION,
|
||||
"sample/Host",
|
||||
null,
|
||||
Type.getInternalName(Object.class),
|
||||
new String[]{Type.getInternalName(Annotation.class)});
|
||||
AnnotationVisitor a = writer.visitAnnotation(Type.getDescriptor(Retention.class), true);
|
||||
a.visitEnum("value", Type.getDescriptor(RetentionPolicy.class), RetentionPolicy.RUNTIME.name());
|
||||
writer.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT,
|
||||
"value",
|
||||
Type.getMethodDescriptor(Type.getType(NoAnnotation[].class)),
|
||||
null,
|
||||
null).visitEnd();
|
||||
writer.visitEnd();
|
||||
return writer.toByteArray();
|
||||
return ClassFile.of().build(ClassDesc.of("sample", "Host"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withInterfaceSymbols(Annotation.class.describeConstable().orElseThrow());
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.ABSTRACT, AccessFlag.INTERFACE,
|
||||
AccessFlag.ANNOTATION);
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
java.lang.classfile.Annotation.of(
|
||||
Retention.class.describeConstable().orElseThrow(),
|
||||
AnnotationElement.of("value", AnnotationValue.of(RetentionPolicy.RUNTIME))
|
||||
)
|
||||
));
|
||||
clb.withMethod("value", MethodTypeDesc.of(NoAnnotation[].class.describeConstable()
|
||||
.orElseThrow()), ACC_PUBLIC | ACC_ABSTRACT, mb -> {});
|
||||
});
|
||||
}
|
||||
|
||||
public interface NoAnnotation { }
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,18 +26,21 @@
|
||||
* @bug 8228988 8266598
|
||||
* @summary An enumeration-typed property of an annotation that is represented as an
|
||||
* incompatible property of another type should yield an AnnotationTypeMismatchException.
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @run main EnumTypeMismatchTest
|
||||
*/
|
||||
|
||||
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.annotation.AnnotationTypeMismatchException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.classfile.Annotation;
|
||||
import java.lang.classfile.AnnotationElement;
|
||||
import java.lang.classfile.AnnotationValue;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
|
||||
import java.lang.constant.ClassDesc;
|
||||
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
|
||||
public class EnumTypeMismatchTest {
|
||||
|
||||
@ -46,12 +49,14 @@ public class EnumTypeMismatchTest {
|
||||
* @AnAnnotation(value = @AnAnnotation) // would now be: value = AnEnum.VALUE
|
||||
* class Carrier { }
|
||||
*/
|
||||
ClassWriter writer = new ClassWriter(0);
|
||||
writer.visit(Opcodes.V1_8, 0, "sample/Carrier", null, Type.getInternalName(Object.class), null);
|
||||
AnnotationVisitor v = writer.visitAnnotation(Type.getDescriptor(AnAnnotation.class), true);
|
||||
v.visitAnnotation("value", Type.getDescriptor(AnAnnotation.class)).visitEnd();
|
||||
writer.visitEnd();
|
||||
byte[] b = writer.toByteArray();
|
||||
ClassDesc anAnnotationDesc = AnAnnotation.class.describeConstable().orElseThrow();
|
||||
byte[] b = ClassFile.of().build(ClassDesc.of("sample", "Carrier"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
Annotation.of(anAnnotationDesc, AnnotationElement.of("value",
|
||||
AnnotationValue.ofAnnotation(Annotation.of(anAnnotationDesc))))
|
||||
));
|
||||
});
|
||||
ByteArrayClassLoader cl = new ByteArrayClassLoader(EnumTypeMismatchTest.class.getClassLoader());
|
||||
cl.init(b);
|
||||
AnAnnotation sample = cl.loadClass("sample.Carrier").getAnnotation(AnAnnotation.class);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -39,8 +39,8 @@ import java.util.stream.Stream;
|
||||
* @test
|
||||
* @bug 8158510
|
||||
* @summary Verify valid annotation
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @modules java.base/sun.reflect.annotation
|
||||
* @enablePreview
|
||||
* @clean AnnotationWithVoidReturn AnnotationWithParameter
|
||||
* AnnotationWithExtraInterface AnnotationWithException
|
||||
* AnnotationWithHashCode AnnotationWithDefaultMember
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -22,18 +22,39 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Create class file using ASM, slightly modified the ASMifier output
|
||||
* Create class file using Class-File API, slightly modified the ASMifier output
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.classfile.Annotation;
|
||||
import java.lang.classfile.AnnotationElement;
|
||||
import java.lang.classfile.AnnotationValue;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.attribute.AnnotationDefaultAttribute;
|
||||
import java.lang.classfile.attribute.ExceptionsAttribute;
|
||||
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
|
||||
import static java.lang.classfile.ClassFile.ACC_ABSTRACT;
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
import static java.lang.constant.ConstantDescs.CD_Exception;
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.constant.ConstantDescs.CD_int;
|
||||
import static java.lang.constant.ConstantDescs.MTD_void;
|
||||
import static java.lang.reflect.AccessFlag.ABSTRACT;
|
||||
import static java.lang.reflect.AccessFlag.INTERFACE;
|
||||
import static java.lang.reflect.AccessFlag.PUBLIC;
|
||||
|
||||
public class ClassFileGenerator {
|
||||
private static final ClassDesc CD_Annotation = java.lang.annotation.Annotation.class.describeConstable().orElseThrow();
|
||||
private static final ClassDesc CD_Retention = Retention.class.describeConstable().orElseThrow();
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
classFileWriter("AnnotationWithVoidReturn.class", AnnotationWithVoidReturnDump.dump());
|
||||
@ -63,35 +84,19 @@ public class ClassFileGenerator {
|
||||
|
||||
*/
|
||||
|
||||
private static class AnnotationWithVoidReturnDump implements Opcodes {
|
||||
public static byte[] dump() throws Exception {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
|
||||
cw.visit(52, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE,
|
||||
"AnnotationWithVoidReturn", null,
|
||||
"java/lang/Object", new String[]{"java/lang/annotation/Annotation"});
|
||||
|
||||
{
|
||||
av0 = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
|
||||
av0.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;",
|
||||
"RUNTIME");
|
||||
av0.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "m", "()V", null, null);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
av0 = mv.visitAnnotationDefault();
|
||||
av0.visit(null, new Integer(1));
|
||||
av0.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
|
||||
private static class AnnotationWithVoidReturnDump {
|
||||
public static byte[] dump() {
|
||||
return ClassFile.of().build(ClassDesc.of("AnnotationWithVoidReturn"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withInterfaceSymbols(CD_Annotation);
|
||||
clb.withFlags(PUBLIC, AccessFlag.ANNOTATION, ABSTRACT, AccessFlag.INTERFACE);
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
Annotation.of(CD_Retention, AnnotationElement.of("value",
|
||||
AnnotationValue.of(RetentionPolicy.RUNTIME)))
|
||||
));
|
||||
clb.withMethod("m", MTD_void, ACC_PUBLIC | ACC_ABSTRACT,
|
||||
mb -> mb.with(AnnotationDefaultAttribute.of(AnnotationValue.ofInt(1))));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,38 +109,19 @@ public class ClassFileGenerator {
|
||||
|
||||
*/
|
||||
|
||||
private static class AnnotationWithParameterDump implements Opcodes {
|
||||
public static byte[] dump() throws Exception {
|
||||
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
|
||||
cw.visit(52, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE,
|
||||
"AnnotationWithParameter", null,
|
||||
"java/lang/Object", new String[]{"java/lang/annotation/Annotation"});
|
||||
|
||||
{
|
||||
av0 = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
|
||||
av0.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;",
|
||||
"RUNTIME");
|
||||
av0.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT,
|
||||
"badValue",
|
||||
"(I)I", // Bad method with a parameter
|
||||
null, null);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
av0 = mv.visitAnnotationDefault();
|
||||
av0.visit(null, new Integer(-1));
|
||||
av0.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
private static class AnnotationWithParameterDump {
|
||||
public static byte[] dump() {
|
||||
return ClassFile.of().build(ClassDesc.of("AnnotationWithParameter"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withInterfaceSymbols(CD_Annotation);
|
||||
clb.withFlags(PUBLIC, AccessFlag.ANNOTATION, ABSTRACT, AccessFlag.INTERFACE);
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
Annotation.of(CD_Retention, AnnotationElement.of("value",
|
||||
AnnotationValue.of(RetentionPolicy.RUNTIME)))
|
||||
));
|
||||
clb.withMethod("m", MethodTypeDesc.of(CD_int, CD_int), ACC_PUBLIC | ACC_ABSTRACT,
|
||||
mb -> mb.with(AnnotationDefaultAttribute.of(AnnotationValue.ofInt(-1))));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,35 +134,19 @@ public class ClassFileGenerator {
|
||||
|
||||
*/
|
||||
|
||||
private static class AnnotationWithExtraInterfaceDump implements Opcodes {
|
||||
public static byte[] dump() throws Exception {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
|
||||
cw.visit(52, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE,
|
||||
"AnnotationWithExtraInterface", null,
|
||||
"java/lang/Object", new String[]{"java/lang/annotation/Annotation",
|
||||
"java/io/Serializable"});
|
||||
|
||||
{
|
||||
av0 = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
|
||||
av0.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;",
|
||||
"RUNTIME");
|
||||
av0.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "m", "()I", null, null);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
av0 = mv.visitAnnotationDefault();
|
||||
av0.visit(null, new Integer(1));
|
||||
av0.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
private static class AnnotationWithExtraInterfaceDump {
|
||||
public static byte[] dump() {
|
||||
return ClassFile.of().build(ClassDesc.of("AnnotationWithExtraInterface"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withInterfaceSymbols(CD_Annotation, Serializable.class.describeConstable().orElseThrow());
|
||||
clb.withFlags(PUBLIC, AccessFlag.ANNOTATION, ABSTRACT, AccessFlag.INTERFACE);
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
Annotation.of(CD_Retention, AnnotationElement.of("value",
|
||||
AnnotationValue.of(RetentionPolicy.RUNTIME)))
|
||||
));
|
||||
clb.withMethod("m", MethodTypeDesc.of(CD_int), ACC_PUBLIC | ACC_ABSTRACT,
|
||||
mb -> mb.with(AnnotationDefaultAttribute.of(AnnotationValue.ofInt(1))));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,35 +159,21 @@ public class ClassFileGenerator {
|
||||
|
||||
*/
|
||||
|
||||
private static class AnnotationWithExceptionDump implements Opcodes {
|
||||
public static byte[] dump() throws Exception {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
|
||||
cw.visit(52, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE,
|
||||
"AnnotationWithException", null,
|
||||
"java/lang/Object", new String[]{"java/lang/annotation/Annotation"});
|
||||
|
||||
{
|
||||
av0 = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
|
||||
av0.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;",
|
||||
"RUNTIME");
|
||||
av0.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "m", "()I", null,
|
||||
new String[] {"java/lang/Exception"});
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
av0 = mv.visitAnnotationDefault();
|
||||
av0.visit(null, new Integer(1));
|
||||
av0.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
private static class AnnotationWithExceptionDump {
|
||||
public static byte[] dump() {
|
||||
return ClassFile.of().build(ClassDesc.of("AnnotationWithException"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withInterfaceSymbols(CD_Annotation);
|
||||
clb.withFlags(PUBLIC, AccessFlag.ANNOTATION, ABSTRACT, AccessFlag.INTERFACE);
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
Annotation.of(CD_Retention, AnnotationElement.of("value",
|
||||
AnnotationValue.of(RetentionPolicy.RUNTIME)))
|
||||
));
|
||||
clb.withMethod("m", MethodTypeDesc.of(CD_int), ACC_PUBLIC | ACC_ABSTRACT, mb -> {
|
||||
mb.with(AnnotationDefaultAttribute.of(AnnotationValue.ofInt(1)));
|
||||
mb.with(ExceptionsAttribute.ofSymbols(CD_Exception));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,34 +186,19 @@ public class ClassFileGenerator {
|
||||
|
||||
*/
|
||||
|
||||
private static class AnnotationWithHashCodeDump implements Opcodes {
|
||||
public static byte[] dump() throws Exception {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
|
||||
cw.visit(52, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE,
|
||||
"AnnotationWithHashCode", null,
|
||||
"java/lang/Object", new String[]{"java/lang/annotation/Annotation"});
|
||||
|
||||
{
|
||||
av0 = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
|
||||
av0.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;",
|
||||
"RUNTIME");
|
||||
av0.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "hashCode", "()I", null, null);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
av0 = mv.visitAnnotationDefault();
|
||||
av0.visit(null, new Integer(1));
|
||||
av0.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
private static class AnnotationWithHashCodeDump {
|
||||
public static byte[] dump() {
|
||||
return ClassFile.of().build(ClassDesc.of("AnnotationWithHashCode"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withInterfaceSymbols(CD_Annotation);
|
||||
clb.withFlags(PUBLIC, AccessFlag.ANNOTATION, ABSTRACT, AccessFlag.INTERFACE);
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
Annotation.of(CD_Retention, AnnotationElement.of("value",
|
||||
AnnotationValue.of(RetentionPolicy.RUNTIME)))
|
||||
));
|
||||
clb.withMethod("hashCode", MethodTypeDesc.of(CD_int), ACC_PUBLIC | ACC_ABSTRACT,
|
||||
mb -> mb.with(AnnotationDefaultAttribute.of(AnnotationValue.ofInt(1))));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,47 +212,26 @@ public class ClassFileGenerator {
|
||||
|
||||
*/
|
||||
|
||||
private static class AnnotationWithDefaultMemberDump implements Opcodes {
|
||||
private static class AnnotationWithDefaultMemberDump {
|
||||
public static byte[] dump() throws Exception {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv, dv;
|
||||
AnnotationVisitor av0;
|
||||
|
||||
cw.visit(52, ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE,
|
||||
"AnnotationWithDefaultMember", null,
|
||||
"java/lang/Object", new String[]{"java/lang/annotation/Annotation"});
|
||||
|
||||
{
|
||||
av0 = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
|
||||
av0.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;",
|
||||
"RUNTIME");
|
||||
av0.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "m", "()I", null, null);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
av0 = mv.visitAnnotationDefault();
|
||||
av0.visit(null, new Integer(1));
|
||||
av0.visitEnd();
|
||||
}
|
||||
{
|
||||
dv = cw.visitMethod(ACC_PUBLIC, "d", "()I", null, null);
|
||||
dv.visitMaxs(1, 1);
|
||||
dv.visitCode();
|
||||
dv.visitInsn(Opcodes.ICONST_2);
|
||||
dv.visitInsn(Opcodes.IRETURN);
|
||||
dv.visitEnd();
|
||||
}
|
||||
{
|
||||
av0 = dv.visitAnnotationDefault();
|
||||
av0.visit(null, new Integer(2));
|
||||
av0.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(ClassDesc.of("AnnotationWithDefaultMember"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withInterfaceSymbols(CD_Annotation);
|
||||
clb.withFlags(PUBLIC, AccessFlag.ANNOTATION, ABSTRACT, AccessFlag.INTERFACE);
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
Annotation.of(CD_Retention, AnnotationElement.of("value",
|
||||
AnnotationValue.of(RetentionPolicy.RUNTIME)))
|
||||
));
|
||||
clb.withMethod("m", MethodTypeDesc.of(CD_int), ACC_PUBLIC | ACC_ABSTRACT,
|
||||
mb -> mb.with(AnnotationDefaultAttribute.of(AnnotationValue.ofInt(1))));
|
||||
clb.withMethod("d", MethodTypeDesc.of(CD_int), ACC_PUBLIC, mb -> {
|
||||
mb.with(AnnotationDefaultAttribute.of(AnnotationValue.ofInt(2)));
|
||||
mb.withCode(cob -> {
|
||||
cob.iconst_2();
|
||||
cob.ireturn();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,34 +244,19 @@ public class ClassFileGenerator {
|
||||
|
||||
*/
|
||||
|
||||
private static class AnnotationWithoutAnnotationAccessModifierDump implements Opcodes {
|
||||
public static byte[] dump() throws Exception {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
|
||||
cw.visit(52, ACC_PUBLIC + /* ACC_ANNOTATION +*/ ACC_ABSTRACT + ACC_INTERFACE,
|
||||
"AnnotationWithoutAnnotationAccessModifier", null,
|
||||
"java/lang/Object", new String[]{"java/lang/annotation/Annotation"});
|
||||
|
||||
{
|
||||
av0 = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
|
||||
av0.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;",
|
||||
"RUNTIME");
|
||||
av0.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "m", "()I", null, null);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
av0 = mv.visitAnnotationDefault();
|
||||
av0.visit(null, new Integer(1));
|
||||
av0.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
private static class AnnotationWithoutAnnotationAccessModifierDump {
|
||||
public static byte[] dump() {
|
||||
return ClassFile.of().build(ClassDesc.of("AnnotationWithoutAnnotationAccessModifier"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withInterfaceSymbols(CD_Annotation);
|
||||
clb.withFlags(PUBLIC, /*AccessFlag.ANNOTATION,*/ ABSTRACT, AccessFlag.INTERFACE);
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
Annotation.of(CD_Retention, AnnotationElement.of("value",
|
||||
AnnotationValue.of(RetentionPolicy.RUNTIME)))
|
||||
));
|
||||
clb.withMethod("m", MethodTypeDesc.of(CD_int), ACC_PUBLIC | ACC_ABSTRACT,
|
||||
mb -> mb.with(AnnotationDefaultAttribute.of(AnnotationValue.ofInt(1))));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -365,24 +270,16 @@ public class ClassFileGenerator {
|
||||
|
||||
*/
|
||||
|
||||
private static class HolderXDump implements Opcodes {
|
||||
public static byte[] dump() throws Exception {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
|
||||
cw.visit(52, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
|
||||
"HolderX", null,
|
||||
"java/lang/Object", new String[0]);
|
||||
|
||||
{
|
||||
AnnotationVisitor av0;
|
||||
av0 = cw.visitAnnotation("LGoodAnnotation;", true);
|
||||
av0.visitEnd();
|
||||
av0 = cw.visitAnnotation("LAnnotationWithoutAnnotationAccessModifier;", true);
|
||||
av0.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
private static class HolderXDump {
|
||||
public static byte[] dump() {
|
||||
return ClassFile.of().build(ClassDesc.of("HolderX"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withFlags(PUBLIC, ABSTRACT, INTERFACE);
|
||||
clb.with(RuntimeVisibleAnnotationsAttribute.of(
|
||||
Annotation.of(ClassDesc.of("GoodAnnotation")),
|
||||
Annotation.of(ClassDesc.of("ClassFileGenerator$AnnotationWithoutAnnotationAccessModifier"))
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
@ -87,7 +87,8 @@ ${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} bootreporter/*.java
|
||||
cd ..
|
||||
|
||||
${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \
|
||||
--add-exports java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED ${AGENT}.java asmlib/*.java
|
||||
--enable-preview --release 23 \
|
||||
${AGENT}.java asmlib/*.java
|
||||
${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -classpath .${PATHSEP}bootpath ${APP}.java
|
||||
|
||||
echo "Manifest-Version: 1.0" > ${AGENT}.mf
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -21,25 +21,29 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
/*
|
||||
* @test
|
||||
* @bug 6263319
|
||||
* @requires ((vm.opt.StartFlightRecording == null) | (vm.opt.StartFlightRecording == false)) & ((vm.opt.FlightRecorder == null) | (vm.opt.FlightRecorder == false))
|
||||
* @summary test setNativeMethodPrefix
|
||||
* @author Robert Field, Sun Microsystems
|
||||
*
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* java.management
|
||||
* @enablePreview
|
||||
* @modules java.management
|
||||
* java.instrument
|
||||
* @run shell/timeout=240 MakeJAR2.sh NativeMethodPrefixAgent NativeMethodPrefixApp 'Can-Retransform-Classes: true' 'Can-Set-Native-Method-Prefix: true'
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-CheckIntrinsics -javaagent:NativeMethodPrefixAgent.jar NativeMethodPrefixApp
|
||||
*/
|
||||
|
||||
import asmlib.Instrumentor;
|
||||
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.instrument.*;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.io.*;
|
||||
|
||||
import asmlib.*;
|
||||
import static java.lang.constant.ConstantDescs.*;
|
||||
|
||||
class NativeMethodPrefixAgent {
|
||||
|
||||
@ -54,6 +58,8 @@ class NativeMethodPrefixAgent {
|
||||
}
|
||||
|
||||
static class Tr implements ClassFileTransformer {
|
||||
private static final ClassDesc CD_StringIdCallbackReporter = ClassDesc.ofInternalName("bootreporter/StringIdCallbackReporter");
|
||||
private static final MethodTypeDesc MTD_void_String_int = MethodTypeDesc.of(CD_void, CD_String, CD_int);
|
||||
final String trname;
|
||||
final int transformId;
|
||||
|
||||
@ -76,11 +82,13 @@ class NativeMethodPrefixAgent {
|
||||
try {
|
||||
byte[] newcf = Instrumentor.instrFor(classfileBuffer)
|
||||
.addNativeMethodTrackingInjection(
|
||||
"wrapped_" + trname + "_",
|
||||
(h)->{
|
||||
h.push(h.getName());
|
||||
h.push(transformId);
|
||||
h.invokeStatic("bootreporter/StringIdCallbackReporter", "tracker", "(Ljava/lang/String;I)V", false);
|
||||
"wrapped_" + trname + "_", (name, h) -> {
|
||||
h.constantInstruction(name);
|
||||
h.constantInstruction(transformId);
|
||||
h.invokestatic(
|
||||
CD_StringIdCallbackReporter,
|
||||
"tracker",
|
||||
MTD_void_String_int);
|
||||
})
|
||||
.apply();
|
||||
/*** debugging ...
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -21,23 +21,28 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
/*
|
||||
* @test
|
||||
* @bug 6274264 6274241 5070281
|
||||
* @summary test retransformClasses
|
||||
* @author Robert Field, Sun Microsystems
|
||||
*
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* java.instrument
|
||||
* @enablePreview
|
||||
* @modules java.instrument
|
||||
* @run shell/timeout=240 MakeJAR2.sh RetransformAgent RetransformApp 'Can-Retransform-Classes: true'
|
||||
* @run main/othervm -javaagent:RetransformAgent.jar RetransformApp
|
||||
*/
|
||||
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.instrument.*;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.io.*;
|
||||
import asmlib.*;
|
||||
|
||||
import static java.lang.constant.ConstantDescs.CD_int;
|
||||
import static java.lang.constant.ConstantDescs.CD_void;
|
||||
|
||||
class RetransformAgent {
|
||||
|
||||
static ClassFileTransformer t1, t2, t3, t4;
|
||||
@ -48,6 +53,8 @@ class RetransformAgent {
|
||||
11, 40, 20, 11, 40, 20, 11, 40, 20, 11, 40, 20};
|
||||
|
||||
static class Tr implements ClassFileTransformer {
|
||||
private static final ClassDesc CD_RetransformAgent = RetransformAgent.class.describeConstable().orElseThrow();
|
||||
private static final MethodTypeDesc MTD_void_int = MethodTypeDesc.of(CD_void, CD_int);
|
||||
final String trname;
|
||||
final boolean onLoad;
|
||||
final int loadIndex;
|
||||
@ -83,9 +90,11 @@ class RetransformAgent {
|
||||
byte[] newcf = Instrumentor.instrFor(classfileBuffer)
|
||||
.addMethodEntryInjection(
|
||||
nname,
|
||||
(h)->{
|
||||
h.push(fixedIndex);
|
||||
h.invokeStatic("RetransformAgent", "callTracker", "(I)V", false);
|
||||
cb -> {
|
||||
cb.constantInstruction(fixedIndex);
|
||||
cb.invokestatic(
|
||||
CD_RetransformAgent,
|
||||
"callTracker", MTD_void_int);
|
||||
})
|
||||
.apply();
|
||||
/*** debugging ...
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,171 +23,133 @@
|
||||
|
||||
package asmlib;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.lang.classfile.AccessFlags;
|
||||
import java.lang.classfile.ClassBuilder;
|
||||
import java.lang.classfile.ClassElement;
|
||||
import java.lang.classfile.ClassModel;
|
||||
import java.lang.classfile.ClassTransform;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.CodeBuilder;
|
||||
import java.lang.classfile.CodeElement;
|
||||
import java.lang.classfile.CodeModel;
|
||||
import java.lang.classfile.CodeTransform;
|
||||
import java.lang.classfile.MethodBuilder;
|
||||
import java.lang.classfile.MethodElement;
|
||||
import java.lang.classfile.MethodModel;
|
||||
import java.lang.classfile.MethodTransform;
|
||||
import java.lang.classfile.Opcode;
|
||||
import java.lang.classfile.TypeKind;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
import static java.lang.classfile.ClassFile.ACC_NATIVE;
|
||||
|
||||
public class Instrumentor {
|
||||
public static class InstrHelper {
|
||||
private final MethodVisitor mv;
|
||||
private final String name;
|
||||
|
||||
InstrHelper(MethodVisitor mv, String name) {
|
||||
this.mv = mv;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void invokeStatic(String owner, String name, String desc, boolean itf) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, itf);
|
||||
}
|
||||
|
||||
public void invokeSpecial(String owner, String name, String desc) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, false);
|
||||
}
|
||||
|
||||
public void invokeVirtual(String owner, String name, String desc) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false);
|
||||
}
|
||||
|
||||
public void push(int val) {
|
||||
if (val >= -1 && val <= 5) {
|
||||
mv.visitInsn(Opcodes.ICONST_0 + val);
|
||||
} else if (val >= Byte.MIN_VALUE && val <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.BIPUSH, val);
|
||||
} else if (val >= Short.MIN_VALUE && val <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.SIPUSH, val);
|
||||
} else {
|
||||
mv.visitLdcInsn(val);
|
||||
}
|
||||
}
|
||||
|
||||
public void push(Object val) {
|
||||
mv.visitLdcInsn(val);
|
||||
}
|
||||
|
||||
public void println(String s) {
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, Type.getInternalName(System.class), "out", Type.getDescriptor(PrintStream.class));
|
||||
mv.visitLdcInsn(s);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(PrintStream.class), "println", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class)), false);
|
||||
}
|
||||
}
|
||||
|
||||
public static Instrumentor instrFor(byte[] classData) {
|
||||
return new Instrumentor(classData);
|
||||
}
|
||||
|
||||
|
||||
private final ClassReader cr;
|
||||
private final ClassWriter output;
|
||||
private ClassVisitor instrumentingVisitor = null;
|
||||
private final AtomicInteger matches = new AtomicInteger(0);
|
||||
private final ClassModel model;
|
||||
private ClassTransform transform = ClassTransform.ACCEPT_ALL;
|
||||
private final AtomicBoolean dirty = new AtomicBoolean(false);
|
||||
|
||||
private Instrumentor(byte[] classData) {
|
||||
cr = new ClassReader(classData);
|
||||
output = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
instrumentingVisitor = output;
|
||||
model = ClassFile.of().parse(classData);
|
||||
}
|
||||
|
||||
public synchronized Instrumentor addMethodEntryInjection(String methodName, Consumer<InstrHelper> injector) {
|
||||
instrumentingVisitor = new ClassVisitor(Opcodes.ASM7, instrumentingVisitor) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
|
||||
|
||||
if (name.equals(methodName)) {
|
||||
matches.getAndIncrement();
|
||||
|
||||
mv = new MethodVisitor(Opcodes.ASM7, mv) {
|
||||
@Override
|
||||
public void visitCode() {
|
||||
injector.accept(new InstrHelper(mv, name));
|
||||
}
|
||||
};
|
||||
}
|
||||
return mv;
|
||||
public synchronized Instrumentor addMethodEntryInjection(String methodName, Consumer<CodeBuilder> injector) {
|
||||
transform = transform.andThen(ClassTransform.transformingMethodBodies(mm -> {
|
||||
if (mm.methodName().equalsString(methodName)) {
|
||||
dirty.set(true);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return false;
|
||||
}, new CodeTransform() {
|
||||
@Override
|
||||
public void atStart(CodeBuilder builder) {
|
||||
injector.accept(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(CodeBuilder builder, CodeElement element) {
|
||||
builder.accept(element);
|
||||
}
|
||||
}));
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized Instrumentor addNativeMethodTrackingInjection(String prefix, Consumer<InstrHelper> injector) {
|
||||
instrumentingVisitor = new ClassVisitor(Opcodes.ASM9, instrumentingVisitor) {
|
||||
private final Set<Consumer<ClassVisitor>> wmGenerators = new HashSet<>();
|
||||
private String className;
|
||||
public synchronized Instrumentor addNativeMethodTrackingInjection(String prefix, BiConsumer<String, CodeBuilder> injector) {
|
||||
transform = transform.andThen(ClassTransform.ofStateful(() -> new ClassTransform() {
|
||||
private final Set<Consumer<ClassBuilder>> wmGenerators = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
this.className = name;
|
||||
super.visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
if ((access & Opcodes.ACC_NATIVE) != 0) {
|
||||
matches.getAndIncrement();
|
||||
public void accept(ClassBuilder builder, ClassElement element) {
|
||||
if (element instanceof MethodModel mm && mm.flags().has(AccessFlag.NATIVE)) {
|
||||
dirty.set(true);
|
||||
|
||||
String name = mm.methodName().stringValue();
|
||||
String newName = prefix + name;
|
||||
wmGenerators.add((v)->{
|
||||
MethodVisitor mv = v.visitMethod(access & ~Opcodes.ACC_NATIVE, name, desc, signature, exceptions);
|
||||
mv.visitCode();
|
||||
injector.accept(new InstrHelper(mv, name));
|
||||
Type[] argTypes = Type.getArgumentTypes(desc);
|
||||
Type retType = Type.getReturnType(desc);
|
||||
|
||||
boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
|
||||
if (!isStatic) {
|
||||
mv.visitIntInsn(Opcodes.ALOAD, 0); // load "this"
|
||||
}
|
||||
|
||||
// load the method parameters
|
||||
if (argTypes.length > 0) {
|
||||
int ptr = isStatic ? 0 : 1;
|
||||
for(Type argType : argTypes) {
|
||||
mv.visitIntInsn(argType.getOpcode(Opcodes.ILOAD), ptr);
|
||||
ptr += argType.getSize();
|
||||
MethodTypeDesc mt = mm.methodTypeSymbol();
|
||||
wmGenerators.add(clb -> clb.transformMethod(mm, new MethodTransform() {
|
||||
@Override
|
||||
public void accept(MethodBuilder mb, MethodElement me) {
|
||||
if (me instanceof AccessFlags flags) {
|
||||
mb.withFlags(flags.flagsMask() & ~ACC_NATIVE);
|
||||
} else if (!(me instanceof CodeModel)) {
|
||||
mb.with(me);
|
||||
}
|
||||
}
|
||||
|
||||
mv.visitMethodInsn(isStatic ? Opcodes.INVOKESTATIC : Opcodes.INVOKESPECIAL, className, newName, desc, false);
|
||||
mv.visitInsn(retType.getOpcode(Opcodes.IRETURN));
|
||||
@Override
|
||||
public void atEnd(MethodBuilder mb) {
|
||||
Consumer<CodeBuilder> injection = cb -> injector.accept(name, cb);
|
||||
mb.withCode(injection.andThen(cb -> {
|
||||
int ptr;
|
||||
boolean isStatic = mm.flags().has(AccessFlag.STATIC);
|
||||
if (!isStatic) {
|
||||
cb.aload(0); // load "this"
|
||||
ptr = 1;
|
||||
} else {
|
||||
ptr = 0;
|
||||
}
|
||||
|
||||
mv.visitMaxs(1, 1); // dummy call; let ClassWriter to deal with this
|
||||
mv.visitEnd();
|
||||
});
|
||||
return super.visitMethod(access, newName, desc, signature, exceptions);
|
||||
// load method parameters
|
||||
for (int i = 0; i < mt.parameterCount(); i++) {
|
||||
TypeKind kind = TypeKind.from(mt.parameterType(i));
|
||||
cb.loadInstruction(kind, ptr);
|
||||
ptr += kind.slotSize();
|
||||
}
|
||||
|
||||
cb.invokeInstruction(isStatic ? Opcode.INVOKESTATIC : Opcode.INVOKESPECIAL,
|
||||
model.thisClass().asSymbol(), newName, mt, false);
|
||||
cb.returnInstruction(TypeKind.from(mt.returnType()));
|
||||
}));
|
||||
}
|
||||
}));
|
||||
|
||||
builder.withMethod(newName, mt, mm.flags().flagsMask(), mm::forEachElement);
|
||||
} else {
|
||||
builder.accept(element);
|
||||
}
|
||||
return super.visitMethod(access, name, desc, signature, exceptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
wmGenerators.stream().forEach((e) -> {
|
||||
e.accept(cv);
|
||||
});
|
||||
super.visitEnd();
|
||||
public void atEnd(ClassBuilder builder) {
|
||||
wmGenerators.forEach(e -> e.accept(builder));
|
||||
}
|
||||
};
|
||||
|
||||
}));
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized byte[] apply() {
|
||||
cr.accept(instrumentingVisitor, ClassReader.SKIP_DEBUG + ClassReader.EXPAND_FRAMES);
|
||||
var bytes = ClassFile.of().transform(model, transform);
|
||||
|
||||
return matches.get() == 0 ? null : output.toByteArray();
|
||||
return dirty.get() ? bytes : null;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,18 +24,14 @@
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.classfile.ClassTransform;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
|
||||
public class BogoLoader extends ClassLoader {
|
||||
|
||||
static interface VisitorMaker {
|
||||
ClassVisitor make(ClassVisitor visitor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use this property to verify that the desired classloading is happening.
|
||||
*/
|
||||
@ -55,7 +51,7 @@ public class BogoLoader extends ClassLoader {
|
||||
/**
|
||||
* Map from class names to a bytecode transformer factory.
|
||||
*/
|
||||
private Map<String, VisitorMaker> replaced;
|
||||
private Map<String, ClassTransform> replaced;
|
||||
|
||||
/**
|
||||
* Keep track (not terribly efficiently) of which classes have already
|
||||
@ -67,7 +63,7 @@ public class BogoLoader extends ClassLoader {
|
||||
return ! nonSystem.contains(name) && ! replaced.containsKey(name);
|
||||
}
|
||||
|
||||
public BogoLoader(Set<String> non_system, Map<String, VisitorMaker> replaced) {
|
||||
public BogoLoader(Set<String> non_system, Map<String, ClassTransform> replaced) {
|
||||
super(Thread.currentThread().getContextClassLoader());
|
||||
this.nonSystem = non_system;
|
||||
this.replaced = replaced;
|
||||
@ -126,11 +122,8 @@ public class BogoLoader extends ClassLoader {
|
||||
if (verbose) {
|
||||
System.err.println("Replacing class " + name);
|
||||
}
|
||||
ClassReader cr = new ClassReader(classData);
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
VisitorMaker vm = replaced.get(name);
|
||||
cr.accept(vm.make(cw), 0);
|
||||
classData = cw.toByteArray();
|
||||
var cf = ClassFile.of();
|
||||
classData = cf.transform(cf.parse(classData), replaced.get(name));
|
||||
}
|
||||
clazz = defineClass(name, classData, 0, classData.length);
|
||||
} catch (java.io.EOFException ioe) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -21,60 +21,53 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
/*
|
||||
* @test
|
||||
* @bug 8022701
|
||||
* @summary Illegal access exceptions via methodhandle invocations threw wrong error.
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @compile -XDignore.symbol.file BogoLoader.java InvokeSeveralWays.java MHIllegalAccess.java MethodSupplier.java
|
||||
* @run main/othervm MHIllegalAccess
|
||||
*/
|
||||
|
||||
import java.lang.classfile.AccessFlags;
|
||||
import java.lang.classfile.ClassTransform;
|
||||
import java.lang.classfile.MethodModel;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
public class MHIllegalAccess implements Opcodes {
|
||||
import static java.lang.classfile.ClassFile.ACC_PRIVATE;
|
||||
import static java.lang.classfile.ClassFile.ACC_PROTECTED;
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
|
||||
public static void main(String args[]) throws Throwable {
|
||||
public class MHIllegalAccess {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
System.out.println("Classpath is " + System.getProperty("java.class.path"));
|
||||
System.out.println();
|
||||
|
||||
/**
|
||||
* Make method m be private to provoke an IllegalAccessError.
|
||||
*/
|
||||
BogoLoader.VisitorMaker privatize = new BogoLoader.VisitorMaker() {
|
||||
public ClassVisitor make(ClassVisitor cv) {
|
||||
return new ClassVisitor(Opcodes.ASM5, cv) {
|
||||
public MethodVisitor visitMethod(int access, String name, String desc,
|
||||
String signature, String[] exceptions) {
|
||||
if (name.equals("m"))
|
||||
access = (access | ACC_PRIVATE) & ~ (ACC_PUBLIC | ACC_PROTECTED);
|
||||
return super.visitMethod(access, name, desc, signature, exceptions);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
var privatize = ClassTransform.transformingMethods(m -> m.methodName().equalsString("m"), (mb, me) -> {
|
||||
if (me instanceof AccessFlags af) {
|
||||
mb.withFlags((af.flagsMask() | ACC_PRIVATE) & ~ (ACC_PUBLIC | ACC_PROTECTED));
|
||||
} else {
|
||||
mb.accept(me);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Rename method m as nemo to provoke a NoSuchMethodError.
|
||||
*/
|
||||
BogoLoader.VisitorMaker changeName = new BogoLoader.VisitorMaker() {
|
||||
public ClassVisitor make(ClassVisitor cv) {
|
||||
return new ClassVisitor(Opcodes.ASM5, cv) {
|
||||
public MethodVisitor visitMethod(int access, String name, String desc,
|
||||
String signature, String[] exceptions) {
|
||||
if (name.equals("m"))
|
||||
name = "nemo";
|
||||
return super.visitMethod(access, name, desc, signature, exceptions);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
ClassTransform changeName = (cb, ce) -> {
|
||||
if (ce instanceof MethodModel mm && mm.methodName().equalsString("m")) {
|
||||
cb.withMethod("nemo", mm.methodTypeSymbol(), mm.flags().flagsMask(), mm::forEachElement);
|
||||
} else {
|
||||
cb.accept(ce);
|
||||
}
|
||||
};
|
||||
|
||||
int failures = 0;
|
||||
failures += testOneError(privatize, args, IllegalAccessError.class);
|
||||
@ -94,8 +87,8 @@ public class MHIllegalAccess implements Opcodes {
|
||||
* @throws ClassNotFoundException
|
||||
* @throws Throwable
|
||||
*/
|
||||
private static int testOneError(BogoLoader.VisitorMaker vm, String[] args, Class expected) throws ClassNotFoundException, Throwable {
|
||||
HashMap<String, BogoLoader.VisitorMaker> replace = new HashMap<String, BogoLoader.VisitorMaker>();
|
||||
private static int testOneError(ClassTransform vm, String[] args, Class<?> expected) throws ClassNotFoundException, Throwable {
|
||||
var replace = new HashMap<String, ClassTransform>();
|
||||
replace.put("MethodSupplier", vm);
|
||||
|
||||
HashSet<String> in_bogus = new HashSet<String>();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,31 +23,38 @@
|
||||
|
||||
/* @test
|
||||
* @modules java.base/java.lang:open
|
||||
* java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @run testng/othervm test.DefineClassTest
|
||||
* @summary Basic test for java.lang.invoke.MethodHandles.Lookup.defineClass
|
||||
*/
|
||||
|
||||
package test;
|
||||
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import static java.lang.invoke.MethodHandles.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.*;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
import static java.lang.classfile.ClassFile.ACC_STATIC;
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.constant.ConstantDescs.CLASS_INIT_NAME;
|
||||
import static java.lang.constant.ConstantDescs.INIT_NAME;
|
||||
import static java.lang.constant.ConstantDescs.MTD_void;
|
||||
import static java.lang.invoke.MethodHandles.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class DefineClassTest {
|
||||
private static final String THIS_PACKAGE = DefineClassTest.class.getPackageName();
|
||||
private static final ClassDesc CD_Runnable = Runnable.class.describeConstable().orElseThrow();
|
||||
private static final ClassDesc CD_MissingSuperClass = ClassDesc.of("MissingSuperClass");
|
||||
|
||||
/**
|
||||
* Test that a class has the same class loader, and is in the same package and
|
||||
@ -251,25 +258,15 @@ public class DefineClassTest {
|
||||
* Generates a class file with the given class name
|
||||
*/
|
||||
byte[] generateClass(String className) {
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V9,
|
||||
ACC_PUBLIC + ACC_SUPER,
|
||||
className.replace(".", "/"),
|
||||
null,
|
||||
"java/lang/Object",
|
||||
null);
|
||||
|
||||
// <init>
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(ClassDesc.of(className), clb -> {
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, PUBLIC, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,33 +277,19 @@ public class DefineClassTest {
|
||||
String targetClass,
|
||||
String targetMethod) throws Exception {
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V9,
|
||||
ACC_PUBLIC + ACC_SUPER,
|
||||
className.replace(".", "/"),
|
||||
null,
|
||||
"java/lang/Object",
|
||||
new String[] { "java/lang/Runnable" });
|
||||
|
||||
// <init>
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// run()
|
||||
String tc = targetClass.replace(".", "/");
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null);
|
||||
mv.visitMethodInsn(INVOKESTATIC, tc, targetMethod, "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(ClassDesc.of(className), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withInterfaceSymbols(CD_Runnable);
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, PUBLIC, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
clb.withMethodBody("run", MTD_void, PUBLIC, cob -> {
|
||||
cob.invokestatic(ClassDesc.of(targetClass), targetMethod, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -317,75 +300,41 @@ public class DefineClassTest {
|
||||
String targetClass,
|
||||
String targetMethod) throws Exception {
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V9,
|
||||
ACC_PUBLIC + ACC_SUPER,
|
||||
className.replace(".", "/"),
|
||||
null,
|
||||
"java/lang/Object",
|
||||
null);
|
||||
|
||||
// <init>
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// <clinit>
|
||||
String tc = targetClass.replace(".", "/");
|
||||
mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
|
||||
mv.visitMethodInsn(INVOKESTATIC, tc, targetMethod, "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(ClassDesc.of(className), clb -> {
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> {
|
||||
cob.invokestatic(ClassDesc.of(targetClass), targetMethod, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a non-linkable class file with the given class name
|
||||
*/
|
||||
byte[] generateNonLinkableClass(String className) {
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V14,
|
||||
ACC_PUBLIC + ACC_SUPER,
|
||||
className.replace(".", "/"),
|
||||
null,
|
||||
"MissingSuperClass",
|
||||
null);
|
||||
|
||||
// <init>
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "MissingSuperClass", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(ClassDesc.of(className), clb -> {
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
clb.withSuperclass(CD_MissingSuperClass);
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_MissingSuperClass, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a class file with the given class name
|
||||
*/
|
||||
byte[] generateModuleInfo() {
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V14,
|
||||
ACC_MODULE,
|
||||
"module-info",
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(ClassDesc.of("module-info"), cb -> cb.withFlags(AccessFlag.MODULE));
|
||||
}
|
||||
|
||||
private int nextNumber() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,17 +25,26 @@
|
||||
* @test
|
||||
* @bug 8230501
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @run testng/othervm ClassDataTest
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.classfile.ClassBuilder;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.TypeKind;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.DirectMethodHandleDesc;
|
||||
import java.lang.constant.DynamicConstantDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -43,18 +52,20 @@ import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static java.lang.classfile.ClassFile.*;
|
||||
import static java.lang.constant.ConstantDescs.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.*;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class ClassDataTest {
|
||||
private static final Lookup LOOKUP = MethodHandles.lookup();
|
||||
private static final ClassDesc CD_ClassDataTest = ClassDataTest.class.describeConstable().orElseThrow();
|
||||
|
||||
@Test
|
||||
public void testOriginalAccess() throws IllegalAccessException {
|
||||
@ -294,15 +305,13 @@ public class ClassDataTest {
|
||||
public void classDataMap() throws ReflectiveOperationException {
|
||||
ClassByteBuilder builder = new ClassByteBuilder("map");
|
||||
// generate classData static method
|
||||
Handle bsm = new Handle(H_INVOKESTATIC, "ClassDataTest", "getClassDataEntry",
|
||||
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;",
|
||||
false);
|
||||
DirectMethodHandleDesc bsm = ConstantDescs.ofConstantBootstrap(CD_ClassDataTest, "getClassDataEntry", CD_Object);
|
||||
// generate two accessor methods to get the entries from class data
|
||||
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, Map.class)
|
||||
.classData(ACC_PUBLIC|ACC_STATIC, "getClass",
|
||||
Class.class, new ConstantDynamic("class", Type.getDescriptor(Class.class), bsm))
|
||||
Class.class, DynamicConstantDesc.ofNamed(bsm, "class", CD_Class))
|
||||
.classData(ACC_PUBLIC|ACC_STATIC, "getMethod",
|
||||
MethodHandle.class, new ConstantDynamic("method", Type.getDescriptor(MethodHandle.class), bsm))
|
||||
MethodHandle.class, DynamicConstantDesc.ofNamed(bsm, "method", CD_MethodHandle))
|
||||
.build();
|
||||
|
||||
// generate a hidden class
|
||||
@ -342,29 +351,28 @@ public class ClassDataTest {
|
||||
private static final String MHS_CLS = "java/lang/invoke/MethodHandles";
|
||||
private static final String CLASS_DATA_BSM_DESCR =
|
||||
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;";
|
||||
private final ClassWriter cw;
|
||||
private final String classname;
|
||||
private Consumer<ClassBuilder> cw;
|
||||
private final ClassDesc classname;
|
||||
|
||||
/**
|
||||
* A builder to generate a class file to access class data
|
||||
* @param classname
|
||||
*/
|
||||
ClassByteBuilder(String classname) {
|
||||
this.classname = classname;
|
||||
this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V14, ACC_FINAL, classname, null, OBJECT_CLS, null);
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, OBJECT_CLS, "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
this.classname = ClassDesc.ofInternalName(classname);
|
||||
this.cw = clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withFlags(AccessFlag.FINAL);
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
byte[] build() {
|
||||
cw.visitEnd();
|
||||
byte[] bytes = cw.toByteArray();
|
||||
byte[] bytes = ClassFile.of().build(classname, cw);
|
||||
Path p = Paths.get(classname + ".class");
|
||||
try (OutputStream os = Files.newOutputStream(p)) {
|
||||
os.write(bytes);
|
||||
@ -378,20 +386,14 @@ public class ClassDataTest {
|
||||
* Generate classData method to load class data via condy
|
||||
*/
|
||||
ClassByteBuilder classData(int accessFlags, Class<?> returnType) {
|
||||
MethodType mtype = MethodType.methodType(returnType);
|
||||
MethodVisitor mv = cw.visitMethod(accessFlags,
|
||||
"classData",
|
||||
mtype.descriptorString(), null, null);
|
||||
mv.visitCode();
|
||||
Handle bsm = new Handle(H_INVOKESTATIC, MHS_CLS, "classData",
|
||||
CLASS_DATA_BSM_DESCR,
|
||||
false);
|
||||
ConstantDynamic dynamic = new ConstantDynamic("_", Type.getDescriptor(returnType), bsm);
|
||||
mv.visitLdcInsn(dynamic);
|
||||
mv.visitInsn(returnType == int.class ? IRETURN :
|
||||
(returnType == float.class ? FRETURN : ARETURN));
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
ClassDesc returnDesc = returnType.describeConstable().orElseThrow();
|
||||
MethodTypeDesc mt = MethodTypeDesc.of(returnDesc);
|
||||
cw = cw.andThen(clb -> {
|
||||
clb.withMethodBody("classData", mt, accessFlags, cob -> {
|
||||
cob.constantInstruction(DynamicConstantDesc.ofNamed(BSM_CLASS_DATA, DEFAULT_NAME, returnDesc));
|
||||
cob.returnInstruction(TypeKind.from(returnType));
|
||||
});
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -399,32 +401,26 @@ public class ClassDataTest {
|
||||
* Generate classDataAt method to load an element from class data via condy
|
||||
*/
|
||||
ClassByteBuilder classDataAt(int accessFlags, Class<?> returnType, int index) {
|
||||
MethodType mtype = MethodType.methodType(returnType);
|
||||
MethodVisitor mv = cw.visitMethod(accessFlags,
|
||||
"classData",
|
||||
mtype.descriptorString(), null, null);
|
||||
mv.visitCode();
|
||||
Handle bsm = new Handle(H_INVOKESTATIC, "java/lang/invoke/MethodHandles", "classDataAt",
|
||||
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;I)Ljava/lang/Object;",
|
||||
false);
|
||||
ConstantDynamic dynamic = new ConstantDynamic("_", Type.getDescriptor(returnType), bsm, index);
|
||||
mv.visitLdcInsn(dynamic);
|
||||
mv.visitInsn(returnType == int.class? IRETURN : ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
ClassDesc returnDesc = returnType.describeConstable().orElseThrow();
|
||||
MethodTypeDesc mt = MethodTypeDesc.of(returnDesc);
|
||||
cw = cw.andThen(clb -> {
|
||||
clb.withMethodBody("classData", mt, accessFlags, cob -> {
|
||||
cob.constantInstruction(DynamicConstantDesc.ofNamed(BSM_CLASS_DATA_AT, DEFAULT_NAME, returnDesc, index));
|
||||
cob.returnInstruction(TypeKind.from(returnType));
|
||||
});
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
ClassByteBuilder classData(int accessFlags, String name, Class<?> returnType, ConstantDynamic dynamic) {
|
||||
MethodType mtype = MethodType.methodType(returnType);
|
||||
MethodVisitor mv = cw.visitMethod(accessFlags,
|
||||
name,
|
||||
mtype.descriptorString(), null, null);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(dynamic);
|
||||
mv.visitInsn(returnType == int.class? IRETURN : ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
ClassByteBuilder classData(int accessFlags, String name, Class<?> returnType, DynamicConstantDesc<?> dynamic) {
|
||||
ClassDesc returnDesc = returnType.describeConstable().orElseThrow();
|
||||
MethodTypeDesc mt = MethodTypeDesc.of(returnDesc);
|
||||
cw = cw.andThen(clb -> {
|
||||
clb.withMethodBody(name, mt, accessFlags, cob -> {
|
||||
cob.constantInstruction(dynamic);
|
||||
cob.returnInstruction(TypeKind.from(returnType));
|
||||
});
|
||||
});
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,19 +24,16 @@
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.classfile.ClassTransform;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
|
||||
// Compile with -XDignore.symbol.file=true
|
||||
|
||||
public class BogoLoader extends ClassLoader {
|
||||
|
||||
static interface VisitorMaker {
|
||||
ClassVisitor make(ClassVisitor visitor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use this property to verify that the desired classloading is happening.
|
||||
*/
|
||||
@ -56,7 +53,7 @@ public class BogoLoader extends ClassLoader {
|
||||
/**
|
||||
* Map from class names to a bytecode transformer factory.
|
||||
*/
|
||||
private Map<String, VisitorMaker> replaced;
|
||||
private Map<String, ClassTransform> replaced;
|
||||
|
||||
/**
|
||||
* Keep track (not terribly efficiently) of which classes have already
|
||||
@ -68,7 +65,7 @@ public class BogoLoader extends ClassLoader {
|
||||
return ! nonSystem.contains(name) && ! replaced.containsKey(name);
|
||||
}
|
||||
|
||||
public BogoLoader(Set<String> non_system, Map<String, VisitorMaker> replaced) {
|
||||
public BogoLoader(Set<String> non_system, Map<String, ClassTransform> replaced) {
|
||||
super(Thread.currentThread().getContextClassLoader());
|
||||
this.nonSystem = non_system;
|
||||
this.replaced = replaced;
|
||||
@ -127,11 +124,8 @@ public class BogoLoader extends ClassLoader {
|
||||
if (verbose) {
|
||||
System.err.println("Replacing class " + name);
|
||||
}
|
||||
ClassReader cr = new ClassReader(classData);
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
VisitorMaker vm = replaced.get(name);
|
||||
cr.accept(vm.make(cw), 0);
|
||||
classData = cw.toByteArray();
|
||||
var cf = ClassFile.of();
|
||||
classData = cf.transform(cf.parse(classData), replaced.get(name));
|
||||
}
|
||||
clazz = defineClass(name, classData, 0, classData.length);
|
||||
} catch (java.io.EOFException ioe) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -21,64 +21,53 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
/*
|
||||
* @test
|
||||
* @bug 8022718
|
||||
* @summary Runtime accessibility checking: protected class, if extended, should be accessible from another package
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @compile -XDignore.symbol.file BogoLoader.java MethodInvoker.java Test.java anotherpkg/MethodSupplierOuter.java
|
||||
* @run main/othervm Test
|
||||
*/
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.classfile.ClassTransform;
|
||||
import java.lang.classfile.attribute.InnerClassInfo;
|
||||
import java.lang.classfile.attribute.InnerClassesAttribute;
|
||||
import java.lang.classfile.constantpool.ClassEntry;
|
||||
import java.lang.classfile.constantpool.Utf8Entry;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
import static java.lang.classfile.ClassFile.ACC_PRIVATE;
|
||||
import static java.lang.classfile.ClassFile.ACC_PROTECTED;
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
|
||||
interface MyFunctionalInterface {
|
||||
|
||||
void invokeMethodReference();
|
||||
}
|
||||
|
||||
class MakeProtected implements BogoLoader.VisitorMaker, Opcodes {
|
||||
|
||||
final boolean whenVisitInner;
|
||||
|
||||
MakeProtected(boolean when_visit_inner) {
|
||||
super();
|
||||
whenVisitInner = when_visit_inner;
|
||||
}
|
||||
|
||||
public ClassVisitor make(ClassVisitor cv) {
|
||||
return new ClassVisitor(Opcodes.ASM7, cv) {
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outerName,
|
||||
String innerName, int access) {
|
||||
if (whenVisitInner) {
|
||||
int access_ = (ACC_PROTECTED | access) & ~(ACC_PRIVATE | ACC_PUBLIC);
|
||||
System.out.println("visitInnerClass: name = " + name
|
||||
+ ", outerName = " + outerName
|
||||
+ ", innerName = " + innerName
|
||||
+ ", access original = 0x" + Integer.toHexString(access)
|
||||
+ ", access modified to 0x" + Integer.toHexString(access_));
|
||||
access = access_;
|
||||
}
|
||||
super.visitInnerClass(name, outerName, innerName, access);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
public class Test {
|
||||
|
||||
public static void main(String argv[]) throws Exception, Throwable {
|
||||
BogoLoader.VisitorMaker makeProtectedNop = new MakeProtected(false);
|
||||
BogoLoader.VisitorMaker makeProtectedMod = new MakeProtected(true);
|
||||
public static void main(String[] argv) throws Throwable {
|
||||
ClassTransform makeProtectedNop = ClassTransform.ACCEPT_ALL;
|
||||
ClassTransform makeProtectedMod = (cb, ce) -> {
|
||||
if (ce instanceof InnerClassesAttribute ica) {
|
||||
cb.accept(InnerClassesAttribute.of(ica.classes().stream().map(ici -> {
|
||||
// AccessFlags doesn't support inner class flags yet
|
||||
var flags = (ACC_PROTECTED | ici.flagsMask()) & ~(ACC_PRIVATE | ACC_PUBLIC);
|
||||
System.out.println("visitInnerClass: name = " + ici.innerClass().asInternalName()
|
||||
+ ", outerName = " + ici.outerClass().map(ClassEntry::asInternalName).orElse("null")
|
||||
+ ", innerName = " + ici.innerName().map(Utf8Entry::stringValue).orElse("null")
|
||||
+ ", access original = 0x" + Integer.toHexString(ici.flagsMask())
|
||||
+ ", access modified to 0x" + Integer.toHexString(flags));
|
||||
return InnerClassInfo.of(ici.innerClass(), ici.outerClass(), ici.innerName(), flags);
|
||||
}).toList()));
|
||||
} else {
|
||||
cb.accept(ce);
|
||||
}
|
||||
};
|
||||
|
||||
int errors = 0;
|
||||
errors += tryModifiedInvocation(makeProtectedNop);
|
||||
@ -89,12 +78,11 @@ public class Test {
|
||||
}
|
||||
}
|
||||
|
||||
private static int tryModifiedInvocation(BogoLoader.VisitorMaker makeProtected)
|
||||
throws Throwable, ClassNotFoundException {
|
||||
HashMap<String, BogoLoader.VisitorMaker> replace
|
||||
= new HashMap<String, BogoLoader.VisitorMaker>();
|
||||
private static int tryModifiedInvocation(ClassTransform makeProtected)
|
||||
throws Throwable {
|
||||
var replace = new HashMap<String, ClassTransform>();
|
||||
replace.put("anotherpkg.MethodSupplierOuter$MethodSupplier", makeProtected);
|
||||
HashSet<String> in_bogus = new HashSet<String>();
|
||||
var in_bogus = new HashSet<String>();
|
||||
in_bogus.add("MethodInvoker");
|
||||
in_bogus.add("MyFunctionalInterface");
|
||||
in_bogus.add("anotherpkg.MethodSupplierOuter"); // seems to be never loaded
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,9 +23,10 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* jdk.compiler
|
||||
* @modules jdk.compiler
|
||||
* @library /test/lib
|
||||
* @enablePreview
|
||||
* @comment Change enablePreview with the flag in setup's compileSources
|
||||
* @compile BadClassFile.jcod
|
||||
* BadClassFile2.jcod
|
||||
* BadClassFileVersion.jcod
|
||||
@ -36,11 +37,9 @@
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.lookup;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@ -51,8 +50,6 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
@ -60,7 +57,11 @@ import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import static java.lang.classfile.ClassFile.*;
|
||||
import static java.lang.constant.ConstantDescs.CD_Enum;
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.invoke.MethodHandles.lookup;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
interface HiddenTest {
|
||||
@ -77,7 +78,7 @@ public class BasicTest {
|
||||
|
||||
@BeforeTest
|
||||
static void setup() throws IOException {
|
||||
compileSources(SRC_DIR, CLASSES_DIR);
|
||||
compileSources(SRC_DIR, CLASSES_DIR, "--enable-preview", "--release", "23");
|
||||
hiddenClassBytes = Files.readAllBytes(CLASSES_DIR.resolve("HiddenClass.class"));
|
||||
|
||||
// compile with --release 10 with no NestHost and NestMembers attribute
|
||||
@ -264,8 +265,8 @@ public class BasicTest {
|
||||
*/
|
||||
@Test(dataProvider = "emptyClasses")
|
||||
public void emptyHiddenClass(String name, int accessFlags) throws Exception {
|
||||
byte[] bytes = (accessFlags == ACC_ENUM) ? classBytes(name, Enum.class, accessFlags)
|
||||
: classBytes(name, accessFlags);
|
||||
byte[] bytes = (accessFlags == ACC_ENUM) ? classBytes(name, CD_Enum, accessFlags)
|
||||
: classBytes(name, accessFlags);
|
||||
Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
|
||||
switch (accessFlags) {
|
||||
case ACC_SYNTHETIC:
|
||||
@ -514,14 +515,13 @@ public class BasicTest {
|
||||
}
|
||||
|
||||
private static byte[] classBytes(String classname, int accessFlags) {
|
||||
return classBytes(classname, Object.class, accessFlags);
|
||||
return classBytes(classname, CD_Object, accessFlags);
|
||||
}
|
||||
|
||||
private static byte[] classBytes(String classname, Class<?> supertType, int accessFlags) {
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V14, ACC_PUBLIC|accessFlags, classname, null, Type.getInternalName(supertType), null);
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
private static byte[] classBytes(String classname, ClassDesc superType, int accessFlags) {
|
||||
return ClassFile.of().build(ClassDesc.ofInternalName(classname), clb -> clb
|
||||
.withVersion(JAVA_14_VERSION, 0)
|
||||
.withFlags(accessFlags | ACC_PUBLIC)
|
||||
.withSuperclass(superType));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,28 +24,35 @@
|
||||
/*
|
||||
* @test
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @build HiddenNestmateTest
|
||||
* @run testng/othervm HiddenNestmateTest
|
||||
*/
|
||||
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.invoke.*;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.constant.ConstantDescs.CD_int;
|
||||
import static java.lang.constant.ConstantDescs.INIT_NAME;
|
||||
import static java.lang.constant.ConstantDescs.MTD_void;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.*;
|
||||
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class HiddenNestmateTest {
|
||||
private static final ClassDesc CD_HiddenNestmateTest = HiddenNestmateTest.class.describeConstable().orElseThrow();
|
||||
private static final byte[] bytes = classBytes("HiddenInjected");
|
||||
|
||||
private static void assertNestmate(Lookup lookup) {
|
||||
@ -165,34 +172,20 @@ public class HiddenNestmateTest {
|
||||
}
|
||||
|
||||
private static byte[] classBytes(String classname) {
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
MethodVisitor mv;
|
||||
|
||||
cw.visit(V12, ACC_FINAL, classname, null, "java/lang/Object", null);
|
||||
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
// access a private member of the nest host class
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "test", "(LHiddenNestmateTest;)I", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "HiddenNestmateTest", "privMethod", "()I");
|
||||
mv.visitInsn(IRETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(ClassDesc.ofInternalName(classname), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withFlags(AccessFlag.FINAL);
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, PUBLIC, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
clb.withMethodBody("test", MethodTypeDesc.of(CD_int, CD_HiddenNestmateTest), PUBLIC, cob -> {
|
||||
cob.aload(1);
|
||||
cob.invokevirtual(CD_HiddenNestmateTest, "privMethod", MethodTypeDesc.of(CD_int));
|
||||
cob.ireturn();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private int privMethod() { return 1234; }
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,22 +24,24 @@
|
||||
/*
|
||||
* @test
|
||||
* @bug 8245432
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* jdk.compiler
|
||||
* @modules jdk.compiler
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.Utils
|
||||
* jdk.test.lib.compiler.CompilerUtils
|
||||
* @run testng PreviewHiddenClass
|
||||
* @summary verify UnsupportedClassVersionError thrown when defining a hidden class
|
||||
* with preview minor version but --enable-preview is not set
|
||||
* @comment This test itself cannot enablePreview, or hidden class definition
|
||||
* will pass
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
@ -62,9 +64,9 @@ public class PreviewHiddenClass {
|
||||
}
|
||||
|
||||
byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("HiddenInterface.class"));
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
int minor = reader.readUnsignedShort(4);
|
||||
assertTrue(minor == 65535);
|
||||
var dis = new DataInputStream(new ByteArrayInputStream(bytes));
|
||||
dis.skipBytes(4); // 0xCAFEBABE
|
||||
assertEquals(dis.readUnsignedShort(), 65535); // Minor version
|
||||
MethodHandles.lookup().defineHiddenClass(bytes, false);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,19 +25,28 @@
|
||||
* @test
|
||||
* @bug 8266925
|
||||
* @summary hidden class members can't be statically invocable
|
||||
* @modules java.base/jdk.internal.misc java.base/jdk.internal.org.objectweb.asm
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @enablePreview
|
||||
* @build java.base/*
|
||||
* @run testng StaticInvocableTest
|
||||
*/
|
||||
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.LookupHelper;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
import static java.lang.classfile.ClassFile.ACC_STATIC;
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.constant.ConstantDescs.CD_int;
|
||||
import static java.lang.constant.ConstantDescs.INIT_NAME;
|
||||
import static java.lang.constant.ConstantDescs.MTD_void;
|
||||
|
||||
public class StaticInvocableTest {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
@ -106,28 +115,19 @@ public class StaticInvocableTest {
|
||||
* }
|
||||
*/
|
||||
public static byte[] dumpClass(String pkg) {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv;
|
||||
|
||||
cw.visit(52, ACC_SUPER | ACC_PUBLIC, pkg+"/MyClass", null, "java/lang/Object", null);
|
||||
{
|
||||
mv = cw.visitMethod(0, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "get", "(I)Ljava/lang/Object;", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitInsn(ACONST_NULL);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(ClassDesc.of(pkg.replace('/', '.'), "MyClass"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, 0, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
clb.withMethodBody("get", MethodTypeDesc.of(CD_Object, CD_int),
|
||||
ACC_PUBLIC | ACC_STATIC, cob -> {
|
||||
cob.aconst_null();
|
||||
cob.areturn();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -25,35 +25,35 @@
|
||||
* @test
|
||||
* @bug 8027232
|
||||
* @library /test/lib/
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* jdk.jdeps/com.sun.tools.classfile
|
||||
* jdk.zipfs
|
||||
* @modules jdk.zipfs
|
||||
* @enablePreview
|
||||
* @compile LambdaAsm.java
|
||||
* @run main/othervm LambdaAsm
|
||||
* @summary ensures that j.l.i.InvokerByteCodeGenerator and ASM visitMethodInsn
|
||||
* generate bytecodes with correct constant pool references
|
||||
* @summary ensures that j.l.i.InvokerByteCodeGenerator and Class-File API
|
||||
* generate bytecodes with correct constant pool references
|
||||
*/
|
||||
import com.sun.tools.classfile.Attribute;
|
||||
import com.sun.tools.classfile.ClassFile;
|
||||
import com.sun.tools.classfile.Code_attribute;
|
||||
import com.sun.tools.classfile.ConstantPool;
|
||||
import com.sun.tools.classfile.ConstantPool.CPInfo;
|
||||
import com.sun.tools.classfile.Instruction;
|
||||
import com.sun.tools.classfile.Method;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.classfile.Attributes;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.ClassModel;
|
||||
import java.lang.classfile.Opcode;
|
||||
import java.lang.classfile.attribute.CodeAttribute;
|
||||
import java.lang.classfile.constantpool.ConstantPool;
|
||||
import java.lang.classfile.constantpool.MethodRefEntry;
|
||||
import java.lang.classfile.instruction.InvokeInstruction;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Path;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
import static java.lang.constant.ConstantDescs.*;
|
||||
import static java.lang.classfile.ClassFile.*;
|
||||
import static java.nio.file.Files.*;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import static jdk.test.lib.process.ProcessTools.*;
|
||||
|
||||
public class LambdaAsm {
|
||||
@ -99,16 +99,14 @@ public class LambdaAsm {
|
||||
}
|
||||
|
||||
static void checkMethod(String cname, String mname, ConstantPool cp,
|
||||
Code_attribute code) throws ConstantPool.InvalidIndex {
|
||||
for (Instruction i : code.getInstructions()) {
|
||||
String iname = i.getMnemonic();
|
||||
if ("invokespecial".equals(iname)
|
||||
|| "invokestatic".equals(iname)) {
|
||||
int idx = i.getByte(2);
|
||||
CodeAttribute code) throws IllegalArgumentException {
|
||||
for (var inst : code.elements()) {
|
||||
if (inst instanceof InvokeInstruction inv && (inv.opcode() == Opcode.INVOKESPECIAL
|
||||
|| inv.opcode() == Opcode.INVOKEINTERFACE)) {
|
||||
var ref = inv.method();
|
||||
System.out.println("Verifying " + cname + ":" + mname +
|
||||
" instruction:" + iname + " index @" + idx);
|
||||
CPInfo cpinfo = cp.get(idx);
|
||||
if (cpinfo instanceof ConstantPool.CONSTANT_Methodref_info) {
|
||||
" instruction:" + inv.opcode() + " index @" + ref.index());
|
||||
if (ref instanceof MethodRefEntry) {
|
||||
throw new RuntimeException("unexpected CP type expected "
|
||||
+ "InterfaceMethodRef, got MethodRef, " + cname
|
||||
+ ", " + mname);
|
||||
@ -117,21 +115,20 @@ public class LambdaAsm {
|
||||
}
|
||||
}
|
||||
|
||||
static int checkMethod(ClassFile cf, String mthd) throws Exception {
|
||||
if (cf.major_version < 52) {
|
||||
static int checkMethod(ClassModel cf, String mthd) throws Exception {
|
||||
if (cf.majorVersion() < 52) {
|
||||
throw new RuntimeException("unexpected class file version, in "
|
||||
+ cf.getName() + "expected 52, got " + cf.major_version);
|
||||
+ cf.thisClass().asInternalName() + "expected 52, got "
|
||||
+ cf.majorVersion());
|
||||
}
|
||||
int count = 0;
|
||||
for (Method m : cf.methods) {
|
||||
String mname = m.getName(cf.constant_pool);
|
||||
for (var m : cf.methods()) {
|
||||
String mname = m.methodName().stringValue();
|
||||
if (mname.equals(mthd)) {
|
||||
for (Attribute a : m.attributes) {
|
||||
if ("Code".equals(a.getName(cf.constant_pool))) {
|
||||
count++;
|
||||
checkMethod(cf.getName(), mname, cf.constant_pool,
|
||||
(Code_attribute) a);
|
||||
}
|
||||
for (var a : m.findAttributes(Attributes.CODE)) {
|
||||
count++;
|
||||
checkMethod(cf.thisClass().asInternalName(), mname,
|
||||
cf.constantPool(), a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,9 +143,9 @@ public class LambdaAsm {
|
||||
"A$I$$Lambda.*.class")) {
|
||||
for (Path p : ds) {
|
||||
System.out.println(p.toFile());
|
||||
ClassFile cf = ClassFile.read(p.toFile());
|
||||
ClassModel cm = ClassFile.of().parse(p);
|
||||
// Check those methods implementing Supplier.get
|
||||
mcount += checkMethod(cf, "get");
|
||||
mcount += checkMethod(cm, "get");
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@ -163,23 +160,21 @@ public class LambdaAsm {
|
||||
}
|
||||
|
||||
static void verifyASM() throws Exception {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
cw.visit(V1_8, ACC_PUBLIC, "X", null, "java/lang/Object", null);
|
||||
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "foo",
|
||||
"()V", null, null);
|
||||
mv.visitMaxs(2, 1);
|
||||
mv.visitMethodInsn(INVOKESTATIC,
|
||||
"java/util/function/Function.class",
|
||||
"identity", "()Ljava/util/function/Function;", true);
|
||||
mv.visitInsn(RETURN);
|
||||
cw.visitEnd();
|
||||
byte[] carray = cw.toByteArray();
|
||||
var functionDesc = ClassDesc.ofInternalName("java/util/function/Function");
|
||||
byte[] carray = ClassFile.of().build(ClassDesc.of("X"), clb -> clb
|
||||
.withVersion(JAVA_8_VERSION, 0)
|
||||
.withFlags(ACC_PUBLIC)
|
||||
.withSuperclass(CD_Object)
|
||||
.withMethodBody("foo", MTD_void, ACC_STATIC, cob -> cob
|
||||
.invokestatic(functionDesc, "identity", MethodTypeDesc.of(functionDesc), true)
|
||||
)
|
||||
);
|
||||
// for debugging
|
||||
// write((new File("X.class")).toPath(), carray, CREATE, TRUNCATE_EXISTING);
|
||||
|
||||
// verify using javap/classfile reader
|
||||
ClassFile cf = ClassFile.read(new ByteArrayInputStream(carray));
|
||||
int mcount = checkMethod(cf, "foo");
|
||||
ClassModel cm = ClassFile.of().parse(carray);
|
||||
int mcount = checkMethod(cm, "foo");
|
||||
if (mcount < 1) {
|
||||
throw new RuntimeException("unexpected method count, expected 1" +
|
||||
"but got " + mcount);
|
||||
|
@ -25,17 +25,19 @@
|
||||
* @test
|
||||
* @bug 8025636
|
||||
* @library /test/lib/
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* jdk.compiler
|
||||
* @modules jdk.compiler
|
||||
* @enablePreview
|
||||
* @compile LambdaStackTrace.java
|
||||
* @run main LambdaStackTrace
|
||||
* @summary Synthetic frames should be hidden in exceptions
|
||||
*/
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
@ -43,10 +45,10 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ABSTRACT;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_INTERFACE;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.constant.ConstantDescs.CD_String;
|
||||
import static java.lang.classfile.ClassFile.*;
|
||||
|
||||
|
||||
public class LambdaStackTrace {
|
||||
|
||||
@ -132,24 +134,27 @@ public class LambdaStackTrace {
|
||||
// interface Maker {
|
||||
// Object make();
|
||||
// }
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
cw.visit(V1_7, ACC_INTERFACE | ACC_ABSTRACT, "Maker", null, "java/lang/Object", null);
|
||||
cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "make",
|
||||
"()Ljava/lang/Object;", null, null);
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(ClassDesc.of("Maker"), clb -> clb
|
||||
.withVersion(JAVA_7_VERSION, 0)
|
||||
.withFlags(ACC_INTERFACE | ACC_ABSTRACT)
|
||||
.withSuperclass(CD_Object)
|
||||
.withMethod("make", MethodTypeDesc.of(CD_Object),
|
||||
ACC_PUBLIC | ACC_ABSTRACT, mb -> {})
|
||||
);
|
||||
}
|
||||
|
||||
private static byte[] generateStringMaker() {
|
||||
// interface StringMaker extends Maker {
|
||||
// String make();
|
||||
// }
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
cw.visit(V1_7, ACC_INTERFACE | ACC_ABSTRACT, "StringMaker", null, "java/lang/Object", new String[]{"Maker"});
|
||||
cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "make",
|
||||
"()Ljava/lang/String;", null, null);
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(ClassDesc.of("StringMaker"), clb -> clb
|
||||
.withVersion(JAVA_7_VERSION, 0)
|
||||
.withFlags(ACC_INTERFACE | ACC_ABSTRACT)
|
||||
.withSuperclass(CD_Object)
|
||||
.withInterfaceSymbols(ClassDesc.of("Maker"))
|
||||
.withMethod("make", MethodTypeDesc.of(CD_String),
|
||||
ACC_PUBLIC | ACC_ABSTRACT, mb -> {})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,18 +24,27 @@
|
||||
/* @test
|
||||
* @bug 8032400
|
||||
* @summary JSR292: invokeSpecial: InternalError attempting to lookup a method
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @compile -XDignore.symbol.file SpecialStatic.java
|
||||
* @run testng test.java.lang.invoke.lookup.SpecialStatic
|
||||
*/
|
||||
package test.java.lang.invoke.lookup;
|
||||
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodHandleDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
|
||||
import org.testng.annotations.*;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
import static java.lang.classfile.ClassFile.ACC_STATIC;
|
||||
import static java.lang.constant.ConstantDescs.*;
|
||||
import static java.lang.constant.DirectMethodHandleDesc.Kind.SPECIAL;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/**
|
||||
@ -72,6 +81,12 @@ public class SpecialStatic {
|
||||
|
||||
private static ClassLoader cl = new CustomClassLoader();
|
||||
private static Class t1, t3;
|
||||
private static final MethodTypeDesc MTD_int = MethodTypeDesc.of(CD_int);
|
||||
private static final MethodTypeDesc MTD_Lookup = MethodTypeDesc.of(CD_MethodHandles_Lookup);
|
||||
private static final String METHOD_NAME = "m";
|
||||
private static final ClassDesc CD_T1 = ClassDesc.of("T1");
|
||||
private static final ClassDesc CD_T2 = ClassDesc.of("T2");
|
||||
private static final ClassDesc CD_T3 = ClassDesc.of("T3");
|
||||
static {
|
||||
try {
|
||||
t1 = cl.loadClass("T1");
|
||||
@ -103,93 +118,60 @@ public class SpecialStatic {
|
||||
}
|
||||
|
||||
public static byte[] dumpT1() {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv;
|
||||
|
||||
cw.visit(52, ACC_PUBLIC + ACC_SUPER, "T1", null, "java/lang/Object", null);
|
||||
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "m", "()I", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitIntInsn(BIPUSH, 1);
|
||||
mv.visitInsn(IRETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(CD_T1, clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
clb.withMethodBody(METHOD_NAME, MTD_int, ACC_PUBLIC, cob -> {
|
||||
cob.bipush(1);
|
||||
cob.ireturn();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static byte[] dumpT2() {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv;
|
||||
|
||||
cw.visit(52, ACC_PUBLIC + ACC_SUPER, "T2", null, "T1", null);
|
||||
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "T1", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
|
||||
mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "m", "()I", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitIntInsn(BIPUSH, 2);
|
||||
mv.visitInsn(IRETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(CD_T2, clb -> {
|
||||
clb.withSuperclass(CD_T1);
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_T1, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
clb.withMethodBody(METHOD_NAME, MTD_int, ACC_PUBLIC | ACC_STATIC, cob -> {
|
||||
cob.bipush(2);
|
||||
cob.ireturn();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static byte[] dumpT3() {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv;
|
||||
|
||||
cw.visit(52, ACC_PUBLIC + ACC_SUPER, "T3", null, "T2", null);
|
||||
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "T2", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "m", "()I", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitIntInsn(BIPUSH, 3);
|
||||
mv.visitInsn(IRETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
|
||||
// getMethodHandle
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "getMethodHandle", "()Ljava/lang/invoke/MethodHandle;", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(new Handle(H_INVOKESPECIAL, "T1", "m", "()I"));
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(1, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// getLookup
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "getLookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(1, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
return ClassFile.of().build(CD_T3, clb -> {
|
||||
clb.withSuperclass(CD_T2);
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_T2, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
clb.withMethodBody(METHOD_NAME, MTD_int, ACC_PUBLIC, cob -> {
|
||||
cob.bipush(3);
|
||||
cob.ireturn();
|
||||
});
|
||||
clb.withMethodBody("getMethodHandle", MethodTypeDesc.of(CD_MethodHandle),
|
||||
ACC_PUBLIC | ACC_STATIC, cob -> {
|
||||
cob.constantInstruction(MethodHandleDesc.ofMethod(SPECIAL, CD_T1, METHOD_NAME, MTD_int));
|
||||
cob.areturn();
|
||||
});
|
||||
clb.withMethodBody("getLookup", MTD_Lookup,
|
||||
ACC_PUBLIC | ACC_STATIC, cob -> {
|
||||
cob.invokestatic(CD_MethodHandles, "lookup", MTD_Lookup);
|
||||
cob.areturn();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,15 +26,21 @@
|
||||
* @bug 8026213
|
||||
* @summary Reflection support for private methods in interfaces
|
||||
* @author Robert Field
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @run main TestPrivateInterfaceMethodReflect
|
||||
*/
|
||||
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.reflect.*;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import static java.lang.classfile.ClassFile.ACC_PRIVATE;
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.constant.ConstantDescs.CD_int;
|
||||
import static java.lang.constant.ConstantDescs.INIT_NAME;
|
||||
import static java.lang.constant.ConstantDescs.MTD_void;
|
||||
|
||||
public class TestPrivateInterfaceMethodReflect {
|
||||
|
||||
@ -42,10 +48,10 @@ public class TestPrivateInterfaceMethodReflect {
|
||||
static final String CLASS_NAME = "PrivateInterfaceMethodReflectTest_Class";
|
||||
static final int EXPECTED = 1234;
|
||||
|
||||
static class TestClassLoader extends ClassLoader implements Opcodes {
|
||||
static class TestClassLoader extends ClassLoader {
|
||||
|
||||
@Override
|
||||
public Class findClass(String name) throws ClassNotFoundException {
|
||||
public Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
byte[] b;
|
||||
try {
|
||||
b = loadClassData(name);
|
||||
@ -56,39 +62,28 @@ public class TestPrivateInterfaceMethodReflect {
|
||||
return defineClass(name, b, 0, b.length);
|
||||
}
|
||||
|
||||
private byte[] loadClassData(String name) throws Exception {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv;
|
||||
switch (name) {
|
||||
case INTERFACE_NAME:
|
||||
cw.visit(V1_8, ACC_ABSTRACT | ACC_INTERFACE | ACC_PUBLIC, INTERFACE_NAME, null, "java/lang/Object", null);
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PRIVATE, "privInstance", "()I", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(EXPECTED);
|
||||
mv.visitInsn(IRETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
break;
|
||||
case CLASS_NAME:
|
||||
cw.visit(52, ACC_SUPER | ACC_PUBLIC, CLASS_NAME, null, "java/lang/Object", new String[]{INTERFACE_NAME});
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cw.visitEnd();
|
||||
|
||||
return cw.toByteArray();
|
||||
private byte[] loadClassData(String name) {
|
||||
return switch (name) {
|
||||
case INTERFACE_NAME -> ClassFile.of().build(ClassDesc.ofInternalName(INTERFACE_NAME), clb -> {
|
||||
clb.withFlags(AccessFlag.ABSTRACT, AccessFlag.INTERFACE, AccessFlag.PUBLIC);
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withMethodBody("privInstance", MethodTypeDesc.of(CD_int), ACC_PRIVATE, cob -> {
|
||||
cob.constantInstruction(EXPECTED);
|
||||
cob.ireturn();
|
||||
});
|
||||
});
|
||||
case CLASS_NAME -> ClassFile.of().build(ClassDesc.of(CLASS_NAME), clb -> {
|
||||
clb.withFlags(AccessFlag.PUBLIC);
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withInterfaceSymbols(ClassDesc.ofInternalName(INTERFACE_NAME));
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
});
|
||||
default -> throw new IllegalArgumentException();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,7 +91,7 @@ public class TestPrivateInterfaceMethodReflect {
|
||||
TestClassLoader tcl = new TestClassLoader();
|
||||
Class<?> itf = tcl.loadClass(INTERFACE_NAME);
|
||||
Class<?> k = tcl.loadClass(CLASS_NAME);
|
||||
Object inst = k.newInstance();
|
||||
Object inst = k.getDeclaredConstructor().newInstance();
|
||||
Method[] meths = itf.getDeclaredMethods();
|
||||
if (meths.length != 1) {
|
||||
throw new Exception("Expected one method in " + INTERFACE_NAME + " instead " + meths.length);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,23 +25,27 @@
|
||||
* @test
|
||||
* @bug 8255560
|
||||
* @summary Class::isRecord should check that the current class is final and not abstract
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* @enablePreview
|
||||
* @library /test/lib
|
||||
* @run testng/othervm IsRecordTest
|
||||
* @run testng/othervm/java.security.policy=allPermissions.policy IsRecordTest
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.lang.classfile.attribute.RecordAttribute;
|
||||
import java.lang.classfile.attribute.RecordComponentInfo;
|
||||
import jdk.test.lib.ByteCodeLoader;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import static java.lang.System.out;
|
||||
import static jdk.internal.org.objectweb.asm.ClassWriter.*;
|
||||
import static java.lang.classfile.ClassFile.ACC_ABSTRACT;
|
||||
import static java.lang.classfile.ClassFile.ACC_FINAL;
|
||||
import static java.lang.constant.ConstantDescs.CD_int;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
@ -82,9 +86,9 @@ public class IsRecordTest {
|
||||
out.println("\n--- testDirectSubClass isFinal=%s, isAbstract=%s, extendsJLR=%s, withRecordAttr=%s, expectIsRecord=%s ---"
|
||||
.formatted(isFinal, isAbstract, extendsJLR, withRecordAttr, expectIsRecord));
|
||||
|
||||
List<RecordComponentEntry> rc = null;
|
||||
List<RecordComponentInfo> rc = null;
|
||||
if (withRecordAttr)
|
||||
rc = List.of(new RecordComponentEntry("x", "I"));
|
||||
rc = List.of(RecordComponentInfo.of("x", CD_int));
|
||||
String superName = extendsJLR ? "java/lang/Record" : "java/lang/Object";
|
||||
var classBytes = generateClassBytes("C", isFinal, isAbstract, superName, rc);
|
||||
Class<?> cls = ByteCodeLoader.load("C", classBytes);
|
||||
@ -109,9 +113,9 @@ public class IsRecordTest {
|
||||
out.println("\n--- testIndirectSubClass isFinal=%s, isAbstract=%s withRecordAttr=%s ---"
|
||||
.formatted(isFinal, isAbstract, withRecordAttr));
|
||||
|
||||
List<RecordComponentEntry> rc = null;
|
||||
List<RecordComponentInfo> rc = null;
|
||||
if (withRecordAttr)
|
||||
rc = List.of(new RecordComponentEntry("x", "I"));
|
||||
rc = List.of(RecordComponentInfo.of("x", CD_int));
|
||||
var supFooClassBytes = generateClassBytes("SupFoo", false, isAbstract, "java/lang/Record", rc);
|
||||
var subFooClassBytes = generateClassBytes("SubFoo", isFinal, isAbstract, "SupFoo", rc);
|
||||
var allClassBytes = Map.of("SupFoo", supFooClassBytes,
|
||||
@ -161,29 +165,18 @@ public class IsRecordTest {
|
||||
boolean isFinal,
|
||||
boolean isAbstract,
|
||||
String superName,
|
||||
List<RecordComponentEntry> components) {
|
||||
ClassWriter cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES);
|
||||
|
||||
int access = 0;
|
||||
if (isFinal)
|
||||
access = access | Opcodes.ACC_FINAL;
|
||||
if (isAbstract)
|
||||
access = access | Opcodes.ACC_ABSTRACT;
|
||||
|
||||
cw.visit(Opcodes.V16,
|
||||
access,
|
||||
className,
|
||||
null,
|
||||
superName,
|
||||
null);
|
||||
|
||||
if (components != null)
|
||||
components.forEach(rc -> cw.visitRecordComponent(rc.name(), rc.descriptor(), null));
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
List<RecordComponentInfo> components) {
|
||||
return ClassFile.of().build(ClassDesc.ofInternalName(className), clb -> {
|
||||
int access = 0;
|
||||
if (isFinal)
|
||||
access = access | ACC_FINAL;
|
||||
if (isAbstract)
|
||||
access = access | ACC_ABSTRACT;
|
||||
clb.withFlags(access);
|
||||
clb.withSuperclass(ClassDesc.ofInternalName(superName));
|
||||
if (components != null)
|
||||
clb.accept(RecordAttribute.of(components));
|
||||
});
|
||||
}
|
||||
|
||||
record RecordComponentEntry (String name, String descriptor) { }
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,16 +24,20 @@
|
||||
/**
|
||||
* @test
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* jdk.compiler
|
||||
* @modules jdk.compiler
|
||||
* @enablePreview
|
||||
* @build jdk.test.lib.compiler.CompilerUtils
|
||||
* @run testng/othervm BadProvidersTest
|
||||
* @summary Basic test of ServiceLoader with bad provider and bad provider
|
||||
* factories deployed on the module path
|
||||
*/
|
||||
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@ -45,14 +49,17 @@ import java.util.ServiceLoader.Provider;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import org.testng.annotations.DataProvider;
|
||||
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
import static java.lang.classfile.ClassFile.ACC_STATIC;
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.constant.ConstantDescs.INIT_NAME;
|
||||
import static java.lang.constant.ConstantDescs.MTD_void;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/**
|
||||
@ -207,55 +214,36 @@ public class BadProvidersTest {
|
||||
public void testWithTwoFactoryMethods() throws Exception {
|
||||
Path mods = compileTest(TEST1_MODULE);
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V9,
|
||||
ACC_PUBLIC + ACC_SUPER,
|
||||
"p/ProviderFactory",
|
||||
null,
|
||||
"java/lang/Object",
|
||||
null);
|
||||
var bytes = ClassFile.of().build(ClassDesc.of("p", "ProviderFactory"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
|
||||
// public static p.Service provider()
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
|
||||
"provider",
|
||||
"()Lp/Service;",
|
||||
null,
|
||||
null);
|
||||
mv.visitTypeInsn(NEW, "p/ProviderFactory$1");
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitMethodInsn(INVOKESPECIAL,
|
||||
"p/ProviderFactory$1",
|
||||
"<init>", "()V",
|
||||
false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
var providerFactory$1 = ClassDesc.of("p", "ProviderFactory$1");
|
||||
|
||||
// public static p.ProviderFactory$1 provider()
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
|
||||
"provider",
|
||||
"()Lp/ProviderFactory$1;",
|
||||
null,
|
||||
null);
|
||||
mv.visitTypeInsn(NEW, "p/ProviderFactory$1");
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitMethodInsn(INVOKESPECIAL,
|
||||
"p/ProviderFactory$1",
|
||||
"<init>",
|
||||
"()V",
|
||||
false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
// public static p.Service provider()
|
||||
clb.withMethodBody("provider", MethodTypeDesc.of(ClassDesc.of("p", "Service")),
|
||||
ACC_PUBLIC | ACC_STATIC, cob -> {
|
||||
cob.new_(providerFactory$1);
|
||||
cob.dup();
|
||||
cob.invokespecial(providerFactory$1, INIT_NAME, MTD_void);
|
||||
cob.areturn();
|
||||
});
|
||||
|
||||
cw.visitEnd();
|
||||
// public static p.ProviderFactory$1 provider()
|
||||
clb.withMethodBody("provider", MethodTypeDesc.of(providerFactory$1),
|
||||
ACC_PUBLIC | ACC_STATIC, cob -> {
|
||||
cob.new_(providerFactory$1);
|
||||
cob.dup();
|
||||
cob.invokespecial(providerFactory$1, INIT_NAME, MTD_void);
|
||||
cob.areturn();
|
||||
});
|
||||
});
|
||||
|
||||
// write the class bytes into the compiled module directory
|
||||
Path classFile = mods.resolve(TEST1_MODULE)
|
||||
.resolve("p")
|
||||
.resolve("ProviderFactory.class");
|
||||
Files.write(classFile, cw.toByteArray());
|
||||
Files.write(classFile, bytes);
|
||||
|
||||
// load providers and instantiate each one
|
||||
loadProviders(mods, TEST1_MODULE).forEach(Provider::get);
|
||||
|
Loading…
x
Reference in New Issue
Block a user