From 3198d99197c912659bbd29a28a6b1de7479b92f0 Mon Sep 17 00:00:00 2001 From: rhel Date: Mon, 4 Nov 2024 22:03:28 -0800 Subject: [PATCH] Update AttributeInfo.java, PermittedSubclassesAttribute.java, and RecordAttribute.java --- .../javassist/bytecode/AttributeInfo.java | 2 + .../PermittedSubclassesAttribute.java | 1 - .../javassist/bytecode/RecordAttribute.java | 221 ++++++++++++++++++ 3 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 src/main/javassist/bytecode/RecordAttribute.java diff --git a/src/main/javassist/bytecode/AttributeInfo.java b/src/main/javassist/bytecode/AttributeInfo.java index 5fcdd392..9f312f60 100644 --- a/src/main/javassist/bytecode/AttributeInfo.java +++ b/src/main/javassist/bytecode/AttributeInfo.java @@ -121,6 +121,8 @@ else if (nameStr.equals(ParameterAnnotationsAttribute.visibleTag) 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 index 933ffc7e..0fc9011c 100644 --- a/src/main/javassist/bytecode/PermittedSubclassesAttribute.java +++ b/src/main/javassist/bytecode/PermittedSubclassesAttribute.java @@ -63,7 +63,6 @@ public void addClass(String clazz) { } } - // Override the write method to output the attribute correctly @Override public void write(DataOutputStream out) throws IOException { out.writeShort(constPool.addUtf8Info(getName())); diff --git a/src/main/javassist/bytecode/RecordAttribute.java b/src/main/javassist/bytecode/RecordAttribute.java new file mode 100644 index 00000000..848b3698 --- /dev/null +++ b/src/main/javassist/bytecode/RecordAttribute.java @@ -0,0 +1,221 @@ +package javassist.bytecode; + +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; + AttributeInfo attr = new AttributeInfo(constPool, attrNameIndex, attrInfo); + 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 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); + } + +}