parent
311522bc86
commit
ccabff6ba3
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
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;
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue