From fb0b4e1e2aecb061048d4c90e5c4bc3450838604 Mon Sep 17 00:00:00 2001 From: rhel Date: Sun, 3 Nov 2024 21:34:34 -0800 Subject: [PATCH 1/4] Add PermittedSubclasses Attribute --- .../javassist/bytecode/AttributeInfo.java | 2 + .../PermittedSubclassesAttribute.java | 122 ++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 src/main/javassist/bytecode/PermittedSubclassesAttribute.java diff --git a/src/main/javassist/bytecode/AttributeInfo.java b/src/main/javassist/bytecode/AttributeInfo.java index 3aa0bd31..5fcdd392 100644 --- a/src/main/javassist/bytecode/AttributeInfo.java +++ b/src/main/javassist/bytecode/AttributeInfo.java @@ -119,6 +119,8 @@ 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(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..933ffc7e --- /dev/null +++ b/src/main/javassist/bytecode/PermittedSubclassesAttribute.java @@ -0,0 +1,122 @@ +/** + * + */ +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 the write method to output the attribute correctly + @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; + } + +} From 3198d99197c912659bbd29a28a6b1de7479b92f0 Mon Sep 17 00:00:00 2001 From: rhel Date: Mon, 4 Nov 2024 22:03:28 -0800 Subject: [PATCH 2/4] 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); + } + +} From cf99ead5b4f6ac0bc9a72e6db668c73d4f9801d4 Mon Sep 17 00:00:00 2001 From: rhel Date: Wed, 6 Nov 2024 23:16:10 -0800 Subject: [PATCH 3/4] Fix reading of Attributes --- src/main/javassist/bytecode/RecordAttribute.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/javassist/bytecode/RecordAttribute.java b/src/main/javassist/bytecode/RecordAttribute.java index 848b3698..95f43546 100644 --- a/src/main/javassist/bytecode/RecordAttribute.java +++ b/src/main/javassist/bytecode/RecordAttribute.java @@ -1,5 +1,6 @@ package javassist.bytecode; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -47,9 +48,22 @@ public RecordAttribute(ConstPool cp, int nameIndex, DataInputStream in) throws I byte[] attrInfo = new byte[attrLength]; System.arraycopy(info, pos, attrInfo, 0, attrLength); pos += attrLength; - AttributeInfo attr = new AttributeInfo(constPool, attrNameIndex, attrInfo); + + // 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); } From 463318db19a35ac1eba51a2589752e5ff9f8fc8e Mon Sep 17 00:00:00 2001 From: rhel Date: Thu, 7 Nov 2024 12:21:28 -0800 Subject: [PATCH 4/4] Name Index Getter --- src/main/javassist/bytecode/RecordAttribute.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/javassist/bytecode/RecordAttribute.java b/src/main/javassist/bytecode/RecordAttribute.java index 95f43546..fca20069 100644 --- a/src/main/javassist/bytecode/RecordAttribute.java +++ b/src/main/javassist/bytecode/RecordAttribute.java @@ -117,6 +117,16 @@ public RecordComponentInfo(ConstPool cp, int nameIndex, int descriptorIndex, Lis 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. *