Skip to content

Commit ccabff6

Browse files
committed
Upgrade to ASM 5.2
Issue: SPR-15071
1 parent 311522b commit ccabff6

File tree

11 files changed

+436
-708
lines changed

11 files changed

+436
-708
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public AnnotationVisitor(final int api, final AnnotationVisitor av) {
8989
* the actual value, whose type must be {@link Byte},
9090
* {@link Boolean}, {@link Character}, {@link Short},
9191
* {@link Integer} , {@link Long}, {@link Float}, {@link Double},
92-
* {@link String} or {@link Type} or OBJECT or ARRAY sort. This
92+
* {@link String} or {@link Type} of OBJECT or ARRAY sort. This
9393
* value can also be an array of byte, boolean, short, char, int,
9494
* long, float or double values (this is equivalent to using
9595
* {@link #visitArray visitArray} and visiting each array element

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

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,21 @@ public class ClassReader {
104104
*/
105105
public static final int EXPAND_FRAMES = 8;
106106

107+
/**
108+
* Flag to expand the ASM pseudo instructions into an equivalent sequence of
109+
* standard bytecode instructions. When resolving a forward jump it may
110+
* happen that the signed 2 bytes offset reserved for it is not sufficient
111+
* to store the bytecode offset. In this case the jump instruction is
112+
* replaced with a temporary ASM pseudo instruction using an unsigned 2
113+
* bytes offset (see Label#resolve). This internal flag is used to re-read
114+
* classes containing such instructions, in order to replace them with
115+
* standard instructions. In addition, when this flag is used, GOTO_W and
116+
* JSR_W are <i>not</i> converted into GOTO and JSR, to make sure that
117+
* infinite loops where a GOTO_W is replaced with a GOTO in ClassReader and
118+
* converted back to a GOTO_W in ClassWriter cannot occur.
119+
*/
120+
static final int EXPAND_ASM_INSNS = 256;
121+
107122
/**
108123
* The class to be parsed. <i>The content of this array must not be
109124
* modified. This field is intended for {@link Attribute} sub classes, and
@@ -1062,6 +1077,10 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
10621077
readLabel(offset + readShort(u + 1), labels);
10631078
u += 3;
10641079
break;
1080+
case ClassWriter.ASM_LABEL_INSN:
1081+
readLabel(offset + readUnsignedShort(u + 1), labels);
1082+
u += 3;
1083+
break;
10651084
case ClassWriter.LABELW_INSN:
10661085
readLabel(offset + readInt(u + 1), labels);
10671086
u += 5;
@@ -1286,8 +1305,23 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
12861305
}
12871306
}
12881307
}
1308+
if ((context.flags & EXPAND_ASM_INSNS) != 0) {
1309+
// Expanding the ASM pseudo instructions can introduce F_INSERT
1310+
// frames, even if the method does not currently have any frame.
1311+
// Also these inserted frames must be computed by simulating the
1312+
// effect of the bytecode instructions one by one, starting from the
1313+
// first one and the last existing frame (or the implicit first
1314+
// one). Finally, due to the way MethodWriter computes this (with
1315+
// the compute = INSERTED_FRAMES option), MethodWriter needs to know
1316+
// maxLocals before the first instruction is visited. For all these
1317+
// reasons we always visit the implicit first frame in this case
1318+
// (passing only maxLocals - the rest can be and is computed in
1319+
// MethodWriter).
1320+
mv.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null);
1321+
}
12891322

12901323
// visits the instructions
1324+
int opcodeDelta = (context.flags & EXPAND_ASM_INSNS) == 0 ? -33 : 0;
12911325
u = codeStart;
12921326
while (u < codeEnd) {
12931327
int offset = u - codeStart;
@@ -1352,9 +1386,42 @@ private void readCode(final MethodVisitor mv, final Context context, int u) {
13521386
u += 3;
13531387
break;
13541388
case ClassWriter.LABELW_INSN:
1355-
mv.visitJumpInsn(opcode - 33, labels[offset + readInt(u + 1)]);
1389+
mv.visitJumpInsn(opcode + opcodeDelta, labels[offset
1390+
+ readInt(u + 1)]);
13561391
u += 5;
13571392
break;
1393+
case ClassWriter.ASM_LABEL_INSN: {
1394+
// changes temporary opcodes 202 to 217 (inclusive), 218
1395+
// and 219 to IFEQ ... JSR (inclusive), IFNULL and
1396+
// IFNONNULL
1397+
opcode = opcode < 218 ? opcode - 49 : opcode - 20;
1398+
Label target = labels[offset + readUnsignedShort(u + 1)];
1399+
// replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
1400+
// <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is
1401+
// the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
1402+
// and where <l'> designates the instruction just after
1403+
// the GOTO_W.
1404+
if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
1405+
mv.visitJumpInsn(opcode + 33, target);
1406+
} else {
1407+
opcode = opcode <= 166 ? ((opcode + 1) ^ 1) - 1
1408+
: opcode ^ 1;
1409+
Label endif = new Label();
1410+
mv.visitJumpInsn(opcode, endif);
1411+
mv.visitJumpInsn(200, target); // GOTO_W
1412+
mv.visitLabel(endif);
1413+
// since we introduced an unconditional jump instruction we
1414+
// also need to insert a stack map frame here, unless there
1415+
// is already one. The actual frame content will be computed
1416+
// in MethodWriter.
1417+
if (FRAMES && stackMap != 0
1418+
&& (frame == null || frame.offset != offset + 3)) {
1419+
mv.visitFrame(ClassWriter.F_INSERT, 0, null, 0, null);
1420+
}
1421+
}
1422+
u += 3;
1423+
break;
1424+
}
13581425
case ClassWriter.WIDE_INSN:
13591426
opcode = b[u + 1] & 0xFF;
13601427
if (opcode == Opcodes.IINC) {
@@ -1848,7 +1915,9 @@ private int readAnnotationValue(int v, final char[] buf, final String name,
18481915
v += 2;
18491916
break;
18501917
case 'Z': // pointer to CONSTANT_Boolean
1851-
av.visit(name, readInt(items[readUnsignedShort(v)]) == 0 ? Boolean.FALSE : Boolean.TRUE);
1918+
av.visit(name,
1919+
readInt(items[readUnsignedShort(v)]) == 0 ? Boolean.FALSE
1920+
: Boolean.TRUE);
18521921
v += 2;
18531922
break;
18541923
case 'S': // pointer to CONSTANT_Short

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

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ public class ClassWriter extends ClassVisitor {
5858
* {@link MethodVisitor#visitFrame} method are ignored, and the stack map
5959
* frames are recomputed from the methods bytecode. The arguments of the
6060
* {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and
61-
* recomputed from the bytecode. In other words, computeFrames implies
62-
* computeMaxs.
61+
* recomputed from the bytecode. In other words, COMPUTE_FRAMES implies
62+
* COMPUTE_MAXS.
6363
*
6464
* @see #ClassWriter(int)
6565
*/
@@ -167,6 +167,22 @@ public class ClassWriter extends ClassVisitor {
167167
*/
168168
static final int WIDE_INSN = 17;
169169

170+
/**
171+
* The type of the ASM pseudo instructions with an unsigned 2 bytes offset
172+
* label (see Label#resolve).
173+
*/
174+
static final int ASM_LABEL_INSN = 18;
175+
176+
/**
177+
* Represents a frame inserted between already existing frames. This kind of
178+
* frame can only be used if the frame content can be computed from the
179+
* previous existing frame and from the instructions between this existing
180+
* frame and the inserted one, without any knowledge of the type hierarchy.
181+
* This kind of frame is only used when an unconditional jump is inserted in
182+
* a method while expanding an ASM pseudo instruction (see ClassReader).
183+
*/
184+
static final int F_INSERT = 256;
185+
170186
/**
171187
* The instruction types of all JVM opcodes.
172188
*/
@@ -484,25 +500,19 @@ public class ClassWriter extends ClassVisitor {
484500
MethodWriter lastMethod;
485501

486502
/**
487-
* <tt>true</tt> if the maximum stack size and number of local variables
488-
* must be automatically computed.
489-
*/
490-
private boolean computeMaxs;
491-
492-
/**
493-
* <tt>true</tt> if the stack map frames must be recomputed from scratch.
503+
* Indicates what must be automatically computed.
504+
*
505+
* @see MethodWriter#compute
494506
*/
495-
private boolean computeFrames;
507+
private int compute;
496508

497509
/**
498-
* <tt>true</tt> if the stack map tables of this class are invalid. The
499-
* {@link MethodWriter#resizeInstructions} method cannot transform existing
500-
* stack map tables, and so produces potentially invalid classes when it is
501-
* executed. In this case the class is reread and rewritten with the
502-
* {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize
503-
* stack map tables when this option is used).
510+
* <tt>true</tt> if some methods have wide forward jumps using ASM pseudo
511+
* instructions, which need to be expanded into sequences of standard
512+
* bytecode instructions. In this case the class is re-read and re-written
513+
* with a ClassReader -> ClassWriter chain to perform this transformation.
504514
*/
505-
boolean invalidFrames;
515+
boolean hasAsmInsns;
506516

507517
// ------------------------------------------------------------------------
508518
// Static initializer
@@ -517,7 +527,7 @@ public class ClassWriter extends ClassVisitor {
517527
String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD"
518528
+ "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
519529
+ "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA"
520-
+ "AAAAGGGGGGGHIFBFAAFFAARQJJKKJJJJJJJJJJJJJJJJJJ";
530+
+ "AAAAGGGGGGGHIFBFAAFFAARQJJKKSSSSSSSSSSSSSSSSSS";
521531
for (i = 0; i < b.length; ++i) {
522532
b[i] = (byte) (s.charAt(i) - 'A');
523533
}
@@ -571,7 +581,7 @@ public class ClassWriter extends ClassVisitor {
571581
// // temporary opcodes used internally by ASM - see Label and
572582
// MethodWriter
573583
// for (i = 202; i < 220; ++i) {
574-
// b[i] = LABEL_INSN;
584+
// b[i] = ASM_LABEL_INSN;
575585
// }
576586
//
577587
// // LDC(_W) instructions
@@ -614,8 +624,9 @@ public ClassWriter(final int flags) {
614624
key2 = new Item();
615625
key3 = new Item();
616626
key4 = new Item();
617-
this.computeMaxs = (flags & COMPUTE_MAXS) != 0;
618-
this.computeFrames = (flags & COMPUTE_FRAMES) != 0;
627+
this.compute = (flags & COMPUTE_FRAMES) != 0 ? MethodWriter.FRAMES
628+
: ((flags & COMPUTE_MAXS) != 0 ? MethodWriter.MAXS
629+
: MethodWriter.NOTHING);
619630
}
620631

621632
/**
@@ -645,9 +656,9 @@ public ClassWriter(final int flags) {
645656
* @param flags
646657
* option flags that can be used to modify the default behavior
647658
* of this class. <i>These option flags do not affect methods
648-
* that are copied as is in the new class. This means that the
649-
* maximum stack size nor the stack frames will be computed for
650-
* these methods</i>. See {@link #COMPUTE_MAXS},
659+
* that are copied as is in the new class. This means that
660+
* neither the maximum stack size nor the stack frames will be
661+
* computed for these methods</i>. See {@link #COMPUTE_MAXS},
651662
* {@link #COMPUTE_FRAMES}.
652663
*/
653664
public ClassWriter(final ClassReader classReader, final int flags) {
@@ -791,7 +802,7 @@ public final FieldVisitor visitField(final int access, final String name,
791802
public final MethodVisitor visitMethod(final int access, final String name,
792803
final String desc, final String signature, final String[] exceptions) {
793804
return new MethodWriter(this, access, name, desc, signature,
794-
exceptions, computeMaxs, computeFrames);
805+
exceptions, compute);
795806
}
796807

797808
@Override
@@ -977,22 +988,20 @@ public byte[] toByteArray() {
977988
if (attrs != null) {
978989
attrs.put(this, null, 0, -1, -1, out);
979990
}
980-
if (invalidFrames) {
991+
if (hasAsmInsns) {
981992
anns = null;
982993
ianns = null;
983994
attrs = null;
984995
innerClassesCount = 0;
985996
innerClasses = null;
986-
bootstrapMethodsCount = 0;
987-
bootstrapMethods = null;
988997
firstField = null;
989998
lastField = null;
990999
firstMethod = null;
9911000
lastMethod = null;
992-
computeMaxs = false;
993-
computeFrames = true;
994-
invalidFrames = false;
995-
new ClassReader(out.data).accept(this, ClassReader.SKIP_FRAMES);
1001+
compute = MethodWriter.INSERTED_FRAMES;
1002+
hasAsmInsns = false;
1003+
new ClassReader(out.data).accept(this, ClassReader.EXPAND_FRAMES
1004+
| ClassReader.EXPAND_ASM_INSNS);
9961005
return toByteArray();
9971006
}
9981007
return out.data;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/***
2+
* ASM: a very small and fast Java bytecode manipulation framework
3+
* Copyright (c) 2000-2011 INRIA, France Telecom
4+
* All rights reserved.
5+
*
6+
* Redistribution and use in source and binary forms, with or without
7+
* modification, are permitted provided that the following conditions
8+
* are met:
9+
* 1. Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
* 2. Redistributions in binary form must reproduce the above copyright
12+
* notice, this list of conditions and the following disclaimer in the
13+
* documentation and/or other materials provided with the distribution.
14+
* 3. Neither the name of the copyright holders nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28+
* THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
31+
package org.springframework.asm;
32+
33+
/**
34+
* Information about the input stack map frame at the "current" instruction of a
35+
* method. This is implemented as a Frame subclass for a "basic block"
36+
* containing only one instruction.
37+
*
38+
* @author Eric Bruneton
39+
*/
40+
class CurrentFrame extends Frame {
41+
42+
/**
43+
* Sets this CurrentFrame to the input stack map frame of the next "current"
44+
* instruction, i.e. the instruction just after the given one. It is assumed
45+
* that the value of this object when this method is called is the stack map
46+
* frame status just before the given instruction is executed.
47+
*/
48+
@Override
49+
void execute(int opcode, int arg, ClassWriter cw, Item item) {
50+
super.execute(opcode, arg, cw, item);
51+
Frame successor = new Frame();
52+
merge(cw, successor, 0);
53+
set(successor);
54+
owner.inputStackTop = 0;
55+
}
56+
}

0 commit comments

Comments
 (0)