Skip to content

Commit

Permalink
Upgrade to ASM master (including early support for Java 22 bytecode)
Browse files Browse the repository at this point in the history
Closes gh-30845
  • Loading branch information
jhoeller committed Jul 17, 2023
1 parent 3a8b511 commit 0d8a843
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,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.V21) {
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V22) {
throw new IllegalArgumentException(
"Unsupported class file major version " + readShort(classFileOffset + 6));
}
Expand Down Expand Up @@ -2052,6 +2052,7 @@ private void readCode(
currentOffset = bytecodeStartOffset;
while (currentOffset < bytecodeEndOffset) {
final int currentBytecodeOffset = currentOffset - bytecodeStartOffset;
readBytecodeInstructionOffset(currentBytecodeOffset);

// Visit the label and the line number(s) for this bytecode offset, if any.
Label currentLabel = labels[currentBytecodeOffset];
Expand Down Expand Up @@ -2667,6 +2668,20 @@ private void readCode(
methodVisitor.visitMaxs(maxStack, maxLocals);
}

/**
* Handles the bytecode offset of the next instruction to be visited in {@link
* #accept(ClassVisitor,int)}. This method is called just before the instruction and before its
* associated label and stack map frame, if any. The default implementation of this method does
* nothing. Subclasses can override this method to store the argument in a mutable field, for
* instance, so that {@link MethodVisitor} instances can get the bytecode offset of each visited
* instruction (if so, the usual concurrency issues related to mutable data should be addressed).
*
* @param bytecodeOffset the bytecode offset of the next instruction to be visited.
*/
protected void readBytecodeInstructionOffset(final int bytecodeOffset) {
// Do nothing by default.
}

/**
* Returns the label corresponding to the given bytecode offset. The default implementation of
* this method creates a label for the given offset if it has not been already created.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ public class ClassWriter extends ClassVisitor {
/**
* Indicates what must be automatically computed in {@link MethodWriter}. Must be one of {@link
* MethodWriter#COMPUTE_NOTHING}, {@link MethodWriter#COMPUTE_MAX_STACK_AND_LOCAL}, {@link
* MethodWriter#COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES}, {@link
* MethodWriter#COMPUTE_INSERTED_FRAMES}, or {@link MethodWriter#COMPUTE_ALL_FRAMES}.
*/
private int compute;
Expand Down
68 changes: 42 additions & 26 deletions spring-core/src/main/java/org/springframework/asm/Frame.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@
* right shift of {@link #DIM_SHIFT}.
* <li>the KIND field, stored in 4 bits, indicates the kind of VALUE used. These 4 bits can be
* retrieved with {@link #KIND_MASK} and, without any shift, must be equal to {@link
* #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND}, {@link #LOCAL_KIND}
* or {@link #STACK_KIND}.
* #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND}, {@link
* #FORWARD_UNINITIALIZED_KIND},{@link #LOCAL_KIND} or {@link #STACK_KIND}.
* <li>the FLAGS field, stored in 2 bits, contains up to 2 boolean flags. Currently only one flag
* is defined, namely {@link #TOP_IF_LONG_OR_DOUBLE_FLAG}.
* <li>the VALUE field, stored in the remaining 20 bits, contains either
Expand All @@ -78,7 +78,10 @@
* <li>the index of a {@link Symbol#TYPE_TAG} {@link Symbol} in the type table of a {@link
* SymbolTable}, if KIND is equal to {@link #REFERENCE_KIND}.
* <li>the index of an {@link Symbol#UNINITIALIZED_TYPE_TAG} {@link Symbol} in the type
* table of a SymbolTable, if KIND is equal to {@link #UNINITIALIZED_KIND}.
* table of a {@link SymbolTable}, if KIND is equal to {@link #UNINITIALIZED_KIND}.
* <li>the index of a {@link Symbol#FORWARD_UNINITIALIZED_TYPE_TAG} {@link Symbol} in the
* type table of a {@link SymbolTable}, if KIND is equal to {@link
* #FORWARD_UNINITIALIZED_KIND}.
* <li>the index of a local variable in the input stack frame, if KIND is equal to {@link
* #LOCAL_KIND}.
* <li>a position relatively to the top of the stack of the input stack frame, if KIND is
Expand All @@ -88,10 +91,10 @@
*
* <p>Output frames can contain abstract types of any kind and with a positive or negative array
* dimension (and even unassigned types, represented by 0 - which does not correspond to any valid
* abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND or
* UNINITIALIZED_KIND abstract types of positive or {@literal null} array dimension. In all cases
* the type table contains only internal type names (array type descriptors are forbidden - array
* dimensions must be represented through the DIM field).
* abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND,
* UNINITIALIZED_KIND or FORWARD_UNINITIALIZED_KIND abstract types of positive or {@literal null}
* array dimension. In all cases the type table contains only internal type names (array type
* descriptors are forbidden - array dimensions must be represented through the DIM field).
*
* <p>The LONG and DOUBLE types are always represented by using two slots (LONG + TOP or DOUBLE +
* TOP), for local variables as well as in the operand stack. This is necessary to be able to
Expand Down Expand Up @@ -159,8 +162,9 @@ class Frame {
private static final int CONSTANT_KIND = 1 << KIND_SHIFT;
private static final int REFERENCE_KIND = 2 << KIND_SHIFT;
private static final int UNINITIALIZED_KIND = 3 << KIND_SHIFT;
private static final int LOCAL_KIND = 4 << KIND_SHIFT;
private static final int STACK_KIND = 5 << KIND_SHIFT;
private static final int FORWARD_UNINITIALIZED_KIND = 4 << KIND_SHIFT;
private static final int LOCAL_KIND = 5 << KIND_SHIFT;
private static final int STACK_KIND = 6 << KIND_SHIFT;

// Possible flags for the FLAGS field of an abstract type.

Expand Down Expand Up @@ -220,13 +224,13 @@ class Frame {

/**
* The abstract types that are initialized in the basic block. A constructor invocation on an
* UNINITIALIZED or UNINITIALIZED_THIS abstract type must replace <i>every occurrence</i> of this
* type in the local variables and in the operand stack. This cannot be done during the first step
* of the algorithm since, during this step, the local variables and the operand stack types are
* still abstract. It is therefore necessary to store the abstract types of the constructors which
* are invoked in the basic block, in order to do this replacement during the second step of the
* algorithm, where the frames are fully computed. Note that this array can contain abstract types
* that are relative to the input locals or to the input stack.
* UNINITIALIZED, FORWARD_UNINITIALIZED or UNINITIALIZED_THIS abstract type must replace <i>every
* occurrence</i> of this type in the local variables and in the operand stack. This cannot be
* done during the first step of the algorithm since, during this step, the local variables and
* the operand stack types are still abstract. It is therefore necessary to store the abstract
* types of the constructors which are invoked in the basic block, in order to do this replacement
* during the second step of the algorithm, where the frames are fully computed. Note that this
* array can contain abstract types that are relative to the input locals or to the input stack.
*/
private int[] initializations;

Expand Down Expand Up @@ -284,8 +288,12 @@ static int getAbstractTypeFromApiFormat(final SymbolTable symbolTable, final Obj
String descriptor = Type.getObjectType((String) type).getDescriptor();
return getAbstractTypeFromDescriptor(symbolTable, descriptor, 0);
} else {
return UNINITIALIZED_KIND
| symbolTable.addUninitializedType("", ((Label) type).bytecodeOffset);
Label label = (Label) type;
if ((label.flags & Label.FLAG_RESOLVED) != 0) {
return UNINITIALIZED_KIND | symbolTable.addUninitializedType("", label.bytecodeOffset);
} else {
return FORWARD_UNINITIALIZED_KIND | symbolTable.addForwardUninitializedType("", label);
}
}
}

Expand Down Expand Up @@ -637,12 +645,14 @@ private void addInitializedType(final int abstractType) {
* @param symbolTable the type table to use to lookup and store type {@link Symbol}.
* @param abstractType an abstract type.
* @return the REFERENCE_KIND abstract type corresponding to abstractType if it is
* UNINITIALIZED_THIS or an UNINITIALIZED_KIND abstract type for one of the types on which a
* constructor is invoked in the basic block. Otherwise returns abstractType.
* UNINITIALIZED_THIS or an UNINITIALIZED_KIND or FORWARD_UNINITIALIZED_KIND abstract type for
* one of the types on which a constructor is invoked in the basic block. Otherwise returns
* abstractType.
*/
private int getInitializedType(final SymbolTable symbolTable, final int abstractType) {
if (abstractType == UNINITIALIZED_THIS
|| (abstractType & (DIM_MASK | KIND_MASK)) == UNINITIALIZED_KIND) {
|| (abstractType & (DIM_MASK | KIND_MASK)) == UNINITIALIZED_KIND
|| (abstractType & (DIM_MASK | KIND_MASK)) == FORWARD_UNINITIALIZED_KIND) {
for (int i = 0; i < initializationCount; ++i) {
int initializedType = initializations[i];
int dim = initializedType & DIM_MASK;
Expand Down Expand Up @@ -1253,11 +1263,12 @@ final boolean merge(
*
* @param symbolTable the type table to use to lookup and store type {@link Symbol}.
* @param sourceType the abstract type with which the abstract type array element must be merged.
* This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND} or {@link
* #UNINITIALIZED_KIND} kind, with positive or {@literal null} array dimensions.
* This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link
* #UNINITIALIZED_KIND} or {@link #FORWARD_UNINITIALIZED_KIND} kind, with positive or
* {@literal null} array dimensions.
* @param dstTypes an array of abstract types. These types should be of {@link #CONSTANT_KIND},
* {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or {@literal
* null} array dimensions.
* {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND} or {@link #FORWARD_UNINITIALIZED_KIND}
* kind, with positive or {@literal null} array dimensions.
* @param dstIndex the index of the type that must be merged in dstTypes.
* @return {@literal true} if the type array has been modified by this operation.
*/
Expand Down Expand Up @@ -1400,7 +1411,8 @@ final void accept(final MethodWriter methodWriter) {
*
* @param symbolTable the type table to use to lookup and store type {@link Symbol}.
* @param abstractType an abstract type, restricted to {@link Frame#CONSTANT_KIND}, {@link
* Frame#REFERENCE_KIND} or {@link Frame#UNINITIALIZED_KIND} types.
* Frame#REFERENCE_KIND}, {@link Frame#UNINITIALIZED_KIND} or {@link
* Frame#FORWARD_UNINITIALIZED_KIND} types.
* @param output where the abstract type must be put.
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.4">JVMS
* 4.7.4</a>
Expand All @@ -1422,6 +1434,10 @@ static void putAbstractType(
case UNINITIALIZED_KIND:
output.putByte(ITEM_UNINITIALIZED).putShort((int) symbolTable.getType(typeValue).data);
break;
case FORWARD_UNINITIALIZED_KIND:
output.putByte(ITEM_UNINITIALIZED);
symbolTable.getForwardUninitializedLabel(typeValue).put(output);
break;
default:
throw new AssertionError();
}
Expand Down
48 changes: 40 additions & 8 deletions spring-core/src/main/java/org/springframework/asm/Label.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ public class Label {
/** A flag indicating that the basic block corresponding to a label is the end of a subroutine. */
static final int FLAG_SUBROUTINE_END = 64;

/** A flag indicating that this label has at least one associated line number. */
static final int FLAG_LINE_NUMBER = 128;

/**
* The number of elements to add to the {@link #otherLineNumbers} array when it needs to be
* resized to store a new source line number.
Expand Down Expand Up @@ -113,6 +116,13 @@ public class Label {
*/
static final int FORWARD_REFERENCE_TYPE_WIDE = 0x20000000;

/**
* The type of forward references stored in two bytes in the <i>stack map table</i>. This is the
* case of the labels of {@link Frame#ITEM_UNINITIALIZED} stack map frame elements, when the NEW
* instruction is after the &lt;init&gt; constructor call (in bytecode offset order).
*/
static final int FORWARD_REFERENCE_TYPE_STACK_MAP = 0x30000000;

/**
* The bit mask to extract the 'handle' of a forward reference to this label. The extracted handle
* is the bytecode offset where the forward reference value is stored (using either 2 or 4 bytes,
Expand Down Expand Up @@ -145,9 +155,9 @@ public class Label {
short flags;

/**
* The source line number corresponding to this label, or 0. If there are several source line
* numbers corresponding to this label, the first one is stored in this field, and the remaining
* ones are stored in {@link #otherLineNumbers}.
* The source line number corresponding to this label, if {@link #FLAG_LINE_NUMBER} is set. If
* there are several source line numbers corresponding to this label, the first one is stored in
* this field, and the remaining ones are stored in {@link #otherLineNumbers}.
*/
private short lineNumber;

Expand Down Expand Up @@ -332,7 +342,8 @@ final Label getCanonicalInstance() {
* @param lineNumber a source line number (which should be strictly positive).
*/
final void addLineNumber(final int lineNumber) {
if (this.lineNumber == 0) {
if ((flags & FLAG_LINE_NUMBER) == 0) {
flags |= FLAG_LINE_NUMBER;
this.lineNumber = (short) lineNumber;
} else {
if (otherLineNumbers == null) {
Expand All @@ -356,7 +367,7 @@ final void addLineNumber(final int lineNumber) {
*/
final void accept(final MethodVisitor methodVisitor, final boolean visitLineNumbers) {
methodVisitor.visitLabel(this);
if (visitLineNumbers && lineNumber != 0) {
if (visitLineNumbers && (flags & FLAG_LINE_NUMBER) != 0) {
methodVisitor.visitLineNumber(lineNumber & 0xFFFF, this);
if (otherLineNumbers != null) {
for (int i = 1; i <= otherLineNumbers[0]; ++i) {
Expand Down Expand Up @@ -400,6 +411,20 @@ final void put(
}
}

/**
* Puts a reference to this label in the <i>stack map table</i> of a method. If the bytecode
* offset of the label is known, it is written directly. Otherwise, a null relative offset is
* written and a new forward reference is declared for this label.
*
* @param stackMapTableEntries the stack map table where the label offset must be added.
*/
final void put(final ByteVector stackMapTableEntries) {
if ((flags & FLAG_RESOLVED) == 0) {
addForwardReference(0, FORWARD_REFERENCE_TYPE_STACK_MAP, stackMapTableEntries.length);
}
stackMapTableEntries.putShort(bytecodeOffset);
}

/**
* Adds a forward reference to this label. This method must be called only for a true forward
* reference, i.e. only if this label is not resolved yet. For backward references, the relative
Expand Down Expand Up @@ -432,17 +457,21 @@ private void addForwardReference(
* Sets the bytecode offset of this label to the given value and resolves the forward references
* to this label, if any. This method must be called when this label is added to the bytecode of
* the method, i.e. when its bytecode offset becomes known. This method fills in the blanks that
* where left in the bytecode by each forward reference previously added to this label.
* where left in the bytecode (and optionally in the stack map table) by each forward reference
* previously added to this label.
*
* @param code the bytecode of the method.
* @param stackMapTableEntries the 'entries' array of the StackMapTable code attribute of the
* method. Maybe {@literal null}.
* @param bytecodeOffset the bytecode offset of this label.
* @return {@literal true} if a blank that was left for this label was too small to store the
* offset. In such a case the corresponding jump instruction is replaced with an equivalent
* ASM specific instruction using an unsigned two bytes offset. These ASM specific
* instructions are later replaced with standard bytecode instructions with wider offsets (4
* bytes instead of 2), in ClassReader.
*/
final boolean resolve(final byte[] code, final int bytecodeOffset) {
final boolean resolve(
final byte[] code, final ByteVector stackMapTableEntries, final int bytecodeOffset) {
this.flags |= FLAG_RESOLVED;
this.bytecodeOffset = bytecodeOffset;
if (forwardReferences == null) {
Expand Down Expand Up @@ -472,11 +501,14 @@ final boolean resolve(final byte[] code, final int bytecodeOffset) {
}
code[handle++] = (byte) (relativeOffset >>> 8);
code[handle] = (byte) relativeOffset;
} else {
} else if ((reference & FORWARD_REFERENCE_TYPE_MASK) == FORWARD_REFERENCE_TYPE_WIDE) {
code[handle++] = (byte) (relativeOffset >>> 24);
code[handle++] = (byte) (relativeOffset >>> 16);
code[handle++] = (byte) (relativeOffset >>> 8);
code[handle] = (byte) relativeOffset;
} else {
stackMapTableEntries.data[handle++] = (byte) (bytecodeOffset >>> 8);
stackMapTableEntries.data[handle] = (byte) bytecodeOffset;
}
}
return hasAsmInstructions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,9 @@ final class MethodWriter extends MethodVisitor {
* the number of stack elements. The local variables start at index 3 and are followed by the
* operand stack elements. In summary frame[0] = offset, frame[1] = numLocal, frame[2] = numStack.
* Local variables and operand stack entries contain abstract types, as defined in {@link Frame},
* but restricted to {@link Frame#CONSTANT_KIND}, {@link Frame#REFERENCE_KIND} or {@link
* Frame#UNINITIALIZED_KIND} abstract types. Long and double types use only one array entry.
* but restricted to {@link Frame#CONSTANT_KIND}, {@link Frame#REFERENCE_KIND}, {@link
* Frame#UNINITIALIZED_KIND} or {@link Frame#FORWARD_UNINITIALIZED_KIND} abstract types. Long and
* double types use only one array entry.
*/
private int[] currentFrame;

Expand Down Expand Up @@ -1199,7 +1200,7 @@ public void visitJumpInsn(final int opcode, final Label label) {
@Override
public void visitLabel(final Label label) {
// Resolve the forward references to this label, if any.
hasAsmInstructions |= label.resolve(code.data, code.length);
hasAsmInstructions |= label.resolve(code.data, stackMapTableEntries, code.length);
// visitLabel starts a new basic block (except for debug only labels), so we need to update the
// previous and current block references and list of successors.
if ((label.flags & Label.FLAG_DEBUG_ONLY) != 0) {
Expand Down Expand Up @@ -1795,7 +1796,7 @@ private void endCurrentBasicBlockWithNoSuccessor() {
if (compute == COMPUTE_ALL_FRAMES) {
Label nextBasicBlock = new Label();
nextBasicBlock.frame = new Frame(nextBasicBlock);
nextBasicBlock.resolve(code.data, code.length);
nextBasicBlock.resolve(code.data, stackMapTableEntries, code.length);
lastBasicBlock.nextBasicBlock = nextBasicBlock;
lastBasicBlock = nextBasicBlock;
currentBasicBlock = null;
Expand Down Expand Up @@ -1979,9 +1980,8 @@ private void putFrameType(final Object type) {
.putByte(Frame.ITEM_OBJECT)
.putShort(symbolTable.addConstantClass((String) type).index);
} else {
stackMapTableEntries
.putByte(Frame.ITEM_UNINITIALIZED)
.putShort(((Label) type).bytecodeOffset);
stackMapTableEntries.putByte(Frame.ITEM_UNINITIALIZED);
((Label) type).put(stackMapTableEntries);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ public interface Opcodes {
int V19 = 0 << 16 | 63;
int V20 = 0 << 16 | 64;
int V21 = 0 << 16 | 65;
int V22 = 0 << 16 | 66;

/**
* Version flag indicating that the class is using 'preview' features.
Expand Down
Loading

0 comments on commit 0d8a843

Please sign in to comment.