Commit 86acc5d7 authored by Remi Forax's avatar Remi Forax
Browse files

Fix code to allow empty record by using a new flag ACC_RECORD

parent 2412662b
Pipeline #8167 passed with stage
in 11 minutes and 40 seconds
......@@ -124,6 +124,7 @@ public abstract class AsmTest {
JDK11_ALL_STRUCTURES("jdk11.AllStructures"),
JDK11_ALL_STRUCTURES_NESTED("jdk11.AllStructures$Nested"),
JDK14_ALL_STRUCTURES_RECORD("jdk14.AllStructures$RecordSubType"),
JDK14_ALL_STRUCTURES_EMPTY_RECORD("jdk14.AllStructures$EmptyRecord"),
JDK15_ALL_STRUCTURES("jdk15.AllStructures"),
;
......
......@@ -40,4 +40,8 @@ public class AllStructures {
@IRCA(v = 4) @VRCA(v = 5) @VTUA(v = 6) @ITUA(v = 7) List<String> component2) {
}
record EmptyRecord() {
}
}
\ No newline at end of file
......@@ -62,6 +62,8 @@ public class AsmTestTest extends AsmTest {
assertEquals("jdk11.AllInstructions", PrecompiledClass.JDK11_ALL_INSTRUCTIONS.toString());
assertTrue(PrecompiledClass.JDK14_ALL_STRUCTURES_RECORD.isMoreRecentThan(Api.ASM7));
assertFalse(PrecompiledClass.JDK14_ALL_STRUCTURES_RECORD.isMoreRecentThan(Api.ASM8));
assertTrue(PrecompiledClass.JDK14_ALL_STRUCTURES_EMPTY_RECORD.isMoreRecentThan(Api.ASM7));
assertFalse(PrecompiledClass.JDK14_ALL_STRUCTURES_EMPTY_RECORD.isMoreRecentThan(Api.ASM8));
assertTrue(PrecompiledClass.JDK15_ALL_STRUCTURES.isMoreRecentThan(Api.ASM8));
assertFalse(PrecompiledClass.JDK15_ALL_STRUCTURES.isMoreRecentThan(Api.ASM9));
}
......
......@@ -54,7 +54,7 @@ public class ClassNode extends ClassVisitor {
/**
* The class's access flags (see {@link org.objectweb.asm.Opcodes}). This field also indicates if
* the class is deprecated.
* the class is deprecated {@link Opcodes#ACC_DEPRECATED} or a record {@link Opcodes#ACC_RECORD}.
*/
public int access;
......@@ -322,7 +322,7 @@ public class ClassNode extends ClassVisitor {
if (api != Opcodes.ASM9_EXPERIMENTAL && permittedSubtypesExperimental != null) {
throw new UnsupportedClassVersionException();
}
if (api < Opcodes.ASM8 && recordComponents != null) {
if (api < Opcodes.ASM8 && ((access & Opcodes.ACC_RECORD) != 0 || recordComponents != null)) {
throw new UnsupportedClassVersionException();
}
if (api < Opcodes.ASM7 && (nestHostClass != null || nestMembers != null)) {
......
......@@ -47,6 +47,7 @@ import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.ModuleVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.RecordComponentVisitor;
import org.objectweb.asm.TypePath;
import org.objectweb.asm.test.AsmTest;
......@@ -177,6 +178,17 @@ public class ClassNodeTest extends AsmTest {
super(api, classVisitor);
}
@Override
public void visit(
final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces) {
super.visit(version, access & ~Opcodes.ACC_RECORD, name, signature, superName, interfaces);
}
@Override
public ModuleVisitor visitModule(final String name, final int access, final String version) {
return null;
......
......@@ -1407,6 +1407,13 @@ public class ASMifier extends Printer {
stringBuilder.append("ACC_DEPRECATED");
isEmpty = false;
}
if ((accessFlags & Opcodes.ACC_RECORD) != 0) {
if (!isEmpty) {
stringBuilder.append(" | ");
}
stringBuilder.append("ACC_RECORD");
isEmpty = false;
}
if ((accessFlags & (Opcodes.ACC_MANDATED | Opcodes.ACC_MODULE)) != 0) {
if (!isEmpty) {
stringBuilder.append(" | ");
......
......@@ -226,6 +226,7 @@ public class CheckClassAdapter extends ClassVisitor {
| Opcodes.ACC_ANNOTATION
| Opcodes.ACC_ENUM
| Opcodes.ACC_DEPRECATED
| Opcodes.ACC_RECORD
| Opcodes.ACC_MODULE);
if (name == null) {
throw new IllegalArgumentException("Illegal class name (null)");
......
......@@ -78,6 +78,7 @@ public class Textifier extends Printer {
private static final String CLASS_SUFFIX = ".class";
private static final String DEPRECATED = "// DEPRECATED\n";
private static final String RECORD = "// RECORD\n";
private static final String INVISIBLE = " // invisible\n";
private static final List<String> FRAME_TYPES =
......@@ -186,6 +187,9 @@ public class Textifier extends Printer {
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
stringBuilder.append(DEPRECATED);
}
if ((access & Opcodes.ACC_RECORD) != 0) {
stringBuilder.append(RECORD);
}
appendRawAccess(access);
appendDescriptor(CLASS_SIGNATURE, signature);
......
// class version 58.65535 (-65478)
// RECORD
// access flags 0x10030
final class jdk14/AllStructures$EmptyRecord extends java/lang/Record {
// compiled from: AllStructures.java
NESTHOST jdk14/AllStructures
// access flags 0x18
final static INNERCLASS jdk14/AllStructures$EmptyRecord jdk14/AllStructures EmptyRecord
// access flags 0x19
public final static INNERCLASS java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup
// access flags 0x1
public <init>()V
L0
LINENUMBER 44 L0
ALOAD 0
INVOKESPECIAL java/lang/Record.<init> ()V
RETURN
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x1
public toString()Ljava/lang/String;
L0
LINENUMBER 44 L0
ALOAD 0
INVOKEDYNAMIC toString(Ljdk14/AllStructures$EmptyRecord;)Ljava/lang/String; [
// handle kind 0x6 : INVOKESTATIC
java/lang/runtime/ObjectMethods.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
// arguments:
jdk14.AllStructures$EmptyRecord.class,
""
]
ARETURN
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x11
public final hashCode()I
L0
LINENUMBER 44 L0
ALOAD 0
INVOKEDYNAMIC hashCode(Ljdk14/AllStructures$EmptyRecord;)I [
// handle kind 0x6 : INVOKESTATIC
java/lang/runtime/ObjectMethods.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
// arguments:
jdk14.AllStructures$EmptyRecord.class,
""
]
IRETURN
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x11
public final equals(Ljava/lang/Object;)Z
L0
LINENUMBER 44 L0
ALOAD 0
ALOAD 1
INVOKEDYNAMIC equals(Ljdk14/AllStructures$EmptyRecord;Ljava/lang/Object;)Z [
// handle kind 0x6 : INVOKESTATIC
java/lang/runtime/ObjectMethods.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
// arguments:
jdk14.AllStructures$EmptyRecord.class,
""
]
IRETURN
MAXSTACK = 2
MAXLOCALS = 2
}
// class version 58.65535 (-65478)
// access flags 0x30
// RECORD
// access flags 0x10030
final class jdk14/AllStructures$RecordSubType extends java/lang/Record {
// compiled from: AllStructures.java
......
......@@ -514,6 +514,7 @@ public class ClassReader {
runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset;
} else if (Constants.RECORD.equals(attributeName)) {
recordOffset = currentAttributeOffset;
accessFlags |= Opcodes.ACC_RECORD;
} else if (Constants.MODULE.equals(attributeName)) {
moduleOffset = currentAttributeOffset;
} else if (Constants.MODULE_MAIN_CLASS.equals(attributeName)) {
......
......@@ -88,7 +88,8 @@ public abstract class ClassVisitor {
* @param version the class version. The minor version is stored in the 16 most significant bits,
* and the major version in the 16 least significant bits.
* @param access the class's access flags (see {@link Opcodes}). This parameter also indicates if
* the class is deprecated.
* the class is deprecated {@link Opcodes#ACC_DEPRECATED} or a record {@link
* Opcodes#ACC_RECORD}.
* @param name the internal name of the class (see {@link Type#getInternalName()}).
* @param signature the signature of this class. May be {@literal null} if the class is not a
* generic one, and does not extend or implement generic classes or interfaces.
......@@ -105,6 +106,9 @@ public abstract class ClassVisitor {
final String signature,
final String superName,
final String[] interfaces) {
if (api < Opcodes.ASM8 && (access & Opcodes.ACC_RECORD) != 0) {
throw new UnsupportedOperationException("Records requires ASM8");
}
if (cv != null) {
cv.visit(version, access, name, signature, superName, interfaces);
}
......
......@@ -79,8 +79,8 @@ public class ClassWriter extends ClassVisitor {
/**
* The access_flags field of the JVMS ClassFile structure. This field can contain ASM specific
* access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the
* ClassFile structure.
* access flags, such as {@link Opcodes#ACC_DEPRECATED} or {}@link Opcodes#ACC_RECORD}, which are
* removed when generating the ClassFile structure.
*/
private int accessFlags;
......@@ -583,7 +583,7 @@ public class ClassWriter extends ClassVisitor {
}
int recordComponentCount = 0;
int recordSize = 0;
if (firstRecordComponent != null) {
if ((accessFlags & Opcodes.ACC_RECORD) != 0 || firstRecordComponent != null) {
RecordComponentWriter recordComponentWriter = firstRecordComponent;
while (recordComponentWriter != null) {
++recordComponentCount;
......@@ -705,7 +705,7 @@ public class ClassWriter extends ClassVisitor {
.putShort(numberOfPermittedSubtypeClasses)
.putByteArray(permittedSubtypeClasses.data, 0, permittedSubtypeClasses.length);
}
if (firstRecordComponent != null) {
if ((accessFlags & Opcodes.ACC_RECORD) != 0 || firstRecordComponent != null) {
result
.putShort(symbolTable.addConstantUtf8(Constants.RECORD))
.putInt(recordSize + 2)
......
......@@ -324,6 +324,7 @@ public interface Opcodes {
// access flags, and also to make sure that these flags are automatically filtered out when
// written in class files (because access flags are stored using 16 bits only).
int ACC_RECORD = 0x10000; // class
int ACC_DEPRECATED = 0x20000; // class, field, method
// Possible values for the type operand of the NEWARRAY instruction.
......
......@@ -374,6 +374,16 @@ public class ClassReaderTest extends AsmTest implements Opcodes {
ClassReader classReader = new ClassReader(classParameter.getBytes());
ClassVisitor classVisitor =
new EmptyClassVisitor(apiParameter.value()) {
@Override
public void visit(
final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces) {
// access may contains ACC_RECORD
}
@Override
public ModuleVisitor visitModule(
......@@ -456,7 +466,9 @@ public class ClassReaderTest extends AsmTest implements Opcodes {
Executable accept = () -> classReader.accept(classVisitor, 0);
boolean hasPermittedSubtypes = classParameter == PrecompiledClass.JDK15_ALL_STRUCTURES;
boolean hasRecord = classParameter == PrecompiledClass.JDK14_ALL_STRUCTURES_RECORD;
boolean hasRecord =
classParameter == PrecompiledClass.JDK14_ALL_STRUCTURES_RECORD
|| classParameter == PrecompiledClass.JDK14_ALL_STRUCTURES_EMPTY_RECORD;
boolean hasNestHostOrMembers =
classParameter == PrecompiledClass.JDK11_ALL_STRUCTURES
|| classParameter == PrecompiledClass.JDK11_ALL_STRUCTURES_NESTED;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment