Commit 76e16723 authored by Remi Forax's avatar Remi Forax

Merge branch 'asm8-permitted-subtypes' into 'master'

add ASM8_EXPERIMENTAL API + support of PermittedSubtypes attribute

See merge request !274
parents 6359986e f14f4bac
Pipeline #6767 passed with stage
in 10 minutes and 49 seconds
......@@ -61,7 +61,7 @@ public class BasicInterpreter extends Interpreter<BasicValue> implements Opcodes
* version.
*/
public BasicInterpreter() {
super(ASM7);
super(/* latest api = */ ASM7);
if (getClass() != BasicInterpreter.class) {
throw new IllegalStateException();
}
......
......@@ -47,7 +47,7 @@ public class BasicVerifier extends BasicInterpreter {
* use this constructor</i>. Instead, they must use the {@link #BasicVerifier(int)} version.
*/
public BasicVerifier() {
super(ASM7);
super(/* latest api = */ ASM7);
if (getClass() != BasicVerifier.class) {
throw new IllegalStateException();
}
......
......@@ -93,7 +93,12 @@ public class SimpleVerifier extends BasicVerifier {
final Type currentSuperClass,
final List<Type> currentClassInterfaces,
final boolean isInterface) {
this(ASM7, currentClass, currentSuperClass, currentClassInterfaces, isInterface);
this(
/* latest api = */ ASM7,
currentClass,
currentSuperClass,
currentClassInterfaces,
isInterface);
if (getClass() != SimpleVerifier.class) {
throw new IllegalStateException();
}
......
......@@ -51,7 +51,7 @@ public class SourceInterpreter extends Interpreter<SourceValue> implements Opcod
* version.
*/
public SourceInterpreter() {
super(ASM7);
super(/* latest api = */ ASM7);
if (getClass() != SourceInterpreter.class) {
throw new IllegalStateException();
}
......
......@@ -1173,7 +1173,7 @@ public class AnalyzerTest extends AsmTest {
private static class MockInterpreter extends Interpreter<MockValue> {
MockInterpreter() {
super(Opcodes.ASM7);
super(/* latest */ Opcodes.ASM8_EXPERIMENTAL);
}
@Override
......
......@@ -125,7 +125,7 @@ public class AnalyzerWithBasicInterpreterTest extends AsmTest {
new ClassReader(PrecompiledClass.JDK8_ALL_FRAMES.getBytes()).accept(classNode, 0);
Analyzer<BasicValue> analyzer =
new Analyzer<BasicValue>(
new BasicInterpreter(Opcodes.ASM7) {
new BasicInterpreter(/* latest */ Opcodes.ASM8_EXPERIMENTAL) {
@Override
public BasicValue merge(final BasicValue value1, final BasicValue value2) {
return new BasicValue(super.merge(value1, value2).getType());
......
......@@ -81,7 +81,12 @@ public class SimpleVerifierTest {
Type superType = Type.getObjectType("D");
Type interfaceType = Type.getObjectType("I");
SimpleVerifier simpleVerifier =
new SimpleVerifier(Opcodes.ASM7, baseType, superType, Arrays.asList(interfaceType), false) {
new SimpleVerifier(
/* latest */ Opcodes.ASM8_EXPERIMENTAL,
baseType,
superType,
Arrays.asList(interfaceType),
false) {
@Override
public boolean isAssignableFrom(final Type type1, final Type type2) {
......@@ -114,7 +119,8 @@ public class SimpleVerifierTest {
Type baseType = Type.getObjectType("C");
Type interfaceType = Type.getObjectType("I");
SimpleVerifier simpleVerifier =
new SimpleVerifier(Opcodes.ASM7, interfaceType, null, null, true) {
new SimpleVerifier(
/* latest */ Opcodes.ASM8_EXPERIMENTAL, interfaceType, null, null, true) {
@Override
protected Type getSuperClass(final Type type) {
......
......@@ -116,7 +116,7 @@ public class AnalyzerAdapter extends MethodVisitor {
final String name,
final String descriptor,
final MethodVisitor methodVisitor) {
this(Opcodes.ASM7, owner, access, name, descriptor, methodVisitor);
this(/* latest api = */ Opcodes.ASM7, owner, access, name, descriptor, methodVisitor);
if (getClass() != AnalyzerAdapter.class) {
throw new IllegalStateException();
}
......
......@@ -49,7 +49,7 @@ public class AnnotationRemapper extends AnnotationVisitor {
* @param remapper the remapper to use to remap the types in the visited annotation.
*/
public AnnotationRemapper(final AnnotationVisitor annotationVisitor, final Remapper remapper) {
this(Opcodes.ASM7, annotationVisitor, remapper);
this(/* latest api = */ Opcodes.ASM7, annotationVisitor, remapper);
}
/**
......
......@@ -73,7 +73,7 @@ public class ClassRemapper extends ClassVisitor {
* @param remapper the remapper to use to remap the types in the visited class.
*/
public ClassRemapper(final ClassVisitor classVisitor, final Remapper remapper) {
this(Opcodes.ASM7, classVisitor, remapper);
this(/* latest api = */ Opcodes.ASM7, classVisitor, remapper);
}
/**
......@@ -204,6 +204,11 @@ public class ClassRemapper extends ClassVisitor {
super.visitNestMember(remapper.mapType(nestMember));
}
@Override
public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
super.visitPermittedSubtypeExperimental(remapper.mapType(permittedSubtype));
}
/**
* Constructs a new remapper for fields. The default implementation of this method returns a new
* {@link FieldRemapper}.
......
......@@ -47,7 +47,7 @@ public class CodeSizeEvaluator extends MethodVisitor implements Opcodes {
private int maxSize;
public CodeSizeEvaluator(final MethodVisitor methodVisitor) {
this(Opcodes.ASM7, methodVisitor);
this(/* latest api = */ Opcodes.ASM7, methodVisitor);
}
protected CodeSizeEvaluator(final int api, final MethodVisitor methodVisitor) {
......
......@@ -51,7 +51,7 @@ public class FieldRemapper extends FieldVisitor {
* @param remapper the remapper to use to remap the types in the visited field.
*/
public FieldRemapper(final FieldVisitor fieldVisitor, final Remapper remapper) {
this(Opcodes.ASM7, fieldVisitor, remapper);
this(/* latest api = */ Opcodes.ASM7, fieldVisitor, remapper);
}
/**
......
......@@ -201,7 +201,7 @@ public class GeneratorAdapter extends LocalVariablesSorter {
final int access,
final String name,
final String descriptor) {
this(Opcodes.ASM7, methodVisitor, access, name, descriptor);
this(/* latest api = */ Opcodes.ASM7, methodVisitor, access, name, descriptor);
if (getClass() != GeneratorAdapter.class) {
throw new IllegalStateException();
}
......
......@@ -53,7 +53,7 @@ public class InstructionAdapter extends MethodVisitor {
* @throws IllegalStateException If a subclass calls this constructor.
*/
public InstructionAdapter(final MethodVisitor methodVisitor) {
this(Opcodes.ASM7, methodVisitor);
this(/* latest api = */ Opcodes.ASM7, methodVisitor);
if (getClass() != InstructionAdapter.class) {
throw new IllegalStateException();
}
......@@ -621,7 +621,7 @@ public class InstructionAdapter extends MethodVisitor {
|| (value instanceof Type && ((Type) value).getSort() == Type.METHOD))) {
throw new UnsupportedOperationException("This feature requires ASM5");
}
if (api != Opcodes.ASM7 && value instanceof ConstantDynamic) {
if (api < Opcodes.ASM7 && value instanceof ConstantDynamic) {
throw new UnsupportedOperationException("This feature requires ASM7");
}
if (value instanceof Integer) {
......
......@@ -98,7 +98,14 @@ public class JSRInlinerAdapter extends MethodNode implements Opcodes {
final String descriptor,
final String signature,
final String[] exceptions) {
this(Opcodes.ASM7, methodVisitor, access, name, descriptor, signature, exceptions);
this(
/* latest api = */ Opcodes.ASM7,
methodVisitor,
access,
name,
descriptor,
signature,
exceptions);
if (getClass() != JSRInlinerAdapter.class) {
throw new IllegalStateException();
}
......
......@@ -81,7 +81,7 @@ public class LocalVariablesSorter extends MethodVisitor {
*/
public LocalVariablesSorter(
final int access, final String descriptor, final MethodVisitor methodVisitor) {
this(Opcodes.ASM7, access, descriptor, methodVisitor);
this(/* latest api = */ Opcodes.ASM7, access, descriptor, methodVisitor);
if (getClass() != LocalVariablesSorter.class) {
throw new IllegalStateException();
}
......
......@@ -53,7 +53,7 @@ public class MethodRemapper extends MethodVisitor {
* @param remapper the remapper to use to remap the types in the visited method.
*/
public MethodRemapper(final MethodVisitor methodVisitor, final Remapper remapper) {
this(Opcodes.ASM7, methodVisitor, remapper);
this(/* latest api = */ Opcodes.ASM7, methodVisitor, remapper);
}
/**
......
......@@ -49,7 +49,7 @@ public class ModuleRemapper extends ModuleVisitor {
* @param remapper the remapper to use to remap the types in the visited module.
*/
public ModuleRemapper(final ModuleVisitor moduleVisitor, final Remapper remapper) {
this(Opcodes.ASM7, moduleVisitor, remapper);
this(/* latest api = */ Opcodes.ASM7, moduleVisitor, remapper);
}
/**
......
......@@ -150,7 +150,7 @@ public class SerialVersionUIDAdder extends ClassVisitor {
* @throws IllegalStateException If a subclass calls this constructor.
*/
public SerialVersionUIDAdder(final ClassVisitor classVisitor) {
this(Opcodes.ASM7, classVisitor);
this(/* latest api = */ Opcodes.ASM7, classVisitor);
if (getClass() != SerialVersionUIDAdder.class) {
throw new IllegalStateException();
}
......
......@@ -53,7 +53,7 @@ public class SignatureRemapper extends SignatureVisitor {
* @param remapper the remapper to use to remap the types in the visited signature.
*/
public SignatureRemapper(final SignatureVisitor signatureVisitor, final Remapper remapper) {
this(Opcodes.ASM7, signatureVisitor, remapper);
this(/* latest api = */ Opcodes.ASM7, signatureVisitor, remapper);
}
/**
......
......@@ -61,7 +61,7 @@ public class StaticInitMerger extends ClassVisitor {
* null.
*/
public StaticInitMerger(final String prefix, final ClassVisitor classVisitor) {
this(Opcodes.ASM7, prefix, classVisitor);
this(/* latest api = */ Opcodes.ASM7, prefix, classVisitor);
}
/**
......
......@@ -70,7 +70,14 @@ public class TryCatchBlockSorter extends MethodNode {
final String descriptor,
final String signature,
final String[] exceptions) {
this(Opcodes.ASM7, methodVisitor, access, name, descriptor, signature, exceptions);
this(
/* latest api = */ Opcodes.ASM7,
methodVisitor,
access,
name,
descriptor,
signature,
exceptions);
if (getClass() != TryCatchBlockSorter.class) {
throw new IllegalStateException();
}
......
......@@ -557,8 +557,8 @@ public class AdviceAdapterTest extends AsmTest {
MethodNode outputMethod = new MethodNode(Opcodes.ACC_PUBLIC, "<init>", "(I)V", null, null);
AdviceAdapter adviceAdapter =
new AdviceAdapter(
Opcodes.ASM7,
new MethodVisitor(Opcodes.ASM7, outputMethod) {},
/* latest */ Opcodes.ASM8_EXPERIMENTAL,
new MethodVisitor(/* latest */ Opcodes.ASM8_EXPERIMENTAL, outputMethod) {},
Opcodes.ACC_PUBLIC,
"<init>",
"()V") {
......@@ -616,7 +616,12 @@ public class AdviceAdapterTest extends AsmTest {
private static class BasicAdviceAdapter extends AdviceAdapter {
BasicAdviceAdapter(final MethodVisitor methodVisitor) {
super(Opcodes.ASM7, methodVisitor, Opcodes.ACC_PUBLIC, "<init>", "(I)V");
super(
/* latest */ Opcodes.ASM8_EXPERIMENTAL,
methodVisitor,
Opcodes.ACC_PUBLIC,
"<init>",
"(I)V");
}
@Override
......
......@@ -181,7 +181,7 @@ public class AnalyzerAdapterTest extends AsmTest {
private boolean hasOriginalFrame;
AnalyzedFramesInserter(final MethodVisitor methodVisitor) {
super(Opcodes.ASM7, methodVisitor);
super(/* latest */ Opcodes.ASM8_EXPERIMENTAL, methodVisitor);
}
void setAnalyzerAdapter(final AnalyzerAdapter analyzerAdapter) {
......
......@@ -149,7 +149,7 @@ public class ClassRemapperTest extends AsmTest {
ClassNode classNode = new ClassNode();
ClassRemapper classRemapper =
new ClassRemapper(
Opcodes.ASM7,
/* latest */ Opcodes.ASM8_EXPERIMENTAL,
classNode,
new Remapper() {
@Override
......
......@@ -848,7 +848,11 @@ public class GeneratorAdapterTest {
textifier = new Textifier();
generatorAdapter =
new GeneratorAdapter(
Opcodes.ASM7, new TraceMethodVisitor(textifier), access, name, descriptor);
/* latest */ Opcodes.ASM8_EXPERIMENTAL,
new TraceMethodVisitor(textifier),
access,
name,
descriptor);
}
public String push(final boolean value) {
......@@ -1221,9 +1225,7 @@ public class GeneratorAdapterTest {
@Override
public String toString() {
String result =
textifier
.text
.stream()
textifier.text.stream()
.map(text -> text.toString().trim())
.collect(Collectors.joining(" "));
textifier.text.clear();
......
......@@ -174,9 +174,7 @@ public class InstructionAdapterTest extends AsmTest {
assertEquals(
"ICONST_0 ICONST_1 ICONST_2 BIPUSH 51 ICONST_4 ICONST_5 LDC 6 LDC 7.0 LDC 8.0 LDC \"9\" "
+ "LDC Lpkg/Class;.class LDC pkg/Class.nameI (1)",
textifier
.text
.stream()
textifier.text.stream()
.map(text -> text.toString().trim())
.collect(Collectors.joining(" ")));
}
......
......@@ -218,7 +218,7 @@ public class LocalVariablesSorterTest extends AsmTest {
new ClassReader(Files.newInputStream(Paths.get("src/test/resources/Issue317586.class")));
ClassWriter classWriter = new ClassWriter(0);
ClassVisitor localVariablesSorter =
new LocalVariablesSorterClassAdapter(Opcodes.ASM7, classWriter);
new LocalVariablesSorterClassAdapter(/* latest */ Opcodes.ASM8_EXPERIMENTAL, classWriter);
classReader.accept(localVariablesSorter, ClassReader.EXPAND_FRAMES);
......
......@@ -60,7 +60,7 @@ public class ModuleHashesAttributeTest {
ModuleHashesAttribute moduleHashesAttribute = new ModuleHashesAttribute();
new ClassReader(classWriter.toByteArray())
.accept(
new ClassVisitor(Opcodes.ASM7) {
new ClassVisitor(/* latest */ Opcodes.ASM8_EXPERIMENTAL) {
@Override
public void visitAttribute(final Attribute attribute) {
......
......@@ -51,7 +51,7 @@ public class ModuleResolutionAttributeTest {
ModuleResolutionAttribute moduleResolutionAttribute = new ModuleResolutionAttribute();
new ClassReader(classWriter.toByteArray())
.accept(
new ClassVisitor(Opcodes.ASM7) {
new ClassVisitor(/* latest */ Opcodes.ASM8_EXPERIMENTAL) {
@Override
public void visitAttribute(final Attribute attribute) {
......
......@@ -51,7 +51,7 @@ public class ModuleTargetAttributeTest {
ModuleTargetAttribute moduleTargetAttribute = new ModuleTargetAttribute();
new ClassReader(classWriter.toByteArray())
.accept(
new ClassVisitor(Opcodes.ASM7) {
new ClassVisitor(/* latest */ Opcodes.ASM8_EXPERIMENTAL) {
@Override
public void visitAttribute(final Attribute attribute) {
......
......@@ -122,7 +122,8 @@ public class SerialVersionUidAdderTest extends AsmTest {
ClassReader classReader = new ClassReader(classParameter.getBytes());
ClassWriter classWriter = new ClassWriter(0);
classReader.accept(new SerialVersionUIDAdder(classWriter), 0);
classReader.accept(
new SerialVersionUIDAdder(/* latest */ Opcodes.ASM8_EXPERIMENTAL, classWriter), 0);
if ((classReader.getAccess() & Opcodes.ACC_ENUM) == 0) {
assertTrue(new ClassFile(classWriter.toByteArray()).toString().contains("serialVersionUID"));
......
......@@ -92,7 +92,7 @@ public abstract class AsmTest {
* The expected pattern (i.e. regular expression) that ASM's UnsupportedOperationException
* messages are supposed to match.
*/
public static final String UNSUPPORTED_OPERATION_MESSAGE_PATTERN = ".* requires ASM[567]";
public static final String UNSUPPORTED_OPERATION_MESSAGE_PATTERN = ".* requires ASM[5678].*";
/**
* A precompiled class, hand-crafted to contain some set of class file structures. These classes
......@@ -122,7 +122,8 @@ public abstract class AsmTest {
JDK9_MODULE("jdk9.module-info"),
JDK11_ALL_INSTRUCTIONS("jdk11.AllInstructions"),
JDK11_ALL_STRUCTURES("jdk11.AllStructures"),
JDK11_ALL_STRUCTURES_NESTED("jdk11.AllStructures$Nested");
JDK11_ALL_STRUCTURES_NESTED("jdk11.AllStructures$Nested"),
JDK14_ALL_STRUCTURES("jdk14.AllStructures");
private final String name;
private byte[] bytes;
......@@ -164,7 +165,13 @@ public abstract class AsmTest {
if (name.startsWith("jdk9.") && api.value() < Api.ASM6.value()) {
return true;
}
return name.startsWith("jdk11.") && api.value() < Api.ASM7.value();
if (name.startsWith("jdk11.") && api.value() < Api.ASM7.value()) {
return true;
}
if (name.startsWith("jdk14.") && api.value() < Api.ASM8.value()) {
return true;
}
return false;
}
/**
......@@ -181,6 +188,9 @@ public abstract class AsmTest {
if (name.startsWith("jdk11.")) {
return Util.getMajorJavaVersion() < 11;
}
if (name.startsWith("jdk14.")) {
return Util.getMajorJavaVersion() < 14;
}
return false;
}
......@@ -247,7 +257,8 @@ public abstract class AsmTest {
ASM4("ASM4", 4 << 16),
ASM5("ASM5", 5 << 16),
ASM6("ASM6", 6 << 16),
ASM7("ASM7", 7 << 16);
ASM7("ASM7", 7 << 16),
ASM8("ASM8", 1 << 24 | 8 << 16 | 0 << 8);
private final String name;
private final int value;
......@@ -260,7 +271,7 @@ public abstract class AsmTest {
/**
* Returns the int value of this version, as expected by ASM.
*
* @return one of the ASM4, ASM5, ASM6 or ASM7 constants from the ASM Opcodes interface.
* @return one of the ASM4, ASM5, ASM6, ASM7 or ASM8 constants from the ASM Opcodes interface.
*/
public int value() {
return value;
......@@ -269,7 +280,7 @@ public abstract class AsmTest {
/**
* Returns a human readable symbol corresponding to this version.
*
* @return one of "ASM4", "ASM5", "ASM6" or "ASM7".
* @return one of "ASM4", "ASM5", "ASM6" "ASM7" or or "ASM8".
*/
@Override
public String toString() {
......@@ -294,10 +305,10 @@ public abstract class AsmTest {
* with {@code @MethodSource("allClassesAndLatestApi")} will be executed on all the precompiled
* classes, with the latest api.
*
* @return all the possible (precompiledClass, ASM7) pairs, for all the precompiled classes.
* @return all the possible (precompiledClass, ASM8) pairs, for all the precompiled classes.
*/
public static Stream<Arguments> allClassesAndLatestApi() {
return classesAndApis(Api.ASM7);
return classesAndApis(Api.ASM8);
}
private static Stream<Arguments> classesAndApis(final Api... apis) {
......
......@@ -494,6 +494,8 @@ public class ClassFile {
dumpNestHostAttribute(parser, builder);
} else if (attributeName.equals("NestMembers")) {
dumpNestMembersAttribute(parser, builder);
} else if (attributeName.equals("PermittedSubtypes")) {
dumpPermittedSubtypesAttribute(parser, builder);
} else if (attributeName.equals("StackMap")) {
dumpStackMapAttribute(parser, builder);
} else if (!attributeName.equals("CodeComment") && !attributeName.equals("Comment")) {
......@@ -1686,6 +1688,22 @@ public class ClassFile {
}
}
/**
* Parses and dumps a PermittedSubtypes attribute.
*
* @param parser a class parser.
* @param builder a dump builder.
* @throws IOException if the class can't be parsed.
* @see <a href="https://openjdk.java.net/jeps/360">JEP 360</a>
*/
private static void dumpPermittedSubtypesAttribute(final Parser parser, final Builder builder)
throws IOException {
int numberOfClasses = builder.add("number_of_classes: ", parser.u2());
for (int i = 0; i < numberOfClasses; ++i) {
builder.addCpInfo("class: ", parser.u2());
}
}
/**
* Parses and dumps a StackMap attribute.
*
......
// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package jdk14;
public sealed interface AllStructures {
final class ClassSubType {
}
record RecordSubType(int component1, String component2) {
}
}
\ No newline at end of file
......@@ -98,7 +98,7 @@ public class AsmTestTest extends AsmTest {
new HashSet<Object>(Arrays.asList(PrecompiledClass.values())),
allArguments.stream().map(arg -> arg.get()[0]).collect(Collectors.toSet()));
assertEquals(
new HashSet<Object>(Arrays.asList(Api.ASM7)),
new HashSet<Object>(Arrays.asList(Api.ASM8)),
allArguments.stream().map(arg -> arg.get()[1]).collect(Collectors.toSet()));
}
}
......@@ -60,7 +60,7 @@ public class AnnotationNode extends AnnotationVisitor {
* @throws IllegalStateException If a subclass calls this constructor.
*/
public AnnotationNode(final String descriptor) {
this(Opcodes.ASM7, descriptor);
this(/* latest api = */ Opcodes.ASM7, descriptor);
if (getClass() != AnnotationNode.class) {
throw new IllegalStateException();
}
......@@ -84,7 +84,7 @@ public class AnnotationNode extends AnnotationVisitor {
* @param values where the visited values must be stored.
*/
AnnotationNode(final List<Object> values) {
super(Opcodes.ASM7);
super(/* latest api = */ Opcodes.ASM7);
this.values = values;
}
......
......@@ -126,6 +126,15 @@ public class ClassNode extends ClassVisitor {
/** The internal names of the nest members of this class. May be {@literal null}. */
public List<String> nestMembers;
/**
* <b>Experimental, use at your own risk. This method will be renamed when it becomes stable, this
* will break existing code using it</b>. The internal names of the permitted subtypes of this
* class. May be {@literal null}.
*
* @deprecated this API is experimental.
*/
@Deprecated public List<String> permittedSubtypesExperimental;
/** The fields of this class. */
public List<FieldNode> fields;
......@@ -236,6 +245,11 @@ public class ClassNode extends ClassVisitor {
nestMembers = Util.add(nestMembers, nestMember);
}
@Override
public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
permittedSubtypesExperimental = Util.add(permittedSubtypesExperimental, permittedSubtype);
}
@Override
public void visitInnerClass(
final String name, final String outerName, final String innerName, final int access) {
......@@ -285,6 +299,9 @@ public class ClassNode extends ClassVisitor {
* {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
*/
public void check(final int api) {
if (api != Opcodes.ASM8_EXPERIMENTAL && permittedSubtypesExperimental != null) {
throw new UnsupportedClassVersionException();
}
if (api < Opcodes.ASM7 && (nestHostClass != null || nestMembers != null)) {
throw new UnsupportedClassVersionException();
}
......@@ -395,6 +412,12 @@ public class ClassNode extends ClassVisitor {
classVisitor.visitNestMember(nestMembers.get(i));
}
}
// Visit the permitted subtypes.
if (permittedSubtypesExperimental != null) {
for (int i = 0, n = permittedSubtypesExperimental.size(); i < n; ++i) {
classVisitor.visitPermittedSubtypeExperimental(permittedSubtypesExperimental.get(i));
}
}
// Visit the inner classes.
for (int i = 0, n = innerClasses.size(); i < n; ++i) {
innerClasses.get(i).accept(classVisitor);
......
......@@ -99,7 +99,7 @@ public class FieldNode extends FieldVisitor {
final String descriptor,
final String signature,
final Object value) {
this(Opcodes.ASM7, access, name, descriptor, signature, value);