parent
311522bc86
commit
ccabff6ba3
|
@ -89,7 +89,7 @@ public abstract class AnnotationVisitor {
|
||||||
* the actual value, whose type must be {@link Byte},
|
* the actual value, whose type must be {@link Byte},
|
||||||
* {@link Boolean}, {@link Character}, {@link Short},
|
* {@link Boolean}, {@link Character}, {@link Short},
|
||||||
* {@link Integer} , {@link Long}, {@link Float}, {@link Double},
|
* {@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,
|
* value can also be an array of byte, boolean, short, char, int,
|
||||||
* long, float or double values (this is equivalent to using
|
* long, float or double values (this is equivalent to using
|
||||||
* {@link #visitArray visitArray} and visiting each array element
|
* {@link #visitArray visitArray} and visiting each array element
|
||||||
|
|
|
@ -104,6 +104,21 @@ public class ClassReader {
|
||||||
*/
|
*/
|
||||||
public static final int EXPAND_FRAMES = 8;
|
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 <i>not</i> 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. <i>The content of this array must not be
|
* The class to be parsed. <i>The content of this array must not be
|
||||||
* modified. This field is intended for {@link Attribute} sub classes, and
|
* modified. This field is intended for {@link Attribute} sub classes, and
|
||||||
|
@ -1062,6 +1077,10 @@ public class ClassReader {
|
||||||
readLabel(offset + readShort(u + 1), labels);
|
readLabel(offset + readShort(u + 1), labels);
|
||||||
u += 3;
|
u += 3;
|
||||||
break;
|
break;
|
||||||
|
case ClassWriter.ASM_LABEL_INSN:
|
||||||
|
readLabel(offset + readUnsignedShort(u + 1), labels);
|
||||||
|
u += 3;
|
||||||
|
break;
|
||||||
case ClassWriter.LABELW_INSN:
|
case ClassWriter.LABELW_INSN:
|
||||||
readLabel(offset + readInt(u + 1), labels);
|
readLabel(offset + readInt(u + 1), labels);
|
||||||
u += 5;
|
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
|
// visits the instructions
|
||||||
|
int opcodeDelta = (context.flags & EXPAND_ASM_INSNS) == 0 ? -33 : 0;
|
||||||
u = codeStart;
|
u = codeStart;
|
||||||
while (u < codeEnd) {
|
while (u < codeEnd) {
|
||||||
int offset = u - codeStart;
|
int offset = u - codeStart;
|
||||||
|
@ -1352,9 +1386,42 @@ public class ClassReader {
|
||||||
u += 3;
|
u += 3;
|
||||||
break;
|
break;
|
||||||
case ClassWriter.LABELW_INSN:
|
case ClassWriter.LABELW_INSN:
|
||||||
mv.visitJumpInsn(opcode - 33, labels[offset + readInt(u + 1)]);
|
mv.visitJumpInsn(opcode + opcodeDelta, labels[offset
|
||||||
|
+ readInt(u + 1)]);
|
||||||
u += 5;
|
u += 5;
|
||||||
break;
|
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
|
||||||
|
// <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is
|
||||||
|
// the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
|
||||||
|
// and where <l'> 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:
|
case ClassWriter.WIDE_INSN:
|
||||||
opcode = b[u + 1] & 0xFF;
|
opcode = b[u + 1] & 0xFF;
|
||||||
if (opcode == Opcodes.IINC) {
|
if (opcode == Opcodes.IINC) {
|
||||||
|
@ -1848,7 +1915,9 @@ public class ClassReader {
|
||||||
v += 2;
|
v += 2;
|
||||||
break;
|
break;
|
||||||
case 'Z': // pointer to CONSTANT_Boolean
|
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;
|
v += 2;
|
||||||
break;
|
break;
|
||||||
case 'S': // pointer to CONSTANT_Short
|
case 'S': // pointer to CONSTANT_Short
|
||||||
|
|
|
@ -58,8 +58,8 @@ public class ClassWriter extends ClassVisitor {
|
||||||
* {@link MethodVisitor#visitFrame} method are ignored, and the stack map
|
* {@link MethodVisitor#visitFrame} method are ignored, and the stack map
|
||||||
* frames are recomputed from the methods bytecode. The arguments of the
|
* frames are recomputed from the methods bytecode. The arguments of the
|
||||||
* {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and
|
* {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and
|
||||||
* recomputed from the bytecode. In other words, computeFrames implies
|
* recomputed from the bytecode. In other words, COMPUTE_FRAMES implies
|
||||||
* computeMaxs.
|
* COMPUTE_MAXS.
|
||||||
*
|
*
|
||||||
* @see #ClassWriter(int)
|
* @see #ClassWriter(int)
|
||||||
*/
|
*/
|
||||||
|
@ -167,6 +167,22 @@ public class ClassWriter extends ClassVisitor {
|
||||||
*/
|
*/
|
||||||
static final int WIDE_INSN = 17;
|
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.
|
* The instruction types of all JVM opcodes.
|
||||||
*/
|
*/
|
||||||
|
@ -484,25 +500,19 @@ public class ClassWriter extends ClassVisitor {
|
||||||
MethodWriter lastMethod;
|
MethodWriter lastMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <tt>true</tt> if the maximum stack size and number of local variables
|
* Indicates what must be automatically computed.
|
||||||
* must be automatically computed.
|
*
|
||||||
|
* @see MethodWriter#compute
|
||||||
*/
|
*/
|
||||||
private boolean computeMaxs;
|
private int compute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <tt>true</tt> if the stack map frames must be recomputed from scratch.
|
* <tt>true</tt> 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;
|
boolean hasAsmInsns;
|
||||||
|
|
||||||
/**
|
|
||||||
* <tt>true</tt> 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;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Static initializer
|
// Static initializer
|
||||||
|
@ -517,7 +527,7 @@ public class ClassWriter extends ClassVisitor {
|
||||||
String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD"
|
String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD"
|
||||||
+ "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
+ "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||||
+ "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA"
|
+ "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA"
|
||||||
+ "AAAAGGGGGGGHIFBFAAFFAARQJJKKJJJJJJJJJJJJJJJJJJ";
|
+ "AAAAGGGGGGGHIFBFAAFFAARQJJKKSSSSSSSSSSSSSSSSSS";
|
||||||
for (i = 0; i < b.length; ++i) {
|
for (i = 0; i < b.length; ++i) {
|
||||||
b[i] = (byte) (s.charAt(i) - 'A');
|
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
|
// // temporary opcodes used internally by ASM - see Label and
|
||||||
// MethodWriter
|
// MethodWriter
|
||||||
// for (i = 202; i < 220; ++i) {
|
// for (i = 202; i < 220; ++i) {
|
||||||
// b[i] = LABEL_INSN;
|
// b[i] = ASM_LABEL_INSN;
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// // LDC(_W) instructions
|
// // LDC(_W) instructions
|
||||||
|
@ -614,8 +624,9 @@ public class ClassWriter extends ClassVisitor {
|
||||||
key2 = new Item();
|
key2 = new Item();
|
||||||
key3 = new Item();
|
key3 = new Item();
|
||||||
key4 = new Item();
|
key4 = new Item();
|
||||||
this.computeMaxs = (flags & COMPUTE_MAXS) != 0;
|
this.compute = (flags & COMPUTE_FRAMES) != 0 ? MethodWriter.FRAMES
|
||||||
this.computeFrames = (flags & COMPUTE_FRAMES) != 0;
|
: ((flags & COMPUTE_MAXS) != 0 ? MethodWriter.MAXS
|
||||||
|
: MethodWriter.NOTHING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -645,9 +656,9 @@ public class ClassWriter extends ClassVisitor {
|
||||||
* @param flags
|
* @param flags
|
||||||
* option flags that can be used to modify the default behavior
|
* option flags that can be used to modify the default behavior
|
||||||
* of this class. <i>These option flags do not affect methods
|
* of this class. <i>These option flags do not affect methods
|
||||||
* that are copied as is in the new class. This means that the
|
* that are copied as is in the new class. This means that
|
||||||
* maximum stack size nor the stack frames will be computed for
|
* neither the maximum stack size nor the stack frames will be
|
||||||
* these methods</i>. See {@link #COMPUTE_MAXS},
|
* computed for these methods</i>. See {@link #COMPUTE_MAXS},
|
||||||
* {@link #COMPUTE_FRAMES}.
|
* {@link #COMPUTE_FRAMES}.
|
||||||
*/
|
*/
|
||||||
public ClassWriter(final ClassReader classReader, final int flags) {
|
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,
|
public final MethodVisitor visitMethod(final int access, final String name,
|
||||||
final String desc, final String signature, final String[] exceptions) {
|
final String desc, final String signature, final String[] exceptions) {
|
||||||
return new MethodWriter(this, access, name, desc, signature,
|
return new MethodWriter(this, access, name, desc, signature,
|
||||||
exceptions, computeMaxs, computeFrames);
|
exceptions, compute);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -977,22 +988,20 @@ public class ClassWriter extends ClassVisitor {
|
||||||
if (attrs != null) {
|
if (attrs != null) {
|
||||||
attrs.put(this, null, 0, -1, -1, out);
|
attrs.put(this, null, 0, -1, -1, out);
|
||||||
}
|
}
|
||||||
if (invalidFrames) {
|
if (hasAsmInsns) {
|
||||||
anns = null;
|
anns = null;
|
||||||
ianns = null;
|
ianns = null;
|
||||||
attrs = null;
|
attrs = null;
|
||||||
innerClassesCount = 0;
|
innerClassesCount = 0;
|
||||||
innerClasses = null;
|
innerClasses = null;
|
||||||
bootstrapMethodsCount = 0;
|
|
||||||
bootstrapMethods = null;
|
|
||||||
firstField = null;
|
firstField = null;
|
||||||
lastField = null;
|
lastField = null;
|
||||||
firstMethod = null;
|
firstMethod = null;
|
||||||
lastMethod = null;
|
lastMethod = null;
|
||||||
computeMaxs = false;
|
compute = MethodWriter.INSERTED_FRAMES;
|
||||||
computeFrames = true;
|
hasAsmInsns = false;
|
||||||
invalidFrames = false;
|
new ClassReader(out.data).accept(this, ClassReader.EXPAND_FRAMES
|
||||||
new ClassReader(out.data).accept(this, ClassReader.SKIP_FRAMES);
|
| ClassReader.EXPAND_ASM_INSNS);
|
||||||
return toByteArray();
|
return toByteArray();
|
||||||
}
|
}
|
||||||
return out.data;
|
return out.data;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ package org.springframework.asm;
|
||||||
*
|
*
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
*/
|
*/
|
||||||
final class Frame {
|
class Frame {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Frames are computed in a two steps process: during the visit of each
|
* 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
|
* When the stack map frames are completely computed, this field is the
|
||||||
* actual number of types in {@link #outputStack}.
|
* actual number of types in {@link #outputStack}.
|
||||||
*/
|
*/
|
||||||
private int outputStackTop;
|
int outputStackTop;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of types that are initialized in the basic block.
|
* Number of types that are initialized in the basic block.
|
||||||
|
@ -520,6 +520,110 @@ final class Frame {
|
||||||
*/
|
*/
|
||||||
private int[] initializations;
|
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.
|
* 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
|
// pushes the type on the output stack
|
||||||
outputStack[outputStackTop++] = type;
|
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;
|
int top = owner.inputStackTop + outputStackTop;
|
||||||
if (top > owner.outputStackMax) {
|
if (top > owner.outputStackMax) {
|
||||||
owner.outputStackMax = top;
|
owner.outputStackMax = top;
|
||||||
|
@ -809,7 +913,7 @@ final class Frame {
|
||||||
* @param maxLocals
|
* @param maxLocals
|
||||||
* the maximum number of local variables of this method.
|
* 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) {
|
final Type[] args, final int maxLocals) {
|
||||||
inputLocals = new int[maxLocals];
|
inputLocals = new int[maxLocals];
|
||||||
inputStack = new int[0];
|
inputStack = new int[0];
|
||||||
|
@ -1283,7 +1387,7 @@ final class Frame {
|
||||||
* @return <tt>true</tt> if the input frame of the given label has been
|
* @return <tt>true</tt> if the input frame of the given label has been
|
||||||
* changed by this operation.
|
* 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;
|
boolean changed = false;
|
||||||
int i, s, dim, kind, t;
|
int i, s, dim, kind, t;
|
||||||
|
|
||||||
|
|
|
@ -201,6 +201,7 @@ final class Item {
|
||||||
* @param strVal3
|
* @param strVal3
|
||||||
* third part of the value of this item.
|
* third part of the value of this item.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("fallthrough")
|
||||||
void set(final int type, final String strVal1, final String strVal2,
|
void set(final int type, final String strVal1, final String strVal2,
|
||||||
final String strVal3) {
|
final String strVal3) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
@ -210,8 +211,6 @@ final class Item {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ClassWriter.CLASS:
|
case ClassWriter.CLASS:
|
||||||
this.intVal = 0; // intVal of a class must be zero, see visitInnerClass
|
this.intVal = 0; // intVal of a class must be zero, see visitInnerClass
|
||||||
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
|
|
||||||
return;
|
|
||||||
case ClassWriter.UTF8:
|
case ClassWriter.UTF8:
|
||||||
case ClassWriter.STR:
|
case ClassWriter.STR:
|
||||||
case ClassWriter.MTYPE:
|
case ClassWriter.MTYPE:
|
||||||
|
|
|
@ -364,9 +364,8 @@ public class Label {
|
||||||
* small to store the offset. In such a case the corresponding jump
|
* small to store the offset. In such a case the corresponding jump
|
||||||
* instruction is replaced with a pseudo instruction (using unused
|
* instruction is replaced with a pseudo instruction (using unused
|
||||||
* opcodes) using an unsigned two bytes offset. These pseudo
|
* opcodes) using an unsigned two bytes offset. These pseudo
|
||||||
* instructions will need to be replaced with true instructions with
|
* instructions will be replaced with standard bytecode instructions
|
||||||
* wider offsets (4 bytes instead of 2). This is done in
|
* with wider offsets (4 bytes instead of 2), in ClassReader.
|
||||||
* {@link MethodWriter#resizeInstructions}.
|
|
||||||
* @throws IllegalArgumentException
|
* @throws IllegalArgumentException
|
||||||
* if this label has already been resolved, or if it has not
|
* if this label has already been resolved, or if it has not
|
||||||
* been created by the given code writer.
|
* been created by the given code writer.
|
||||||
|
|
|
@ -33,15 +33,16 @@ package org.springframework.asm;
|
||||||
* A visitor to visit a Java method. The methods of this class must be called in
|
* A visitor to visit a Java method. The methods of this class must be called in
|
||||||
* the following order: ( <tt>visitParameter</tt> )* [
|
* the following order: ( <tt>visitParameter</tt> )* [
|
||||||
* <tt>visitAnnotationDefault</tt> ] ( <tt>visitAnnotation</tt> |
|
* <tt>visitAnnotationDefault</tt> ] ( <tt>visitAnnotation</tt> |
|
||||||
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* [
|
* <tt>visitParameterAnnotation</tt> <tt>visitTypeAnnotation</tt> |
|
||||||
* <tt>visitCode</tt> ( <tt>visitFrame</tt> | <tt>visit<i>X</i>Insn</tt> |
|
* <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> |
|
||||||
* <tt>visitLabel</tt> | <tt>visitInsnAnnotation</tt> |
|
* <tt>visit<i>X</i>Insn</tt> | <tt>visitLabel</tt> |
|
||||||
* <tt>visitTryCatchBlock</tt> | <tt>visitTryCatchBlockAnnotation</tt> |
|
* <tt>visitInsnAnnotation</tt> | <tt>visitTryCatchBlock</tt> |
|
||||||
* <tt>visitLocalVariable</tt> | <tt>visitLocalVariableAnnotation</tt> |
|
* <tt>visitTryCatchAnnotation</tt> | <tt>visitLocalVariable</tt> |
|
||||||
* <tt>visitLineNumber</tt> )* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In
|
* <tt>visitLocalVariableAnnotation</tt> | <tt>visitLineNumber</tt> )*
|
||||||
* addition, the <tt>visit<i>X</i>Insn</tt> and <tt>visitLabel</tt> methods must
|
* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In addition, the
|
||||||
* be called in the sequential order of the bytecode instructions of the visited
|
* <tt>visit<i>X</i>Insn</tt> and <tt>visitLabel</tt> methods must be called in
|
||||||
* code, <tt>visitInsnAnnotation</tt> must be called <i>after</i> the annotated
|
* the sequential order of the bytecode instructions of the visited code,
|
||||||
|
* <tt>visitInsnAnnotation</tt> must be called <i>after</i> the annotated
|
||||||
* instruction, <tt>visitTryCatchBlock</tt> must be called <i>before</i> the
|
* instruction, <tt>visitTryCatchBlock</tt> must be called <i>before</i> the
|
||||||
* labels passed as arguments have been visited,
|
* labels passed as arguments have been visited,
|
||||||
* <tt>visitTryCatchBlockAnnotation</tt> must be called <i>after</i> the
|
* <tt>visitTryCatchBlockAnnotation</tt> must be called <i>after</i> the
|
||||||
|
|
|
@ -99,7 +99,19 @@ class MethodWriter extends MethodVisitor {
|
||||||
*
|
*
|
||||||
* @see #compute
|
* @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
|
* Indicates that the maximum stack size and number of local variables must
|
||||||
|
@ -107,14 +119,14 @@ class MethodWriter extends MethodVisitor {
|
||||||
*
|
*
|
||||||
* @see #compute
|
* @see #compute
|
||||||
*/
|
*/
|
||||||
private static final int MAXS = 1;
|
static final int MAXS = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that nothing must be automatically computed.
|
* Indicates that nothing must be automatically computed.
|
||||||
*
|
*
|
||||||
* @see #compute
|
* @see #compute
|
||||||
*/
|
*/
|
||||||
private static final int NOTHING = 2;
|
static final int NOTHING = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class writer to which this method must be added.
|
* The class writer to which this method must be added.
|
||||||
|
@ -354,11 +366,6 @@ class MethodWriter extends MethodVisitor {
|
||||||
*/
|
*/
|
||||||
private Attribute cattrs;
|
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.
|
* The number of subroutines in this method.
|
||||||
*/
|
*/
|
||||||
|
@ -380,6 +387,7 @@ class MethodWriter extends MethodVisitor {
|
||||||
* Indicates what must be automatically computed.
|
* Indicates what must be automatically computed.
|
||||||
*
|
*
|
||||||
* @see #FRAMES
|
* @see #FRAMES
|
||||||
|
* @see #INSERTED_FRAMES
|
||||||
* @see #MAXS
|
* @see #MAXS
|
||||||
* @see #NOTHING
|
* @see #NOTHING
|
||||||
*/
|
*/
|
||||||
|
@ -442,17 +450,12 @@ class MethodWriter extends MethodVisitor {
|
||||||
* @param exceptions
|
* @param exceptions
|
||||||
* the internal names of the method's exceptions. May be
|
* the internal names of the method's exceptions. May be
|
||||||
* <tt>null</tt>.
|
* <tt>null</tt>.
|
||||||
* @param computeMaxs
|
* @param compute
|
||||||
* <tt>true</tt> if the maximum stack size and number of local
|
* Indicates what must be automatically computed (see #compute).
|
||||||
* variables must be automatically computed.
|
|
||||||
* @param computeFrames
|
|
||||||
* <tt>true</tt> if the stack map tables must be recomputed from
|
|
||||||
* scratch.
|
|
||||||
*/
|
*/
|
||||||
MethodWriter(final ClassWriter cw, final int access, final String name,
|
MethodWriter(final ClassWriter cw, final int access, final String name,
|
||||||
final String desc, final String signature,
|
final String desc, final String signature,
|
||||||
final String[] exceptions, final boolean computeMaxs,
|
final String[] exceptions, final int compute) {
|
||||||
final boolean computeFrames) {
|
|
||||||
super(Opcodes.ASM5);
|
super(Opcodes.ASM5);
|
||||||
if (cw.firstMethod == null) {
|
if (cw.firstMethod == null) {
|
||||||
cw.firstMethod = this;
|
cw.firstMethod = this;
|
||||||
|
@ -478,8 +481,8 @@ class MethodWriter extends MethodVisitor {
|
||||||
this.exceptions[i] = cw.newClass(exceptions[i]);
|
this.exceptions[i] = cw.newClass(exceptions[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING);
|
this.compute = compute;
|
||||||
if (computeMaxs || computeFrames) {
|
if (compute != NOTHING) {
|
||||||
// updates maxLocals
|
// updates maxLocals
|
||||||
int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2;
|
int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2;
|
||||||
if ((access & Opcodes.ACC_STATIC) != 0) {
|
if ((access & Opcodes.ACC_STATIC) != 0) {
|
||||||
|
@ -614,7 +617,29 @@ class MethodWriter extends MethodVisitor {
|
||||||
return;
|
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) {
|
if (previousFrame == null) {
|
||||||
visitImplicitFirstFrame();
|
visitImplicitFirstFrame();
|
||||||
}
|
}
|
||||||
|
@ -718,7 +743,7 @@ class MethodWriter extends MethodVisitor {
|
||||||
// update currentBlock
|
// update currentBlock
|
||||||
// Label currentBlock = this.currentBlock;
|
// Label currentBlock = this.currentBlock;
|
||||||
if (currentBlock != null) {
|
if (currentBlock != null) {
|
||||||
if (compute == FRAMES) {
|
if (compute == FRAMES || compute == INSERTED_FRAMES) {
|
||||||
currentBlock.frame.execute(opcode, 0, null, null);
|
currentBlock.frame.execute(opcode, 0, null, null);
|
||||||
} else {
|
} else {
|
||||||
// updates current and max stack sizes
|
// updates current and max stack sizes
|
||||||
|
@ -741,7 +766,7 @@ class MethodWriter extends MethodVisitor {
|
||||||
lastCodeOffset = code.length;
|
lastCodeOffset = code.length;
|
||||||
// Label currentBlock = this.currentBlock;
|
// Label currentBlock = this.currentBlock;
|
||||||
if (currentBlock != null) {
|
if (currentBlock != null) {
|
||||||
if (compute == FRAMES) {
|
if (compute == FRAMES || compute == INSERTED_FRAMES) {
|
||||||
currentBlock.frame.execute(opcode, operand, null, null);
|
currentBlock.frame.execute(opcode, operand, null, null);
|
||||||
} else if (opcode != Opcodes.NEWARRAY) {
|
} else if (opcode != Opcodes.NEWARRAY) {
|
||||||
// updates current and max stack sizes only for NEWARRAY
|
// updates current and max stack sizes only for NEWARRAY
|
||||||
|
@ -766,7 +791,7 @@ class MethodWriter extends MethodVisitor {
|
||||||
lastCodeOffset = code.length;
|
lastCodeOffset = code.length;
|
||||||
// Label currentBlock = this.currentBlock;
|
// Label currentBlock = this.currentBlock;
|
||||||
if (currentBlock != null) {
|
if (currentBlock != null) {
|
||||||
if (compute == FRAMES) {
|
if (compute == FRAMES || compute == INSERTED_FRAMES) {
|
||||||
currentBlock.frame.execute(opcode, var, null, null);
|
currentBlock.frame.execute(opcode, var, null, null);
|
||||||
} else {
|
} else {
|
||||||
// updates current and max stack sizes
|
// updates current and max stack sizes
|
||||||
|
@ -826,7 +851,7 @@ class MethodWriter extends MethodVisitor {
|
||||||
Item i = cw.newClassItem(type);
|
Item i = cw.newClassItem(type);
|
||||||
// Label currentBlock = this.currentBlock;
|
// Label currentBlock = this.currentBlock;
|
||||||
if (currentBlock != null) {
|
if (currentBlock != null) {
|
||||||
if (compute == FRAMES) {
|
if (compute == FRAMES || compute == INSERTED_FRAMES) {
|
||||||
currentBlock.frame.execute(opcode, code.length, cw, i);
|
currentBlock.frame.execute(opcode, code.length, cw, i);
|
||||||
} else if (opcode == Opcodes.NEW) {
|
} else if (opcode == Opcodes.NEW) {
|
||||||
// updates current and max stack sizes only if opcode == 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);
|
Item i = cw.newFieldItem(owner, name, desc);
|
||||||
// Label currentBlock = this.currentBlock;
|
// Label currentBlock = this.currentBlock;
|
||||||
if (currentBlock != null) {
|
if (currentBlock != null) {
|
||||||
if (compute == FRAMES) {
|
if (compute == FRAMES || compute == INSERTED_FRAMES) {
|
||||||
currentBlock.frame.execute(opcode, 0, cw, i);
|
currentBlock.frame.execute(opcode, 0, cw, i);
|
||||||
} else {
|
} else {
|
||||||
int size;
|
int size;
|
||||||
|
@ -889,7 +914,7 @@ class MethodWriter extends MethodVisitor {
|
||||||
int argSize = i.intVal;
|
int argSize = i.intVal;
|
||||||
// Label currentBlock = this.currentBlock;
|
// Label currentBlock = this.currentBlock;
|
||||||
if (currentBlock != null) {
|
if (currentBlock != null) {
|
||||||
if (compute == FRAMES) {
|
if (compute == FRAMES || compute == INSERTED_FRAMES) {
|
||||||
currentBlock.frame.execute(opcode, 0, cw, i);
|
currentBlock.frame.execute(opcode, 0, cw, i);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
|
@ -941,7 +966,7 @@ class MethodWriter extends MethodVisitor {
|
||||||
int argSize = i.intVal;
|
int argSize = i.intVal;
|
||||||
// Label currentBlock = this.currentBlock;
|
// Label currentBlock = this.currentBlock;
|
||||||
if (currentBlock != null) {
|
if (currentBlock != null) {
|
||||||
if (compute == FRAMES) {
|
if (compute == FRAMES || compute == INSERTED_FRAMES) {
|
||||||
currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i);
|
currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
|
@ -975,7 +1000,9 @@ class MethodWriter extends MethodVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
lastCodeOffset = code.length;
|
||||||
Label nextInsn = null;
|
Label nextInsn = null;
|
||||||
// Label currentBlock = this.currentBlock;
|
// Label currentBlock = this.currentBlock;
|
||||||
|
@ -990,6 +1017,8 @@ class MethodWriter extends MethodVisitor {
|
||||||
// creates a Label for the next basic block
|
// creates a Label for the next basic block
|
||||||
nextInsn = new Label();
|
nextInsn = new Label();
|
||||||
}
|
}
|
||||||
|
} else if (compute == INSERTED_FRAMES) {
|
||||||
|
currentBlock.frame.execute(opcode, 0, null, null);
|
||||||
} else {
|
} else {
|
||||||
if (opcode == Opcodes.JSR) {
|
if (opcode == Opcodes.JSR) {
|
||||||
if ((label.status & Label.SUBROUTINE) == 0) {
|
if ((label.status & Label.SUBROUTINE) == 0) {
|
||||||
|
@ -1041,6 +1070,14 @@ class MethodWriter extends MethodVisitor {
|
||||||
code.putByte(200); // GOTO_W
|
code.putByte(200); // GOTO_W
|
||||||
}
|
}
|
||||||
label.put(this, code, code.length - 1, true);
|
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 {
|
} else {
|
||||||
/*
|
/*
|
||||||
* case of a backward jump with an offset >= -32768, or of a forward
|
* case of a backward jump with an offset >= -32768, or of a forward
|
||||||
|
@ -1068,7 +1105,7 @@ class MethodWriter extends MethodVisitor {
|
||||||
@Override
|
@Override
|
||||||
public void visitLabel(final Label label) {
|
public void visitLabel(final Label label) {
|
||||||
// resolves previous forward references to label, if any
|
// 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
|
// updates currentBlock
|
||||||
if ((label.status & Label.DEBUG) != 0) {
|
if ((label.status & Label.DEBUG) != 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -1101,6 +1138,18 @@ class MethodWriter extends MethodVisitor {
|
||||||
previousBlock.successor = label;
|
previousBlock.successor = label;
|
||||||
}
|
}
|
||||||
previousBlock = 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) {
|
} else if (compute == MAXS) {
|
||||||
if (currentBlock != null) {
|
if (currentBlock != null) {
|
||||||
// ends current block (with one new successor)
|
// ends current block (with one new successor)
|
||||||
|
@ -1126,7 +1175,7 @@ class MethodWriter extends MethodVisitor {
|
||||||
Item i = cw.newConstItem(cst);
|
Item i = cw.newConstItem(cst);
|
||||||
// Label currentBlock = this.currentBlock;
|
// Label currentBlock = this.currentBlock;
|
||||||
if (currentBlock != null) {
|
if (currentBlock != null) {
|
||||||
if (compute == FRAMES) {
|
if (compute == FRAMES || compute == INSERTED_FRAMES) {
|
||||||
currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);
|
currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);
|
||||||
} else {
|
} else {
|
||||||
int size;
|
int size;
|
||||||
|
@ -1158,7 +1207,7 @@ class MethodWriter extends MethodVisitor {
|
||||||
public void visitIincInsn(final int var, final int increment) {
|
public void visitIincInsn(final int var, final int increment) {
|
||||||
lastCodeOffset = code.length;
|
lastCodeOffset = code.length;
|
||||||
if (currentBlock != null) {
|
if (currentBlock != null) {
|
||||||
if (compute == FRAMES) {
|
if (compute == FRAMES || compute == INSERTED_FRAMES) {
|
||||||
currentBlock.frame.execute(Opcodes.IINC, var, null, null);
|
currentBlock.frame.execute(Opcodes.IINC, var, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1245,7 +1294,7 @@ class MethodWriter extends MethodVisitor {
|
||||||
Item i = cw.newClassItem(desc);
|
Item i = cw.newClassItem(desc);
|
||||||
// Label currentBlock = this.currentBlock;
|
// Label currentBlock = this.currentBlock;
|
||||||
if (currentBlock != null) {
|
if (currentBlock != null) {
|
||||||
if (compute == FRAMES) {
|
if (compute == FRAMES || compute == INSERTED_FRAMES) {
|
||||||
currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i);
|
currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i);
|
||||||
} else {
|
} else {
|
||||||
// updates current stack size (max stack size unchanged because
|
// updates current stack size (max stack size unchanged because
|
||||||
|
@ -1401,14 +1450,6 @@ class MethodWriter extends MethodVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitMaxs(final int maxStack, final int maxLocals) {
|
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) {
|
if (ClassReader.FRAMES && compute == FRAMES) {
|
||||||
// completes the control flow graph with exception handler blocks
|
// completes the control flow graph with exception handler blocks
|
||||||
Handler handler = firstHandler;
|
Handler handler = firstHandler;
|
||||||
|
@ -1439,8 +1480,8 @@ class MethodWriter extends MethodVisitor {
|
||||||
|
|
||||||
// creates and visits the first (implicit) frame
|
// creates and visits the first (implicit) frame
|
||||||
Frame f = labels.frame;
|
Frame f = labels.frame;
|
||||||
Type[] args = Type.getArgumentTypes(descriptor);
|
f.initInputFrame(cw, access, Type.getArgumentTypes(descriptor),
|
||||||
f.initInputFrame(cw, access, args, this.maxLocals);
|
this.maxLocals);
|
||||||
visitFrame(f);
|
visitFrame(f);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1688,7 +1729,9 @@ class MethodWriter extends MethodVisitor {
|
||||||
} else {
|
} else {
|
||||||
currentBlock.outputStackMax = maxStackSize;
|
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);
|
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.
|
|
||||||
* <p>
|
|
||||||
* <i>This method must be called after all the method that is being built
|
|
||||||
* has been visited</i>. 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 <l> instruction with
|
|
||||||
// IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx
|
|
||||||
// is the "opposite" opcode of IFxxx (i.e.,
|
|
||||||
// IFNE for IFEQ) and where <l'> 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
|
|
||||||
// <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is
|
|
||||||
// the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
|
|
||||||
// and where <l'> 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.
|
|
||||||
* <p>
|
|
||||||
* Note: it is possible to have several entries for the same instruction in
|
|
||||||
* the <tt>indexes</tt> and <tt>sizes</tt>: 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 <i>last</i>
|
|
||||||
* byte, plus one (or, in other words, by the index of the
|
|
||||||
* <i>first</i> byte of the <i>next</i> instruction).
|
|
||||||
* @param sizes
|
|
||||||
* the number of bytes to be <i>added</i> to the above
|
|
||||||
* instructions. More precisely, for each i < <tt>len</tt>,
|
|
||||||
* <tt>sizes</tt>[i] bytes will be added at the end of the
|
|
||||||
* instruction designated by <tt>indexes</tt>[i] or, if
|
|
||||||
* <tt>sizes</tt>[i] is negative, the <i>last</i> |
|
|
||||||
* <tt>sizes[i]</tt>| bytes of the instruction will be removed
|
|
||||||
* (the instruction size <i>must not</i> 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 <i>last</i>
|
|
||||||
* byte, plus one (or, in other words, by the index of the
|
|
||||||
* <i>first</i> byte of the <i>next</i> instruction).
|
|
||||||
* @param sizes
|
|
||||||
* the number of bytes to be <i>added</i> to the above
|
|
||||||
* instructions. More precisely, for each i < <tt>len</tt>,
|
|
||||||
* <tt>sizes</tt>[i] bytes will be added at the end of the
|
|
||||||
* instruction designated by <tt>indexes</tt>[i] or, if
|
|
||||||
* <tt>sizes</tt>[i] is negative, the <i>last</i> |
|
|
||||||
* <tt>sizes[i]</tt>| bytes of the instruction will be removed
|
|
||||||
* (the instruction size <i>must not</i> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,13 +146,17 @@ public interface Opcodes {
|
||||||
*/
|
*/
|
||||||
int F_SAME1 = 4;
|
int F_SAME1 = 4;
|
||||||
|
|
||||||
Integer TOP = 0;
|
// Do not try to change the following code to use auto-boxing,
|
||||||
Integer INTEGER = 1;
|
// these values are compared by reference and not by value
|
||||||
Integer FLOAT = 2;
|
// The constructor of Integer was deprecated in 9
|
||||||
Integer DOUBLE = 3;
|
// but we are stuck with it by backward compatibility
|
||||||
Integer LONG = 4;
|
@SuppressWarnings("deprecation") Integer TOP = new Integer(0);
|
||||||
Integer NULL = 5;
|
@SuppressWarnings("deprecation") Integer INTEGER = new Integer(1);
|
||||||
Integer UNINITIALIZED_THIS = 6;
|
@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)
|
// opcodes // visit method (- = idem)
|
||||||
|
|
||||||
|
|
|
@ -377,7 +377,16 @@ public class Type {
|
||||||
*/
|
*/
|
||||||
public static Type getReturnType(final String methodDescriptor) {
|
public static Type getReturnType(final String methodDescriptor) {
|
||||||
char[] buf = methodDescriptor.toCharArray();
|
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.
|
* @return the descriptor corresponding to this Java type.
|
||||||
*/
|
*/
|
||||||
public String getDescriptor() {
|
public String getDescriptor() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
getDescriptor(sb);
|
getDescriptor(buf);
|
||||||
return sb.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -643,34 +652,34 @@ public class Type {
|
||||||
*/
|
*/
|
||||||
public static String getMethodDescriptor(final Type returnType,
|
public static String getMethodDescriptor(final Type returnType,
|
||||||
final Type... argumentTypes) {
|
final Type... argumentTypes) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
sb.append('(');
|
buf.append('(');
|
||||||
for (int i = 0; i < argumentTypes.length; ++i) {
|
for (int i = 0; i < argumentTypes.length; ++i) {
|
||||||
argumentTypes[i].getDescriptor(sb);
|
argumentTypes[i].getDescriptor(buf);
|
||||||
}
|
}
|
||||||
sb.append(')');
|
buf.append(')');
|
||||||
returnType.getDescriptor(sb);
|
returnType.getDescriptor(buf);
|
||||||
return sb.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends the descriptor corresponding to this Java type to the given
|
* Appends the descriptor corresponding to this Java type to the given
|
||||||
* string builder.
|
* string buffer.
|
||||||
*
|
*
|
||||||
* @param sb
|
* @param buf
|
||||||
* the string builder to which the descriptor must be appended.
|
* 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) {
|
if (this.buf == null) {
|
||||||
// descriptor is in byte 3 of 'off' for primitive types (buf ==
|
// descriptor is in byte 3 of 'off' for primitive types (buf ==
|
||||||
// null)
|
// null)
|
||||||
sb.append((char) ((off & 0xFF000000) >>> 24));
|
buf.append((char) ((off & 0xFF000000) >>> 24));
|
||||||
} else if (sort == OBJECT) {
|
} else if (sort == OBJECT) {
|
||||||
sb.append('L');
|
buf.append('L');
|
||||||
sb.append(this.buf, off, len);
|
buf.append(this.buf, off, len);
|
||||||
sb.append(';');
|
buf.append(';');
|
||||||
} else { // sort == ARRAY || sort == METHOD
|
} 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.
|
* @return the descriptor corresponding to the given class.
|
||||||
*/
|
*/
|
||||||
public static String getDescriptor(final Class<?> c) {
|
public static String getDescriptor(final Class<?> c) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
getDescriptor(sb, c);
|
getDescriptor(buf, c);
|
||||||
return sb.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -714,12 +723,12 @@ public class Type {
|
||||||
*/
|
*/
|
||||||
public static String getConstructorDescriptor(final Constructor<?> c) {
|
public static String getConstructorDescriptor(final Constructor<?> c) {
|
||||||
Class<?>[] parameters = c.getParameterTypes();
|
Class<?>[] parameters = c.getParameterTypes();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
sb.append('(');
|
buf.append('(');
|
||||||
for (int i = 0; i < parameters.length; ++i) {
|
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) {
|
public static String getMethodDescriptor(final Method m) {
|
||||||
Class<?>[] parameters = m.getParameterTypes();
|
Class<?>[] parameters = m.getParameterTypes();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
sb.append('(');
|
buf.append('(');
|
||||||
for (int i = 0; i < parameters.length; ++i) {
|
for (int i = 0; i < parameters.length; ++i) {
|
||||||
getDescriptor(sb, parameters[i]);
|
getDescriptor(buf, parameters[i]);
|
||||||
}
|
}
|
||||||
sb.append(')');
|
buf.append(')');
|
||||||
getDescriptor(sb, m.getReturnType());
|
getDescriptor(buf, m.getReturnType());
|
||||||
return sb.toString();
|
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.
|
* the string buffer to which the descriptor must be appended.
|
||||||
* @param c
|
* @param c
|
||||||
* the class whose descriptor must be computed.
|
* 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;
|
Class<?> d = c;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (d.isPrimitive()) {
|
if (d.isPrimitive()) {
|
||||||
|
@ -773,20 +782,20 @@ public class Type {
|
||||||
} else /* if (d == Long.TYPE) */{
|
} else /* if (d == Long.TYPE) */{
|
||||||
car = 'J';
|
car = 'J';
|
||||||
}
|
}
|
||||||
sb.append(car);
|
buf.append(car);
|
||||||
return;
|
return;
|
||||||
} else if (d.isArray()) {
|
} else if (d.isArray()) {
|
||||||
sb.append('[');
|
buf.append('[');
|
||||||
d = d.getComponentType();
|
d = d.getComponentType();
|
||||||
} else {
|
} else {
|
||||||
sb.append('L');
|
buf.append('L');
|
||||||
String name = d.getName();
|
String name = d.getName();
|
||||||
int len = name.length();
|
int len = name.length();
|
||||||
for (int i = 0; i < len; ++i) {
|
for (int i = 0; i < len; ++i) {
|
||||||
char car = name.charAt(i);
|
char car = name.charAt(i);
|
||||||
sb.append(car == '.' ? '/' : car);
|
buf.append(car == '.' ? '/' : car);
|
||||||
}
|
}
|
||||||
sb.append(';');
|
buf.append(';');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue