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