Upgrade to ASM 5.2

Issue: SPR-15071
This commit is contained in:
Juergen Hoeller 2016-12-30 11:20:54 +01:00
parent 311522bc86
commit ccabff6ba3
11 changed files with 436 additions and 708 deletions

View File

@ -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

View File

@ -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 <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
* 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
// <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:
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

View File

@ -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;
/**
* <tt>true</tt> 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;
/**
* <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;
/**
* <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;
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. <i>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</i>. 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</i>. 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;

View File

@ -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;
}
}

View File

@ -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 <tt>true</tt> 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;

View File

@ -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:

View File

@ -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.

View File

@ -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: ( <tt>visitParameter</tt> )* [
* <tt>visitAnnotationDefault</tt> ] ( <tt>visitAnnotation</tt> |
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* [
* <tt>visitCode</tt> ( <tt>visitFrame</tt> | <tt>visit<i>X</i>Insn</tt> |
* <tt>visitLabel</tt> | <tt>visitInsnAnnotation</tt> |
* <tt>visitTryCatchBlock</tt> | <tt>visitTryCatchBlockAnnotation</tt> |
* <tt>visitLocalVariable</tt> | <tt>visitLocalVariableAnnotation</tt> |
* <tt>visitLineNumber</tt> )* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In
* addition, the <tt>visit<i>X</i>Insn</tt> and <tt>visitLabel</tt> methods must
* be called in the sequential order of the bytecode instructions of the visited
* code, <tt>visitInsnAnnotation</tt> must be called <i>after</i> the annotated
* <tt>visitParameterAnnotation</tt> <tt>visitTypeAnnotation</tt> |
* <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> |
* <tt>visit<i>X</i>Insn</tt> | <tt>visitLabel</tt> |
* <tt>visitInsnAnnotation</tt> | <tt>visitTryCatchBlock</tt> |
* <tt>visitTryCatchAnnotation</tt> | <tt>visitLocalVariable</tt> |
* <tt>visitLocalVariableAnnotation</tt> | <tt>visitLineNumber</tt> )*
* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In addition, the
* <tt>visit<i>X</i>Insn</tt> and <tt>visitLabel</tt> methods must be called in
* 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
* labels passed as arguments have been visited,
* <tt>visitTryCatchBlockAnnotation</tt> must be called <i>after</i> the

View File

@ -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
* <tt>null</tt>.
* @param computeMaxs
* <tt>true</tt> if the maximum stack size and number of local
* variables must be automatically computed.
* @param computeFrames
* <tt>true</tt> 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.
* <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;
}
}
}

View File

@ -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)

View File

@ -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;
}
}