diff --git a/src/main/javassist/bytecode/AttributeInfo.java b/src/main/javassist/bytecode/AttributeInfo.java
index 3aa0bd31..9f312f60 100644
--- a/src/main/javassist/bytecode/AttributeInfo.java
+++ b/src/main/javassist/bytecode/AttributeInfo.java
@@ -119,6 +119,10 @@ else if (nameStr.equals(AnnotationsAttribute.visibleTag)
else if (nameStr.equals(ParameterAnnotationsAttribute.visibleTag)
|| nameStr.equals(ParameterAnnotationsAttribute.invisibleTag))
return new ParameterAnnotationsAttribute(cp, name, in);
+ else if (nameStr.equals(PermittedSubclassesAttribute.tag))
+ return new PermittedSubclassesAttribute(cp, name, in);
+ else if (nameStr.equals(RecordAttribute.tag))
+ return new RecordAttribute(cp, name, in);
else if (nameStr.equals(TypeAnnotationsAttribute.visibleTag)
|| nameStr.equals(TypeAnnotationsAttribute.invisibleTag))
return new TypeAnnotationsAttribute(cp, name, in);
diff --git a/src/main/javassist/bytecode/PermittedSubclassesAttribute.java b/src/main/javassist/bytecode/PermittedSubclassesAttribute.java
new file mode 100644
index 00000000..0fc9011c
--- /dev/null
+++ b/src/main/javassist/bytecode/PermittedSubclassesAttribute.java
@@ -0,0 +1,121 @@
+/**
+ *
+ */
+package javassist.bytecode;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class representing PermittedSubclasses_attribute
+ */
+public class PermittedSubclassesAttribute extends AttributeInfo {
+
+ /**
+ * The name of the PermittedSubclasses
attribute.
+ */
+ public static final String tag = "PermittedSubclasses";
+ private List classes = new ArrayList();
+
+ protected PermittedSubclassesAttribute(ConstPool cp, int n, DataInputStream in) throws IOException {
+ super(cp, n, in);
+ classes = new ArrayList<>();
+ int pos = 0;
+ int number_of_classes = ByteArray.readU16bit(info, pos);
+ pos += 2;
+ for (int i = 0; i < number_of_classes; i++) {
+ int class_index = ByteArray.readU16bit(info, pos);
+ pos += 2;
+ classes.add(class_index);
+ }
+
+ }
+
+ /**
+ * Constructs an empty PermittedSubclassesAttribute attribute.
+ *
+ * @see #setPermittedSubclasses(List)
+ */
+ public PermittedSubclassesAttribute(ConstPool cp) {
+ super(cp, tag, new byte[2]);
+ ByteArray.write16bit(0, get(), 0);
+
+ }
+
+ /**
+ * Gets a read-only List of the class names. Does not reflect the actual map.
+ */
+ public List getPermittedSubclasses() {
+ List str = new ArrayList();
+ for (int clazz : classes) {
+ str.add(constPool.getClassInfo(clazz));
+ }
+ return str;
+ }
+
+ public void addClass(String clazz) {
+ if(!getPermittedSubclasses().contains(clazz)) {
+ classes.add(constPool.addClassInfo(clazz.replace(".", "/")));
+ }
+ }
+
+ @Override
+ public void write(DataOutputStream out) throws IOException {
+ out.writeShort(constPool.addUtf8Info(getName()));
+ int size = 2 + classes.size() * 2;
+ info = new byte[size];
+ ByteArray.write16bit(classes.size(), info, 0);
+ int pos = 2;
+ for (int clazz : classes) {
+ String className = constPool.getClassInfo(clazz);
+ ByteArray.write16bit(constPool.addClassInfo(className), info, pos);
+ pos += 2;
+ }
+ out.writeInt(info.length);
+ out.write(info);
+ }
+
+ @Override
+ void renameClass(String oldname, String newname) {
+ List str = new ArrayList();
+ for (int clazz : classes) {
+ String className = constPool.getClassInfo(clazz);
+ if (className.equals(oldname)) {
+ str.add(constPool.addClassInfo(newname));
+ } else {
+ str.add(clazz);
+ }
+ }
+ this.classes = str;
+ }
+
+ @Override
+ void renameClass(Map classnames) {
+ for (Map.Entry entry : classnames.entrySet()) {
+ renameClass(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ void getRefClasses(Map classnames) {
+ renameClass(classnames);
+ }
+
+ /**
+ * Copies this attribute and returns a new copy.
+ */
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ PermittedSubclassesAttribute attr = new PermittedSubclassesAttribute(newCp);
+ for(int clazz:classes) {
+ String name = constPool.getClassInfo(clazz);
+ attr.addClass(name);
+ }
+ return attr;
+ }
+
+}
diff --git a/src/main/javassist/bytecode/RecordAttribute.java b/src/main/javassist/bytecode/RecordAttribute.java
new file mode 100644
index 00000000..fca20069
--- /dev/null
+++ b/src/main/javassist/bytecode/RecordAttribute.java
@@ -0,0 +1,245 @@
+package javassist.bytecode;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represents the Record attribute in a class file, which stores information
+ * about the record components of a record class.
+ */
+public class RecordAttribute extends AttributeInfo {
+ public static final String tag = "Record";
+
+ private List components = new ArrayList<>();
+
+ /**
+ * Constructs a RecordAttribute by reading from a DataInputStream.
+ *
+ * @param cp the constant pool
+ * @param nameIndex the index into the constant pool of the attribute name
+ * @param in the input stream to read from
+ * @throws IOException if an I/O error occurs
+ */
+ public RecordAttribute(ConstPool cp, int nameIndex, DataInputStream in) throws IOException {
+ super(cp, nameIndex, in);
+ this.constPool = cp;
+ int pos = 0;
+ int componentsCount = ByteArray.readU16bit(info, pos);
+ pos += 2;
+ for (int i = 0; i < componentsCount; i++) {
+ int name = ByteArray.readU16bit(info, pos);
+ pos += 2;
+ int descriptorIndex = ByteArray.readU16bit(info, pos);
+ pos += 2;
+ int attributesCount = ByteArray.readU16bit(info, pos);
+ pos += 2;
+ List attributes = new ArrayList<>(attributesCount);
+ for (int j = 0; j < attributesCount; j++) {
+ int attrNameIndex = ByteArray.readU16bit(info, pos);
+ pos += 2;
+ int attrLength = ByteArray.read32bit(info, pos);
+ pos += 4;
+ byte[] attrInfo = new byte[attrLength];
+ System.arraycopy(info, pos, attrInfo, 0, attrLength);
+ pos += attrLength;
+
+ // Create a byte array containing the attribute_name_index, attribute_length, and attrInfo
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ dos.writeShort(attrNameIndex); // Write attribute_name_index (u2)
+ dos.writeInt(attrLength); // Write attribute_length (u4)
+ dos.write(attrInfo); // Write attribute_info (u1[attrLength])
+ dos.flush();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(baos.toByteArray());
+ DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);
+
+ AttributeInfo attr = AttributeInfo.read(constPool, dataInputStream);
+ attributes.add(attr);
+ }
+
+ RecordComponentInfo component = new RecordComponentInfo(constPool, name, descriptorIndex, attributes);
+ components.add(component);
+ }
+ }
+
+ /**
+ * Constructs a RecordAttribute with specified components.
+ *
+ * @param cp the constant pool
+ * @param attrName the attribute name
+ * @param components the list of record components
+ */
+ public RecordAttribute(ConstPool cp, List components) {
+ super(cp, tag);
+ this.constPool = cp;
+ this.components = components;
+ }
+
+ /**
+ * Returns the list of record components.
+ *
+ * @return the list of RecordComponentInfo
+ */
+ public List getComponents() {
+ return components;
+ }
+
+ /**
+ * Represents a single record component within the Record attribute.
+ */
+ public static class RecordComponentInfo {
+ private ConstPool constPool;
+ int nameIndex;
+ int descriptorIndex;
+ private List attributes;
+
+ /**
+ * Constructs a RecordComponentInfo.
+ *
+ * @param cp the constant pool
+ * @param nameIndex the index into the constant pool of the component's
+ * name
+ * @param descriptorIndex the index into the constant pool of the component's
+ * descriptor
+ * @param attributes the list of attributes associated with the component
+ */
+ public RecordComponentInfo(ConstPool cp, int nameIndex, int descriptorIndex, List attributes) {
+ this.constPool = cp;
+ this.nameIndex = nameIndex;
+ this.descriptorIndex = descriptorIndex;
+ this.attributes = attributes;
+ }
+
+ /**
+ * Returns the name Index of the component.
+ *
+ * @return the component name
+ */
+ public int getNameIndex() {
+ return nameIndex;
+ }
+
+
+ /**
+ * Returns the name of the component.
+ *
+ * @return the component name
+ */
+ public String getName() {
+ return constPool.getUtf8Info(nameIndex);
+ }
+
+ /**
+ * Returns the descriptor of the component.
+ *
+ * @return the component descriptor
+ */
+ public String getDescriptor() {
+ return constPool.getUtf8Info(descriptorIndex);
+ }
+
+ /**
+ * Returns the list of attributes associated with the component.
+ *
+ * @return the list of AttributeInfo
+ */
+ public List getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * Sets the descriptor index after renaming.
+ *
+ * @param index the new descriptor index
+ */
+ public void setDescriptorIndex(int index) {
+ this.descriptorIndex = index;
+ }
+
+ public void setNameIndex(int index) {
+ this.nameIndex=index;
+ }
+
+ }
+
+ @Override
+ public AttributeInfo copy(ConstPool newCp, Map classnames) {
+ List newComponents = new ArrayList<>(components.size());
+ for (RecordComponentInfo component : components) {
+ String name = component.getName();
+ int newNameIndex = newCp.addUtf8Info(name);
+ String descriptor = component.getDescriptor();
+ String newDescriptor = Descriptor.rename(descriptor, classnames);
+ int newDescriptorIndex = newCp.addUtf8Info(newDescriptor);
+ List newAttributes = new ArrayList<>();
+ for (AttributeInfo attr : component.getAttributes()) {
+ AttributeInfo newAttr = attr.copy(newCp, classnames);
+ newAttributes.add(newAttr);
+ }
+
+ RecordComponentInfo newComponent = new RecordComponentInfo(newCp, newNameIndex, newDescriptorIndex,
+ newAttributes);
+ newComponents.add(newComponent);
+ }
+ RecordAttribute newAttribute = new RecordAttribute(newCp, newComponents);
+ return newAttribute;
+ }
+
+ @Override
+ void renameClass(String oldname, String newname) {
+ for (RecordComponentInfo component : components) {
+ // Update descriptor
+ String descriptor = component.getDescriptor();
+ String newDescriptor = Descriptor.rename(descriptor, oldname, newname);
+ if (!descriptor.equals(newDescriptor)) {
+ int newDescriptorIndex = constPool.addUtf8Info(newDescriptor);
+ component.setDescriptorIndex(newDescriptorIndex);
+ }
+ // Rename class in attributes
+ for (AttributeInfo attr : component.getAttributes()) {
+ attr.renameClass(oldname, newname);
+ }
+ }
+ }
+
+ @Override
+ void renameClass(Map classnames) {
+ for (Map.Entry entry : classnames.entrySet()) {
+ renameClass(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ void getRefClasses(Map classnames) {
+ renameClass(classnames);
+ }
+
+ @Override
+ public void write(DataOutputStream out) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ dos.writeShort(components.size());
+ for (RecordComponentInfo component : components) {
+ dos.writeShort(component.nameIndex);
+ dos.writeShort(component.descriptorIndex);
+ List attributes = component.getAttributes();
+ dos.writeShort(attributes.size());
+ for (AttributeInfo attr : attributes) {
+ attr.write(dos);
+ }
+ }
+ dos.flush();
+ byte[] newInfo = baos.toByteArray();
+ out.writeShort(name);
+ out.writeInt(newInfo.length);
+ out.write(newInfo);
+ }
+
+}