Skip to content

Commit 0d8a843

Browse files
committed
Upgrade to ASM master (including early support for Java 22 bytecode)
Closes gh-30845
1 parent 3a8b511 commit 0d8a843

File tree

8 files changed

+276
-54
lines changed

8 files changed

+276
-54
lines changed

spring-core/src/main/java/org/springframework/asm/ClassReader.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ public ClassReader(
194194
this.b = classFileBuffer;
195195
// Check the class' major_version. This field is after the magic and minor_version fields, which
196196
// use 4 and 2 bytes respectively.
197-
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V21) {
197+
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V22) {
198198
throw new IllegalArgumentException(
199199
"Unsupported class file major version " + readShort(classFileOffset + 6));
200200
}
@@ -2052,6 +2052,7 @@ private void readCode(
20522052
currentOffset = bytecodeStartOffset;
20532053
while (currentOffset < bytecodeEndOffset) {
20542054
final int currentBytecodeOffset = currentOffset - bytecodeStartOffset;
2055+
readBytecodeInstructionOffset(currentBytecodeOffset);
20552056

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

2671+
/**
2672+
* Handles the bytecode offset of the next instruction to be visited in {@link
2673+
* #accept(ClassVisitor,int)}. This method is called just before the instruction and before its
2674+
* associated label and stack map frame, if any. The default implementation of this method does
2675+
* nothing. Subclasses can override this method to store the argument in a mutable field, for
2676+
* instance, so that {@link MethodVisitor} instances can get the bytecode offset of each visited
2677+
* instruction (if so, the usual concurrency issues related to mutable data should be addressed).
2678+
*
2679+
* @param bytecodeOffset the bytecode offset of the next instruction to be visited.
2680+
*/
2681+
protected void readBytecodeInstructionOffset(final int bytecodeOffset) {
2682+
// Do nothing by default.
2683+
}
2684+
26702685
/**
26712686
* Returns the label corresponding to the given bytecode offset. The default implementation of
26722687
* this method creates a label for the given offset if it has not been already created.

spring-core/src/main/java/org/springframework/asm/ClassWriter.java

+1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ public class ClassWriter extends ClassVisitor {
217217
/**
218218
* Indicates what must be automatically computed in {@link MethodWriter}. Must be one of {@link
219219
* MethodWriter#COMPUTE_NOTHING}, {@link MethodWriter#COMPUTE_MAX_STACK_AND_LOCAL}, {@link
220+
* MethodWriter#COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES}, {@link
220221
* MethodWriter#COMPUTE_INSERTED_FRAMES}, or {@link MethodWriter#COMPUTE_ALL_FRAMES}.
221222
*/
222223
private int compute;

spring-core/src/main/java/org/springframework/asm/Frame.java

+42-26
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@
6464
* right shift of {@link #DIM_SHIFT}.
6565
* <li>the KIND field, stored in 4 bits, indicates the kind of VALUE used. These 4 bits can be
6666
* retrieved with {@link #KIND_MASK} and, without any shift, must be equal to {@link
67-
* #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND}, {@link #LOCAL_KIND}
68-
* or {@link #STACK_KIND}.
67+
* #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND}, {@link
68+
* #FORWARD_UNINITIALIZED_KIND},{@link #LOCAL_KIND} or {@link #STACK_KIND}.
6969
* <li>the FLAGS field, stored in 2 bits, contains up to 2 boolean flags. Currently only one flag
7070
* is defined, namely {@link #TOP_IF_LONG_OR_DOUBLE_FLAG}.
7171
* <li>the VALUE field, stored in the remaining 20 bits, contains either
@@ -78,7 +78,10 @@
7878
* <li>the index of a {@link Symbol#TYPE_TAG} {@link Symbol} in the type table of a {@link
7979
* SymbolTable}, if KIND is equal to {@link #REFERENCE_KIND}.
8080
* <li>the index of an {@link Symbol#UNINITIALIZED_TYPE_TAG} {@link Symbol} in the type
81-
* table of a SymbolTable, if KIND is equal to {@link #UNINITIALIZED_KIND}.
81+
* table of a {@link SymbolTable}, if KIND is equal to {@link #UNINITIALIZED_KIND}.
82+
* <li>the index of a {@link Symbol#FORWARD_UNINITIALIZED_TYPE_TAG} {@link Symbol} in the
83+
* type table of a {@link SymbolTable}, if KIND is equal to {@link
84+
* #FORWARD_UNINITIALIZED_KIND}.
8285
* <li>the index of a local variable in the input stack frame, if KIND is equal to {@link
8386
* #LOCAL_KIND}.
8487
* <li>a position relatively to the top of the stack of the input stack frame, if KIND is
@@ -88,10 +91,10 @@
8891
*
8992
* <p>Output frames can contain abstract types of any kind and with a positive or negative array
9093
* dimension (and even unassigned types, represented by 0 - which does not correspond to any valid
91-
* abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND or
92-
* UNINITIALIZED_KIND abstract types of positive or {@literal null} array dimension. In all cases
93-
* the type table contains only internal type names (array type descriptors are forbidden - array
94-
* dimensions must be represented through the DIM field).
94+
* abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND,
95+
* UNINITIALIZED_KIND or FORWARD_UNINITIALIZED_KIND abstract types of positive or {@literal null}
96+
* array dimension. In all cases the type table contains only internal type names (array type
97+
* descriptors are forbidden - array dimensions must be represented through the DIM field).
9598
*
9699
* <p>The LONG and DOUBLE types are always represented by using two slots (LONG + TOP or DOUBLE +
97100
* TOP), for local variables as well as in the operand stack. This is necessary to be able to
@@ -159,8 +162,9 @@ class Frame {
159162
private static final int CONSTANT_KIND = 1 << KIND_SHIFT;
160163
private static final int REFERENCE_KIND = 2 << KIND_SHIFT;
161164
private static final int UNINITIALIZED_KIND = 3 << KIND_SHIFT;
162-
private static final int LOCAL_KIND = 4 << KIND_SHIFT;
163-
private static final int STACK_KIND = 5 << KIND_SHIFT;
165+
private static final int FORWARD_UNINITIALIZED_KIND = 4 << KIND_SHIFT;
166+
private static final int LOCAL_KIND = 5 << KIND_SHIFT;
167+
private static final int STACK_KIND = 6 << KIND_SHIFT;
164168

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

@@ -220,13 +224,13 @@ class Frame {
220224

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

@@ -284,8 +288,12 @@ static int getAbstractTypeFromApiFormat(final SymbolTable symbolTable, final Obj
284288
String descriptor = Type.getObjectType((String) type).getDescriptor();
285289
return getAbstractTypeFromDescriptor(symbolTable, descriptor, 0);
286290
} else {
287-
return UNINITIALIZED_KIND
288-
| symbolTable.addUninitializedType("", ((Label) type).bytecodeOffset);
291+
Label label = (Label) type;
292+
if ((label.flags & Label.FLAG_RESOLVED) != 0) {
293+
return UNINITIALIZED_KIND | symbolTable.addUninitializedType("", label.bytecodeOffset);
294+
} else {
295+
return FORWARD_UNINITIALIZED_KIND | symbolTable.addForwardUninitializedType("", label);
296+
}
289297
}
290298
}
291299

@@ -637,12 +645,14 @@ private void addInitializedType(final int abstractType) {
637645
* @param symbolTable the type table to use to lookup and store type {@link Symbol}.
638646
* @param abstractType an abstract type.
639647
* @return the REFERENCE_KIND abstract type corresponding to abstractType if it is
640-
* UNINITIALIZED_THIS or an UNINITIALIZED_KIND abstract type for one of the types on which a
641-
* constructor is invoked in the basic block. Otherwise returns abstractType.
648+
* UNINITIALIZED_THIS or an UNINITIALIZED_KIND or FORWARD_UNINITIALIZED_KIND abstract type for
649+
* one of the types on which a constructor is invoked in the basic block. Otherwise returns
650+
* abstractType.
642651
*/
643652
private int getInitializedType(final SymbolTable symbolTable, final int abstractType) {
644653
if (abstractType == UNINITIALIZED_THIS
645-
|| (abstractType & (DIM_MASK | KIND_MASK)) == UNINITIALIZED_KIND) {
654+
|| (abstractType & (DIM_MASK | KIND_MASK)) == UNINITIALIZED_KIND
655+
|| (abstractType & (DIM_MASK | KIND_MASK)) == FORWARD_UNINITIALIZED_KIND) {
646656
for (int i = 0; i < initializationCount; ++i) {
647657
int initializedType = initializations[i];
648658
int dim = initializedType & DIM_MASK;
@@ -1253,11 +1263,12 @@ final boolean merge(
12531263
*
12541264
* @param symbolTable the type table to use to lookup and store type {@link Symbol}.
12551265
* @param sourceType the abstract type with which the abstract type array element must be merged.
1256-
* This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND} or {@link
1257-
* #UNINITIALIZED_KIND} kind, with positive or {@literal null} array dimensions.
1266+
* This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link
1267+
* #UNINITIALIZED_KIND} or {@link #FORWARD_UNINITIALIZED_KIND} kind, with positive or
1268+
* {@literal null} array dimensions.
12581269
* @param dstTypes an array of abstract types. These types should be of {@link #CONSTANT_KIND},
1259-
* {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or {@literal
1260-
* null} array dimensions.
1270+
* {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND} or {@link #FORWARD_UNINITIALIZED_KIND}
1271+
* kind, with positive or {@literal null} array dimensions.
12611272
* @param dstIndex the index of the type that must be merged in dstTypes.
12621273
* @return {@literal true} if the type array has been modified by this operation.
12631274
*/
@@ -1400,7 +1411,8 @@ final void accept(final MethodWriter methodWriter) {
14001411
*
14011412
* @param symbolTable the type table to use to lookup and store type {@link Symbol}.
14021413
* @param abstractType an abstract type, restricted to {@link Frame#CONSTANT_KIND}, {@link
1403-
* Frame#REFERENCE_KIND} or {@link Frame#UNINITIALIZED_KIND} types.
1414+
* Frame#REFERENCE_KIND}, {@link Frame#UNINITIALIZED_KIND} or {@link
1415+
* Frame#FORWARD_UNINITIALIZED_KIND} types.
14041416
* @param output where the abstract type must be put.
14051417
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.4">JVMS
14061418
* 4.7.4</a>
@@ -1422,6 +1434,10 @@ static void putAbstractType(
14221434
case UNINITIALIZED_KIND:
14231435
output.putByte(ITEM_UNINITIALIZED).putShort((int) symbolTable.getType(typeValue).data);
14241436
break;
1437+
case FORWARD_UNINITIALIZED_KIND:
1438+
output.putByte(ITEM_UNINITIALIZED);
1439+
symbolTable.getForwardUninitializedLabel(typeValue).put(output);
1440+
break;
14251441
default:
14261442
throw new AssertionError();
14271443
}

spring-core/src/main/java/org/springframework/asm/Label.java

+40-8
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ public class Label {
8181
/** A flag indicating that the basic block corresponding to a label is the end of a subroutine. */
8282
static final int FLAG_SUBROUTINE_END = 64;
8383

84+
/** A flag indicating that this label has at least one associated line number. */
85+
static final int FLAG_LINE_NUMBER = 128;
86+
8487
/**
8588
* The number of elements to add to the {@link #otherLineNumbers} array when it needs to be
8689
* resized to store a new source line number.
@@ -113,6 +116,13 @@ public class Label {
113116
*/
114117
static final int FORWARD_REFERENCE_TYPE_WIDE = 0x20000000;
115118

119+
/**
120+
* The type of forward references stored in two bytes in the <i>stack map table</i>. This is the
121+
* case of the labels of {@link Frame#ITEM_UNINITIALIZED} stack map frame elements, when the NEW
122+
* instruction is after the &lt;init&gt; constructor call (in bytecode offset order).
123+
*/
124+
static final int FORWARD_REFERENCE_TYPE_STACK_MAP = 0x30000000;
125+
116126
/**
117127
* The bit mask to extract the 'handle' of a forward reference to this label. The extracted handle
118128
* is the bytecode offset where the forward reference value is stored (using either 2 or 4 bytes,
@@ -145,9 +155,9 @@ public class Label {
145155
short flags;
146156

147157
/**
148-
* The source line number corresponding to this label, or 0. If there are several source line
149-
* numbers corresponding to this label, the first one is stored in this field, and the remaining
150-
* ones are stored in {@link #otherLineNumbers}.
158+
* The source line number corresponding to this label, if {@link #FLAG_LINE_NUMBER} is set. If
159+
* there are several source line numbers corresponding to this label, the first one is stored in
160+
* this field, and the remaining ones are stored in {@link #otherLineNumbers}.
151161
*/
152162
private short lineNumber;
153163

@@ -332,7 +342,8 @@ final Label getCanonicalInstance() {
332342
* @param lineNumber a source line number (which should be strictly positive).
333343
*/
334344
final void addLineNumber(final int lineNumber) {
335-
if (this.lineNumber == 0) {
345+
if ((flags & FLAG_LINE_NUMBER) == 0) {
346+
flags |= FLAG_LINE_NUMBER;
336347
this.lineNumber = (short) lineNumber;
337348
} else {
338349
if (otherLineNumbers == null) {
@@ -356,7 +367,7 @@ final void addLineNumber(final int lineNumber) {
356367
*/
357368
final void accept(final MethodVisitor methodVisitor, final boolean visitLineNumbers) {
358369
methodVisitor.visitLabel(this);
359-
if (visitLineNumbers && lineNumber != 0) {
370+
if (visitLineNumbers && (flags & FLAG_LINE_NUMBER) != 0) {
360371
methodVisitor.visitLineNumber(lineNumber & 0xFFFF, this);
361372
if (otherLineNumbers != null) {
362373
for (int i = 1; i <= otherLineNumbers[0]; ++i) {
@@ -400,6 +411,20 @@ final void put(
400411
}
401412
}
402413

414+
/**
415+
* Puts a reference to this label in the <i>stack map table</i> of a method. If the bytecode
416+
* offset of the label is known, it is written directly. Otherwise, a null relative offset is
417+
* written and a new forward reference is declared for this label.
418+
*
419+
* @param stackMapTableEntries the stack map table where the label offset must be added.
420+
*/
421+
final void put(final ByteVector stackMapTableEntries) {
422+
if ((flags & FLAG_RESOLVED) == 0) {
423+
addForwardReference(0, FORWARD_REFERENCE_TYPE_STACK_MAP, stackMapTableEntries.length);
424+
}
425+
stackMapTableEntries.putShort(bytecodeOffset);
426+
}
427+
403428
/**
404429
* Adds a forward reference to this label. This method must be called only for a true forward
405430
* reference, i.e. only if this label is not resolved yet. For backward references, the relative
@@ -432,17 +457,21 @@ private void addForwardReference(
432457
* Sets the bytecode offset of this label to the given value and resolves the forward references
433458
* to this label, if any. This method must be called when this label is added to the bytecode of
434459
* the method, i.e. when its bytecode offset becomes known. This method fills in the blanks that
435-
* where left in the bytecode by each forward reference previously added to this label.
460+
* where left in the bytecode (and optionally in the stack map table) by each forward reference
461+
* previously added to this label.
436462
*
437463
* @param code the bytecode of the method.
464+
* @param stackMapTableEntries the 'entries' array of the StackMapTable code attribute of the
465+
* method. Maybe {@literal null}.
438466
* @param bytecodeOffset the bytecode offset of this label.
439467
* @return {@literal true} if a blank that was left for this label was too small to store the
440468
* offset. In such a case the corresponding jump instruction is replaced with an equivalent
441469
* ASM specific instruction using an unsigned two bytes offset. These ASM specific
442470
* instructions are later replaced with standard bytecode instructions with wider offsets (4
443471
* bytes instead of 2), in ClassReader.
444472
*/
445-
final boolean resolve(final byte[] code, final int bytecodeOffset) {
473+
final boolean resolve(
474+
final byte[] code, final ByteVector stackMapTableEntries, final int bytecodeOffset) {
446475
this.flags |= FLAG_RESOLVED;
447476
this.bytecodeOffset = bytecodeOffset;
448477
if (forwardReferences == null) {
@@ -472,11 +501,14 @@ final boolean resolve(final byte[] code, final int bytecodeOffset) {
472501
}
473502
code[handle++] = (byte) (relativeOffset >>> 8);
474503
code[handle] = (byte) relativeOffset;
475-
} else {
504+
} else if ((reference & FORWARD_REFERENCE_TYPE_MASK) == FORWARD_REFERENCE_TYPE_WIDE) {
476505
code[handle++] = (byte) (relativeOffset >>> 24);
477506
code[handle++] = (byte) (relativeOffset >>> 16);
478507
code[handle++] = (byte) (relativeOffset >>> 8);
479508
code[handle] = (byte) relativeOffset;
509+
} else {
510+
stackMapTableEntries.data[handle++] = (byte) (bytecodeOffset >>> 8);
511+
stackMapTableEntries.data[handle] = (byte) bytecodeOffset;
480512
}
481513
}
482514
return hasAsmInstructions;

spring-core/src/main/java/org/springframework/asm/MethodWriter.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -534,8 +534,9 @@ final class MethodWriter extends MethodVisitor {
534534
* the number of stack elements. The local variables start at index 3 and are followed by the
535535
* operand stack elements. In summary frame[0] = offset, frame[1] = numLocal, frame[2] = numStack.
536536
* Local variables and operand stack entries contain abstract types, as defined in {@link Frame},
537-
* but restricted to {@link Frame#CONSTANT_KIND}, {@link Frame#REFERENCE_KIND} or {@link
538-
* Frame#UNINITIALIZED_KIND} abstract types. Long and double types use only one array entry.
537+
* but restricted to {@link Frame#CONSTANT_KIND}, {@link Frame#REFERENCE_KIND}, {@link
538+
* Frame#UNINITIALIZED_KIND} or {@link Frame#FORWARD_UNINITIALIZED_KIND} abstract types. Long and
539+
* double types use only one array entry.
539540
*/
540541
private int[] currentFrame;
541542

@@ -1199,7 +1200,7 @@ public void visitJumpInsn(final int opcode, final Label label) {
11991200
@Override
12001201
public void visitLabel(final Label label) {
12011202
// Resolve the forward references to this label, if any.
1202-
hasAsmInstructions |= label.resolve(code.data, code.length);
1203+
hasAsmInstructions |= label.resolve(code.data, stackMapTableEntries, code.length);
12031204
// visitLabel starts a new basic block (except for debug only labels), so we need to update the
12041205
// previous and current block references and list of successors.
12051206
if ((label.flags & Label.FLAG_DEBUG_ONLY) != 0) {
@@ -1795,7 +1796,7 @@ private void endCurrentBasicBlockWithNoSuccessor() {
17951796
if (compute == COMPUTE_ALL_FRAMES) {
17961797
Label nextBasicBlock = new Label();
17971798
nextBasicBlock.frame = new Frame(nextBasicBlock);
1798-
nextBasicBlock.resolve(code.data, code.length);
1799+
nextBasicBlock.resolve(code.data, stackMapTableEntries, code.length);
17991800
lastBasicBlock.nextBasicBlock = nextBasicBlock;
18001801
lastBasicBlock = nextBasicBlock;
18011802
currentBasicBlock = null;
@@ -1979,9 +1980,8 @@ private void putFrameType(final Object type) {
19791980
.putByte(Frame.ITEM_OBJECT)
19801981
.putShort(symbolTable.addConstantClass((String) type).index);
19811982
} else {
1982-
stackMapTableEntries
1983-
.putByte(Frame.ITEM_UNINITIALIZED)
1984-
.putShort(((Label) type).bytecodeOffset);
1983+
stackMapTableEntries.putByte(Frame.ITEM_UNINITIALIZED);
1984+
((Label) type).put(stackMapTableEntries);
19851985
}
19861986
}
19871987

spring-core/src/main/java/org/springframework/asm/Opcodes.java

+1
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ public interface Opcodes {
286286
int V19 = 0 << 16 | 63;
287287
int V20 = 0 << 16 | 64;
288288
int V21 = 0 << 16 | 65;
289+
int V22 = 0 << 16 | 66;
289290

290291
/**
291292
* Version flag indicating that the class is using 'preview' features.

0 commit comments

Comments
 (0)