This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-bcel.git
The following commit(s) were added to refs/heads/master by this push: new 97f2bec8 Support for Java 16 Record feature (#290) 97f2bec8 is described below commit 97f2bec8c46e99aa6e29b636fd414f23320f4a9f Author: Pablo Nicolas Diaz <pablonicolasd...@users.noreply.github.com> AuthorDate: Thu Apr 4 09:05:43 2024 -0300 Support for Java 16 Record feature (#290) * Support for java 17 Record feature * Fix whitespace * Fix whitespace * Fix whitespace * More whitespace fixing * Fix whitespace * Add Javadoc since tag * Fix Javadoc since tag * Remove trailing whitespace * Remove trailing whitespace * Remove trailing whitespace * Remove trailing whitespace * Remove trailing whitespace * added a missing new line * visitRecordComponent declared as a default method and documented. * removed visitRecordComponent, as is unneeded from previous commit * fixed version reference * fixed comments * javadoc added * fixed checkstyle errors * fixed checkstyle and pmd errors * fixed variable names and comments * adding tests to cover new functionality * fixed checkstyle and pmd errors * added new test cases * fixing javadoc and comments from pr * Order Javadoc tags * Javadoc * Javadoc * Javadoc * Javadoc * Javadoc * Fix whitespace * fixed typo * solved pr comments * constant pool check added * visitor fixed --------- Co-authored-by: nicolas <nicolas@nicoLaptop> Co-authored-by: Gary Gregory <garydgreg...@users.noreply.github.com> --- src/main/java/org/apache/bcel/Const.java | 5 +- .../java/org/apache/bcel/classfile/Attribute.java | 2 + .../apache/bcel/classfile/DescendingVisitor.java | 16 +++ .../java/org/apache/bcel/classfile/JavaClass.java | 26 ++++ .../java/org/apache/bcel/classfile/Record.java | 149 +++++++++++++++++++++ .../apache/bcel/classfile/RecordComponentInfo.java | 135 +++++++++++++++++++ .../java/org/apache/bcel/classfile/Visitor.java | 23 ++++ .../verifier/statics/StringRepresentation.java | 13 ++ .../org/apache/bcel/CounterVisitorTestCase.java | 5 + .../org/apache/bcel/classfile/RecordTestCase.java | 121 +++++++++++++++++ .../org/apache/bcel/visitors/CountingVisitor.java | 19 +++ src/test/resources/record/SimpleRecord.class | Bin 0 -> 1506 bytes src/test/resources/record/SimpleRecord.java | 5 + 13 files changed, 517 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/bcel/Const.java b/src/main/java/org/apache/bcel/Const.java index edf739ae..fd554493 100644 --- a/src/main/java/org/apache/bcel/Const.java +++ b/src/main/java/org/apache/bcel/Const.java @@ -3073,12 +3073,13 @@ public final class Const { public static final byte ATTR_MODULE_MAIN_CLASS = 24; public static final byte ATTR_NEST_HOST = 25; public static final byte ATTR_NEST_MEMBERS = 26; + public static final byte ATTR_RECORD = 27; - public static final short KNOWN_ATTRIBUTES = 27; // count of attributes + public static final short KNOWN_ATTRIBUTES = 28; // count of attributes private static final String[] ATTRIBUTE_NAMES = {"SourceFile", "ConstantValue", "Code", "Exceptions", "LineNumberTable", "LocalVariableTable", "InnerClasses", "Synthetic", "Deprecated", "PMGClass", "Signature", "StackMap", "RuntimeVisibleAnnotations", "RuntimeInvisibleAnnotations", "RuntimeVisibleParameterAnnotations", "RuntimeInvisibleParameterAnnotations", "AnnotationDefault", "LocalVariableTypeTable", "EnclosingMethod", - "StackMapTable", "BootstrapMethods", "MethodParameters", "Module", "ModulePackages", "ModuleMainClass", "NestHost", "NestMembers"}; + "StackMapTable", "BootstrapMethods", "MethodParameters", "Module", "ModulePackages", "ModuleMainClass", "NestHost", "NestMembers", "Record"}; /** * Constants used in the StackMap attribute. */ diff --git a/src/main/java/org/apache/bcel/classfile/Attribute.java b/src/main/java/org/apache/bcel/classfile/Attribute.java index 42510a9a..0dd994c2 100644 --- a/src/main/java/org/apache/bcel/classfile/Attribute.java +++ b/src/main/java/org/apache/bcel/classfile/Attribute.java @@ -191,6 +191,8 @@ public abstract class Attribute implements Cloneable, Node { return new NestHost(nameIndex, length, dataInput, constantPool); case Const.ATTR_NEST_MEMBERS: return new NestMembers(nameIndex, length, dataInput, constantPool); + case Const.ATTR_RECORD: + return new Record(nameIndex, length, dataInput, constantPool); default: // Never reached throw new IllegalStateException("Unrecognized attribute type tag parsed: " + tag); diff --git a/src/main/java/org/apache/bcel/classfile/DescendingVisitor.java b/src/main/java/org/apache/bcel/classfile/DescendingVisitor.java index 85de2a2a..880f1009 100644 --- a/src/main/java/org/apache/bcel/classfile/DescendingVisitor.java +++ b/src/main/java/org/apache/bcel/classfile/DescendingVisitor.java @@ -560,4 +560,20 @@ public class DescendingVisitor implements Visitor { attribute.accept(visitor); stack.pop(); } + + @Override + public void visitRecord(Record record) { + stack.push(record); + record.accept(visitor); + accept(record.getComponents()); + stack.pop(); + } + + @Override + public void visitRecordComponent(RecordComponentInfo recordComponentInfo) { + stack.push(recordComponentInfo); + recordComponentInfo.accept(visitor); + stack.pop(); + } + } diff --git a/src/main/java/org/apache/bcel/classfile/JavaClass.java b/src/main/java/org/apache/bcel/classfile/JavaClass.java index de4ac4b1..fe967983 100644 --- a/src/main/java/org/apache/bcel/classfile/JavaClass.java +++ b/src/main/java/org/apache/bcel/classfile/JavaClass.java @@ -133,8 +133,10 @@ public class JavaClass extends AccessFlags implements Cloneable, Node, Comparabl private boolean isAnonymous; private boolean isNested; + private boolean isRecord; private boolean computedNestedTypeStatus; + private boolean computedRecord; /** * In cases where we go ahead and create something, use the default SyntheticRepository, because we don't know any @@ -904,4 +906,28 @@ public class JavaClass extends AccessFlags implements Cloneable, Node, Comparabl } return buf.toString(); } + + /** + * Tests whether this class was declared as a record + * + * @return true if a record attribute is present, false otherwise. + * @since 6.9.0 + */ + public boolean isRecord() { + computeIsRecord(); + return this.isRecord; + } + + private void computeIsRecord() { + if (computedRecord) { + return; + } + for (final Attribute attribute : this.attributes) { + if (attribute instanceof Record) { + isRecord = true; + break; + } + } + this.computedRecord = true; + } } diff --git a/src/main/java/org/apache/bcel/classfile/Record.java b/src/main/java/org/apache/bcel/classfile/Record.java new file mode 100644 index 00000000..e459a0fe --- /dev/null +++ b/src/main/java/org/apache/bcel/classfile/Record.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.bcel.classfile; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.apache.bcel.Const; +import org.apache.bcel.util.Args; + +/** + * Extends {@link Attribute} and records the classes and + * interfaces that are authorized to claim membership in the nest hosted by the + * current class or interface. There may be at most one Record attribute in a + * ClassFile structure. + * + * @see Attribute + * @since 6.9.0 + */ +public final class Record extends Attribute { + + private static final RecordComponentInfo[] EMPTY_RCI_ARRAY = new RecordComponentInfo[] {}; + + private RecordComponentInfo[] components; + + /** + * Constructs object from input stream. + * + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + Record(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) + throws IOException { + this(nameIndex, length, readComponents(input, constantPool), constantPool); + } + + private static RecordComponentInfo[] readComponents(final DataInput input, final ConstantPool constantPool) + throws IOException { + final int classCount = input.readUnsignedShort(); + final RecordComponentInfo[] components = new RecordComponentInfo[classCount]; + for (int i = 0; i < classCount; i++) { + components[i] = new RecordComponentInfo(input, constantPool); + } + return components; + } + + /** + * Constructs a new instance using components. + * + * @param nameIndex Index in constant pool + * @param length Content length in bytes + * @param classes Array of Record Component Info elements + * @param constantPool Array of constants + */ + public Record(final int nameIndex, final int length, final RecordComponentInfo[] classes, + final ConstantPool constantPool) { + super(Const.ATTR_RECORD, nameIndex, length, constantPool); + this.components = classes != null ? classes : EMPTY_RCI_ARRAY; + Args.requireU2(this.components.length, "attributes.length"); + } + + /** + * Called by objects that are traversing the nodes of the tree implicitly + * defined by the contents of a Java class. For example, the hierarchy of methods, + * fields, attributes, etc. spawns a tree of objects. + * + * @param v Visitor object + */ + @Override + public void accept(final Visitor v) { + v.visitRecord(this); + } + + /** + * Copies this instance and its components. + * + * @return a deep copy of this instance and its components. + */ + @Override + public Attribute copy(final ConstantPool constantPool) { + final Record c = (Record) clone(); + if (components.length > 0) { + c.components = components.clone(); + } + c.setConstantPool(constantPool); + return c; + } + + /** + * Dumps this instance into a file stream in binary format. + * + * @param file output stream. + * @throws IOException if an I/O error occurs. + */ + @Override + public void dump(final DataOutputStream file) throws IOException { + super.dump(file); + file.writeShort(components.length); + for (final RecordComponentInfo component : components) { + component.dump(file); + } + } + + /** + * Gets all the record components. + * + * @return array of Record Component Info elements. + */ + public RecordComponentInfo[] getComponents() { + return components; + } + + /** + * Converts this instance to a String suitable for debugging. + * + * @return String a String suitable for debugging. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + buf.append("Record("); + buf.append(components.length); + buf.append("):\n"); + for (final RecordComponentInfo component : components) { + buf.append(" ").append(component.toString()).append("\n"); + } + return buf.substring(0, buf.length() - 1); // remove the last newline + } + +} diff --git a/src/main/java/org/apache/bcel/classfile/RecordComponentInfo.java b/src/main/java/org/apache/bcel/classfile/RecordComponentInfo.java new file mode 100644 index 00000000..bf489d0f --- /dev/null +++ b/src/main/java/org/apache/bcel/classfile/RecordComponentInfo.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.bcel.classfile; + +import java.io.DataInput; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.apache.bcel.Const; + +/** + * Record component info from a record. Instances from this class maps + * every component from a given record. + * + * @see <a href="https://docs.oracle.com/javase/specs/jvms/se14/preview/specs/records-jvms.html#jvms-4.7.30"> + * The Java Virtual Machine Specification, Java SE 14 Edition, Records (preview)</a> + * @since 6.9.0 + */ +public class RecordComponentInfo implements Node { + + private final int index; + private final int descriptorIndex; + private final Attribute[] attributes; + private final ConstantPool constantPool; + + /** + * Constructs a new instance from an input stream. + * + * @param input Input stream + * @param constantPool Array of constants + * @throws IOException if an I/O error occurs. + */ + public RecordComponentInfo(final DataInput input, ConstantPool constantPool) throws IOException { + this.index = input.readUnsignedShort(); + this.descriptorIndex = input.readUnsignedShort(); + final int attributesCount = input.readUnsignedShort(); + this.attributes = new Attribute[attributesCount]; + for (int j = 0; j < attributesCount; j++) { + attributes[j] = Attribute.readAttribute(input, constantPool); + } + this.constantPool = constantPool; + } + + /** + * Dumps contents into a file stream in binary format. + * + * @param file Output file stream + * @throws IOException if an I/O error occurs. + */ + public void dump(DataOutputStream file) throws IOException { + file.writeShort(index); + file.writeShort(descriptorIndex); + file.writeShort(attributes.length); + for (final Attribute attribute : attributes) { + attribute.dump(file); + } + } + + @Override + public void accept(Visitor v) { + v.visitRecordComponent(this); + } + + /** + * Gets the name index. + * + * @return index in constant pool of this record component name. + */ + public int getIndex() { + return index; + } + + /** + * Gets the description index. + * + * @return index in constant pool of this record component descriptor. + */ + public int getDescriptorIndex() { + return descriptorIndex; + } + + /** + * Gets all attributes. + * + * @return all attributes. + */ + public Attribute[] getAttributes() { + return attributes; + } + + /** + * Gets the constant pool. + * + * @return Constant pool. + */ + public ConstantPool getConstantPool() { + return constantPool; + } + + /** + * Converts this instance to a String suitable for debugging. + * + * @return a String suitable for debugging. + */ + @Override + public String toString() { + final StringBuilder buf = new StringBuilder(); + buf.append("RecordComponentInfo("); + buf.append(constantPool.getConstantString(index, Const.CONSTANT_Utf8)); + buf.append(","); + buf.append(constantPool.getConstantString(descriptorIndex, Const.CONSTANT_Utf8)); + buf.append(","); + buf.append(attributes.length); + buf.append("):\n"); + for (final Attribute attribute : attributes) { + buf.append(" ").append(attribute.toString()).append("\n"); + } + return buf.substring(0, buf.length() - 1); // remove the last newline + } + +} diff --git a/src/main/java/org/apache/bcel/classfile/Visitor.java b/src/main/java/org/apache/bcel/classfile/Visitor.java index 4f4b5c21..6f739324 100644 --- a/src/main/java/org/apache/bcel/classfile/Visitor.java +++ b/src/main/java/org/apache/bcel/classfile/Visitor.java @@ -207,6 +207,17 @@ public interface Visitor { // empty } + /** + * Visits a {@link Record} object. + * + * @param obj Record to visit + * @since 6.9.0 + */ + default void visitRecord(final Record obj) { + // empty + } + + /** * @since 6.0 */ @@ -227,6 +238,7 @@ public interface Visitor { /** * Visits a {@link StackMapType} object. + * * @param obj object to visit * @since 6.8.0 */ @@ -237,4 +249,15 @@ public interface Visitor { void visitSynthetic(Synthetic obj); void visitUnknown(Unknown obj); + + /** + * Visits a {@link RecordComponentInfo} object. + * + * @param record component to visit + * @since 6.9.0 + */ + default void visitRecordComponent(RecordComponentInfo record) { + // noop + } + } diff --git a/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java b/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java index a8941746..af12ed5a 100644 --- a/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java +++ b/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java @@ -59,6 +59,8 @@ import org.apache.bcel.classfile.NestMembers; import org.apache.bcel.classfile.Node; import org.apache.bcel.classfile.ParameterAnnotationEntry; import org.apache.bcel.classfile.ParameterAnnotations; +import org.apache.bcel.classfile.Record; +import org.apache.bcel.classfile.RecordComponentInfo; import org.apache.bcel.classfile.Signature; import org.apache.bcel.classfile.SourceFile; import org.apache.bcel.classfile.StackMap; @@ -429,4 +431,15 @@ public class StringRepresentation extends org.apache.bcel.classfile.EmptyVisitor public void visitUnknown(final Unknown obj) { tostring = toString(obj); } + + @Override + public void visitRecord(Record obj) { + tostring = toString(obj); + } + + @Override + public void visitRecordComponent(RecordComponentInfo obj) { + tostring = toString(obj); + } + } diff --git a/src/test/java/org/apache/bcel/CounterVisitorTestCase.java b/src/test/java/org/apache/bcel/CounterVisitorTestCase.java index d1fcfa5e..224369ba 100644 --- a/src/test/java/org/apache/bcel/CounterVisitorTestCase.java +++ b/src/test/java/org/apache/bcel/CounterVisitorTestCase.java @@ -217,4 +217,9 @@ public class CounterVisitorTestCase extends AbstractCounterVisitorTestCase { public void testUnknownCount() { assertEquals(0, getVisitor().unknownCount, "unknownCount"); } + + @Test + public void testRecordCount() { + assertEquals(0, getVisitor().recordCount, "recordCount"); + } } diff --git a/src/test/java/org/apache/bcel/classfile/RecordTestCase.java b/src/test/java/org/apache/bcel/classfile/RecordTestCase.java new file mode 100644 index 00000000..7a2e639d --- /dev/null +++ b/src/test/java/org/apache/bcel/classfile/RecordTestCase.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.bcel.classfile; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.IOException; + +import org.apache.bcel.AbstractTestCase; +import org.apache.bcel.util.SyntheticRepository; +import org.apache.bcel.visitors.CountingVisitor; +import org.junit.jupiter.api.Test; + +public class RecordTestCase extends AbstractTestCase { + + /** + * A record type, once compiled, should result in a class file that is + * marked such that we can determine from the access flags + * (through BCEL) that it is in fact a record. + * + * @throws IOException + * @throws ClassFormatException + */ + @Test + public void testRecordClassSaysItIs() throws ClassNotFoundException, ClassFormatException, IOException { + final JavaClass clazz = new ClassParser("src/test/resources/record/SimpleRecord.class").parse(); + assertTrue(clazz.isRecord(), "Expected SimpleRecord class to say it was a record - but it didn't !"); + final JavaClass simpleClazz = getTestJavaClass(PACKAGE_BASE_NAME + ".data.SimpleClass"); + assertFalse(simpleClazz.isRecord(), "Expected SimpleClass class to say it was not a record - but it didn't !"); + } + + /** + * A simple record with two simple fields, an integer and a String field, should + * show its content in its string representation. + * + * @throws ClassNotFoundException + * @throws ClassFormatException + * @throws IOException + */ + @Test + public void testRecordToString() throws ClassNotFoundException, ClassFormatException, IOException { + final JavaClass clazz = new ClassParser("src/test/resources/record/SimpleRecord.class").parse(); + final Attribute[] attributes = clazz.getAttributes(); + final Record recordAttribute = (Record) findAttribute("Record",clazz)[0]; + assertEquals(4, attributes.length); + assertEquals("SourceFile: SimpleRecord.java", attributes[0].toString()); + assertEquals("Record(2):\n" + + " RecordComponentInfo(aNumber,I,0):\n" + + " RecordComponentInfo(aString,Ljava/lang/String;,1):\n" + + " RuntimeVisibleAnnotations:\n" + + " @Ljavax/annotation/Nonnull;" + ,recordAttribute.toString()); + final RecordComponentInfo firstComponent = recordAttribute.getComponents()[0]; + assertEquals(5, firstComponent.getIndex()); + assertEquals(6, firstComponent.getDescriptorIndex()); + assertEquals(0, firstComponent.getAttributes().length); + assertEquals(recordAttribute.getConstantPool(),firstComponent.getConstantPool()); + assertEquals("RecordComponentInfo(aNumber,I,0):", firstComponent.toString()); + } + + + /** + * Check that we can save and load the attribute correctly. + */ + @Test + public void testAttributeSerializtion() throws ClassNotFoundException, IOException { + final JavaClass clazz = new ClassParser("src/test/resources/record/SimpleRecord.class").parse(); + final File tfile = createTestdataFile("SimpleRecord.class"); + final Record recordAttribute = (Record)findAttribute("Record",clazz)[0]; + clazz.dump(tfile); + // Read in the new version and check it is OK + final SyntheticRepository repos2 = createRepos("."); + final JavaClass clazzFromRepo = repos2.loadClass("SimpleRecord"); + assertNotNull(clazzFromRepo); // Use the variable to avoid a warning + final Record recordAttributeFromRepo = (Record)findAttribute("Record",clazzFromRepo)[0]; + assertEquals(recordAttribute.toString(), recordAttributeFromRepo.toString(), "Both attributes needs to be equal"); + tfile.deleteOnExit(); + } + + /** + * Check that we can copy a attribute correctly. + */ + @Test + public void recordsCanBeCopied() throws ClassNotFoundException, IOException { + final JavaClass clazz = new ClassParser("src/test/resources/record/SimpleRecord.class").parse(); + final JavaClass copyClazz = clazz.copy(); + assertEquals(clazz.toString(), copyClazz.toString(), "both records should have the same value"); + } + + /** + * Check that a record can be visited by our visitors + */ + @Test + public void recordsCanBeVisited() throws ClassNotFoundException, IOException { + final JavaClass clazz = new ClassParser("src/test/resources/record/SimpleRecord.class").parse(); + final CountingVisitor countVisitor = new CountingVisitor(); + final DescendingVisitor desendingVisitor = new DescendingVisitor(clazz, countVisitor); + desendingVisitor.visit(); + assertEquals(1,countVisitor.recordCount, "should count one record"); + assertEquals(2,countVisitor.recordComponentCount, "should count two record components"); + } + +} diff --git a/src/test/java/org/apache/bcel/visitors/CountingVisitor.java b/src/test/java/org/apache/bcel/visitors/CountingVisitor.java index 3522df26..93695479 100644 --- a/src/test/java/org/apache/bcel/visitors/CountingVisitor.java +++ b/src/test/java/org/apache/bcel/visitors/CountingVisitor.java @@ -68,6 +68,8 @@ import org.apache.bcel.classfile.NestHost; import org.apache.bcel.classfile.NestMembers; import org.apache.bcel.classfile.ParameterAnnotationEntry; import org.apache.bcel.classfile.ParameterAnnotations; +import org.apache.bcel.classfile.Record; +import org.apache.bcel.classfile.RecordComponentInfo; import org.apache.bcel.classfile.Signature; import org.apache.bcel.classfile.SourceFile; import org.apache.bcel.classfile.StackMap; @@ -204,6 +206,13 @@ public class CountingVisitor implements Visitor { /** @since 6.4.0 */ public int nestMembersCount; + + /** @since 6.9.0 */ + public int recordCount; + + /** @since 6.9.0 */ + public int recordComponentCount; + // CHECKSTYLE:ON @Override @@ -517,4 +526,14 @@ public class CountingVisitor implements Visitor { public void visitUnknown(final Unknown obj) { unknownCount++; } + + @Override + public void visitRecord(Record v) { + recordCount++; + } + + @Override + public void visitRecordComponent(RecordComponentInfo v) { + recordComponentCount++; + } } diff --git a/src/test/resources/record/SimpleRecord.class b/src/test/resources/record/SimpleRecord.class new file mode 100644 index 00000000..bf6b8119 Binary files /dev/null and b/src/test/resources/record/SimpleRecord.class differ diff --git a/src/test/resources/record/SimpleRecord.java b/src/test/resources/record/SimpleRecord.java new file mode 100644 index 00000000..10316b07 --- /dev/null +++ b/src/test/resources/record/SimpleRecord.java @@ -0,0 +1,5 @@ +import javax.annotation.Nonnull; + +public record SimpleRecord(int aNumber,@Nonnull String aString) { + +}