diff --git a/spring-core/src/main/java/org/springframework/asm/AnnotationVisitor.java b/spring-core/src/main/java/org/springframework/asm/AnnotationVisitor.java
index 5f48e99324..c2d30f588c 100644
--- a/spring-core/src/main/java/org/springframework/asm/AnnotationVisitor.java
+++ b/spring-core/src/main/java/org/springframework/asm/AnnotationVisitor.java
@@ -89,7 +89,7 @@ public abstract class AnnotationVisitor {
* the actual value, whose type must be {@link Byte},
* {@link Boolean}, {@link Character}, {@link Short},
* {@link Integer} , {@link Long}, {@link Float}, {@link Double},
- * {@link String} or {@link Type} or OBJECT or ARRAY sort. This
+ * {@link String} or {@link Type} of OBJECT or ARRAY sort. This
* value can also be an array of byte, boolean, short, char, int,
* long, float or double values (this is equivalent to using
* {@link #visitArray visitArray} and visiting each array element
diff --git a/spring-core/src/main/java/org/springframework/asm/ClassReader.java b/spring-core/src/main/java/org/springframework/asm/ClassReader.java
index 29520b40ff..9cca0b5a0a 100644
--- a/spring-core/src/main/java/org/springframework/asm/ClassReader.java
+++ b/spring-core/src/main/java/org/springframework/asm/ClassReader.java
@@ -104,6 +104,21 @@ public class ClassReader {
*/
public static final int EXPAND_FRAMES = 8;
+ /**
+ * Flag to expand the ASM pseudo instructions into an equivalent sequence of
+ * standard bytecode instructions. When resolving a forward jump it may
+ * happen that the signed 2 bytes offset reserved for it is not sufficient
+ * to store the bytecode offset. In this case the jump instruction is
+ * replaced with a temporary ASM pseudo instruction using an unsigned 2
+ * bytes offset (see Label#resolve). This internal flag is used to re-read
+ * classes containing such instructions, in order to replace them with
+ * standard instructions. In addition, when this flag is used, GOTO_W and
+ * JSR_W are not converted into GOTO and JSR, to make sure that
+ * infinite loops where a GOTO_W is replaced with a GOTO in ClassReader and
+ * converted back to a GOTO_W in ClassWriter cannot occur.
+ */
+ static final int EXPAND_ASM_INSNS = 256;
+
/**
* The class to be parsed. The content of this array must not be
* modified. This field is intended for {@link Attribute} sub classes, and
@@ -1062,6 +1077,10 @@ public class ClassReader {
readLabel(offset + readShort(u + 1), labels);
u += 3;
break;
+ case ClassWriter.ASM_LABEL_INSN:
+ readLabel(offset + readUnsignedShort(u + 1), labels);
+ u += 3;
+ break;
case ClassWriter.LABELW_INSN:
readLabel(offset + readInt(u + 1), labels);
u += 5;
@@ -1286,8 +1305,23 @@ public class ClassReader {
}
}
}
+ if ((context.flags & EXPAND_ASM_INSNS) != 0) {
+ // Expanding the ASM pseudo instructions can introduce F_INSERT
+ // frames, even if the method does not currently have any frame.
+ // Also these inserted frames must be computed by simulating the
+ // effect of the bytecode instructions one by one, starting from the
+ // first one and the last existing frame (or the implicit first
+ // one). Finally, due to the way MethodWriter computes this (with
+ // the compute = INSERTED_FRAMES option), MethodWriter needs to know
+ // maxLocals before the first instruction is visited. For all these
+ // reasons we always visit the implicit first frame in this case
+ // (passing only maxLocals - the rest can be and is computed in
+ // MethodWriter).
+ mv.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null);
+ }
// visits the instructions
+ int opcodeDelta = (context.flags & EXPAND_ASM_INSNS) == 0 ? -33 : 0;
u = codeStart;
while (u < codeEnd) {
int offset = u - codeStart;
@@ -1352,9 +1386,42 @@ public class ClassReader {
u += 3;
break;
case ClassWriter.LABELW_INSN:
- mv.visitJumpInsn(opcode - 33, labels[offset + readInt(u + 1)]);
+ mv.visitJumpInsn(opcode + opcodeDelta, labels[offset
+ + readInt(u + 1)]);
u += 5;
break;
+ case ClassWriter.ASM_LABEL_INSN: {
+ // changes temporary opcodes 202 to 217 (inclusive), 218
+ // and 219 to IFEQ ... JSR (inclusive), IFNULL and
+ // IFNONNULL
+ opcode = opcode < 218 ? opcode - 49 : opcode - 20;
+ Label target = labels[offset + readUnsignedShort(u + 1)];
+ // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
+ // with IFNOTxxx GOTO_W , where IFNOTxxx is
+ // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
+ // and where designates the instruction just after
+ // the GOTO_W.
+ if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
+ mv.visitJumpInsn(opcode + 33, target);
+ } else {
+ opcode = opcode <= 166 ? ((opcode + 1) ^ 1) - 1
+ : opcode ^ 1;
+ Label endif = new Label();
+ mv.visitJumpInsn(opcode, endif);
+ mv.visitJumpInsn(200, target); // GOTO_W
+ mv.visitLabel(endif);
+ // since we introduced an unconditional jump instruction we
+ // also need to insert a stack map frame here, unless there
+ // is already one. The actual frame content will be computed
+ // in MethodWriter.
+ if (FRAMES && stackMap != 0
+ && (frame == null || frame.offset != offset + 3)) {
+ mv.visitFrame(ClassWriter.F_INSERT, 0, null, 0, null);
+ }
+ }
+ u += 3;
+ break;
+ }
case ClassWriter.WIDE_INSN:
opcode = b[u + 1] & 0xFF;
if (opcode == Opcodes.IINC) {
@@ -1848,7 +1915,9 @@ public class ClassReader {
v += 2;
break;
case 'Z': // pointer to CONSTANT_Boolean
- av.visit(name, readInt(items[readUnsignedShort(v)]) == 0 ? Boolean.FALSE : Boolean.TRUE);
+ av.visit(name,
+ readInt(items[readUnsignedShort(v)]) == 0 ? Boolean.FALSE
+ : Boolean.TRUE);
v += 2;
break;
case 'S': // pointer to CONSTANT_Short
diff --git a/spring-core/src/main/java/org/springframework/asm/ClassWriter.java b/spring-core/src/main/java/org/springframework/asm/ClassWriter.java
index 72add9d60e..1393c78e15 100644
--- a/spring-core/src/main/java/org/springframework/asm/ClassWriter.java
+++ b/spring-core/src/main/java/org/springframework/asm/ClassWriter.java
@@ -58,8 +58,8 @@ public class ClassWriter extends ClassVisitor {
* {@link MethodVisitor#visitFrame} method are ignored, and the stack map
* frames are recomputed from the methods bytecode. The arguments of the
* {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and
- * recomputed from the bytecode. In other words, computeFrames implies
- * computeMaxs.
+ * recomputed from the bytecode. In other words, COMPUTE_FRAMES implies
+ * COMPUTE_MAXS.
*
* @see #ClassWriter(int)
*/
@@ -167,6 +167,22 @@ public class ClassWriter extends ClassVisitor {
*/
static final int WIDE_INSN = 17;
+ /**
+ * The type of the ASM pseudo instructions with an unsigned 2 bytes offset
+ * label (see Label#resolve).
+ */
+ static final int ASM_LABEL_INSN = 18;
+
+ /**
+ * Represents a frame inserted between already existing frames. This kind of
+ * frame can only be used if the frame content can be computed from the
+ * previous existing frame and from the instructions between this existing
+ * frame and the inserted one, without any knowledge of the type hierarchy.
+ * This kind of frame is only used when an unconditional jump is inserted in
+ * a method while expanding an ASM pseudo instruction (see ClassReader).
+ */
+ static final int F_INSERT = 256;
+
/**
* The instruction types of all JVM opcodes.
*/
@@ -484,25 +500,19 @@ public class ClassWriter extends ClassVisitor {
MethodWriter lastMethod;
/**
- * true if the maximum stack size and number of local variables
- * must be automatically computed.
+ * Indicates what must be automatically computed.
+ *
+ * @see MethodWriter#compute
*/
- private boolean computeMaxs;
+ private int compute;
/**
- * true if the stack map frames must be recomputed from scratch.
+ * true if some methods have wide forward jumps using ASM pseudo
+ * instructions, which need to be expanded into sequences of standard
+ * bytecode instructions. In this case the class is re-read and re-written
+ * with a ClassReader -> ClassWriter chain to perform this transformation.
*/
- private boolean computeFrames;
-
- /**
- * true if the stack map tables of this class are invalid. The
- * {@link MethodWriter#resizeInstructions} method cannot transform existing
- * stack map tables, and so produces potentially invalid classes when it is
- * executed. In this case the class is reread and rewritten with the
- * {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize
- * stack map tables when this option is used).
- */
- boolean invalidFrames;
+ boolean hasAsmInsns;
// ------------------------------------------------------------------------
// Static initializer
@@ -517,7 +527,7 @@ public class ClassWriter extends ClassVisitor {
String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD"
+ "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA"
- + "AAAAGGGGGGGHIFBFAAFFAARQJJKKJJJJJJJJJJJJJJJJJJ";
+ + "AAAAGGGGGGGHIFBFAAFFAARQJJKKSSSSSSSSSSSSSSSSSS";
for (i = 0; i < b.length; ++i) {
b[i] = (byte) (s.charAt(i) - 'A');
}
@@ -571,7 +581,7 @@ public class ClassWriter extends ClassVisitor {
// // temporary opcodes used internally by ASM - see Label and
// MethodWriter
// for (i = 202; i < 220; ++i) {
- // b[i] = LABEL_INSN;
+ // b[i] = ASM_LABEL_INSN;
// }
//
// // LDC(_W) instructions
@@ -614,8 +624,9 @@ public class ClassWriter extends ClassVisitor {
key2 = new Item();
key3 = new Item();
key4 = new Item();
- this.computeMaxs = (flags & COMPUTE_MAXS) != 0;
- this.computeFrames = (flags & COMPUTE_FRAMES) != 0;
+ this.compute = (flags & COMPUTE_FRAMES) != 0 ? MethodWriter.FRAMES
+ : ((flags & COMPUTE_MAXS) != 0 ? MethodWriter.MAXS
+ : MethodWriter.NOTHING);
}
/**
@@ -645,9 +656,9 @@ public class ClassWriter extends ClassVisitor {
* @param flags
* option flags that can be used to modify the default behavior
* of this class. These option flags do not affect methods
- * that are copied as is in the new class. This means that the
- * maximum stack size nor the stack frames will be computed for
- * these methods. See {@link #COMPUTE_MAXS},
+ * that are copied as is in the new class. This means that
+ * neither the maximum stack size nor the stack frames will be
+ * computed for these methods. See {@link #COMPUTE_MAXS},
* {@link #COMPUTE_FRAMES}.
*/
public ClassWriter(final ClassReader classReader, final int flags) {
@@ -791,7 +802,7 @@ public class ClassWriter extends ClassVisitor {
public final MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
return new MethodWriter(this, access, name, desc, signature,
- exceptions, computeMaxs, computeFrames);
+ exceptions, compute);
}
@Override
@@ -977,22 +988,20 @@ public class ClassWriter extends ClassVisitor {
if (attrs != null) {
attrs.put(this, null, 0, -1, -1, out);
}
- if (invalidFrames) {
+ if (hasAsmInsns) {
anns = null;
ianns = null;
attrs = null;
innerClassesCount = 0;
innerClasses = null;
- bootstrapMethodsCount = 0;
- bootstrapMethods = null;
firstField = null;
lastField = null;
firstMethod = null;
lastMethod = null;
- computeMaxs = false;
- computeFrames = true;
- invalidFrames = false;
- new ClassReader(out.data).accept(this, ClassReader.SKIP_FRAMES);
+ compute = MethodWriter.INSERTED_FRAMES;
+ hasAsmInsns = false;
+ new ClassReader(out.data).accept(this, ClassReader.EXPAND_FRAMES
+ | ClassReader.EXPAND_ASM_INSNS);
return toByteArray();
}
return out.data;
diff --git a/spring-core/src/main/java/org/springframework/asm/CurrentFrame.java b/spring-core/src/main/java/org/springframework/asm/CurrentFrame.java
new file mode 100644
index 0000000000..715014bc6c
--- /dev/null
+++ b/spring-core/src/main/java/org/springframework/asm/CurrentFrame.java
@@ -0,0 +1,56 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.springframework.asm;
+
+/**
+ * Information about the input stack map frame at the "current" instruction of a
+ * method. This is implemented as a Frame subclass for a "basic block"
+ * containing only one instruction.
+ *
+ * @author Eric Bruneton
+ */
+class CurrentFrame extends Frame {
+
+ /**
+ * Sets this CurrentFrame to the input stack map frame of the next "current"
+ * instruction, i.e. the instruction just after the given one. It is assumed
+ * that the value of this object when this method is called is the stack map
+ * frame status just before the given instruction is executed.
+ */
+ @Override
+ void execute(int opcode, int arg, ClassWriter cw, Item item) {
+ super.execute(opcode, arg, cw, item);
+ Frame successor = new Frame();
+ merge(cw, successor, 0);
+ set(successor);
+ owner.inputStackTop = 0;
+ }
+}
diff --git a/spring-core/src/main/java/org/springframework/asm/Frame.java b/spring-core/src/main/java/org/springframework/asm/Frame.java
index 389e416ba0..30c7f0d588 100644
--- a/spring-core/src/main/java/org/springframework/asm/Frame.java
+++ b/spring-core/src/main/java/org/springframework/asm/Frame.java
@@ -34,7 +34,7 @@ package org.springframework.asm;
*
* @author Eric Bruneton
*/
-final class Frame {
+class Frame {
/*
* Frames are computed in a two steps process: during the visit of each
@@ -496,7 +496,7 @@ final class Frame {
* When the stack map frames are completely computed, this field is the
* actual number of types in {@link #outputStack}.
*/
- private int outputStackTop;
+ int outputStackTop;
/**
* Number of types that are initialized in the basic block.
@@ -520,6 +520,110 @@ final class Frame {
*/
private int[] initializations;
+ /**
+ * Sets this frame to the given value.
+ *
+ * @param cw
+ * the ClassWriter to which this label belongs.
+ * @param nLocal
+ * the number of local variables.
+ * @param local
+ * the local variable types. Primitive types are represented by
+ * {@link Opcodes#TOP}, {@link Opcodes#INTEGER},
+ * {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
+ * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
+ * {@link Opcodes#UNINITIALIZED_THIS} (long and double are
+ * represented by a single element). Reference types are
+ * represented by String objects (representing internal names),
+ * and uninitialized types by Label objects (this label
+ * designates the NEW instruction that created this uninitialized
+ * value).
+ * @param nStack
+ * the number of operand stack elements.
+ * @param stack
+ * the operand stack types (same format as the "local" array).
+ */
+ final void set(ClassWriter cw, final int nLocal, final Object[] local,
+ final int nStack, final Object[] stack) {
+ int i = convert(cw, nLocal, local, inputLocals);
+ while (i < local.length) {
+ inputLocals[i++] = TOP;
+ }
+ int nStackTop = 0;
+ for (int j = 0; j < nStack; ++j) {
+ if (stack[j] == Opcodes.LONG || stack[j] == Opcodes.DOUBLE) {
+ ++nStackTop;
+ }
+ }
+ inputStack = new int[nStack + nStackTop];
+ convert(cw, nStack, stack, inputStack);
+ outputStackTop = 0;
+ initializationCount = 0;
+ }
+
+ /**
+ * Converts types from the MethodWriter.visitFrame() format to the Frame
+ * format.
+ *
+ * @param cw
+ * the ClassWriter to which this label belongs.
+ * @param nInput
+ * the number of types to convert.
+ * @param input
+ * the types to convert. Primitive types are represented by
+ * {@link Opcodes#TOP}, {@link Opcodes#INTEGER},
+ * {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
+ * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
+ * {@link Opcodes#UNINITIALIZED_THIS} (long and double are
+ * represented by a single element). Reference types are
+ * represented by String objects (representing internal names),
+ * and uninitialized types by Label objects (this label
+ * designates the NEW instruction that created this uninitialized
+ * value).
+ * @param output
+ * where to store the converted types.
+ * @return the number of output elements.
+ */
+ private static int convert(ClassWriter cw, int nInput, Object[] input,
+ int[] output) {
+ int i = 0;
+ for (int j = 0; j < nInput; ++j) {
+ if (input[j] instanceof Integer) {
+ output[i++] = BASE | ((Integer) input[j]).intValue();
+ if (input[j] == Opcodes.LONG || input[j] == Opcodes.DOUBLE) {
+ output[i++] = TOP;
+ }
+ } else if (input[j] instanceof String) {
+ output[i++] = type(cw, Type.getObjectType((String) input[j])
+ .getDescriptor());
+ } else {
+ output[i++] = UNINITIALIZED
+ | cw.addUninitializedType("",
+ ((Label) input[j]).position);
+ }
+ }
+ return i;
+ }
+
+ /**
+ * Sets this frame to the value of the given frame. WARNING: after this
+ * method is called the two frames share the same data structures. It is
+ * recommended to discard the given frame f to avoid unexpected side
+ * effects.
+ *
+ * @param f
+ * The new frame value.
+ */
+ final void set(final Frame f) {
+ inputLocals = f.inputLocals;
+ inputStack = f.inputStack;
+ outputLocals = f.outputLocals;
+ outputStack = f.outputStack;
+ outputStackTop = f.outputStackTop;
+ initializationCount = f.initializationCount;
+ initializations = f.initializations;
+ }
+
/**
* Returns the output frame local variable type at the given index.
*
@@ -585,7 +689,7 @@ final class Frame {
}
// pushes the type on the output stack
outputStack[outputStackTop++] = type;
- // updates the maximun height reached by the output stack, if needed
+ // updates the maximum height reached by the output stack, if needed
int top = owner.inputStackTop + outputStackTop;
if (top > owner.outputStackMax) {
owner.outputStackMax = top;
@@ -809,7 +913,7 @@ final class Frame {
* @param maxLocals
* the maximum number of local variables of this method.
*/
- void initInputFrame(final ClassWriter cw, final int access,
+ final void initInputFrame(final ClassWriter cw, final int access,
final Type[] args, final int maxLocals) {
inputLocals = new int[maxLocals];
inputStack = new int[0];
@@ -1283,7 +1387,7 @@ final class Frame {
* @return true if the input frame of the given label has been
* changed by this operation.
*/
- boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
+ final boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
boolean changed = false;
int i, s, dim, kind, t;
diff --git a/spring-core/src/main/java/org/springframework/asm/Item.java b/spring-core/src/main/java/org/springframework/asm/Item.java
index 91dc701d09..7bebf34946 100644
--- a/spring-core/src/main/java/org/springframework/asm/Item.java
+++ b/spring-core/src/main/java/org/springframework/asm/Item.java
@@ -201,6 +201,7 @@ final class Item {
* @param strVal3
* third part of the value of this item.
*/
+ @SuppressWarnings("fallthrough")
void set(final int type, final String strVal1, final String strVal2,
final String strVal3) {
this.type = type;
@@ -210,8 +211,6 @@ final class Item {
switch (type) {
case ClassWriter.CLASS:
this.intVal = 0; // intVal of a class must be zero, see visitInnerClass
- hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
- return;
case ClassWriter.UTF8:
case ClassWriter.STR:
case ClassWriter.MTYPE:
diff --git a/spring-core/src/main/java/org/springframework/asm/Label.java b/spring-core/src/main/java/org/springframework/asm/Label.java
index deb84a0941..eb25824c82 100644
--- a/spring-core/src/main/java/org/springframework/asm/Label.java
+++ b/spring-core/src/main/java/org/springframework/asm/Label.java
@@ -364,9 +364,8 @@ public class Label {
* small to store the offset. In such a case the corresponding jump
* instruction is replaced with a pseudo instruction (using unused
* opcodes) using an unsigned two bytes offset. These pseudo
- * instructions will need to be replaced with true instructions with
- * wider offsets (4 bytes instead of 2). This is done in
- * {@link MethodWriter#resizeInstructions}.
+ * instructions will be replaced with standard bytecode instructions
+ * with wider offsets (4 bytes instead of 2), in ClassReader.
* @throws IllegalArgumentException
* if this label has already been resolved, or if it has not
* been created by the given code writer.
diff --git a/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java b/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java
index 4d5c50b98e..d512aed688 100644
--- a/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java
+++ b/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java
@@ -33,15 +33,16 @@ package org.springframework.asm;
* A visitor to visit a Java method. The methods of this class must be called in
* the following order: ( visitParameter )* [
* visitAnnotationDefault ] ( visitAnnotation |
- * visitTypeAnnotation | visitAttribute )* [
- * visitCode ( visitFrame | visitXInsn |
- * visitLabel | visitInsnAnnotation |
- * visitTryCatchBlock | visitTryCatchBlockAnnotation |
- * visitLocalVariable | visitLocalVariableAnnotation |
- * visitLineNumber )* visitMaxs ] visitEnd. In
- * addition, the visitXInsn and visitLabel methods must
- * be called in the sequential order of the bytecode instructions of the visited
- * code, visitInsnAnnotation must be called after the annotated
+ * visitParameterAnnotation visitTypeAnnotation |
+ * visitAttribute )* [ visitCode ( visitFrame |
+ * visitXInsn | visitLabel |
+ * visitInsnAnnotation | visitTryCatchBlock |
+ * visitTryCatchAnnotation | visitLocalVariable |
+ * visitLocalVariableAnnotation | visitLineNumber )*
+ * visitMaxs ] visitEnd. In addition, the
+ * visitXInsn and visitLabel methods must be called in
+ * the sequential order of the bytecode instructions of the visited code,
+ * visitInsnAnnotation must be called after the annotated
* instruction, visitTryCatchBlock must be called before the
* labels passed as arguments have been visited,
* visitTryCatchBlockAnnotation must be called after the
diff --git a/spring-core/src/main/java/org/springframework/asm/MethodWriter.java b/spring-core/src/main/java/org/springframework/asm/MethodWriter.java
index e8b226d230..c26e41665a 100644
--- a/spring-core/src/main/java/org/springframework/asm/MethodWriter.java
+++ b/spring-core/src/main/java/org/springframework/asm/MethodWriter.java
@@ -99,7 +99,19 @@ class MethodWriter extends MethodVisitor {
*
* @see #compute
*/
- private static final int FRAMES = 0;
+ static final int FRAMES = 0;
+
+ /**
+ * Indicates that the stack map frames of type F_INSERT must be computed.
+ * The other frames are not (re)computed. They should all be of type F_NEW
+ * and should be sufficient to compute the content of the F_INSERT frames,
+ * together with the bytecode instructions between a F_NEW and a F_INSERT
+ * frame - and without any knowledge of the type hierarchy (by definition of
+ * F_INSERT).
+ *
+ * @see #compute
+ */
+ static final int INSERTED_FRAMES = 1;
/**
* Indicates that the maximum stack size and number of local variables must
@@ -107,14 +119,14 @@ class MethodWriter extends MethodVisitor {
*
* @see #compute
*/
- private static final int MAXS = 1;
+ static final int MAXS = 2;
/**
* Indicates that nothing must be automatically computed.
*
* @see #compute
*/
- private static final int NOTHING = 2;
+ static final int NOTHING = 3;
/**
* The class writer to which this method must be added.
@@ -354,11 +366,6 @@ class MethodWriter extends MethodVisitor {
*/
private Attribute cattrs;
- /**
- * Indicates if some jump instructions are too small and need to be resized.
- */
- private boolean resize;
-
/**
* The number of subroutines in this method.
*/
@@ -380,6 +387,7 @@ class MethodWriter extends MethodVisitor {
* Indicates what must be automatically computed.
*
* @see #FRAMES
+ * @see #INSERTED_FRAMES
* @see #MAXS
* @see #NOTHING
*/
@@ -442,17 +450,12 @@ class MethodWriter extends MethodVisitor {
* @param exceptions
* the internal names of the method's exceptions. May be
* null.
- * @param computeMaxs
- * true if the maximum stack size and number of local
- * variables must be automatically computed.
- * @param computeFrames
- * true if the stack map tables must be recomputed from
- * scratch.
+ * @param compute
+ * Indicates what must be automatically computed (see #compute).
*/
MethodWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature,
- final String[] exceptions, final boolean computeMaxs,
- final boolean computeFrames) {
+ final String[] exceptions, final int compute) {
super(Opcodes.ASM5);
if (cw.firstMethod == null) {
cw.firstMethod = this;
@@ -478,8 +481,8 @@ class MethodWriter extends MethodVisitor {
this.exceptions[i] = cw.newClass(exceptions[i]);
}
}
- this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING);
- if (computeMaxs || computeFrames) {
+ this.compute = compute;
+ if (compute != NOTHING) {
// updates maxLocals
int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2;
if ((access & Opcodes.ACC_STATIC) != 0) {
@@ -614,7 +617,29 @@ class MethodWriter extends MethodVisitor {
return;
}
- if (type == Opcodes.F_NEW) {
+ if (compute == INSERTED_FRAMES) {
+ if (currentBlock.frame == null) {
+ // This should happen only once, for the implicit first frame
+ // (which is explicitly visited in ClassReader if the
+ // EXPAND_ASM_INSNS option is used).
+ currentBlock.frame = new CurrentFrame();
+ currentBlock.frame.owner = currentBlock;
+ currentBlock.frame.initInputFrame(cw, access,
+ Type.getArgumentTypes(descriptor), nLocal);
+ visitImplicitFirstFrame();
+ } else {
+ if (type == Opcodes.F_NEW) {
+ currentBlock.frame.set(cw, nLocal, local, nStack, stack);
+ } else {
+ // In this case type is equal to F_INSERT by hypothesis, and
+ // currentBlock.frame contains the stack map frame at the
+ // current instruction, computed from the last F_NEW frame
+ // and the bytecode instructions in between (via calls to
+ // CurrentFrame#execute).
+ }
+ visitFrame(currentBlock.frame);
+ }
+ } else if (type == Opcodes.F_NEW) {
if (previousFrame == null) {
visitImplicitFirstFrame();
}
@@ -718,7 +743,7 @@ class MethodWriter extends MethodVisitor {
// update currentBlock
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, 0, null, null);
} else {
// updates current and max stack sizes
@@ -741,7 +766,7 @@ class MethodWriter extends MethodVisitor {
lastCodeOffset = code.length;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, operand, null, null);
} else if (opcode != Opcodes.NEWARRAY) {
// updates current and max stack sizes only for NEWARRAY
@@ -766,7 +791,7 @@ class MethodWriter extends MethodVisitor {
lastCodeOffset = code.length;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, var, null, null);
} else {
// updates current and max stack sizes
@@ -826,7 +851,7 @@ class MethodWriter extends MethodVisitor {
Item i = cw.newClassItem(type);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, code.length, cw, i);
} else if (opcode == Opcodes.NEW) {
// updates current and max stack sizes only if opcode == NEW
@@ -849,7 +874,7 @@ class MethodWriter extends MethodVisitor {
Item i = cw.newFieldItem(owner, name, desc);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, 0, cw, i);
} else {
int size;
@@ -889,7 +914,7 @@ class MethodWriter extends MethodVisitor {
int argSize = i.intVal;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, 0, cw, i);
} else {
/*
@@ -941,7 +966,7 @@ class MethodWriter extends MethodVisitor {
int argSize = i.intVal;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i);
} else {
/*
@@ -975,7 +1000,9 @@ class MethodWriter extends MethodVisitor {
}
@Override
- public void visitJumpInsn(final int opcode, final Label label) {
+ public void visitJumpInsn(int opcode, final Label label) {
+ boolean isWide = opcode >= 200; // GOTO_W
+ opcode = isWide ? opcode - 33 : opcode;
lastCodeOffset = code.length;
Label nextInsn = null;
// Label currentBlock = this.currentBlock;
@@ -990,6 +1017,8 @@ class MethodWriter extends MethodVisitor {
// creates a Label for the next basic block
nextInsn = new Label();
}
+ } else if (compute == INSERTED_FRAMES) {
+ currentBlock.frame.execute(opcode, 0, null, null);
} else {
if (opcode == Opcodes.JSR) {
if ((label.status & Label.SUBROUTINE) == 0) {
@@ -1041,6 +1070,14 @@ class MethodWriter extends MethodVisitor {
code.putByte(200); // GOTO_W
}
label.put(this, code, code.length - 1, true);
+ } else if (isWide) {
+ /*
+ * case of a GOTO_W or JSR_W specified by the user (normally
+ * ClassReader when used to resize instructions). In this case we
+ * keep the original instruction.
+ */
+ code.putByte(opcode + 33);
+ label.put(this, code, code.length - 1, true);
} else {
/*
* case of a backward jump with an offset >= -32768, or of a forward
@@ -1068,7 +1105,7 @@ class MethodWriter extends MethodVisitor {
@Override
public void visitLabel(final Label label) {
// resolves previous forward references to label, if any
- resize |= label.resolve(this, code.length, code.data);
+ cw.hasAsmInsns |= label.resolve(this, code.length, code.data);
// updates currentBlock
if ((label.status & Label.DEBUG) != 0) {
return;
@@ -1101,6 +1138,18 @@ class MethodWriter extends MethodVisitor {
previousBlock.successor = label;
}
previousBlock = label;
+ } else if (compute == INSERTED_FRAMES) {
+ if (currentBlock == null) {
+ // This case should happen only once, for the visitLabel call in
+ // the constructor. Indeed, if compute is equal to
+ // INSERTED_FRAMES currentBlock can not be set back to null (see
+ // #noSuccessor).
+ currentBlock = label;
+ } else {
+ // Updates the frame owner so that a correct frame offset is
+ // computed in visitFrame(Frame).
+ currentBlock.frame.owner = label;
+ }
} else if (compute == MAXS) {
if (currentBlock != null) {
// ends current block (with one new successor)
@@ -1126,7 +1175,7 @@ class MethodWriter extends MethodVisitor {
Item i = cw.newConstItem(cst);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);
} else {
int size;
@@ -1158,7 +1207,7 @@ class MethodWriter extends MethodVisitor {
public void visitIincInsn(final int var, final int increment) {
lastCodeOffset = code.length;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(Opcodes.IINC, var, null, null);
}
}
@@ -1245,7 +1294,7 @@ class MethodWriter extends MethodVisitor {
Item i = cw.newClassItem(desc);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
- if (compute == FRAMES) {
+ if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i);
} else {
// updates current stack size (max stack size unchanged because
@@ -1401,14 +1450,6 @@ class MethodWriter extends MethodVisitor {
@Override
public void visitMaxs(final int maxStack, final int maxLocals) {
- if (resize) {
- // replaces the temporary jump opcodes introduced by Label.resolve.
- if (ClassReader.RESIZE) {
- resizeInstructions();
- } else {
- throw new RuntimeException("Method code too large!");
- }
- }
if (ClassReader.FRAMES && compute == FRAMES) {
// completes the control flow graph with exception handler blocks
Handler handler = firstHandler;
@@ -1439,8 +1480,8 @@ class MethodWriter extends MethodVisitor {
// creates and visits the first (implicit) frame
Frame f = labels.frame;
- Type[] args = Type.getArgumentTypes(descriptor);
- f.initInputFrame(cw, access, args, this.maxLocals);
+ f.initInputFrame(cw, access, Type.getArgumentTypes(descriptor),
+ this.maxLocals);
visitFrame(f);
/*
@@ -1688,7 +1729,9 @@ class MethodWriter extends MethodVisitor {
} else {
currentBlock.outputStackMax = maxStackSize;
}
- currentBlock = null;
+ if (compute != INSERTED_FRAMES) {
+ currentBlock = null;
+ }
}
// ------------------------------------------------------------------------
@@ -2347,569 +2390,4 @@ class MethodWriter extends MethodVisitor {
attrs.put(cw, null, 0, -1, -1, out);
}
}
-
- // ------------------------------------------------------------------------
- // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W)
- // ------------------------------------------------------------------------
-
- /**
- * Resizes and replaces the temporary instructions inserted by
- * {@link Label#resolve} for wide forward jumps, while keeping jump offsets
- * and instruction addresses consistent. This may require to resize other
- * existing instructions, or even to introduce new instructions: for
- * example, increasing the size of an instruction by 2 at the middle of a
- * method can increases the offset of an IFEQ instruction from 32766 to
- * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W
- * 32765. This, in turn, may require to increase the size of another jump
- * instruction, and so on... All these operations are handled automatically
- * by this method.
- *
- * This method must be called after all the method that is being built
- * has been visited. In particular, the {@link Label Label} objects used
- * to construct the method are no longer valid after this method has been
- * called.
- */
- private void resizeInstructions() {
- byte[] b = code.data; // bytecode of the method
- int u, v, label; // indexes in b
- int i, j; // loop indexes
- /*
- * 1st step: As explained above, resizing an instruction may require to
- * resize another one, which may require to resize yet another one, and
- * so on. The first step of the algorithm consists in finding all the
- * instructions that need to be resized, without modifying the code.
- * This is done by the following "fix point" algorithm:
- *
- * Parse the code to find the jump instructions whose offset will need
- * more than 2 bytes to be stored (the future offset is computed from
- * the current offset and from the number of bytes that will be inserted
- * or removed between the source and target instructions). For each such
- * instruction, adds an entry in (a copy of) the indexes and sizes
- * arrays (if this has not already been done in a previous iteration!).
- *
- * If at least one entry has been added during the previous step, go
- * back to the beginning, otherwise stop.
- *
- * In fact the real algorithm is complicated by the fact that the size
- * of TABLESWITCH and LOOKUPSWITCH instructions depends on their
- * position in the bytecode (because of padding). In order to ensure the
- * convergence of the algorithm, the number of bytes to be added or
- * removed from these instructions is over estimated during the previous
- * loop, and computed exactly only after the loop is finished (this
- * requires another pass to parse the bytecode of the method).
- */
- int[] allIndexes = new int[0]; // copy of indexes
- int[] allSizes = new int[0]; // copy of sizes
- boolean[] resize; // instructions to be resized
- int newOffset; // future offset of a jump instruction
-
- resize = new boolean[code.length];
-
- // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
- int state = 3;
- do {
- if (state == 3) {
- state = 2;
- }
- u = 0;
- while (u < b.length) {
- int opcode = b[u] & 0xFF; // opcode of current instruction
- int insert = 0; // bytes to be added after this instruction
-
- switch (ClassWriter.TYPE[opcode]) {
- case ClassWriter.NOARG_INSN:
- case ClassWriter.IMPLVAR_INSN:
- u += 1;
- break;
- case ClassWriter.LABEL_INSN:
- if (opcode > 201) {
- // converts temporary opcodes 202 to 217, 218 and
- // 219 to IFEQ ... JSR (inclusive), IFNULL and
- // IFNONNULL
- opcode = opcode < 218 ? opcode - 49 : opcode - 20;
- label = u + readUnsignedShort(b, u + 1);
- } else {
- label = u + readShort(b, u + 1);
- }
- newOffset = getNewOffset(allIndexes, allSizes, u, label);
- if (newOffset < Short.MIN_VALUE
- || newOffset > Short.MAX_VALUE) {
- if (!resize[u]) {
- if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
- // two additional bytes will be required to
- // replace this GOTO or JSR instruction with
- // a GOTO_W or a JSR_W
- insert = 2;
- } else {
- // five additional bytes will be required to
- // replace this IFxxx instruction with
- // IFNOTxxx GOTO_W , where IFNOTxxx
- // is the "opposite" opcode of IFxxx (i.e.,
- // IFNE for IFEQ) and where designates
- // the instruction just after the GOTO_W.
- insert = 5;
- }
- resize[u] = true;
- }
- }
- u += 3;
- break;
- case ClassWriter.LABELW_INSN:
- u += 5;
- break;
- case ClassWriter.TABL_INSN:
- if (state == 1) {
- // true number of bytes to be added (or removed)
- // from this instruction = (future number of padding
- // bytes - current number of padding byte) -
- // previously over estimated variation =
- // = ((3 - newOffset%4) - (3 - u%4)) - u%4
- // = (-newOffset%4 + u%4) - u%4
- // = -(newOffset & 3)
- newOffset = getNewOffset(allIndexes, allSizes, 0, u);
- insert = -(newOffset & 3);
- } else if (!resize[u]) {
- // over estimation of the number of bytes to be
- // added to this instruction = 3 - current number
- // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3
- insert = u & 3;
- resize[u] = true;
- }
- // skips instruction
- u = u + 4 - (u & 3);
- u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12;
- break;
- case ClassWriter.LOOK_INSN:
- if (state == 1) {
- // like TABL_INSN
- newOffset = getNewOffset(allIndexes, allSizes, 0, u);
- insert = -(newOffset & 3);
- } else if (!resize[u]) {
- // like TABL_INSN
- insert = u & 3;
- resize[u] = true;
- }
- // skips instruction
- u = u + 4 - (u & 3);
- u += 8 * readInt(b, u + 4) + 8;
- break;
- case ClassWriter.WIDE_INSN:
- opcode = b[u + 1] & 0xFF;
- if (opcode == Opcodes.IINC) {
- u += 6;
- } else {
- u += 4;
- }
- break;
- case ClassWriter.VAR_INSN:
- case ClassWriter.SBYTE_INSN:
- case ClassWriter.LDC_INSN:
- u += 2;
- break;
- case ClassWriter.SHORT_INSN:
- case ClassWriter.LDCW_INSN:
- case ClassWriter.FIELDORMETH_INSN:
- case ClassWriter.TYPE_INSN:
- case ClassWriter.IINC_INSN:
- u += 3;
- break;
- case ClassWriter.ITFMETH_INSN:
- case ClassWriter.INDYMETH_INSN:
- u += 5;
- break;
- // case ClassWriter.MANA_INSN:
- default:
- u += 4;
- break;
- }
- if (insert != 0) {
- // adds a new (u, insert) entry in the allIndexes and
- // allSizes arrays
- int[] newIndexes = new int[allIndexes.length + 1];
- int[] newSizes = new int[allSizes.length + 1];
- System.arraycopy(allIndexes, 0, newIndexes, 0,
- allIndexes.length);
- System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length);
- newIndexes[allIndexes.length] = u;
- newSizes[allSizes.length] = insert;
- allIndexes = newIndexes;
- allSizes = newSizes;
- if (insert > 0) {
- state = 3;
- }
- }
- }
- if (state < 3) {
- --state;
- }
- } while (state != 0);
-
- // 2nd step:
- // copies the bytecode of the method into a new bytevector, updates the
- // offsets, and inserts (or removes) bytes as requested.
-
- ByteVector newCode = new ByteVector(code.length);
-
- u = 0;
- while (u < code.length) {
- int opcode = b[u] & 0xFF;
- switch (ClassWriter.TYPE[opcode]) {
- case ClassWriter.NOARG_INSN:
- case ClassWriter.IMPLVAR_INSN:
- newCode.putByte(opcode);
- u += 1;
- break;
- case ClassWriter.LABEL_INSN:
- if (opcode > 201) {
- // changes temporary opcodes 202 to 217 (inclusive), 218
- // and 219 to IFEQ ... JSR (inclusive), IFNULL and
- // IFNONNULL
- opcode = opcode < 218 ? opcode - 49 : opcode - 20;
- label = u + readUnsignedShort(b, u + 1);
- } else {
- label = u + readShort(b, u + 1);
- }
- newOffset = getNewOffset(allIndexes, allSizes, u, label);
- if (resize[u]) {
- // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
- // with IFNOTxxx GOTO_W , where IFNOTxxx is
- // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
- // and where designates the instruction just after
- // the GOTO_W.
- if (opcode == Opcodes.GOTO) {
- newCode.putByte(200); // GOTO_W
- } else if (opcode == Opcodes.JSR) {
- newCode.putByte(201); // JSR_W
- } else {
- newCode.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1
- : opcode ^ 1);
- newCode.putShort(8); // jump offset
- newCode.putByte(200); // GOTO_W
- // newOffset now computed from start of GOTO_W
- newOffset -= 3;
- }
- newCode.putInt(newOffset);
- } else {
- newCode.putByte(opcode);
- newCode.putShort(newOffset);
- }
- u += 3;
- break;
- case ClassWriter.LABELW_INSN:
- label = u + readInt(b, u + 1);
- newOffset = getNewOffset(allIndexes, allSizes, u, label);
- newCode.putByte(opcode);
- newCode.putInt(newOffset);
- u += 5;
- break;
- case ClassWriter.TABL_INSN:
- // skips 0 to 3 padding bytes
- v = u;
- u = u + 4 - (v & 3);
- // reads and copies instruction
- newCode.putByte(Opcodes.TABLESWITCH);
- newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4);
- label = v + readInt(b, u);
- u += 4;
- newOffset = getNewOffset(allIndexes, allSizes, v, label);
- newCode.putInt(newOffset);
- j = readInt(b, u);
- u += 4;
- newCode.putInt(j);
- j = readInt(b, u) - j + 1;
- u += 4;
- newCode.putInt(readInt(b, u - 4));
- for (; j > 0; --j) {
- label = v + readInt(b, u);
- u += 4;
- newOffset = getNewOffset(allIndexes, allSizes, v, label);
- newCode.putInt(newOffset);
- }
- break;
- case ClassWriter.LOOK_INSN:
- // skips 0 to 3 padding bytes
- v = u;
- u = u + 4 - (v & 3);
- // reads and copies instruction
- newCode.putByte(Opcodes.LOOKUPSWITCH);
- newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4);
- label = v + readInt(b, u);
- u += 4;
- newOffset = getNewOffset(allIndexes, allSizes, v, label);
- newCode.putInt(newOffset);
- j = readInt(b, u);
- u += 4;
- newCode.putInt(j);
- for (; j > 0; --j) {
- newCode.putInt(readInt(b, u));
- u += 4;
- label = v + readInt(b, u);
- u += 4;
- newOffset = getNewOffset(allIndexes, allSizes, v, label);
- newCode.putInt(newOffset);
- }
- break;
- case ClassWriter.WIDE_INSN:
- opcode = b[u + 1] & 0xFF;
- if (opcode == Opcodes.IINC) {
- newCode.putByteArray(b, u, 6);
- u += 6;
- } else {
- newCode.putByteArray(b, u, 4);
- u += 4;
- }
- break;
- case ClassWriter.VAR_INSN:
- case ClassWriter.SBYTE_INSN:
- case ClassWriter.LDC_INSN:
- newCode.putByteArray(b, u, 2);
- u += 2;
- break;
- case ClassWriter.SHORT_INSN:
- case ClassWriter.LDCW_INSN:
- case ClassWriter.FIELDORMETH_INSN:
- case ClassWriter.TYPE_INSN:
- case ClassWriter.IINC_INSN:
- newCode.putByteArray(b, u, 3);
- u += 3;
- break;
- case ClassWriter.ITFMETH_INSN:
- case ClassWriter.INDYMETH_INSN:
- newCode.putByteArray(b, u, 5);
- u += 5;
- break;
- // case MANA_INSN:
- default:
- newCode.putByteArray(b, u, 4);
- u += 4;
- break;
- }
- }
-
- // updates the stack map frame labels
- if (compute == FRAMES) {
- Label l = labels;
- while (l != null) {
- /*
- * Detects the labels that are just after an IF instruction that
- * has been resized with the IFNOT GOTO_W pattern. These labels
- * are now the target of a jump instruction (the IFNOT
- * instruction). Note that we need the original label position
- * here. getNewOffset must therefore never have been called for
- * this label.
- */
- u = l.position - 3;
- if (u >= 0 && resize[u]) {
- l.status |= Label.TARGET;
- }
- getNewOffset(allIndexes, allSizes, l);
- l = l.successor;
- }
- // Update the offsets in the uninitialized types
- if (cw.typeTable != null) {
- for (i = 0; i < cw.typeTable.length; ++i) {
- Item item = cw.typeTable[i];
- if (item != null && item.type == ClassWriter.TYPE_UNINIT) {
- item.intVal = getNewOffset(allIndexes, allSizes, 0,
- item.intVal);
- }
- }
- }
- // The stack map frames are not serialized yet, so we don't need
- // to update them. They will be serialized in visitMaxs.
- } else if (frameCount > 0) {
- /*
- * Resizing an existing stack map frame table is really hard. Not
- * only the table must be parsed to update the offets, but new
- * frames may be needed for jump instructions that were inserted by
- * this method. And updating the offsets or inserting frames can
- * change the format of the following frames, in case of packed
- * frames. In practice the whole table must be recomputed. For this
- * the frames are marked as potentially invalid. This will cause the
- * whole class to be reread and rewritten with the COMPUTE_FRAMES
- * option (see the ClassWriter.toByteArray method). This is not very
- * efficient but is much easier and requires much less code than any
- * other method I can think of.
- */
- cw.invalidFrames = true;
- }
- // updates the exception handler block labels
- Handler h = firstHandler;
- while (h != null) {
- getNewOffset(allIndexes, allSizes, h.start);
- getNewOffset(allIndexes, allSizes, h.end);
- getNewOffset(allIndexes, allSizes, h.handler);
- h = h.next;
- }
- // updates the instructions addresses in the
- // local var and line number tables
- for (i = 0; i < 2; ++i) {
- ByteVector bv = i == 0 ? localVar : localVarType;
- if (bv != null) {
- b = bv.data;
- u = 0;
- while (u < bv.length) {
- label = readUnsignedShort(b, u);
- newOffset = getNewOffset(allIndexes, allSizes, 0, label);
- writeShort(b, u, newOffset);
- label += readUnsignedShort(b, u + 2);
- newOffset = getNewOffset(allIndexes, allSizes, 0, label)
- - newOffset;
- writeShort(b, u + 2, newOffset);
- u += 10;
- }
- }
- }
- if (lineNumber != null) {
- b = lineNumber.data;
- u = 0;
- while (u < lineNumber.length) {
- writeShort(
- b,
- u,
- getNewOffset(allIndexes, allSizes, 0,
- readUnsignedShort(b, u)));
- u += 4;
- }
- }
- // updates the labels of the other attributes
- Attribute attr = cattrs;
- while (attr != null) {
- Label[] labels = attr.getLabels();
- if (labels != null) {
- for (i = labels.length - 1; i >= 0; --i) {
- getNewOffset(allIndexes, allSizes, labels[i]);
- }
- }
- attr = attr.next;
- }
-
- // replaces old bytecodes with new ones
- code = newCode;
- }
-
- /**
- * Reads an unsigned short value in the given byte array.
- *
- * @param b
- * a byte array.
- * @param index
- * the start index of the value to be read.
- * @return the read value.
- */
- static int readUnsignedShort(final byte[] b, final int index) {
- return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
- }
-
- /**
- * Reads a signed short value in the given byte array.
- *
- * @param b
- * a byte array.
- * @param index
- * the start index of the value to be read.
- * @return the read value.
- */
- static short readShort(final byte[] b, final int index) {
- return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
- }
-
- /**
- * Reads a signed int value in the given byte array.
- *
- * @param b
- * a byte array.
- * @param index
- * the start index of the value to be read.
- * @return the read value.
- */
- static int readInt(final byte[] b, final int index) {
- return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)
- | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
- }
-
- /**
- * Writes a short value in the given byte array.
- *
- * @param b
- * a byte array.
- * @param index
- * where the first byte of the short value must be written.
- * @param s
- * the value to be written in the given byte array.
- */
- static void writeShort(final byte[] b, final int index, final int s) {
- b[index] = (byte) (s >>> 8);
- b[index + 1] = (byte) s;
- }
-
- /**
- * Computes the future value of a bytecode offset.
- *
- * Note: it is possible to have several entries for the same instruction in
- * the indexes and sizes: two entries (index=a,size=b) and
- * (index=a,size=b') are equivalent to a single entry (index=a,size=b+b').
- *
- * @param indexes
- * current positions of the instructions to be resized. Each
- * instruction must be designated by the index of its last
- * byte, plus one (or, in other words, by the index of the
- * first byte of the next instruction).
- * @param sizes
- * the number of bytes to be added to the above
- * instructions. More precisely, for each i < len,
- * sizes[i] bytes will be added at the end of the
- * instruction designated by indexes[i] or, if
- * sizes[i] is negative, the last |
- * sizes[i]| bytes of the instruction will be removed
- * (the instruction size must not become negative or
- * null).
- * @param begin
- * index of the first byte of the source instruction.
- * @param end
- * index of the first byte of the target instruction.
- * @return the future value of the given bytecode offset.
- */
- static int getNewOffset(final int[] indexes, final int[] sizes,
- final int begin, final int end) {
- int offset = end - begin;
- for (int i = 0; i < indexes.length; ++i) {
- if (begin < indexes[i] && indexes[i] <= end) {
- // forward jump
- offset += sizes[i];
- } else if (end < indexes[i] && indexes[i] <= begin) {
- // backward jump
- offset -= sizes[i];
- }
- }
- return offset;
- }
-
- /**
- * Updates the offset of the given label.
- *
- * @param indexes
- * current positions of the instructions to be resized. Each
- * instruction must be designated by the index of its last
- * byte, plus one (or, in other words, by the index of the
- * first byte of the next instruction).
- * @param sizes
- * the number of bytes to be added to the above
- * instructions. More precisely, for each i < len,
- * sizes[i] bytes will be added at the end of the
- * instruction designated by indexes[i] or, if
- * sizes[i] is negative, the last |
- * sizes[i]| bytes of the instruction will be removed
- * (the instruction size must not become negative or
- * null).
- * @param label
- * the label whose offset must be updated.
- */
- static void getNewOffset(final int[] indexes, final int[] sizes,
- final Label label) {
- if ((label.status & Label.RESIZED) == 0) {
- label.position = getNewOffset(indexes, sizes, 0, label.position);
- label.status |= Label.RESIZED;
- }
- }
}
diff --git a/spring-core/src/main/java/org/springframework/asm/Opcodes.java b/spring-core/src/main/java/org/springframework/asm/Opcodes.java
index a77a939056..a84b300b85 100644
--- a/spring-core/src/main/java/org/springframework/asm/Opcodes.java
+++ b/spring-core/src/main/java/org/springframework/asm/Opcodes.java
@@ -146,13 +146,17 @@ public interface Opcodes {
*/
int F_SAME1 = 4;
- Integer TOP = 0;
- Integer INTEGER = 1;
- Integer FLOAT = 2;
- Integer DOUBLE = 3;
- Integer LONG = 4;
- Integer NULL = 5;
- Integer UNINITIALIZED_THIS = 6;
+ // Do not try to change the following code to use auto-boxing,
+ // these values are compared by reference and not by value
+ // The constructor of Integer was deprecated in 9
+ // but we are stuck with it by backward compatibility
+ @SuppressWarnings("deprecation") Integer TOP = new Integer(0);
+ @SuppressWarnings("deprecation") Integer INTEGER = new Integer(1);
+ @SuppressWarnings("deprecation") Integer FLOAT = new Integer(2);
+ @SuppressWarnings("deprecation") Integer DOUBLE = new Integer(3);
+ @SuppressWarnings("deprecation") Integer LONG = new Integer(4);
+ @SuppressWarnings("deprecation") Integer NULL = new Integer(5);
+ @SuppressWarnings("deprecation") Integer UNINITIALIZED_THIS = new Integer(6);
// opcodes // visit method (- = idem)
diff --git a/spring-core/src/main/java/org/springframework/asm/Type.java b/spring-core/src/main/java/org/springframework/asm/Type.java
index 0fa911538f..c40d51f427 100644
--- a/spring-core/src/main/java/org/springframework/asm/Type.java
+++ b/spring-core/src/main/java/org/springframework/asm/Type.java
@@ -377,7 +377,16 @@ public class Type {
*/
public static Type getReturnType(final String methodDescriptor) {
char[] buf = methodDescriptor.toCharArray();
- return getType(buf, methodDescriptor.indexOf(')') + 1);
+ int off = 1;
+ while (true) {
+ char car = buf[off++];
+ if (car == ')') {
+ return getType(buf, off);
+ } else if (car == 'L') {
+ while (buf[off++] != ';') {
+ }
+ }
+ }
}
/**
@@ -625,9 +634,9 @@ public class Type {
* @return the descriptor corresponding to this Java type.
*/
public String getDescriptor() {
- StringBuilder sb = new StringBuilder();
- getDescriptor(sb);
- return sb.toString();
+ StringBuilder buf = new StringBuilder();
+ getDescriptor(buf);
+ return buf.toString();
}
/**
@@ -643,34 +652,34 @@ public class Type {
*/
public static String getMethodDescriptor(final Type returnType,
final Type... argumentTypes) {
- StringBuilder sb = new StringBuilder();
- sb.append('(');
+ StringBuilder buf = new StringBuilder();
+ buf.append('(');
for (int i = 0; i < argumentTypes.length; ++i) {
- argumentTypes[i].getDescriptor(sb);
+ argumentTypes[i].getDescriptor(buf);
}
- sb.append(')');
- returnType.getDescriptor(sb);
- return sb.toString();
+ buf.append(')');
+ returnType.getDescriptor(buf);
+ return buf.toString();
}
/**
* Appends the descriptor corresponding to this Java type to the given
- * string builder.
+ * string buffer.
*
- * @param sb
- * the string builder to which the descriptor must be appended.
+ * @param buf
+ * the string buffer to which the descriptor must be appended.
*/
- private void getDescriptor(final StringBuilder sb) {
+ private void getDescriptor(final StringBuilder buf) {
if (this.buf == null) {
// descriptor is in byte 3 of 'off' for primitive types (buf ==
// null)
- sb.append((char) ((off & 0xFF000000) >>> 24));
+ buf.append((char) ((off & 0xFF000000) >>> 24));
} else if (sort == OBJECT) {
- sb.append('L');
- sb.append(this.buf, off, len);
- sb.append(';');
+ buf.append('L');
+ buf.append(this.buf, off, len);
+ buf.append(';');
} else { // sort == ARRAY || sort == METHOD
- sb.append(this.buf, off, len);
+ buf.append(this.buf, off, len);
}
}
@@ -700,9 +709,9 @@ public class Type {
* @return the descriptor corresponding to the given class.
*/
public static String getDescriptor(final Class> c) {
- StringBuilder sb = new StringBuilder();
- getDescriptor(sb, c);
- return sb.toString();
+ StringBuilder buf = new StringBuilder();
+ getDescriptor(buf, c);
+ return buf.toString();
}
/**
@@ -714,12 +723,12 @@ public class Type {
*/
public static String getConstructorDescriptor(final Constructor> c) {
Class>[] parameters = c.getParameterTypes();
- StringBuilder sb = new StringBuilder();
- sb.append('(');
+ StringBuilder buf = new StringBuilder();
+ buf.append('(');
for (int i = 0; i < parameters.length; ++i) {
- getDescriptor(sb, parameters[i]);
+ getDescriptor(buf, parameters[i]);
}
- return sb.append(")V").toString();
+ return buf.append(")V").toString();
}
/**
@@ -731,25 +740,25 @@ public class Type {
*/
public static String getMethodDescriptor(final Method m) {
Class>[] parameters = m.getParameterTypes();
- StringBuilder sb = new StringBuilder();
- sb.append('(');
+ StringBuilder buf = new StringBuilder();
+ buf.append('(');
for (int i = 0; i < parameters.length; ++i) {
- getDescriptor(sb, parameters[i]);
+ getDescriptor(buf, parameters[i]);
}
- sb.append(')');
- getDescriptor(sb, m.getReturnType());
- return sb.toString();
+ buf.append(')');
+ getDescriptor(buf, m.getReturnType());
+ return buf.toString();
}
/**
- * Appends the descriptor of the given class to the given string builder.
+ * Appends the descriptor of the given class to the given string buffer.
*
- * @param sb
+ * @param buf
* the string buffer to which the descriptor must be appended.
* @param c
* the class whose descriptor must be computed.
*/
- private static void getDescriptor(final StringBuilder sb, final Class> c) {
+ private static void getDescriptor(final StringBuilder buf, final Class> c) {
Class> d = c;
while (true) {
if (d.isPrimitive()) {
@@ -773,20 +782,20 @@ public class Type {
} else /* if (d == Long.TYPE) */{
car = 'J';
}
- sb.append(car);
+ buf.append(car);
return;
} else if (d.isArray()) {
- sb.append('[');
+ buf.append('[');
d = d.getComponentType();
} else {
- sb.append('L');
+ buf.append('L');
String name = d.getName();
int len = name.length();
for (int i = 0; i < len; ++i) {
char car = name.charAt(i);
- sb.append(car == '.' ? '/' : car);
+ buf.append(car == '.' ? '/' : car);
}
- sb.append(';');
+ buf.append(';');
return;
}
}