Skip to content

Commit

Permalink
Merge pull request #217 from ebean-orm/feature/asm971
Browse files Browse the repository at this point in the history
Update to ASM 9.7.1
  • Loading branch information
rbygrave authored Oct 10, 2024
2 parents c035b80 + 2fe80c7 commit 8f6d26f
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 55 deletions.
2 changes: 1 addition & 1 deletion ebean-agent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<groupId>io.ebean</groupId>
<artifactId>ebean-agent</artifactId>
<version>14.6.1</version>
<version>14.7.0</version>
<packaging>jar</packaging>

<name>ebean-agent</name>
Expand Down
139 changes: 129 additions & 10 deletions ebean-agent/src/main/java/io/ebean/enhance/asm/Attribute.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ public class Attribute {
public final String type;

/**
* The raw content of this attribute, only used for unknown attributes (see {@link #isUnknown()}).
* The 6 header bytes of the attribute (attribute_name_index and attribute_length) are <i>not</i>
* included.
* The raw content of this attribute, as returned by {@link
* #write(ClassWriter,byte[],int,int,int)}. The 6 header bytes of the attribute
* (attribute_name_index and attribute_length) are <i>not</i> included.
*/
private byte[] content;
private ByteVector cachedContent;

/**
* The next attribute in this attribute list (Attribute instances can be linked via this field to
Expand Down Expand Up @@ -93,7 +93,9 @@ public boolean isCodeAttribute() {
*
* @return the labels corresponding to this attribute, or {@literal null} if this attribute is not
* a Code attribute that contains labels.
* @deprecated no longer used by ASM.
*/
@Deprecated
protected Label[] getLabels() {
return new Label[0];
}
Expand All @@ -115,7 +117,9 @@ protected Label[] getLabels() {
* attribute header bytes (attribute_name_index and attribute_length) are not taken into
* account here.
* @param labels the labels of the method's code, or {@literal null} if the attribute to be read
* is not a Code attribute.
* is not a Code attribute. Labels defined in the attribute must be created and added to this
* array, if not already present, by calling the {@link #readLabel} method (do not create
* {@link Label} instances directly).
* @return a <i>new</i> {@link Attribute} object corresponding to the specified bytes.
*/
protected Attribute read(
Expand All @@ -126,16 +130,99 @@ protected Attribute read(
final int codeAttributeOffset,
final Label[] labels) {
Attribute attribute = new Attribute(type);
attribute.content = new byte[length];
System.arraycopy(classReader.classFileBuffer, offset, attribute.content, 0, length);
attribute.cachedContent = new ByteVector(classReader.readBytes(offset, length));
return attribute;
}

/**
* Reads an attribute with the same {@link #type} as the given attribute. This method returns a
* new {@link Attribute} object, corresponding to the 'length' bytes starting at 'offset', in the
* given ClassReader.
*
* @param attribute The attribute prototype that is used for reading.
* @param classReader the class that contains the attribute to be read.
* @param offset index of the first byte of the attribute's content in {@link ClassReader}. The 6
* attribute header bytes (attribute_name_index and attribute_length) are not taken into
* account here.
* @param length the length of the attribute's content (excluding the 6 attribute header bytes).
* @param charBuffer the buffer to be used to call the ClassReader methods requiring a
* 'charBuffer' parameter.
* @param codeAttributeOffset index of the first byte of content of the enclosing Code attribute
* in {@link ClassReader}, or -1 if the attribute to be read is not a Code attribute. The 6
* attribute header bytes (attribute_name_index and attribute_length) are not taken into
* account here.
* @param labels the labels of the method's code, or {@literal null} if the attribute to be read
* is not a Code attribute. Labels defined in the attribute are added to this array, if not
* already present.
* @return a new {@link Attribute} object corresponding to the specified bytes.
*/
public static Attribute read(
final Attribute attribute,
final ClassReader classReader,
final int offset,
final int length,
final char[] charBuffer,
final int codeAttributeOffset,
final Label[] labels) {
return attribute.read(classReader, offset, length, charBuffer, codeAttributeOffset, labels);
}

/**
* Returns the label corresponding to the given bytecode offset by calling {@link
* ClassReader#readLabel}. This creates and adds the label to the given array if it is not already
* present. Note that this created label may be a {@link Label} subclass instance, if the given
* ClassReader overrides {@link ClassReader#readLabel}. Hence {@link #read(ClassReader, int, int,
* char[], int, Label[])} must not manually create {@link Label} instances.
*
* @param bytecodeOffset a bytecode offset in a method.
* @param labels the already created labels, indexed by their offset. If a label already exists
* for bytecodeOffset this method does not create a new one. Otherwise it stores the new label
* in this array.
* @return a label for the given bytecode offset.
*/
public static Label readLabel(
final ClassReader classReader, final int bytecodeOffset, final Label[] labels) {
return classReader.readLabel(bytecodeOffset, labels);
}

/**
* Calls {@link #write(ClassWriter,byte[],int,int,int)} if it has not already been called and
* returns its result or its (cached) previous result.
*
* @param classWriter the class to which this attribute must be added. This parameter can be used
* to add the items that corresponds to this attribute to the constant pool of this class.
* @param code the bytecode of the method corresponding to this Code attribute, or {@literal null}
* if this attribute is not a Code attribute. Corresponds to the 'code' field of the Code
* attribute.
* @param codeLength the length of the bytecode of the method corresponding to this code
* attribute, or 0 if this attribute is not a Code attribute. Corresponds to the 'code_length'
* field of the Code attribute.
* @param maxStack the maximum stack size of the method corresponding to this Code attribute, or
* -1 if this attribute is not a Code attribute.
* @param maxLocals the maximum number of local variables of the method corresponding to this code
* attribute, or -1 if this attribute is not a Code attribute.
* @return the byte array form of this attribute.
*/
private ByteVector maybeWrite(
final ClassWriter classWriter,
final byte[] code,
final int codeLength,
final int maxStack,
final int maxLocals) {
if (cachedContent == null) {
cachedContent = write(classWriter, code, codeLength, maxStack, maxLocals);
}
return cachedContent;
}

/**
* Returns the byte array form of the content of this attribute. The 6 header bytes
* (attribute_name_index and attribute_length) must <i>not</i> be added in the returned
* ByteVector.
*
* <p>This method is only invoked once to compute the binary form of this attribute. Subsequent
* changes to the attribute after it was written for the first time will not be considered.
*
* @param classWriter the class to which this attribute must be added. This parameter can be used
* to add the items that corresponds to this attribute to the constant pool of this class.
* @param code the bytecode of the method corresponding to this Code attribute, or {@literal null}
Expand All @@ -156,7 +243,39 @@ protected ByteVector write(
final int codeLength,
final int maxStack,
final int maxLocals) {
return new ByteVector(content);
return cachedContent;
}

/**
* Returns the byte array form of the content of the given attribute. The 6 header bytes
* (attribute_name_index and attribute_length) are <i>not</i> added in the returned byte array.
*
* @param attribute The attribute that should be written.
* @param classWriter the class to which this attribute must be added. This parameter can be used
* to add the items that corresponds to this attribute to the constant pool of this class.
* @param code the bytecode of the method corresponding to this Code attribute, or {@literal null}
* if this attribute is not a Code attribute. Corresponds to the 'code' field of the Code
* attribute.
* @param codeLength the length of the bytecode of the method corresponding to this code
* attribute, or 0 if this attribute is not a Code attribute. Corresponds to the 'code_length'
* field of the Code attribute.
* @param maxStack the maximum stack size of the method corresponding to this Code attribute, or
* -1 if this attribute is not a Code attribute.
* @param maxLocals the maximum number of local variables of the method corresponding to this code
* attribute, or -1 if this attribute is not a Code attribute.
* @return the byte array form of this attribute.
*/
public static byte[] write(
final Attribute attribute,
final ClassWriter classWriter,
final byte[] code,
final int codeLength,
final int maxStack,
final int maxLocals) {
ByteVector content = attribute.maybeWrite(classWriter, code, codeLength, maxStack, maxLocals);
byte[] result = new byte[content.length];
System.arraycopy(content.data, 0, result, 0, content.length);
return result;
}

/**
Expand Down Expand Up @@ -221,7 +340,7 @@ final int computeAttributesSize(
Attribute attribute = this;
while (attribute != null) {
symbolTable.addConstantUtf8(attribute.type);
size += 6 + attribute.write(classWriter, code, codeLength, maxStack, maxLocals).length;
size += 6 + attribute.maybeWrite(classWriter, code, codeLength, maxStack, maxLocals).length;
attribute = attribute.nextAttribute;
}
return size;
Expand Down Expand Up @@ -308,7 +427,7 @@ final void putAttributes(
Attribute attribute = this;
while (attribute != null) {
ByteVector attributeContent =
attribute.write(classWriter, code, codeLength, maxStack, maxLocals);
attribute.maybeWrite(classWriter, code, codeLength, maxStack, maxLocals);
// Put attribute_name_index and attribute_length.
output.putShort(symbolTable.addConstantUtf8(attribute.type)).putInt(attributeContent.length);
output.putByteArray(attributeContent.data, 0, attributeContent.length);
Expand Down
16 changes: 15 additions & 1 deletion ebean-agent/src/main/java/io/ebean/enhance/asm/ClassReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public ClassReader(
this.b = classFileBuffer;
// Check the class' major_version. This field is after the magic and minor_version fields, which
// use 4 and 2 bytes respectively.
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V23) {
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V24) {
throw new IllegalArgumentException(
"Unsupported class file major version " + readShort(classFileOffset + 6));
}
Expand Down Expand Up @@ -3597,6 +3597,20 @@ public int readByte(final int offset) {
return classFileBuffer[offset] & 0xFF;
}

/**
* Reads several bytes in this {@link ClassReader}. <i>This method is intended for {@link
* Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
*
* @param offset the start offset of the bytes to be read in this {@link ClassReader}.
* @param length the number of bytes to read.
* @return the read bytes.
*/
public byte[] readBytes(final int offset, final int length) {
byte[] result = new byte[length];
System.arraycopy(classFileBuffer, offset, result, 0, length);
return result;
}

/**
* Reads an unsigned short value in this {@link ClassReader}. <i>This method is intended for
* {@link Attribute} sub classes, and is normally not needed by class generators or adapters.</i>
Expand Down
30 changes: 23 additions & 7 deletions ebean-agent/src/main/java/io/ebean/enhance/asm/ClassWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,7 @@ public ClassWriter(final ClassReader classReader, final int flags) {
super(/* latest api = */ Opcodes.ASM9);
this.flags = flags;
symbolTable = classReader == null ? new SymbolTable(this) : new SymbolTable(this, classReader);
if ((flags & COMPUTE_FRAMES) != 0) {
compute = MethodWriter.COMPUTE_ALL_FRAMES;
} else if ((flags & COMPUTE_MAXS) != 0) {
compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL;
} else {
compute = MethodWriter.COMPUTE_NOTHING;
}
setFlags(flags);
}

// -----------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -1020,6 +1014,28 @@ public int newNameType(final String name, final String descriptor) {
return symbolTable.addConstantNameAndType(name, descriptor);
}

/**
* Changes the computation strategy of method properties like max stack size, max number of local
* variables, and frames.
*
* <p><b>WARNING</b>: {@link #setFlags(int)} method changes the behavior of new method visitors
* returned from {@link #visitMethod(int, String, String, String, String[])}. The behavior will be
* changed only after the next method visitor is returned. All the previously returned method
* visitors keep their previous behavior.
*
* @param flags option flags that can be used to modify the default behavior of this class. Must
* be zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}.
*/
public final void setFlags(final int flags) {
if ((flags & ClassWriter.COMPUTE_FRAMES) != 0) {
compute = MethodWriter.COMPUTE_ALL_FRAMES;
} else if ((flags & ClassWriter.COMPUTE_MAXS) != 0) {
compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL;
} else {
compute = MethodWriter.COMPUTE_NOTHING;
}
}

// -----------------------------------------------------------------------------------------------
// Default method to compute common super classes when computing stack map frames
// -----------------------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ static void checkIsPreview(final InputStream classInputStream) {
}
if (minorVersion != 0xFFFF) {
throw new IllegalStateException(
"ASM9_EXPERIMENTAL can only be used by classes compiled with --enable-preview");
"ASM10_EXPERIMENTAL can only be used by classes compiled with --enable-preview");
}
}
}
19 changes: 10 additions & 9 deletions ebean-agent/src/main/java/io/ebean/enhance/asm/MethodVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,16 @@
* visitTypeAnnotation} | {@code visitAttribute} )* [ {@code visitCode} ( {@code visitFrame} |
* {@code visit<i>X</i>Insn} | {@code visitLabel} | {@code visitInsnAnnotation} | {@code
* visitTryCatchBlock} | {@code visitTryCatchAnnotation} | {@code visitLocalVariable} | {@code
* visitLocalVariableAnnotation} | {@code visitLineNumber} )* {@code visitMaxs} ] {@code visitEnd}.
* In addition, the {@code visit<i>X</i>Insn} and {@code visitLabel} methods must be called in the
* sequential order of the bytecode instructions of the visited code, {@code visitInsnAnnotation}
* must be called <i>after</i> the annotated instruction, {@code visitTryCatchBlock} must be called
* <i>before</i> the labels passed as arguments have been visited, {@code
* visitTryCatchBlockAnnotation} must be called <i>after</i> the corresponding try catch block has
* been visited, and the {@code visitLocalVariable}, {@code visitLocalVariableAnnotation} and {@code
* visitLineNumber} methods must be called <i>after</i> the labels passed as arguments have been
* visited.
* visitLocalVariableAnnotation} | {@code visitLineNumber} | {@code visitAttribute} )* {@code
* visitMaxs} ] {@code visitEnd}. In addition, the {@code visit<i>X</i>Insn} and {@code visitLabel}
* methods must be called in the sequential order of the bytecode instructions of the visited code,
* {@code visitInsnAnnotation} must be called <i>after</i> the annotated instruction, {@code
* visitTryCatchBlock} must be called <i>before</i> the labels passed as arguments have been
* visited, {@code visitTryCatchBlockAnnotation} must be called <i>after</i> the corresponding try
* catch block has been visited, and the {@code visitLocalVariable}, {@code
* visitLocalVariableAnnotation} and {@code visitLineNumber} methods must be called <i>after</i> the
* labels passed as arguments have been visited. Finally, the {@code visitAttribute} method must be
* called before {@code visitCode} for non-code attributes, and after it for code attributes.
*
* @author Eric Bruneton
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ public interface Opcodes {
int V21 = 0 << 16 | 65;
int V22 = 0 << 16 | 66;
int V23 = 0 << 16 | 67;
int V24 = 0 << 16 | 68;

/**
* Version flag indicating that the class is using 'preview' features.
Expand Down
4 changes: 3 additions & 1 deletion ebean-agent/src/main/java/io/ebean/enhance/asm/Symbol.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ abstract class Symbol {
* <li>the symbol's value for {@link #CONSTANT_INTEGER_TAG},{@link #CONSTANT_FLOAT_TAG}, {@link
* #CONSTANT_LONG_TAG}, {@link #CONSTANT_DOUBLE_TAG},
* <li>the CONSTANT_MethodHandle_info reference_kind field value for {@link
* #CONSTANT_METHOD_HANDLE_TAG} symbols,
* #CONSTANT_METHOD_HANDLE_TAG} symbols (or this value left shifted by 8 bits for
* reference_kind values larger than or equal to H_INVOKEVIRTUAL and if the method owner is
* an interface),
* <li>the CONSTANT_InvokeDynamic_info bootstrap_method_attr_index field value for {@link
* #CONSTANT_INVOKE_DYNAMIC_TAG} symbols,
* <li>the offset of a bootstrap method in the BootstrapMethods boostrap_methods array, for
Expand Down
Loading

0 comments on commit 8f6d26f

Please sign in to comment.