Skip to content

Commit 56896e4

Browse files
committed
Merge branch '317995-invalid-stack-map-with-new-after-init' into 'master'
Add support for 'new' after 'invokespecial <init>' (in bytecode offset order). Closes #317995 See merge request asm/asm!372
2 parents 9737ea0 + 0caa0d4 commit 56896e4

File tree

11 files changed

+380
-55
lines changed

11 files changed

+380
-55
lines changed

asm-commons/src/main/java/org/objectweb/asm/commons/AnalyzerAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ public void visitMethodInsn(
308308
if (value == Opcodes.UNINITIALIZED_THIS) {
309309
initializedValue = this.owner;
310310
} else {
311-
initializedValue = uninitializedTypes.get(value);
311+
initializedValue = owner;
312312
}
313313
for (int i = 0; i < locals.size(); ++i) {
314314
if (locals.get(i) == value) {
Binary file not shown.

asm-test/src/resources/java/jdk8/DumpArtificialStructures.java

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,11 @@ private static byte[] dump() {
5757
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
5858
MethodVisitor methodVisitor;
5959

60+
final String INTERNAL_NAME = "jdk8/Artificial$()$Structures";
6061
classWriter.visit(
6162
V1_8,
6263
ACC_PUBLIC + ACC_SUPER,
63-
"jdk8/Artificial$()$Structures",
64+
INTERNAL_NAME,
6465
null,
6566
"java/lang/Object",
6667
null);
@@ -73,7 +74,7 @@ private static byte[] dump() {
7374
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
7475
methodVisitor.visitVarInsn(ALOAD, 0);
7576
methodVisitor.visitVarInsn(ILOAD, 1);
76-
methodVisitor.visitFieldInsn(PUTFIELD, "jdk8/Artificial$()$Structures", "value", "I");
77+
methodVisitor.visitFieldInsn(PUTFIELD, INTERNAL_NAME, "value", "I");
7778
methodVisitor.visitInsn(RETURN);
7879
methodVisitor.visitMaxs(0, 0);
7980
methodVisitor.visitEnd();
@@ -89,13 +90,13 @@ private static byte[] dump() {
8990
Label elseLabel = new Label();
9091
methodVisitor.visitJumpInsn(IFNULL, elseLabel);
9192
methodVisitor.visitVarInsn(ALOAD, 1);
92-
methodVisitor.visitFieldInsn(GETFIELD, "jdk8/Artificial$()$Structures", "value", "I");
93+
methodVisitor.visitFieldInsn(GETFIELD, INTERNAL_NAME, "value", "I");
9394
Label endIfLabel = new Label();
9495
methodVisitor.visitJumpInsn(GOTO, endIfLabel);
9596
methodVisitor.visitLabel(elseLabel);
9697
methodVisitor.visitInsn(ICONST_0);
9798
methodVisitor.visitLabel(endIfLabel);
98-
methodVisitor.visitFieldInsn(PUTFIELD, "jdk8/Artificial$()$Structures", "value", "I");
99+
methodVisitor.visitFieldInsn(PUTFIELD, INTERNAL_NAME, "value", "I");
99100
methodVisitor.visitInsn(RETURN);
100101
methodVisitor.visitMaxs(0, 0);
101102
methodVisitor.visitEnd();
@@ -104,12 +105,12 @@ private static byte[] dump() {
104105
classWriter.visitMethod(
105106
ACC_PUBLIC, "clone", "()Ljdk8/Artificial$()$Structures;", null, null);
106107
methodVisitor.visitCode();
107-
methodVisitor.visitTypeInsn(NEW, "jdk8/Artificial$()$Structures");
108+
methodVisitor.visitTypeInsn(NEW, INTERNAL_NAME);
108109
methodVisitor.visitInsn(DUP);
109110
methodVisitor.visitVarInsn(ALOAD, 0);
110111
methodVisitor.visitMethodInsn(
111112
INVOKESPECIAL,
112-
"jdk8/Artificial$()$Structures",
113+
INTERNAL_NAME,
113114
"<init>",
114115
"(Ljdk8/Artificial$()$Structures;)V",
115116
false);
@@ -126,6 +127,50 @@ private static byte[] dump() {
126127
methodVisitor.visitMaxs(0, 0);
127128
methodVisitor.visitEnd();
128129

130+
methodVisitor =
131+
classWriter.visitMethod(
132+
ACC_PUBLIC | ACC_STATIC, "frameWithForwardLabelReferences", "([Ljava/lang/String;)V", null, null);
133+
methodVisitor.visitCode();
134+
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
135+
Label labelNew = new Label();
136+
methodVisitor.visitJumpInsn(GOTO, labelNew);
137+
138+
Label labelInit = new Label();
139+
methodVisitor.visitLabel(labelInit);
140+
methodVisitor.visitFrame(
141+
Opcodes.F_NEW,
142+
1,
143+
new Object[] {"[Ljava/lang/String;"},
144+
3,
145+
new Object[] {"java/io/PrintStream", labelNew, labelNew});
146+
methodVisitor.visitMethodInsn(INVOKESPECIAL, INTERNAL_NAME, "<init>", "()V", false);
147+
Label labelAfterInit = new Label();
148+
methodVisitor.visitJumpInsn(GOTO, labelAfterInit);
149+
150+
methodVisitor.visitLabel(labelNew);
151+
methodVisitor.visitFrame(
152+
Opcodes.F_NEW,
153+
1,
154+
new Object[] {"[Ljava/lang/String;"},
155+
1,
156+
new Object[] {"java/io/PrintStream"});
157+
methodVisitor.visitTypeInsn(NEW, INTERNAL_NAME);
158+
methodVisitor.visitInsn(DUP);
159+
methodVisitor.visitJumpInsn(GOTO, labelInit);
160+
161+
methodVisitor.visitLabel(labelAfterInit);
162+
methodVisitor.visitFrame(
163+
Opcodes.F_NEW,
164+
1,
165+
new Object[] {"[Ljava/lang/String;"},
166+
2,
167+
new Object[] {"java/io/PrintStream", INTERNAL_NAME});
168+
methodVisitor.visitMethodInsn(
169+
INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false);
170+
methodVisitor.visitInsn(RETURN);
171+
methodVisitor.visitMaxs(3, 1);
172+
methodVisitor.visitEnd();
173+
129174
classWriter.visitEnd();
130175
return classWriter.toByteArray();
131176
}

asm-util/src/test/resources/jdk8.Artificial$()$Structures.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,24 @@ public class jdk8/Artificial$()$Structures {
5353
ARETURN
5454
MAXSTACK = 1
5555
MAXLOCALS = 0
56+
57+
// access flags 0x9
58+
public static frameWithForwardLabelReferences([Ljava/lang/String;)V
59+
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
60+
GOTO L0
61+
L1
62+
FRAME FULL [[Ljava/lang/String;] [java/io/PrintStream L0 L0]
63+
INVOKESPECIAL jdk8/Artificial$()$Structures.<init> ()V
64+
GOTO L2
65+
L0
66+
FRAME SAME1 java/io/PrintStream
67+
NEW jdk8/Artificial$()$Structures
68+
DUP
69+
GOTO L1
70+
L2
71+
FRAME FULL [[Ljava/lang/String;] [java/io/PrintStream jdk8/Artificial$()$Structures]
72+
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
73+
RETURN
74+
MAXSTACK = 3
75+
MAXLOCALS = 1
5676
}

asm/src/main/java/org/objectweb/asm/ClassWriter.java

Lines changed: 1 addition & 0 deletions
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;

asm/src/main/java/org/objectweb/asm/Frame.java

Lines changed: 42 additions & 26 deletions
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
}

asm/src/main/java/org/objectweb/asm/Label.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ public class Label {
116116
*/
117117
static final int FORWARD_REFERENCE_TYPE_WIDE = 0x20000000;
118118

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+
119126
/**
120127
* The bit mask to extract the 'handle' of a forward reference to this label. The extracted handle
121128
* is the bytecode offset where the forward reference value is stored (using either 2 or 4 bytes,
@@ -404,6 +411,20 @@ final void put(
404411
}
405412
}
406413

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+
407428
/**
408429
* Adds a forward reference to this label. This method must be called only for a true forward
409430
* reference, i.e. only if this label is not resolved yet. For backward references, the relative
@@ -436,17 +457,21 @@ private void addForwardReference(
436457
* Sets the bytecode offset of this label to the given value and resolves the forward references
437458
* to this label, if any. This method must be called when this label is added to the bytecode of
438459
* the method, i.e. when its bytecode offset becomes known. This method fills in the blanks that
439-
* 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.
440462
*
441463
* @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}.
442466
* @param bytecodeOffset the bytecode offset of this label.
443467
* @return {@literal true} if a blank that was left for this label was too small to store the
444468
* offset. In such a case the corresponding jump instruction is replaced with an equivalent
445469
* ASM specific instruction using an unsigned two bytes offset. These ASM specific
446470
* instructions are later replaced with standard bytecode instructions with wider offsets (4
447471
* bytes instead of 2), in ClassReader.
448472
*/
449-
final boolean resolve(final byte[] code, final int bytecodeOffset) {
473+
final boolean resolve(
474+
final byte[] code, final ByteVector stackMapTableEntries, final int bytecodeOffset) {
450475
this.flags |= FLAG_RESOLVED;
451476
this.bytecodeOffset = bytecodeOffset;
452477
if (forwardReferences == null) {
@@ -476,11 +501,14 @@ final boolean resolve(final byte[] code, final int bytecodeOffset) {
476501
}
477502
code[handle++] = (byte) (relativeOffset >>> 8);
478503
code[handle] = (byte) relativeOffset;
479-
} else {
504+
} else if ((reference & FORWARD_REFERENCE_TYPE_MASK) == FORWARD_REFERENCE_TYPE_WIDE) {
480505
code[handle++] = (byte) (relativeOffset >>> 24);
481506
code[handle++] = (byte) (relativeOffset >>> 16);
482507
code[handle++] = (byte) (relativeOffset >>> 8);
483508
code[handle] = (byte) relativeOffset;
509+
} else {
510+
stackMapTableEntries.data[handle++] = (byte) (bytecodeOffset >>> 8);
511+
stackMapTableEntries.data[handle] = (byte) bytecodeOffset;
484512
}
485513
}
486514
return hasAsmInstructions;

0 commit comments

Comments
 (0)