// 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.objectweb.asm;
/**
* A {@link MethodVisitor} that generates a corresponding 'method_info' structure, as defined in the
* Java Virtual Machine Specification (JVMS).
*
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.6">JVMS
* 4.6</a>
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
final class
MethodWriter extends
MethodVisitor {
/** Indicates that nothing must be computed. */
static final int
COMPUTE_NOTHING = 0;
/**
* Indicates that the maximum stack size and the maximum number of local variables must be
* computed, from scratch.
*/
static final int
COMPUTE_MAX_STACK_AND_LOCAL = 1;
/**
* Indicates that the maximum stack size and the maximum number of local variables must be
* computed, from the existing stack map frames. This can be done more efficiently than with the
* control flow graph algorithm used for {@link #COMPUTE_MAX_STACK_AND_LOCAL}, by using a linear
* scan of the bytecode instructions.
*/
static final int
COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES = 2;
/**
* Indicates that the stack map frames of type F_INSERT must be computed. The other frames are not
* 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).
*/
static final int
COMPUTE_INSERTED_FRAMES = 3;
/**
* Indicates that all the stack map frames must be computed. In this case the maximum stack size
* and the maximum number of local variables is also computed.
*/
static final int
COMPUTE_ALL_FRAMES = 4;
/** Indicates that {@link #STACK_SIZE_DELTA} is not applicable (not constant or never used). */
private static final int
NA = 0;
/**
* The stack size variation corresponding to each JVM opcode. The stack size variation for opcode
* 'o' is given by the array element at index 'o'.
*
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html">JVMS 6</a>
*/
private static final int[]
STACK_SIZE_DELTA = {
0, // nop = 0 (0x0)
1, // aconst_null = 1 (0x1)
1, // iconst_m1 = 2 (0x2)
1, // iconst_0 = 3 (0x3)
1, // iconst_1 = 4 (0x4)
1, // iconst_2 = 5 (0x5)
1, // iconst_3 = 6 (0x6)
1, // iconst_4 = 7 (0x7)
1, // iconst_5 = 8 (0x8)
2, // lconst_0 = 9 (0x9)
2, // lconst_1 = 10 (0xa)
1, // fconst_0 = 11 (0xb)
1, // fconst_1 = 12 (0xc)
1, // fconst_2 = 13 (0xd)
2, // dconst_0 = 14 (0xe)
2, // dconst_1 = 15 (0xf)
1, // bipush = 16 (0x10)
1, // sipush = 17 (0x11)
1, // ldc = 18 (0x12)
NA, // ldc_w = 19 (0x13)
NA, // ldc2_w = 20 (0x14)
1, // iload = 21 (0x15)
2, // lload = 22 (0x16)
1, // fload = 23 (0x17)
2, // dload = 24 (0x18)
1, // aload = 25 (0x19)
NA, // iload_0 = 26 (0x1a)
NA, // iload_1 = 27 (0x1b)
NA, // iload_2 = 28 (0x1c)
NA, // iload_3 = 29 (0x1d)
NA, // lload_0 = 30 (0x1e)
NA, // lload_1 = 31 (0x1f)
NA, // lload_2 = 32 (0x20)
NA, // lload_3 = 33 (0x21)
NA, // fload_0 = 34 (0x22)
NA, // fload_1 = 35 (0x23)
NA, // fload_2 = 36 (0x24)
NA, // fload_3 = 37 (0x25)
NA, // dload_0 = 38 (0x26)
NA, // dload_1 = 39 (0x27)
NA, // dload_2 = 40 (0x28)
NA, // dload_3 = 41 (0x29)
NA, // aload_0 = 42 (0x2a)
NA, // aload_1 = 43 (0x2b)
NA, // aload_2 = 44 (0x2c)
NA, // aload_3 = 45 (0x2d)
-1, // iaload = 46 (0x2e)
0, // laload = 47 (0x2f)
-1, // faload = 48 (0x30)
0, // daload = 49 (0x31)
-1, // aaload = 50 (0x32)
-1, // baload = 51 (0x33)
-1, // caload = 52 (0x34)
-1, // saload = 53 (0x35)
-1, // istore = 54 (0x36)
-2, // lstore = 55 (0x37)
-1, // fstore = 56 (0x38)
-2, // dstore = 57 (0x39)
-1, // astore = 58 (0x3a)
NA, // istore_0 = 59 (0x3b)
NA, // istore_1 = 60 (0x3c)
NA, // istore_2 = 61 (0x3d)
NA, // istore_3 = 62 (0x3e)
NA, // lstore_0 = 63 (0x3f)
NA, // lstore_1 = 64 (0x40)
NA, // lstore_2 = 65 (0x41)
NA, // lstore_3 = 66 (0x42)
NA, // fstore_0 = 67 (0x43)
NA, // fstore_1 = 68 (0x44)
NA, // fstore_2 = 69 (0x45)
NA, // fstore_3 = 70 (0x46)
NA, // dstore_0 = 71 (0x47)
NA, // dstore_1 = 72 (0x48)
NA, // dstore_2 = 73 (0x49)
NA, // dstore_3 = 74 (0x4a)
NA, // astore_0 = 75 (0x4b)
NA, // astore_1 = 76 (0x4c)
NA, // astore_2 = 77 (0x4d)
NA, // astore_3 = 78 (0x4e)
-3, // iastore = 79 (0x4f)
-4, // lastore = 80 (0x50)
-3, // fastore = 81 (0x51)
-4, // dastore = 82 (0x52)
-3, // aastore = 83 (0x53)
-3, // bastore = 84 (0x54)
-3, // castore = 85 (0x55)
-3, // sastore = 86 (0x56)
-1, // pop = 87 (0x57)
-2, // pop2 = 88 (0x58)
1, // dup = 89 (0x59)
1, // dup_x1 = 90 (0x5a)
1, // dup_x2 = 91 (0x5b)
2, // dup2 = 92 (0x5c)
2, // dup2_x1 = 93 (0x5d)
2, // dup2_x2 = 94 (0x5e)
0, // swap = 95 (0x5f)
-1, // iadd = 96 (0x60)
-2, // ladd = 97 (0x61)
-1, // fadd = 98 (0x62)
-2, // dadd = 99 (0x63)
-1, // isub = 100 (0x64)
-2, // lsub = 101 (0x65)
-1, // fsub = 102 (0x66)
-2, // dsub = 103 (0x67)
-1, // imul = 104 (0x68)
-2, // lmul = 105 (0x69)
-1, // fmul = 106 (0x6a)
-2, // dmul = 107 (0x6b)
-1, // idiv = 108 (0x6c)
-2, // ldiv = 109 (0x6d)
-1, // fdiv = 110 (0x6e)
-2, // ddiv = 111 (0x6f)
-1, // irem = 112 (0x70)
-2, // lrem = 113 (0x71)
-1, // frem = 114 (0x72)
-2, // drem = 115 (0x73)
0, // ineg = 116 (0x74)
0, // lneg = 117 (0x75)
0, // fneg = 118 (0x76)
0, // dneg = 119 (0x77)
-1, // ishl = 120 (0x78)
-1, // lshl = 121 (0x79)
-1, // ishr = 122 (0x7a)
-1, // lshr = 123 (0x7b)
-1, // iushr = 124 (0x7c)
-1, // lushr = 125 (0x7d)
-1, // iand = 126 (0x7e)
-2, // land = 127 (0x7f)
-1, // ior = 128 (0x80)
-2, // lor = 129 (0x81)
-1, // ixor = 130 (0x82)
-2, // lxor = 131 (0x83)
0, // iinc = 132 (0x84)
1, // i2l = 133 (0x85)
0, // i2f = 134 (0x86)
1, // i2d = 135 (0x87)
-1, // l2i = 136 (0x88)
-1, // l2f = 137 (0x89)
0, // l2d = 138 (0x8a)
0, // f2i = 139 (0x8b)
1, // f2l = 140 (0x8c)
1, // f2d = 141 (0x8d)
-1, // d2i = 142 (0x8e)
0, // d2l = 143 (0x8f)
-1, // d2f = 144 (0x90)
0, // i2b = 145 (0x91)
0, // i2c = 146 (0x92)
0, // i2s = 147 (0x93)
-3, // lcmp = 148 (0x94)
-1, // fcmpl = 149 (0x95)
-1, // fcmpg = 150 (0x96)
-3, // dcmpl = 151 (0x97)
-3, // dcmpg = 152 (0x98)
-1, // ifeq = 153 (0x99)
-1, // ifne = 154 (0x9a)
-1, // iflt = 155 (0x9b)
-1, // ifge = 156 (0x9c)
-1, // ifgt = 157 (0x9d)
-1, // ifle = 158 (0x9e)
-2, // if_icmpeq = 159 (0x9f)
-2, // if_icmpne = 160 (0xa0)
-2, // if_icmplt = 161 (0xa1)
-2, // if_icmpge = 162 (0xa2)
-2, // if_icmpgt = 163 (0xa3)
-2, // if_icmple = 164 (0xa4)
-2, // if_acmpeq = 165 (0xa5)
-2, // if_acmpne = 166 (0xa6)
0, // goto = 167 (0xa7)
1, // jsr = 168 (0xa8)
0, // ret = 169 (0xa9)
-1, // tableswitch = 170 (0xaa)
-1, // lookupswitch = 171 (0xab)
-1, // ireturn = 172 (0xac)
-2, // lreturn = 173 (0xad)
-1, // freturn = 174 (0xae)
-2, // dreturn = 175 (0xaf)
-1, // areturn = 176 (0xb0)
0, // return = 177 (0xb1)
NA, // getstatic = 178 (0xb2)
NA, // putstatic = 179 (0xb3)
NA, // getfield = 180 (0xb4)
NA, // putfield = 181 (0xb5)
NA, // invokevirtual = 182 (0xb6)
NA, // invokespecial = 183 (0xb7)
NA, // invokestatic = 184 (0xb8)
NA, // invokeinterface = 185 (0xb9)
NA, // invokedynamic = 186 (0xba)
1, // new = 187 (0xbb)
0, // newarray = 188 (0xbc)
0, // anewarray = 189 (0xbd)
0, // arraylength = 190 (0xbe)
NA, // athrow = 191 (0xbf)
0, // checkcast = 192 (0xc0)
0, // instanceof = 193 (0xc1)
-1, // monitorenter = 194 (0xc2)
-1, // monitorexit = 195 (0xc3)
NA, // wide = 196 (0xc4)
NA, // multianewarray = 197 (0xc5)
-1, // ifnull = 198 (0xc6)
-1, // ifnonnull = 199 (0xc7)
NA, // goto_w = 200 (0xc8)
NA // jsr_w = 201 (0xc9)
};
/** Where the constants used in this MethodWriter must be stored. */
private final
SymbolTable symbolTable;
// Note: fields are ordered as in the method_info structure, and those related to attributes are
// ordered as in Section 4.7 of the JVMS.
/**
* The access_flags field of the method_info JVMS structure. This field can contain ASM specific
* access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the
* ClassFile structure.
*/
private final int
accessFlags;
/** The name_index field of the method_info JVMS structure. */
private final int
nameIndex;
/** The name of this method. */
private final
String name;
/** The descriptor_index field of the method_info JVMS structure. */
private final int
descriptorIndex;
/** The descriptor of this method. */
private final
String descriptor;
// Code attribute fields and sub attributes:
/** The max_stack field of the Code attribute. */
private int
maxStack;
/** The max_locals field of the Code attribute. */
private int
maxLocals;
/** The 'code' field of the Code attribute. */
private final
ByteVector code = new
ByteVector();
/**
* The first element in the exception handler list (used to generate the exception_table of the
* Code attribute). The next ones can be accessed with the {@link Handler#nextHandler} field. May
* be {@literal null}.
*/
private
Handler firstHandler;
/**
* The last element in the exception handler list (used to generate the exception_table of the
* Code attribute). The next ones can be accessed with the {@link Handler#nextHandler} field. May
* be {@literal null}.
*/
private
Handler lastHandler;
/** The line_number_table_length field of the LineNumberTable code attribute. */
private int
lineNumberTableLength;
/** The line_number_table array of the LineNumberTable code attribute, or {@literal null}. */
private
ByteVector lineNumberTable;
/** The local_variable_table_length field of the LocalVariableTable code attribute. */
private int
localVariableTableLength;
/**
* The local_variable_table array of the LocalVariableTable code attribute, or {@literal null}.
*/
private
ByteVector localVariableTable;
/** The local_variable_type_table_length field of the LocalVariableTypeTable code attribute. */
private int
localVariableTypeTableLength;
/**
* The local_variable_type_table array of the LocalVariableTypeTable code attribute, or {@literal
* null}.
*/
private
ByteVector localVariableTypeTable;
/** The number_of_entries field of the StackMapTable code attribute. */
private int
stackMapTableNumberOfEntries;
/** The 'entries' array of the StackMapTable code attribute. */
private
ByteVector stackMapTableEntries;
/**
* The last runtime visible type annotation of the Code attribute. The previous ones can be
* accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
*/
private
AnnotationWriter lastCodeRuntimeVisibleTypeAnnotation;
/**
* The last runtime invisible type annotation of the Code attribute. The previous ones can be
* accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
*/
private
AnnotationWriter lastCodeRuntimeInvisibleTypeAnnotation;
/**
* The first non standard attribute of the Code attribute. The next ones can be accessed with the
* {@link Attribute#nextAttribute} field. May be {@literal null}.
*
* <p><b>WARNING</b>: this list stores the attributes in the <i>reverse</i> order of their visit.
* firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link
* #putMethodInfo} method writes the attributes in the order defined by this list, i.e. in the
* reverse order specified by the user.
*/
private
Attribute firstCodeAttribute;
// Other method_info attributes:
/** The number_of_exceptions field of the Exceptions attribute. */
private final int
numberOfExceptions;
/** The exception_index_table array of the Exceptions attribute, or {@literal null}. */
private final int[]
exceptionIndexTable;
/** The signature_index field of the Signature attribute. */
private final int
signatureIndex;
/**
* The last runtime visible annotation of this method. The previous ones can be accessed with the
* {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
*/
private
AnnotationWriter lastRuntimeVisibleAnnotation;
/**
* The last runtime invisible annotation of this method. The previous ones can be accessed with
* the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
*/
private
AnnotationWriter lastRuntimeInvisibleAnnotation;
/** The number of method parameters that can have runtime visible annotations, or 0. */
private int
visibleAnnotableParameterCount;
/**
* The runtime visible parameter annotations of this method. Each array element contains the last
* annotation of a parameter (which can be {@literal null} - the previous ones can be accessed
* with the {@link AnnotationWriter#previousAnnotation} field). May be {@literal null}.
*/
private
AnnotationWriter[]
lastRuntimeVisibleParameterAnnotations;
/** The number of method parameters that can have runtime visible annotations, or 0. */
private int
invisibleAnnotableParameterCount;
/**
* The runtime invisible parameter annotations of this method. Each array element contains the
* last annotation of a parameter (which can be {@literal null} - the previous ones can be
* accessed with the {@link AnnotationWriter#previousAnnotation} field). May be {@literal null}.
*/
private
AnnotationWriter[]
lastRuntimeInvisibleParameterAnnotations;
/**
* The last runtime visible type annotation of this method. The previous ones can be accessed with
* the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
*/
private
AnnotationWriter lastRuntimeVisibleTypeAnnotation;
/**
* The last runtime invisible type annotation of this method. The previous ones can be accessed
* with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
*/
private
AnnotationWriter lastRuntimeInvisibleTypeAnnotation;
/** The default_value field of the AnnotationDefault attribute, or {@literal null}. */
private
ByteVector defaultValue;
/** The parameters_count field of the MethodParameters attribute. */
private int
parametersCount;
/** The 'parameters' array of the MethodParameters attribute, or {@literal null}. */
private
ByteVector parameters;
/**
* The first non standard attribute of this method. The next ones can be accessed with the {@link
* Attribute#nextAttribute} field. May be {@literal null}.
*
* <p><b>WARNING</b>: this list stores the attributes in the <i>reverse</i> order of their visit.
* firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link
* #putMethodInfo} method writes the attributes in the order defined by this list, i.e. in the
* reverse order specified by the user.
*/
private
Attribute firstAttribute;
// -----------------------------------------------------------------------------------------------
// Fields used to compute the maximum stack size and number of locals, and the stack map frames
// -----------------------------------------------------------------------------------------------
/**
* Indicates what must be computed. Must be one of {@link #COMPUTE_ALL_FRAMES}, {@link
* #COMPUTE_INSERTED_FRAMES}, {@link #COMPUTE_MAX_STACK_AND_LOCAL} or {@link #COMPUTE_NOTHING}.
*/
private final int
compute;
/**
* The first basic block of the method. The next ones (in bytecode offset order) can be accessed
* with the {@link Label#nextBasicBlock} field.
*/
private
Label firstBasicBlock;
/**
* The last basic block of the method (in bytecode offset order). This field is updated each time
* a basic block is encountered, and is used to append it at the end of the basic block list.
*/
private
Label lastBasicBlock;
/**
* The current basic block, i.e. the basic block of the last visited instruction. When {@link
* #compute} is equal to {@link #COMPUTE_MAX_STACK_AND_LOCAL} or {@link #COMPUTE_ALL_FRAMES}, this
* field is {@literal null} for unreachable code. When {@link #compute} is equal to {@link
* #COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES} or {@link #COMPUTE_INSERTED_FRAMES}, this field stays
* unchanged throughout the whole method (i.e. the whole code is seen as a single basic block;
* indeed, the existing frames are sufficient by hypothesis to compute any intermediate frame -
* and the maximum stack size as well - without using any control flow graph).
*/
private
Label currentBasicBlock;
/**
* The relative stack size after the last visited instruction. This size is relative to the
* beginning of {@link #currentBasicBlock}, i.e. the true stack size after the last visited
* instruction is equal to the {@link Label#inputStackSize} of the current basic block plus {@link
* #relativeStackSize}. When {@link #compute} is equal to {@link
* #COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES}, {@link #currentBasicBlock} is always the start of
* the method, so this relative size is also equal to the absolute stack size after the last
* visited instruction.
*/
private int
relativeStackSize;
/**
* The maximum relative stack size after the last visited instruction. This size is relative to
* the beginning of {@link #currentBasicBlock}, i.e. the true maximum stack size after the last
* visited instruction is equal to the {@link Label#inputStackSize} of the current basic block
* plus {@link #maxRelativeStackSize}.When {@link #compute} is equal to {@link
* #COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES}, {@link #currentBasicBlock} is always the start of
* the method, so this relative size is also equal to the absolute maximum stack size after the
* last visited instruction.
*/
private int
maxRelativeStackSize;
/** The number of local variables in the last visited stack map frame. */
private int
currentLocals;
/** The bytecode offset of the last frame that was written in {@link #stackMapTableEntries}. */
private int
previousFrameOffset;
/**
* The last frame that was written in {@link #stackMapTableEntries}. This field has the same
* format as {@link #currentFrame}.
*/
private int[]
previousFrame;
/**
* The current stack map frame. The first element contains the bytecode offset of the instruction
* to which the frame corresponds, the second element is the number of locals and the third one is
* the number of stack elements. The local variables start at index 3 and are followed by the
* operand stack elements. In summary frame[0] = offset, frame[1] = numLocal, frame[2] = numStack.
* Local variables and operand stack entries contain abstract types, as defined in {@link Frame},
* but restricted to {@link Frame#CONSTANT_KIND}, {@link Frame#REFERENCE_KIND} or {@link
* Frame#UNINITIALIZED_KIND} abstract types. Long and double types use only one array entry.
*/
private int[]
currentFrame;
/** Whether this method contains subroutines. */
private boolean
hasSubroutines;
// -----------------------------------------------------------------------------------------------
// Other miscellaneous status fields
// -----------------------------------------------------------------------------------------------
/** Whether the bytecode of this method contains ASM specific instructions. */
private boolean
hasAsmInstructions;
/**
* The start offset of the last visited instruction. Used to set the offset field of type
* annotations of type 'offset_target' (see <a
* href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.1">JVMS
* 4.7.20.1</a>).
*/
private int
lastBytecodeOffset;
/**
* The offset in bytes in {@link SymbolTable#getSource} from which the method_info for this method
* (excluding its first 6 bytes) must be copied, or 0.
*/
private int
sourceOffset;
/**
* The length in bytes in {@link SymbolTable#getSource} which must be copied to get the
* method_info for this method (excluding its first 6 bytes for access_flags, name_index and
* descriptor_index).
*/
private int
sourceLength;
// -----------------------------------------------------------------------------------------------
// Constructor and accessors
// -----------------------------------------------------------------------------------------------
/**
* Constructs a new {@link MethodWriter}.
*
* @param symbolTable where the constants used in this AnnotationWriter must be stored.
* @param access the method's access flags (see {@link Opcodes}).
* @param name the method's name.
* @param descriptor the method's descriptor (see {@link Type}).
* @param signature the method's signature. May be {@literal null}.
* @param exceptions the internal names of the method's exceptions. May be {@literal null}.
* @param compute indicates what must be computed (see #compute).
*/
MethodWriter(
final
SymbolTable symbolTable,
final int
access,
final
String name,
final
String descriptor,
final
String signature,
final
String[]
exceptions,
final int
compute) {
super(
Opcodes.
ASM7);
this.
symbolTable =
symbolTable;
this.
accessFlags = "<init>".
equals(
name) ?
access |
Constants.
ACC_CONSTRUCTOR :
access;
this.
nameIndex =
symbolTable.
addConstantUtf8(
name);
this.
name =
name;
this.
descriptorIndex =
symbolTable.
addConstantUtf8(
descriptor);
this.
descriptor =
descriptor;
this.
signatureIndex =
signature == null ? 0 :
symbolTable.
addConstantUtf8(
signature);
if (
exceptions != null &&
exceptions.length > 0) {
numberOfExceptions =
exceptions.length;
this.
exceptionIndexTable = new int[
numberOfExceptions];
for (int
i = 0;
i <
numberOfExceptions; ++
i) {
this.
exceptionIndexTable[
i] =
symbolTable.
addConstantClass(
exceptions[
i]).
index;
}
} else {
numberOfExceptions = 0;
this.
exceptionIndexTable = null;
}
this.
compute =
compute;
if (
compute !=
COMPUTE_NOTHING) {
// Update maxLocals and currentLocals.
int
argumentsSize =
Type.
getArgumentsAndReturnSizes(
descriptor) >> 2;
if ((
access &
Opcodes.
ACC_STATIC) != 0) {
--
argumentsSize;
}
maxLocals =
argumentsSize;
currentLocals =
argumentsSize;
// Create and visit the label for the first basic block.
firstBasicBlock = new
Label();
visitLabel(
firstBasicBlock);
}
}
boolean
hasFrames() {
return
stackMapTableNumberOfEntries > 0;
}
boolean
hasAsmInstructions() {
return
hasAsmInstructions;
}
// -----------------------------------------------------------------------------------------------
// Implementation of the MethodVisitor abstract class
// -----------------------------------------------------------------------------------------------
@
Override
public void
visitParameter(final
String name, final int
access) {
if (
parameters == null) {
parameters = new
ByteVector();
}
++
parametersCount;
parameters.
putShort((
name == null) ? 0 :
symbolTable.
addConstantUtf8(
name)).
putShort(
access);
}
@
Override
public
AnnotationVisitor visitAnnotationDefault() {
defaultValue = new
ByteVector();
return new
AnnotationWriter(
symbolTable, /* useNamedValues = */ false,
defaultValue, null);
}
@
Override
public
AnnotationVisitor visitAnnotation(final
String descriptor, final boolean
visible) {
// Create a ByteVector to hold an 'annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
ByteVector annotation = new
ByteVector();
// Write type_index and reserve space for num_element_value_pairs.
annotation.
putShort(
symbolTable.
addConstantUtf8(
descriptor)).
putShort(0);
if (
visible) {
return
lastRuntimeVisibleAnnotation =
new
AnnotationWriter(
symbolTable,
annotation,
lastRuntimeVisibleAnnotation);
} else {
return
lastRuntimeInvisibleAnnotation =
new
AnnotationWriter(
symbolTable,
annotation,
lastRuntimeInvisibleAnnotation);
}
}
@
Override
public
AnnotationVisitor visitTypeAnnotation(
final int
typeRef, final
TypePath typePath, final
String descriptor, final boolean
visible) {
// Create a ByteVector to hold a 'type_annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
ByteVector typeAnnotation = new
ByteVector();
// Write target_type, target_info, and target_path.
TypeReference.
putTarget(
typeRef,
typeAnnotation);
TypePath.
put(
typePath,
typeAnnotation);
// Write type_index and reserve space for num_element_value_pairs.
typeAnnotation.
putShort(
symbolTable.
addConstantUtf8(
descriptor)).
putShort(0);
if (
visible) {
return
lastRuntimeVisibleTypeAnnotation =
new
AnnotationWriter(
symbolTable,
typeAnnotation,
lastRuntimeVisibleTypeAnnotation);
} else {
return
lastRuntimeInvisibleTypeAnnotation =
new
AnnotationWriter(
symbolTable,
typeAnnotation,
lastRuntimeInvisibleTypeAnnotation);
}
}
@
Override
public void
visitAnnotableParameterCount(final int
parameterCount, final boolean
visible) {
if (
visible) {
visibleAnnotableParameterCount =
parameterCount;
} else {
invisibleAnnotableParameterCount =
parameterCount;
}
}
@
Override
public
AnnotationVisitor visitParameterAnnotation(
final int
parameter, final
String annotationDescriptor, final boolean
visible) {
// Create a ByteVector to hold an 'annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
ByteVector annotation = new
ByteVector();
// Write type_index and reserve space for num_element_value_pairs.
annotation.
putShort(
symbolTable.
addConstantUtf8(
annotationDescriptor)).
putShort(0);
if (
visible) {
if (
lastRuntimeVisibleParameterAnnotations == null) {
lastRuntimeVisibleParameterAnnotations =
new
AnnotationWriter[
Type.
getArgumentTypes(
descriptor).length];
}
return
lastRuntimeVisibleParameterAnnotations[
parameter] =
new
AnnotationWriter(
symbolTable,
annotation,
lastRuntimeVisibleParameterAnnotations[
parameter]);
} else {
if (
lastRuntimeInvisibleParameterAnnotations == null) {
lastRuntimeInvisibleParameterAnnotations =
new
AnnotationWriter[
Type.
getArgumentTypes(
descriptor).length];
}
return
lastRuntimeInvisibleParameterAnnotations[
parameter] =
new
AnnotationWriter(
symbolTable,
annotation,
lastRuntimeInvisibleParameterAnnotations[
parameter]);
}
}
@
Override
public void
visitAttribute(final
Attribute attribute) {
// Store the attributes in the <i>reverse</i> order of their visit by this method.
if (
attribute.
isCodeAttribute()) {
attribute.
nextAttribute =
firstCodeAttribute;
firstCodeAttribute =
attribute;
} else {
attribute.
nextAttribute =
firstAttribute;
firstAttribute =
attribute;
}
}
@
Override
public void
visitCode() {
// Nothing to do.
}
@
Override
public void
visitFrame(
final int
type,
final int
numLocal,
final
Object[]
local,
final int
numStack,
final
Object[]
stack) {
if (
compute ==
COMPUTE_ALL_FRAMES) {
return;
}
if (
compute ==
COMPUTE_INSERTED_FRAMES) {
if (
currentBasicBlock.
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 - and COMPUTE_INSERTED_FRAMES
// can't be set if EXPAND_ASM_INSNS is not used).
currentBasicBlock.
frame = new
CurrentFrame(
currentBasicBlock);
currentBasicBlock.
frame.
setInputFrameFromDescriptor(
symbolTable,
accessFlags,
descriptor,
numLocal);
currentBasicBlock.
frame.
accept(this);
} else {
if (
type ==
Opcodes.
F_NEW) {
currentBasicBlock.
frame.
setInputFrameFromApiFormat(
symbolTable,
numLocal,
local,
numStack,
stack);
}
// If type is not F_NEW then it is 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).
currentBasicBlock.
frame.
accept(this);
}
} else if (
type ==
Opcodes.
F_NEW) {
if (
previousFrame == null) {
int
argumentsSize =
Type.
getArgumentsAndReturnSizes(
descriptor) >> 2;
Frame implicitFirstFrame = new
Frame(new
Label());
implicitFirstFrame.
setInputFrameFromDescriptor(
symbolTable,
accessFlags,
descriptor,
argumentsSize);
implicitFirstFrame.
accept(this);
}
currentLocals =
numLocal;
int
frameIndex =
visitFrameStart(
code.
length,
numLocal,
numStack);
for (int
i = 0;
i <
numLocal; ++
i) {
currentFrame[
frameIndex++] =
Frame.
getAbstractTypeFromApiFormat(
symbolTable,
local[
i]);
}
for (int
i = 0;
i <
numStack; ++
i) {
currentFrame[
frameIndex++] =
Frame.
getAbstractTypeFromApiFormat(
symbolTable,
stack[
i]);
}
visitFrameEnd();
} else {
int
offsetDelta;
if (
stackMapTableEntries == null) {
stackMapTableEntries = new
ByteVector();
offsetDelta =
code.
length;
} else {
offsetDelta =
code.
length -
previousFrameOffset - 1;
if (
offsetDelta < 0) {
if (
type ==
Opcodes.
F_SAME) {
return;
} else {
throw new
IllegalStateException();
}
}
}
switch (
type) {
case
Opcodes.
F_FULL:
currentLocals =
numLocal;
stackMapTableEntries.
putByte(
Frame.
FULL_FRAME).
putShort(
offsetDelta).
putShort(
numLocal);
for (int
i = 0;
i <
numLocal; ++
i) {
putFrameType(
local[
i]);
}
stackMapTableEntries.
putShort(
numStack);
for (int
i = 0;
i <
numStack; ++
i) {
putFrameType(
stack[
i]);
}
break;
case
Opcodes.
F_APPEND:
currentLocals +=
numLocal;
stackMapTableEntries.
putByte(
Frame.
SAME_FRAME_EXTENDED +
numLocal).
putShort(
offsetDelta);
for (int
i = 0;
i <
numLocal; ++
i) {
putFrameType(
local[
i]);
}
break;
case
Opcodes.
F_CHOP:
currentLocals -=
numLocal;
stackMapTableEntries.
putByte(
Frame.
SAME_FRAME_EXTENDED -
numLocal).
putShort(
offsetDelta);
break;
case
Opcodes.
F_SAME:
if (
offsetDelta < 64) {
stackMapTableEntries.
putByte(
offsetDelta);
} else {
stackMapTableEntries.
putByte(
Frame.
SAME_FRAME_EXTENDED).
putShort(
offsetDelta);
}
break;
case
Opcodes.
F_SAME1:
if (
offsetDelta < 64) {
stackMapTableEntries.
putByte(
Frame.
SAME_LOCALS_1_STACK_ITEM_FRAME +
offsetDelta);
} else {
stackMapTableEntries
.
putByte(
Frame.
SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
.
putShort(
offsetDelta);
}
putFrameType(
stack[0]);
break;
default:
throw new
IllegalArgumentException();
}
previousFrameOffset =
code.
length;
++
stackMapTableNumberOfEntries;
}
if (
compute ==
COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) {
relativeStackSize =
numStack;
for (int
i = 0;
i <
numStack; ++
i) {
if (
stack[
i] ==
Opcodes.
LONG ||
stack[
i] ==
Opcodes.
DOUBLE) {
relativeStackSize++;
}
}
if (
relativeStackSize >
maxRelativeStackSize) {
maxRelativeStackSize =
relativeStackSize;
}
}
maxStack =
Math.
max(
maxStack,
numStack);
maxLocals =
Math.
max(
maxLocals,
currentLocals);
}
@
Override
public void
visitInsn(final int
opcode) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
code.
putByte(
opcode);
// If needed, update the maximum stack size and number of locals, and stack map frames.
if (
currentBasicBlock != null) {
if (
compute ==
COMPUTE_ALL_FRAMES ||
compute ==
COMPUTE_INSERTED_FRAMES) {
currentBasicBlock.
frame.
execute(
opcode, 0, null, null);
} else {
int
size =
relativeStackSize +
STACK_SIZE_DELTA[
opcode];
if (
size >
maxRelativeStackSize) {
maxRelativeStackSize =
size;
}
relativeStackSize =
size;
}
if ((
opcode >=
Opcodes.
IRETURN &&
opcode <=
Opcodes.
RETURN) ||
opcode ==
Opcodes.
ATHROW) {
endCurrentBasicBlockWithNoSuccessor();
}
}
}
@
Override
public void
visitIntInsn(final int
opcode, final int
operand) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
if (
opcode ==
Opcodes.
SIPUSH) {
code.
put12(
opcode,
operand);
} else { // BIPUSH or NEWARRAY
code.
put11(
opcode,
operand);
}
// If needed, update the maximum stack size and number of locals, and stack map frames.
if (
currentBasicBlock != null) {
if (
compute ==
COMPUTE_ALL_FRAMES ||
compute ==
COMPUTE_INSERTED_FRAMES) {
currentBasicBlock.
frame.
execute(
opcode,
operand, null, null);
} else if (
opcode !=
Opcodes.
NEWARRAY) {
// The stack size delta is 1 for BIPUSH or SIPUSH, and 0 for NEWARRAY.
int
size =
relativeStackSize + 1;
if (
size >
maxRelativeStackSize) {
maxRelativeStackSize =
size;
}
relativeStackSize =
size;
}
}
}
@
Override
public void
visitVarInsn(final int
opcode, final int
var) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
if (
var < 4 &&
opcode !=
Opcodes.
RET) {
int
optimizedOpcode;
if (
opcode <
Opcodes.
ISTORE) {
optimizedOpcode =
Constants.
ILOAD_0 + ((
opcode -
Opcodes.
ILOAD) << 2) +
var;
} else {
optimizedOpcode =
Constants.
ISTORE_0 + ((
opcode -
Opcodes.
ISTORE) << 2) +
var;
}
code.
putByte(
optimizedOpcode);
} else if (
var >= 256) {
code.
putByte(
Constants.
WIDE).
put12(
opcode,
var);
} else {
code.
put11(
opcode,
var);
}
// If needed, update the maximum stack size and number of locals, and stack map frames.
if (
currentBasicBlock != null) {
if (
compute ==
COMPUTE_ALL_FRAMES ||
compute ==
COMPUTE_INSERTED_FRAMES) {
currentBasicBlock.
frame.
execute(
opcode,
var, null, null);
} else {
if (
opcode ==
Opcodes.
RET) {
// No stack size delta.
currentBasicBlock.
flags |=
Label.
FLAG_SUBROUTINE_END;
currentBasicBlock.
outputStackSize = (short)
relativeStackSize;
endCurrentBasicBlockWithNoSuccessor();
} else { // xLOAD or xSTORE
int
size =
relativeStackSize +
STACK_SIZE_DELTA[
opcode];
if (
size >
maxRelativeStackSize) {
maxRelativeStackSize =
size;
}
relativeStackSize =
size;
}
}
}
if (
compute !=
COMPUTE_NOTHING) {
int
currentMaxLocals;
if (
opcode ==
Opcodes.
LLOAD
||
opcode ==
Opcodes.
DLOAD
||
opcode ==
Opcodes.
LSTORE
||
opcode ==
Opcodes.
DSTORE) {
currentMaxLocals =
var + 2;
} else {
currentMaxLocals =
var + 1;
}
if (
currentMaxLocals >
maxLocals) {
maxLocals =
currentMaxLocals;
}
}
if (
opcode >=
Opcodes.
ISTORE &&
compute ==
COMPUTE_ALL_FRAMES &&
firstHandler != null) {
// If there are exception handler blocks, each instruction within a handler range is, in
// theory, a basic block (since execution can jump from this instruction to the exception
// handler). As a consequence, the local variable types at the beginning of the handler
// block should be the merge of the local variable types at all the instructions within the
// handler range. However, instead of creating a basic block for each instruction, we can
// get the same result in a more efficient way. Namely, by starting a new basic block after
// each xSTORE instruction, which is what we do here.
visitLabel(new
Label());
}
}
@
Override
public void
visitTypeInsn(final int
opcode, final
String type) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
Symbol typeSymbol =
symbolTable.
addConstantClass(
type);
code.
put12(
opcode,
typeSymbol.
index);
// If needed, update the maximum stack size and number of locals, and stack map frames.
if (
currentBasicBlock != null) {
if (
compute ==
COMPUTE_ALL_FRAMES ||
compute ==
COMPUTE_INSERTED_FRAMES) {
currentBasicBlock.
frame.
execute(
opcode,
lastBytecodeOffset,
typeSymbol,
symbolTable);
} else if (
opcode ==
Opcodes.
NEW) {
// The stack size delta is 1 for NEW, and 0 for ANEWARRAY, CHECKCAST, or INSTANCEOF.
int
size =
relativeStackSize + 1;
if (
size >
maxRelativeStackSize) {
maxRelativeStackSize =
size;
}
relativeStackSize =
size;
}
}
}
@
Override
public void
visitFieldInsn(
final int
opcode, final
String owner, final
String name, final
String descriptor) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
Symbol fieldrefSymbol =
symbolTable.
addConstantFieldref(
owner,
name,
descriptor);
code.
put12(
opcode,
fieldrefSymbol.
index);
// If needed, update the maximum stack size and number of locals, and stack map frames.
if (
currentBasicBlock != null) {
if (
compute ==
COMPUTE_ALL_FRAMES ||
compute ==
COMPUTE_INSERTED_FRAMES) {
currentBasicBlock.
frame.
execute(
opcode, 0,
fieldrefSymbol,
symbolTable);
} else {
int
size;
char
firstDescChar =
descriptor.
charAt(0);
switch (
opcode) {
case
Opcodes.
GETSTATIC:
size =
relativeStackSize + (
firstDescChar == 'D' ||
firstDescChar == 'J' ? 2 : 1);
break;
case
Opcodes.
PUTSTATIC:
size =
relativeStackSize + (
firstDescChar == 'D' ||
firstDescChar == 'J' ? -2 : -1);
break;
case
Opcodes.
GETFIELD:
size =
relativeStackSize + (
firstDescChar == 'D' ||
firstDescChar == 'J' ? 1 : 0);
break;
case
Opcodes.
PUTFIELD:
default:
size =
relativeStackSize + (
firstDescChar == 'D' ||
firstDescChar == 'J' ? -3 : -2);
break;
}
if (
size >
maxRelativeStackSize) {
maxRelativeStackSize =
size;
}
relativeStackSize =
size;
}
}
}
@
Override
public void
visitMethodInsn(
final int
opcode,
final
String owner,
final
String name,
final
String descriptor,
final boolean
isInterface) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
Symbol methodrefSymbol =
symbolTable.
addConstantMethodref(
owner,
name,
descriptor,
isInterface);
if (
opcode ==
Opcodes.
INVOKEINTERFACE) {
code.
put12(
Opcodes.
INVOKEINTERFACE,
methodrefSymbol.
index)
.
put11(
methodrefSymbol.
getArgumentsAndReturnSizes() >> 2, 0);
} else {
code.
put12(
opcode,
methodrefSymbol.
index);
}
// If needed, update the maximum stack size and number of locals, and stack map frames.
if (
currentBasicBlock != null) {
if (
compute ==
COMPUTE_ALL_FRAMES ||
compute ==
COMPUTE_INSERTED_FRAMES) {
currentBasicBlock.
frame.
execute(
opcode, 0,
methodrefSymbol,
symbolTable);
} else {
int
argumentsAndReturnSize =
methodrefSymbol.
getArgumentsAndReturnSizes();
int
stackSizeDelta = (
argumentsAndReturnSize & 3) - (
argumentsAndReturnSize >> 2);
int
size;
if (
opcode ==
Opcodes.
INVOKESTATIC) {
size =
relativeStackSize +
stackSizeDelta + 1;
} else {
size =
relativeStackSize +
stackSizeDelta;
}
if (
size >
maxRelativeStackSize) {
maxRelativeStackSize =
size;
}
relativeStackSize =
size;
}
}
}
@
Override
public void
visitInvokeDynamicInsn(
final
String name,
final
String descriptor,
final
Handle bootstrapMethodHandle,
final
Object...
bootstrapMethodArguments) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
Symbol invokeDynamicSymbol =
symbolTable.
addConstantInvokeDynamic(
name,
descriptor,
bootstrapMethodHandle,
bootstrapMethodArguments);
code.
put12(
Opcodes.
INVOKEDYNAMIC,
invokeDynamicSymbol.
index);
code.
putShort(0);
// If needed, update the maximum stack size and number of locals, and stack map frames.
if (
currentBasicBlock != null) {
if (
compute ==
COMPUTE_ALL_FRAMES ||
compute ==
COMPUTE_INSERTED_FRAMES) {
currentBasicBlock.
frame.
execute(
Opcodes.
INVOKEDYNAMIC, 0,
invokeDynamicSymbol,
symbolTable);
} else {
int
argumentsAndReturnSize =
invokeDynamicSymbol.
getArgumentsAndReturnSizes();
int
stackSizeDelta = (
argumentsAndReturnSize & 3) - (
argumentsAndReturnSize >> 2) + 1;
int
size =
relativeStackSize +
stackSizeDelta;
if (
size >
maxRelativeStackSize) {
maxRelativeStackSize =
size;
}
relativeStackSize =
size;
}
}
}
@
Override
public void
visitJumpInsn(final int
opcode, final
Label label) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
// Compute the 'base' opcode, i.e. GOTO or JSR if opcode is GOTO_W or JSR_W, otherwise opcode.
int
baseOpcode =
opcode >=
Constants.
GOTO_W ?
opcode -
Constants.
WIDE_JUMP_OPCODE_DELTA :
opcode;
boolean
nextInsnIsJumpTarget = false;
if ((
label.
flags &
Label.
FLAG_RESOLVED) != 0
&&
label.
bytecodeOffset -
code.
length <
Short.
MIN_VALUE) {
// Case of a backward jump with an offset < -32768. In this case we automatically replace GOTO
// with GOTO_W, JSR with JSR_W and IFxxx <l> with IFNOTxxx <L> GOTO_W <l> L:..., where
// IFNOTxxx is the "opposite" opcode of IFxxx (e.g. IFNE for IFEQ) and where <L> designates
// the instruction just after the GOTO_W.
if (
baseOpcode ==
Opcodes.
GOTO) {
code.
putByte(
Constants.
GOTO_W);
} else if (
baseOpcode ==
Opcodes.
JSR) {
code.
putByte(
Constants.
JSR_W);
} else {
// Put the "opposite" opcode of baseOpcode. This can be done by flipping the least
// significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ (with a
// pre and post offset by 1). The jump offset is 8 bytes (3 for IFNOTxxx, 5 for GOTO_W).
code.
putByte(
baseOpcode >=
Opcodes.
IFNULL ?
baseOpcode ^ 1 : ((
baseOpcode + 1) ^ 1) - 1);
code.
putShort(8);
// Here we could put a GOTO_W in theory, but if ASM specific instructions are used in this
// method or another one, and if the class has frames, we will need to insert a frame after
// this GOTO_W during the additional ClassReader -> ClassWriter round trip to remove the ASM
// specific instructions. To not miss this additional frame, we need to use an ASM_GOTO_W
// here, which has the unfortunate effect of forcing this additional round trip (which in
// some case would not have been really necessary, but we can't know this at this point).
code.
putByte(
Constants.
ASM_GOTO_W);
hasAsmInstructions = true;
// The instruction after the GOTO_W becomes the target of the IFNOT instruction.
nextInsnIsJumpTarget = true;
}
label.
put(
code,
code.
length - 1, true);
} else if (
baseOpcode !=
opcode) {
// Case of a GOTO_W or JSR_W specified by the user (normally ClassReader when used to remove
// ASM specific instructions). In this case we keep the original instruction.
code.
putByte(
opcode);
label.
put(
code,
code.
length - 1, true);
} else {
// Case of a jump with an offset >= -32768, or of a jump with an unknown offset. In these
// cases we store the offset in 2 bytes (which will be increased via a ClassReader ->
// ClassWriter round trip if it turns out that 2 bytes are not sufficient).
code.
putByte(
baseOpcode);
label.
put(
code,
code.
length - 1, false);
}
// If needed, update the maximum stack size and number of locals, and stack map frames.
if (
currentBasicBlock != null) {
Label nextBasicBlock = null;
if (
compute ==
COMPUTE_ALL_FRAMES) {
currentBasicBlock.
frame.
execute(
baseOpcode, 0, null, null);
// Record the fact that 'label' is the target of a jump instruction.
label.
getCanonicalInstance().
flags |=
Label.
FLAG_JUMP_TARGET;
// Add 'label' as a successor of the current basic block.
addSuccessorToCurrentBasicBlock(
Edge.
JUMP,
label);
if (
baseOpcode !=
Opcodes.
GOTO) {
// The next instruction starts a new basic block (except for GOTO: by default the code
// following a goto is unreachable - unless there is an explicit label for it - and we
// should not compute stack frame types for its instructions).
nextBasicBlock = new
Label();
}
} else if (
compute ==
COMPUTE_INSERTED_FRAMES) {
currentBasicBlock.
frame.
execute(
baseOpcode, 0, null, null);
} else if (
compute ==
COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) {
// No need to update maxRelativeStackSize (the stack size delta is always negative).
relativeStackSize +=
STACK_SIZE_DELTA[
baseOpcode];
} else {
if (
baseOpcode ==
Opcodes.
JSR) {
// Record the fact that 'label' designates a subroutine, if not already done.
if ((
label.
flags &
Label.
FLAG_SUBROUTINE_START) == 0) {
label.
flags |=
Label.
FLAG_SUBROUTINE_START;
hasSubroutines = true;
}
currentBasicBlock.
flags |=
Label.
FLAG_SUBROUTINE_CALLER;
// Note that, by construction in this method, a block which calls a subroutine has at
// least two successors in the control flow graph: the first one (added below) leads to
// the instruction after the JSR, while the second one (added here) leads to the JSR
// target. Note that the first successor is virtual (it does not correspond to a possible
// execution path): it is only used to compute the successors of the basic blocks ending
// with a ret, in {@link Label#addSubroutineRetSuccessors}.
addSuccessorToCurrentBasicBlock(
relativeStackSize + 1,
label);
// The instruction after the JSR starts a new basic block.
nextBasicBlock = new
Label();
} else {
// No need to update maxRelativeStackSize (the stack size delta is always negative).
relativeStackSize +=
STACK_SIZE_DELTA[
baseOpcode];
addSuccessorToCurrentBasicBlock(
relativeStackSize,
label);
}
}
// If the next instruction starts a new basic block, call visitLabel to add the label of this
// instruction as a successor of the current block, and to start a new basic block.
if (
nextBasicBlock != null) {
if (
nextInsnIsJumpTarget) {
nextBasicBlock.
flags |=
Label.
FLAG_JUMP_TARGET;
}
visitLabel(
nextBasicBlock);
}
if (
baseOpcode ==
Opcodes.
GOTO) {
endCurrentBasicBlockWithNoSuccessor();
}
}
}
@
Override
public void
visitLabel(final
Label label) {
// Resolve the forward references to this label, if any.
hasAsmInstructions |=
label.
resolve(
code.
data,
code.
length);
// visitLabel starts a new basic block (except for debug only labels), so we need to update the
// previous and current block references and list of successors.
if ((
label.
flags &
Label.
FLAG_DEBUG_ONLY) != 0) {
return;
}
if (
compute ==
COMPUTE_ALL_FRAMES) {
if (
currentBasicBlock != null) {
if (
label.
bytecodeOffset ==
currentBasicBlock.
bytecodeOffset) {
// We use {@link Label#getCanonicalInstance} to store the state of a basic block in only
// one place, but this does not work for labels which have not been visited yet.
// Therefore, when we detect here two labels having the same bytecode offset, we need to
// - consolidate the state scattered in these two instances into the canonical instance:
currentBasicBlock.
flags |= (
label.
flags &
Label.
FLAG_JUMP_TARGET);
// - make sure the two instances share the same Frame instance (the implementation of
// {@link Label#getCanonicalInstance} relies on this property; here label.frame should be
// null):
label.
frame =
currentBasicBlock.
frame;
// - and make sure to NOT assign 'label' into 'currentBasicBlock' or 'lastBasicBlock', so
// that they still refer to the canonical instance for this bytecode offset.
return;
}
// End the current basic block (with one new successor).
addSuccessorToCurrentBasicBlock(
Edge.
JUMP,
label);
}
// Append 'label' at the end of the basic block list.
if (
lastBasicBlock != null) {
if (
label.
bytecodeOffset ==
lastBasicBlock.
bytecodeOffset) {
// Same comment as above.
lastBasicBlock.
flags |= (
label.
flags &
Label.
FLAG_JUMP_TARGET);
// Here label.frame should be null.
label.
frame =
lastBasicBlock.
frame;
currentBasicBlock =
lastBasicBlock;
return;
}
lastBasicBlock.
nextBasicBlock =
label;
}
lastBasicBlock =
label;
// Make it the new current basic block.
currentBasicBlock =
label;
// Here label.frame should be null.
label.
frame = new
Frame(
label);
} else if (
compute ==
COMPUTE_INSERTED_FRAMES) {
if (
currentBasicBlock == null) {
// This case should happen only once, for the visitLabel call in the constructor. Indeed, if
// compute is equal to COMPUTE_INSERTED_FRAMES, currentBasicBlock stays unchanged.
currentBasicBlock =
label;
} else {
// Update the frame owner so that a correct frame offset is computed in Frame.accept().
currentBasicBlock.
frame.
owner =
label;
}
} else if (
compute ==
COMPUTE_MAX_STACK_AND_LOCAL) {
if (
currentBasicBlock != null) {
// End the current basic block (with one new successor).
currentBasicBlock.
outputStackMax = (short)
maxRelativeStackSize;
addSuccessorToCurrentBasicBlock(
relativeStackSize,
label);
}
// Start a new current basic block, and reset the current and maximum relative stack sizes.
currentBasicBlock =
label;
relativeStackSize = 0;
maxRelativeStackSize = 0;
// Append the new basic block at the end of the basic block list.
if (
lastBasicBlock != null) {
lastBasicBlock.
nextBasicBlock =
label;
}
lastBasicBlock =
label;
} else if (
compute ==
COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES &&
currentBasicBlock == null) {
// This case should happen only once, for the visitLabel call in the constructor. Indeed, if
// compute is equal to COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES, currentBasicBlock stays
// unchanged.
currentBasicBlock =
label;
}
}
@
Override
public void
visitLdcInsn(final
Object value) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
Symbol constantSymbol =
symbolTable.
addConstant(
value);
int
constantIndex =
constantSymbol.
index;
char
firstDescriptorChar;
boolean
isLongOrDouble =
constantSymbol.
tag ==
Symbol.
CONSTANT_LONG_TAG
||
constantSymbol.
tag ==
Symbol.
CONSTANT_DOUBLE_TAG
|| (
constantSymbol.
tag ==
Symbol.
CONSTANT_DYNAMIC_TAG
&& ((
firstDescriptorChar =
constantSymbol.
value.
charAt(0)) == 'J'
||
firstDescriptorChar == 'D'));
if (
isLongOrDouble) {
code.
put12(
Constants.
LDC2_W,
constantIndex);
} else if (
constantIndex >= 256) {
code.
put12(
Constants.
LDC_W,
constantIndex);
} else {
code.
put11(
Opcodes.
LDC,
constantIndex);
}
// If needed, update the maximum stack size and number of locals, and stack map frames.
if (
currentBasicBlock != null) {
if (
compute ==
COMPUTE_ALL_FRAMES ||
compute ==
COMPUTE_INSERTED_FRAMES) {
currentBasicBlock.
frame.
execute(
Opcodes.
LDC, 0,
constantSymbol,
symbolTable);
} else {
int
size =
relativeStackSize + (
isLongOrDouble ? 2 : 1);
if (
size >
maxRelativeStackSize) {
maxRelativeStackSize =
size;
}
relativeStackSize =
size;
}
}
}
@
Override
public void
visitIincInsn(final int
var, final int
increment) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
if ((
var > 255) || (
increment > 127) || (
increment < -128)) {
code.
putByte(
Constants.
WIDE).
put12(
Opcodes.
IINC,
var).
putShort(
increment);
} else {
code.
putByte(
Opcodes.
IINC).
put11(
var,
increment);
}
// If needed, update the maximum stack size and number of locals, and stack map frames.
if (
currentBasicBlock != null
&& (
compute ==
COMPUTE_ALL_FRAMES ||
compute ==
COMPUTE_INSERTED_FRAMES)) {
currentBasicBlock.
frame.
execute(
Opcodes.
IINC,
var, null, null);
}
if (
compute !=
COMPUTE_NOTHING) {
int
currentMaxLocals =
var + 1;
if (
currentMaxLocals >
maxLocals) {
maxLocals =
currentMaxLocals;
}
}
}
@
Override
public void
visitTableSwitchInsn(
final int
min, final int
max, final
Label dflt, final
Label...
labels) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
code.
putByte(
Opcodes.
TABLESWITCH).
putByteArray(null, 0, (4 -
code.
length % 4) % 4);
dflt.
put(
code,
lastBytecodeOffset, true);
code.
putInt(
min).
putInt(
max);
for (
Label label :
labels) {
label.
put(
code,
lastBytecodeOffset, true);
}
// If needed, update the maximum stack size and number of locals, and stack map frames.
visitSwitchInsn(
dflt,
labels);
}
@
Override
public void
visitLookupSwitchInsn(final
Label dflt, final int[]
keys, final
Label[]
labels) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
code.
putByte(
Opcodes.
LOOKUPSWITCH).
putByteArray(null, 0, (4 -
code.
length % 4) % 4);
dflt.
put(
code,
lastBytecodeOffset, true);
code.
putInt(
labels.length);
for (int
i = 0;
i <
labels.length; ++
i) {
code.
putInt(
keys[
i]);
labels[
i].
put(
code,
lastBytecodeOffset, true);
}
// If needed, update the maximum stack size and number of locals, and stack map frames.
visitSwitchInsn(
dflt,
labels);
}
private void
visitSwitchInsn(final
Label dflt, final
Label[]
labels) {
if (
currentBasicBlock != null) {
if (
compute ==
COMPUTE_ALL_FRAMES) {
currentBasicBlock.
frame.
execute(
Opcodes.
LOOKUPSWITCH, 0, null, null);
// Add all the labels as successors of the current basic block.
addSuccessorToCurrentBasicBlock(
Edge.
JUMP,
dflt);
dflt.
getCanonicalInstance().
flags |=
Label.
FLAG_JUMP_TARGET;
for (
Label label :
labels) {
addSuccessorToCurrentBasicBlock(
Edge.
JUMP,
label);
label.
getCanonicalInstance().
flags |=
Label.
FLAG_JUMP_TARGET;
}
} else if (
compute ==
COMPUTE_MAX_STACK_AND_LOCAL) {
// No need to update maxRelativeStackSize (the stack size delta is always negative).
--
relativeStackSize;
// Add all the labels as successors of the current basic block.
addSuccessorToCurrentBasicBlock(
relativeStackSize,
dflt);
for (
Label label :
labels) {
addSuccessorToCurrentBasicBlock(
relativeStackSize,
label);
}
}
// End the current basic block.
endCurrentBasicBlockWithNoSuccessor();
}
}
@
Override
public void
visitMultiANewArrayInsn(final
String descriptor, final int
numDimensions) {
lastBytecodeOffset =
code.
length;
// Add the instruction to the bytecode of the method.
Symbol descSymbol =
symbolTable.
addConstantClass(
descriptor);
code.
put12(
Opcodes.
MULTIANEWARRAY,
descSymbol.
index).
putByte(
numDimensions);
// If needed, update the maximum stack size and number of locals, and stack map frames.
if (
currentBasicBlock != null) {
if (
compute ==
COMPUTE_ALL_FRAMES ||
compute ==
COMPUTE_INSERTED_FRAMES) {
currentBasicBlock.
frame.
execute(
Opcodes.
MULTIANEWARRAY,
numDimensions,
descSymbol,
symbolTable);
} else {
// No need to update maxRelativeStackSize (the stack size delta is always negative).
relativeStackSize += 1 -
numDimensions;
}
}
}
@
Override
public
AnnotationVisitor visitInsnAnnotation(
final int
typeRef, final
TypePath typePath, final
String descriptor, final boolean
visible) {
// Create a ByteVector to hold a 'type_annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
ByteVector typeAnnotation = new
ByteVector();
// Write target_type, target_info, and target_path.
TypeReference.
putTarget((
typeRef & 0xFF0000FF) | (
lastBytecodeOffset << 8),
typeAnnotation);
TypePath.
put(
typePath,
typeAnnotation);
// Write type_index and reserve space for num_element_value_pairs.
typeAnnotation.
putShort(
symbolTable.
addConstantUtf8(
descriptor)).
putShort(0);
if (
visible) {
return
lastCodeRuntimeVisibleTypeAnnotation =
new
AnnotationWriter(
symbolTable,
typeAnnotation,
lastCodeRuntimeVisibleTypeAnnotation);
} else {
return
lastCodeRuntimeInvisibleTypeAnnotation =
new
AnnotationWriter(
symbolTable,
typeAnnotation,
lastCodeRuntimeInvisibleTypeAnnotation);
}
}
@
Override
public void
visitTryCatchBlock(
final
Label start, final
Label end, final
Label handler, final
String type) {
Handler newHandler =
new
Handler(
start,
end,
handler,
type != null ?
symbolTable.
addConstantClass(
type).
index : 0,
type);
if (
firstHandler == null) {
firstHandler =
newHandler;
} else {
lastHandler.
nextHandler =
newHandler;
}
lastHandler =
newHandler;
}
@
Override
public
AnnotationVisitor visitTryCatchAnnotation(
final int
typeRef, final
TypePath typePath, final
String descriptor, final boolean
visible) {
// Create a ByteVector to hold a 'type_annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
ByteVector typeAnnotation = new
ByteVector();
// Write target_type, target_info, and target_path.
TypeReference.
putTarget(
typeRef,
typeAnnotation);
TypePath.
put(
typePath,
typeAnnotation);
// Write type_index and reserve space for num_element_value_pairs.
typeAnnotation.
putShort(
symbolTable.
addConstantUtf8(
descriptor)).
putShort(0);
if (
visible) {
return
lastCodeRuntimeVisibleTypeAnnotation =
new
AnnotationWriter(
symbolTable,
typeAnnotation,
lastCodeRuntimeVisibleTypeAnnotation);
} else {
return
lastCodeRuntimeInvisibleTypeAnnotation =
new
AnnotationWriter(
symbolTable,
typeAnnotation,
lastCodeRuntimeInvisibleTypeAnnotation);
}
}
@
Override
public void
visitLocalVariable(
final
String name,
final
String descriptor,
final
String signature,
final
Label start,
final
Label end,
final int
index) {
if (
signature != null) {
if (
localVariableTypeTable == null) {
localVariableTypeTable = new
ByteVector();
}
++
localVariableTypeTableLength;
localVariableTypeTable
.
putShort(
start.
bytecodeOffset)
.
putShort(
end.
bytecodeOffset -
start.
bytecodeOffset)
.
putShort(
symbolTable.
addConstantUtf8(
name))
.
putShort(
symbolTable.
addConstantUtf8(
signature))
.
putShort(
index);
}
if (
localVariableTable == null) {
localVariableTable = new
ByteVector();
}
++
localVariableTableLength;
localVariableTable
.
putShort(
start.
bytecodeOffset)
.
putShort(
end.
bytecodeOffset -
start.
bytecodeOffset)
.
putShort(
symbolTable.
addConstantUtf8(
name))
.
putShort(
symbolTable.
addConstantUtf8(
descriptor))
.
putShort(
index);
if (
compute !=
COMPUTE_NOTHING) {
char
firstDescChar =
descriptor.
charAt(0);
int
currentMaxLocals =
index + (
firstDescChar == 'J' ||
firstDescChar == 'D' ? 2 : 1);
if (
currentMaxLocals >
maxLocals) {
maxLocals =
currentMaxLocals;
}
}
}
@
Override
public
AnnotationVisitor visitLocalVariableAnnotation(
final int
typeRef,
final
TypePath typePath,
final
Label[]
start,
final
Label[]
end,
final int[]
index,
final
String descriptor,
final boolean
visible) {
// Create a ByteVector to hold a 'type_annotation' JVMS structure.
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
ByteVector typeAnnotation = new
ByteVector();
// Write target_type, target_info, and target_path.
typeAnnotation.
putByte(
typeRef >>> 24).
putShort(
start.length);
for (int
i = 0;
i <
start.length; ++
i) {
typeAnnotation
.
putShort(
start[
i].
bytecodeOffset)
.
putShort(
end[
i].
bytecodeOffset -
start[
i].
bytecodeOffset)
.
putShort(
index[
i]);
}
TypePath.
put(
typePath,
typeAnnotation);
// Write type_index and reserve space for num_element_value_pairs.
typeAnnotation.
putShort(
symbolTable.
addConstantUtf8(
descriptor)).
putShort(0);
if (
visible) {
return
lastCodeRuntimeVisibleTypeAnnotation =
new
AnnotationWriter(
symbolTable,
typeAnnotation,
lastCodeRuntimeVisibleTypeAnnotation);
} else {
return
lastCodeRuntimeInvisibleTypeAnnotation =
new
AnnotationWriter(
symbolTable,
typeAnnotation,
lastCodeRuntimeInvisibleTypeAnnotation);
}
}
@
Override
public void
visitLineNumber(final int
line, final
Label start) {
if (
lineNumberTable == null) {
lineNumberTable = new
ByteVector();
}
++
lineNumberTableLength;
lineNumberTable.
putShort(
start.
bytecodeOffset);
lineNumberTable.
putShort(
line);
}
@
Override
public void
visitMaxs(final int
maxStack, final int
maxLocals) {
if (
compute ==
COMPUTE_ALL_FRAMES) {
computeAllFrames();
} else if (
compute ==
COMPUTE_MAX_STACK_AND_LOCAL) {
computeMaxStackAndLocal();
} else if (
compute ==
COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) {
this.
maxStack =
maxRelativeStackSize;
} else {
this.
maxStack =
maxStack;
this.
maxLocals =
maxLocals;
}
}
/** Computes all the stack map frames of the method, from scratch. */
private void
computeAllFrames() {
// Complete the control flow graph with exception handler blocks.
Handler handler =
firstHandler;
while (
handler != null) {
String catchTypeDescriptor =
handler.
catchTypeDescriptor == null ? "java/lang/Throwable" :
handler.
catchTypeDescriptor;
int
catchType =
Frame.
getAbstractTypeFromInternalName(
symbolTable,
catchTypeDescriptor);
// Mark handlerBlock as an exception handler.
Label handlerBlock =
handler.
handlerPc.
getCanonicalInstance();
handlerBlock.
flags |=
Label.
FLAG_JUMP_TARGET;
// Add handlerBlock as a successor of all the basic blocks in the exception handler range.
Label handlerRangeBlock =
handler.
startPc.
getCanonicalInstance();
Label handlerRangeEnd =
handler.
endPc.
getCanonicalInstance();
while (
handlerRangeBlock !=
handlerRangeEnd) {
handlerRangeBlock.
outgoingEdges =
new
Edge(
catchType,
handlerBlock,
handlerRangeBlock.
outgoingEdges);
handlerRangeBlock =
handlerRangeBlock.
nextBasicBlock;
}
handler =
handler.
nextHandler;
}
// Create and visit the first (implicit) frame.
Frame firstFrame =
firstBasicBlock.
frame;
firstFrame.
setInputFrameFromDescriptor(
symbolTable,
accessFlags,
descriptor, this.
maxLocals);
firstFrame.
accept(this);
// Fix point algorithm: add the first basic block to a list of blocks to process (i.e. blocks
// whose stack map frame has changed) and, while there are blocks to process, remove one from
// the list and update the stack map frames of its successor blocks in the control flow graph
// (which might change them, in which case these blocks must be processed too, and are thus
// added to the list of blocks to process). Also compute the maximum stack size of the method,
// as a by-product.
Label listOfBlocksToProcess =
firstBasicBlock;
listOfBlocksToProcess.
nextListElement =
Label.
EMPTY_LIST;
int
maxStackSize = 0;
while (
listOfBlocksToProcess !=
Label.
EMPTY_LIST) {
// Remove a basic block from the list of blocks to process.
Label basicBlock =
listOfBlocksToProcess;
listOfBlocksToProcess =
listOfBlocksToProcess.
nextListElement;
basicBlock.
nextListElement = null;
// By definition, basicBlock is reachable.
basicBlock.
flags |=
Label.
FLAG_REACHABLE;
// Update the (absolute) maximum stack size.
int
maxBlockStackSize =
basicBlock.
frame.
getInputStackSize() +
basicBlock.
outputStackMax;
if (
maxBlockStackSize >
maxStackSize) {
maxStackSize =
maxBlockStackSize;
}
// Update the successor blocks of basicBlock in the control flow graph.
Edge outgoingEdge =
basicBlock.
outgoingEdges;
while (
outgoingEdge != null) {
Label successorBlock =
outgoingEdge.
successor.
getCanonicalInstance();
boolean
successorBlockChanged =
basicBlock.
frame.
merge(
symbolTable,
successorBlock.
frame,
outgoingEdge.
info);
if (
successorBlockChanged &&
successorBlock.
nextListElement == null) {
// If successorBlock has changed it must be processed. Thus, if it is not already in the
// list of blocks to process, add it to this list.
successorBlock.
nextListElement =
listOfBlocksToProcess;
listOfBlocksToProcess =
successorBlock;
}
outgoingEdge =
outgoingEdge.
nextEdge;
}
}
// Loop over all the basic blocks and visit the stack map frames that must be stored in the
// StackMapTable attribute. Also replace unreachable code with NOP* ATHROW, and remove it from
// exception handler ranges.
Label basicBlock =
firstBasicBlock;
while (
basicBlock != null) {
if ((
basicBlock.
flags & (
Label.
FLAG_JUMP_TARGET |
Label.
FLAG_REACHABLE))
== (
Label.
FLAG_JUMP_TARGET |
Label.
FLAG_REACHABLE)) {
basicBlock.
frame.
accept(this);
}
if ((
basicBlock.
flags &
Label.
FLAG_REACHABLE) == 0) {
// Find the start and end bytecode offsets of this unreachable block.
Label nextBasicBlock =
basicBlock.
nextBasicBlock;
int
startOffset =
basicBlock.
bytecodeOffset;
int
endOffset = (
nextBasicBlock == null ?
code.
length :
nextBasicBlock.
bytecodeOffset) - 1;
if (
endOffset >=
startOffset) {
// Replace its instructions with NOP ... NOP ATHROW.
for (int
i =
startOffset;
i <
endOffset; ++
i) {
code.
data[
i] =
Opcodes.
NOP;
}
code.
data[
endOffset] = (byte)
Opcodes.
ATHROW;
// Emit a frame for this unreachable block, with no local and a Throwable on the stack
// (so that the ATHROW could consume this Throwable if it were reachable).
int
frameIndex =
visitFrameStart(
startOffset, /* numLocal = */ 0, /* numStack = */ 1);
currentFrame[
frameIndex] =
Frame.
getAbstractTypeFromInternalName(
symbolTable, "java/lang/Throwable");
visitFrameEnd();
// Remove this unreachable basic block from the exception handler ranges.
firstHandler =
Handler.
removeRange(
firstHandler,
basicBlock,
nextBasicBlock);
// The maximum stack size is now at least one, because of the Throwable declared above.
maxStackSize =
Math.
max(
maxStackSize, 1);
}
}
basicBlock =
basicBlock.
nextBasicBlock;
}
this.
maxStack =
maxStackSize;
}
/** Computes the maximum stack size of the method. */
private void
computeMaxStackAndLocal() {
// Complete the control flow graph with exception handler blocks.
Handler handler =
firstHandler;
while (
handler != null) {
Label handlerBlock =
handler.
handlerPc;
Label handlerRangeBlock =
handler.
startPc;
Label handlerRangeEnd =
handler.
endPc;
// Add handlerBlock as a successor of all the basic blocks in the exception handler range.
while (
handlerRangeBlock !=
handlerRangeEnd) {
if ((
handlerRangeBlock.
flags &
Label.
FLAG_SUBROUTINE_CALLER) == 0) {
handlerRangeBlock.
outgoingEdges =
new
Edge(
Edge.
EXCEPTION,
handlerBlock,
handlerRangeBlock.
outgoingEdges);
} else {
// If handlerRangeBlock is a JSR block, add handlerBlock after the first two outgoing
// edges to preserve the hypothesis about JSR block successors order (see
// {@link #visitJumpInsn}).
handlerRangeBlock.
outgoingEdges.
nextEdge.
nextEdge =
new
Edge(
Edge.
EXCEPTION,
handlerBlock,
handlerRangeBlock.
outgoingEdges.
nextEdge.
nextEdge);
}
handlerRangeBlock =
handlerRangeBlock.
nextBasicBlock;
}
handler =
handler.
nextHandler;
}
// Complete the control flow graph with the successor blocks of subroutines, if needed.
if (
hasSubroutines) {
// First step: find the subroutines. This step determines, for each basic block, to which
// subroutine(s) it belongs. Start with the main "subroutine":
short
numSubroutines = 1;
firstBasicBlock.
markSubroutine(
numSubroutines);
// Then, mark the subroutines called by the main subroutine, then the subroutines called by
// those called by the main subroutine, etc.
for (short
currentSubroutine = 1;
currentSubroutine <=
numSubroutines; ++
currentSubroutine) {
Label basicBlock =
firstBasicBlock;
while (
basicBlock != null) {
if ((
basicBlock.
flags &
Label.
FLAG_SUBROUTINE_CALLER) != 0
&&
basicBlock.
subroutineId ==
currentSubroutine) {
Label jsrTarget =
basicBlock.
outgoingEdges.
nextEdge.
successor;
if (
jsrTarget.
subroutineId == 0) {
// If this subroutine has not been marked yet, find its basic blocks.
jsrTarget.
markSubroutine(++
numSubroutines);
}
}
basicBlock =
basicBlock.
nextBasicBlock;
}
}
// Second step: find the successors in the control flow graph of each subroutine basic block
// 'r' ending with a RET instruction. These successors are the virtual successors of the basic
// blocks ending with JSR instructions (see {@link #visitJumpInsn)} that can reach 'r'.
Label basicBlock =
firstBasicBlock;
while (
basicBlock != null) {
if ((
basicBlock.
flags &
Label.
FLAG_SUBROUTINE_CALLER) != 0) {
// By construction, jsr targets are stored in the second outgoing edge of basic blocks
// that ends with a jsr instruction (see {@link #FLAG_SUBROUTINE_CALLER}).
Label subroutine =
basicBlock.
outgoingEdges.
nextEdge.
successor;
subroutine.
addSubroutineRetSuccessors(
basicBlock);
}
basicBlock =
basicBlock.
nextBasicBlock;
}
}
// Data flow algorithm: put the first basic block in a list of blocks to process (i.e. blocks
// whose input stack size has changed) and, while there are blocks to process, remove one
// from the list, update the input stack size of its successor blocks in the control flow
// graph, and add these blocks to the list of blocks to process (if not already done).
Label listOfBlocksToProcess =
firstBasicBlock;
listOfBlocksToProcess.
nextListElement =
Label.
EMPTY_LIST;
int
maxStackSize =
maxStack;
while (
listOfBlocksToProcess !=
Label.
EMPTY_LIST) {
// Remove a basic block from the list of blocks to process. Note that we don't reset
// basicBlock.nextListElement to null on purpose, to make sure we don't reprocess already
// processed basic blocks.
Label basicBlock =
listOfBlocksToProcess;
listOfBlocksToProcess =
listOfBlocksToProcess.
nextListElement;
// Compute the (absolute) input stack size and maximum stack size of this block.
int
inputStackTop =
basicBlock.
inputStackSize;
int
maxBlockStackSize =
inputStackTop +
basicBlock.
outputStackMax;
// Update the absolute maximum stack size of the method.
if (
maxBlockStackSize >
maxStackSize) {
maxStackSize =
maxBlockStackSize;
}
// Update the input stack size of the successor blocks of basicBlock in the control flow
// graph, and add these blocks to the list of blocks to process, if not already done.
Edge outgoingEdge =
basicBlock.
outgoingEdges;
if ((
basicBlock.
flags &
Label.
FLAG_SUBROUTINE_CALLER) != 0) {
// Ignore the first outgoing edge of the basic blocks ending with a jsr: these are virtual
// edges which lead to the instruction just after the jsr, and do not correspond to a
// possible execution path (see {@link #visitJumpInsn} and
// {@link Label#FLAG_SUBROUTINE_CALLER}).
outgoingEdge =
outgoingEdge.
nextEdge;
}
while (
outgoingEdge != null) {
Label successorBlock =
outgoingEdge.
successor;
if (
successorBlock.
nextListElement == null) {
successorBlock.
inputStackSize =
(short) (
outgoingEdge.
info ==
Edge.
EXCEPTION ? 1 :
inputStackTop +
outgoingEdge.
info);
successorBlock.
nextListElement =
listOfBlocksToProcess;
listOfBlocksToProcess =
successorBlock;
}
outgoingEdge =
outgoingEdge.
nextEdge;
}
}
this.
maxStack =
maxStackSize;
}
@
Override
public void
visitEnd() {
// Nothing to do.
}
// -----------------------------------------------------------------------------------------------
// Utility methods: control flow analysis algorithm
// -----------------------------------------------------------------------------------------------
/**
* Adds a successor to {@link #currentBasicBlock} in the control flow graph.
*
* @param info information about the control flow edge to be added.
* @param successor the successor block to be added to the current basic block.
*/
private void
addSuccessorToCurrentBasicBlock(final int
info, final
Label successor) {
currentBasicBlock.
outgoingEdges = new
Edge(
info,
successor,
currentBasicBlock.
outgoingEdges);
}
/**
* Ends the current basic block. This method must be used in the case where the current basic
* block does not have any successor.
*
* <p>WARNING: this method must be called after the currently visited instruction has been put in
* {@link #code} (if frames are computed, this method inserts a new Label to start a new basic
* block after the current instruction).
*/
private void
endCurrentBasicBlockWithNoSuccessor() {
if (
compute ==
COMPUTE_ALL_FRAMES) {
Label nextBasicBlock = new
Label();
nextBasicBlock.
frame = new
Frame(
nextBasicBlock);
nextBasicBlock.
resolve(
code.
data,
code.
length);
lastBasicBlock.
nextBasicBlock =
nextBasicBlock;
lastBasicBlock =
nextBasicBlock;
currentBasicBlock = null;
} else if (
compute ==
COMPUTE_MAX_STACK_AND_LOCAL) {
currentBasicBlock.
outputStackMax = (short)
maxRelativeStackSize;
currentBasicBlock = null;
}
}
// -----------------------------------------------------------------------------------------------
// Utility methods: stack map frames
// -----------------------------------------------------------------------------------------------
/**
* Starts the visit of a new stack map frame, stored in {@link #currentFrame}.
*
* @param offset the bytecode offset of the instruction to which the frame corresponds.
* @param numLocal the number of local variables in the frame.
* @param numStack the number of stack elements in the frame.
* @return the index of the next element to be written in this frame.
*/
int
visitFrameStart(final int
offset, final int
numLocal, final int
numStack) {
int
frameLength = 3 +
numLocal +
numStack;
if (
currentFrame == null ||
currentFrame.length <
frameLength) {
currentFrame = new int[
frameLength];
}
currentFrame[0] =
offset;
currentFrame[1] =
numLocal;
currentFrame[2] =
numStack;
return 3;
}
/**
* Sets an abstract type in {@link #currentFrame}.
*
* @param frameIndex the index of the element to be set in {@link #currentFrame}.
* @param abstractType an abstract type.
*/
void
visitAbstractType(final int
frameIndex, final int
abstractType) {
currentFrame[
frameIndex] =
abstractType;
}
/**
* Ends the visit of {@link #currentFrame} by writing it in the StackMapTable entries and by
* updating the StackMapTable number_of_entries (except if the current frame is the first one,
* which is implicit in StackMapTable). Then resets {@link #currentFrame} to {@literal null}.
*/
void
visitFrameEnd() {
if (
previousFrame != null) {
if (
stackMapTableEntries == null) {
stackMapTableEntries = new
ByteVector();
}
putFrame();
++
stackMapTableNumberOfEntries;
}
previousFrame =
currentFrame;
currentFrame = null;
}
/** Compresses and writes {@link #currentFrame} in a new StackMapTable entry. */
private void
putFrame() {
final int
numLocal =
currentFrame[1];
final int
numStack =
currentFrame[2];
if (
symbolTable.
getMajorVersion() <
Opcodes.
V1_6) {
// Generate a StackMap attribute entry, which are always uncompressed.
stackMapTableEntries.
putShort(
currentFrame[0]).
putShort(
numLocal);
putAbstractTypes(3, 3 +
numLocal);
stackMapTableEntries.
putShort(
numStack);
putAbstractTypes(3 +
numLocal, 3 +
numLocal +
numStack);
return;
}
final int
offsetDelta =
stackMapTableNumberOfEntries == 0
?
currentFrame[0]
:
currentFrame[0] -
previousFrame[0] - 1;
final int
previousNumlocal =
previousFrame[1];
final int
numLocalDelta =
numLocal -
previousNumlocal;
int
type =
Frame.
FULL_FRAME;
if (
numStack == 0) {
switch (
numLocalDelta) {
case -3:
case -2:
case -1:
type =
Frame.
CHOP_FRAME;
break;
case 0:
type =
offsetDelta < 64 ?
Frame.
SAME_FRAME :
Frame.
SAME_FRAME_EXTENDED;
break;
case 1:
case 2:
case 3:
type =
Frame.
APPEND_FRAME;
break;
default:
// Keep the FULL_FRAME type.
break;
}
} else if (
numLocalDelta == 0 &&
numStack == 1) {
type =
offsetDelta < 63
?
Frame.
SAME_LOCALS_1_STACK_ITEM_FRAME
:
Frame.
SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
}
if (
type !=
Frame.
FULL_FRAME) {
// Verify if locals are the same as in the previous frame.
int
frameIndex = 3;
for (int
i = 0;
i <
previousNumlocal &&
i <
numLocal;
i++) {
if (
currentFrame[
frameIndex] !=
previousFrame[
frameIndex]) {
type =
Frame.
FULL_FRAME;
break;
}
frameIndex++;
}
}
switch (
type) {
case
Frame.
SAME_FRAME:
stackMapTableEntries.
putByte(
offsetDelta);
break;
case
Frame.
SAME_LOCALS_1_STACK_ITEM_FRAME:
stackMapTableEntries.
putByte(
Frame.
SAME_LOCALS_1_STACK_ITEM_FRAME +
offsetDelta);
putAbstractTypes(3 +
numLocal, 4 +
numLocal);
break;
case
Frame.
SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
stackMapTableEntries
.
putByte(
Frame.
SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
.
putShort(
offsetDelta);
putAbstractTypes(3 +
numLocal, 4 +
numLocal);
break;
case
Frame.
SAME_FRAME_EXTENDED:
stackMapTableEntries.
putByte(
Frame.
SAME_FRAME_EXTENDED).
putShort(
offsetDelta);
break;
case
Frame.
CHOP_FRAME:
stackMapTableEntries
.
putByte(
Frame.
SAME_FRAME_EXTENDED +
numLocalDelta)
.
putShort(
offsetDelta);
break;
case
Frame.
APPEND_FRAME:
stackMapTableEntries
.
putByte(
Frame.
SAME_FRAME_EXTENDED +
numLocalDelta)
.
putShort(
offsetDelta);
putAbstractTypes(3 +
previousNumlocal, 3 +
numLocal);
break;
case
Frame.
FULL_FRAME:
default:
stackMapTableEntries.
putByte(
Frame.
FULL_FRAME).
putShort(
offsetDelta).
putShort(
numLocal);
putAbstractTypes(3, 3 +
numLocal);
stackMapTableEntries.
putShort(
numStack);
putAbstractTypes(3 +
numLocal, 3 +
numLocal +
numStack);
break;
}
}
/**
* Puts some abstract types of {@link #currentFrame} in {@link #stackMapTableEntries} , using the
* JVMS verification_type_info format used in StackMapTable attributes.
*
* @param start index of the first type in {@link #currentFrame} to write.
* @param end index of last type in {@link #currentFrame} to write (exclusive).
*/
private void
putAbstractTypes(final int
start, final int
end) {
for (int
i =
start;
i <
end; ++
i) {
Frame.
putAbstractType(
symbolTable,
currentFrame[
i],
stackMapTableEntries);
}
}
/**
* Puts the given public API frame element type in {@link #stackMapTableEntries} , using the JVMS
* verification_type_info format used in StackMapTable attributes.
*
* @param type a frame element type described using the same format as in {@link
* MethodVisitor#visitFrame}, i.e. either {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link
* Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL}, or
* {@link Opcodes#UNINITIALIZED_THIS}, or the internal name of a class, or a Label designating
* a NEW instruction (for uninitialized types).
*/
private void
putFrameType(final
Object type) {
if (
type instanceof
Integer) {
stackMapTableEntries.
putByte(((
Integer)
type).
intValue());
} else if (
type instanceof
String) {
stackMapTableEntries
.
putByte(
Frame.
ITEM_OBJECT)
.
putShort(
symbolTable.
addConstantClass((
String)
type).
index);
} else {
stackMapTableEntries
.
putByte(
Frame.
ITEM_UNINITIALIZED)
.
putShort(((
Label)
type).
bytecodeOffset);
}
}
// -----------------------------------------------------------------------------------------------
// Utility methods
// -----------------------------------------------------------------------------------------------
/**
* Returns whether the attributes of this method can be copied from the attributes of the given
* method (assuming there is no method visitor between the given ClassReader and this
* MethodWriter). This method should only be called just after this MethodWriter has been created,
* and before any content is visited. It returns true if the attributes corresponding to the
* constructor arguments (at most a Signature, an Exception, a Deprecated and a Synthetic
* attribute) are the same as the corresponding attributes in the given method.
*
* @param source the source ClassReader from which the attributes of this method might be copied.
* @param methodInfoOffset the offset in 'source.b' of the method_info JVMS structure from which
* the attributes of this method might be copied.
* @param methodInfoLength the length in 'source.b' of the method_info JVMS structure from which
* the attributes of this method might be copied.
* @param hasSyntheticAttribute whether the method_info JVMS structure from which the attributes
* of this method might be copied contains a Synthetic attribute.
* @param hasDeprecatedAttribute whether the method_info JVMS structure from which the attributes
* of this method might be copied contains a Deprecated attribute.
* @param descriptorIndex the descriptor_index field of the method_info JVMS structure from which
* the attributes of this method might be copied.
* @param signatureIndex the constant pool index contained in the Signature attribute of the
* method_info JVMS structure from which the attributes of this method might be copied, or 0.
* @param exceptionsOffset the offset in 'source.b' of the Exceptions attribute of the method_info
* JVMS structure from which the attributes of this method might be copied, or 0.
* @return whether the attributes of this method can be copied from the attributes of the
* method_info JVMS structure in 'source.b', between 'methodInfoOffset' and 'methodInfoOffset'
* + 'methodInfoLength'.
*/
boolean
canCopyMethodAttributes(
final
ClassReader source,
final int
methodInfoOffset,
final int
methodInfoLength,
final boolean
hasSyntheticAttribute,
final boolean
hasDeprecatedAttribute,
final int
descriptorIndex,
final int
signatureIndex,
final int
exceptionsOffset) {
// If the method descriptor has changed, with more locals than the max_locals field of the
// original Code attribute, if any, then the original method attributes can't be copied. A
// conservative check on the descriptor changes alone ensures this (being more precise is not
// worth the additional complexity, because these cases should be rare -- if a transform changes
// a method descriptor, most of the time it needs to change the method's code too).
if (
source !=
symbolTable.
getSource()
||
descriptorIndex != this.
descriptorIndex
||
signatureIndex != this.
signatureIndex
||
hasDeprecatedAttribute != ((
accessFlags &
Opcodes.
ACC_DEPRECATED) != 0)) {
return false;
}
boolean
needSyntheticAttribute =
symbolTable.
getMajorVersion() <
Opcodes.
V1_5 && (
accessFlags &
Opcodes.
ACC_SYNTHETIC) != 0;
if (
hasSyntheticAttribute !=
needSyntheticAttribute) {
return false;
}
if (
exceptionsOffset == 0) {
if (
numberOfExceptions != 0) {
return false;
}
} else if (
source.
readUnsignedShort(
exceptionsOffset) ==
numberOfExceptions) {
int
currentExceptionOffset =
exceptionsOffset + 2;
for (int
i = 0;
i <
numberOfExceptions; ++
i) {
if (
source.
readUnsignedShort(
currentExceptionOffset) !=
exceptionIndexTable[
i]) {
return false;
}
currentExceptionOffset += 2;
}
}
// Don't copy the attributes yet, instead store their location in the source class reader so
// they can be copied later, in {@link #putMethodInfo}. Note that we skip the 6 header bytes
// of the method_info JVMS structure.
this.
sourceOffset =
methodInfoOffset + 6;
this.
sourceLength =
methodInfoLength - 6;
return true;
}
/**
* Returns the size of the method_info JVMS structure generated by this MethodWriter. Also add the
* names of the attributes of this method in the constant pool.
*
* @return the size in bytes of the method_info JVMS structure.
*/
int
computeMethodInfoSize() {
// If this method_info must be copied from an existing one, the size computation is trivial.
if (
sourceOffset != 0) {
// sourceLength excludes the first 6 bytes for access_flags, name_index and descriptor_index.
return 6 +
sourceLength;
}
// 2 bytes each for access_flags, name_index, descriptor_index and attributes_count.
int
size = 8;
// For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
if (
code.
length > 0) {
if (
code.
length > 65535) {
throw new
MethodTooLargeException(
symbolTable.
getClassName(),
name,
descriptor,
code.
length);
}
symbolTable.
addConstantUtf8(
Constants.
CODE);
// The Code attribute has 6 header bytes, plus 2, 2, 4 and 2 bytes respectively for max_stack,
// max_locals, code_length and attributes_count, plus the bytecode and the exception table.
size += 16 +
code.
length +
Handler.
getExceptionTableSize(
firstHandler);
if (
stackMapTableEntries != null) {
boolean
useStackMapTable =
symbolTable.
getMajorVersion() >=
Opcodes.
V1_6;
symbolTable.
addConstantUtf8(
useStackMapTable ?
Constants.
STACK_MAP_TABLE : "StackMap");
// 6 header bytes and 2 bytes for number_of_entries.
size += 8 +
stackMapTableEntries.
length;
}
if (
lineNumberTable != null) {
symbolTable.
addConstantUtf8(
Constants.
LINE_NUMBER_TABLE);
// 6 header bytes and 2 bytes for line_number_table_length.
size += 8 +
lineNumberTable.
length;
}
if (
localVariableTable != null) {
symbolTable.
addConstantUtf8(
Constants.
LOCAL_VARIABLE_TABLE);
// 6 header bytes and 2 bytes for local_variable_table_length.
size += 8 +
localVariableTable.
length;
}
if (
localVariableTypeTable != null) {
symbolTable.
addConstantUtf8(
Constants.
LOCAL_VARIABLE_TYPE_TABLE);
// 6 header bytes and 2 bytes for local_variable_type_table_length.
size += 8 +
localVariableTypeTable.
length;
}
if (
lastCodeRuntimeVisibleTypeAnnotation != null) {
size +=
lastCodeRuntimeVisibleTypeAnnotation.
computeAnnotationsSize(
Constants.
RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
}
if (
lastCodeRuntimeInvisibleTypeAnnotation != null) {
size +=
lastCodeRuntimeInvisibleTypeAnnotation.
computeAnnotationsSize(
Constants.
RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
}
if (
firstCodeAttribute != null) {
size +=
firstCodeAttribute.
computeAttributesSize(
symbolTable,
code.
data,
code.
length,
maxStack,
maxLocals);
}
}
if (
numberOfExceptions > 0) {
symbolTable.
addConstantUtf8(
Constants.
EXCEPTIONS);
size += 8 + 2 *
numberOfExceptions;
}
boolean
useSyntheticAttribute =
symbolTable.
getMajorVersion() <
Opcodes.
V1_5;
if ((
accessFlags &
Opcodes.
ACC_SYNTHETIC) != 0 &&
useSyntheticAttribute) {
symbolTable.
addConstantUtf8(
Constants.
SYNTHETIC);
size += 6;
}
if (
signatureIndex != 0) {
symbolTable.
addConstantUtf8(
Constants.
SIGNATURE);
size += 8;
}
if ((
accessFlags &
Opcodes.
ACC_DEPRECATED) != 0) {
symbolTable.
addConstantUtf8(
Constants.
DEPRECATED);
size += 6;
}
if (
lastRuntimeVisibleAnnotation != null) {
size +=
lastRuntimeVisibleAnnotation.
computeAnnotationsSize(
Constants.
RUNTIME_VISIBLE_ANNOTATIONS);
}
if (
lastRuntimeInvisibleAnnotation != null) {
size +=
lastRuntimeInvisibleAnnotation.
computeAnnotationsSize(
Constants.
RUNTIME_INVISIBLE_ANNOTATIONS);
}
if (
lastRuntimeVisibleParameterAnnotations != null) {
size +=
AnnotationWriter.
computeParameterAnnotationsSize(
Constants.
RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS,
lastRuntimeVisibleParameterAnnotations,
visibleAnnotableParameterCount == 0
?
lastRuntimeVisibleParameterAnnotations.length
:
visibleAnnotableParameterCount);
}
if (
lastRuntimeInvisibleParameterAnnotations != null) {
size +=
AnnotationWriter.
computeParameterAnnotationsSize(
Constants.
RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS,
lastRuntimeInvisibleParameterAnnotations,
invisibleAnnotableParameterCount == 0
?
lastRuntimeInvisibleParameterAnnotations.length
:
invisibleAnnotableParameterCount);
}
if (
lastRuntimeVisibleTypeAnnotation != null) {
size +=
lastRuntimeVisibleTypeAnnotation.
computeAnnotationsSize(
Constants.
RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
}
if (
lastRuntimeInvisibleTypeAnnotation != null) {
size +=
lastRuntimeInvisibleTypeAnnotation.
computeAnnotationsSize(
Constants.
RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
}
if (
defaultValue != null) {
symbolTable.
addConstantUtf8(
Constants.
ANNOTATION_DEFAULT);
size += 6 +
defaultValue.
length;
}
if (
parameters != null) {
symbolTable.
addConstantUtf8(
Constants.
METHOD_PARAMETERS);
// 6 header bytes and 1 byte for parameters_count.
size += 7 +
parameters.
length;
}
if (
firstAttribute != null) {
size +=
firstAttribute.
computeAttributesSize(
symbolTable);
}
return
size;
}
/**
* Puts the content of the method_info JVMS structure generated by this MethodWriter into the
* given ByteVector.
*
* @param output where the method_info structure must be put.
*/
void
putMethodInfo(final
ByteVector output) {
boolean
useSyntheticAttribute =
symbolTable.
getMajorVersion() <
Opcodes.
V1_5;
int
mask =
useSyntheticAttribute ?
Opcodes.
ACC_SYNTHETIC : 0;
output.
putShort(
accessFlags & ~
mask).
putShort(
nameIndex).
putShort(
descriptorIndex);
// If this method_info must be copied from an existing one, copy it now and return early.
if (
sourceOffset != 0) {
output.
putByteArray(
symbolTable.
getSource().
b,
sourceOffset,
sourceLength);
return;
}
// For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
int
attributeCount = 0;
if (
code.
length > 0) {
++
attributeCount;
}
if (
numberOfExceptions > 0) {
++
attributeCount;
}
if ((
accessFlags &
Opcodes.
ACC_SYNTHETIC) != 0 &&
useSyntheticAttribute) {
++
attributeCount;
}
if (
signatureIndex != 0) {
++
attributeCount;
}
if ((
accessFlags &
Opcodes.
ACC_DEPRECATED) != 0) {
++
attributeCount;
}
if (
lastRuntimeVisibleAnnotation != null) {
++
attributeCount;
}
if (
lastRuntimeInvisibleAnnotation != null) {
++
attributeCount;
}
if (
lastRuntimeVisibleParameterAnnotations != null) {
++
attributeCount;
}
if (
lastRuntimeInvisibleParameterAnnotations != null) {
++
attributeCount;
}
if (
lastRuntimeVisibleTypeAnnotation != null) {
++
attributeCount;
}
if (
lastRuntimeInvisibleTypeAnnotation != null) {
++
attributeCount;
}
if (
defaultValue != null) {
++
attributeCount;
}
if (
parameters != null) {
++
attributeCount;
}
if (
firstAttribute != null) {
attributeCount +=
firstAttribute.
getAttributeCount();
}
// For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
output.
putShort(
attributeCount);
if (
code.
length > 0) {
// 2, 2, 4 and 2 bytes respectively for max_stack, max_locals, code_length and
// attributes_count, plus the bytecode and the exception table.
int
size = 10 +
code.
length +
Handler.
getExceptionTableSize(
firstHandler);
int
codeAttributeCount = 0;
if (
stackMapTableEntries != null) {
// 6 header bytes and 2 bytes for number_of_entries.
size += 8 +
stackMapTableEntries.
length;
++
codeAttributeCount;
}
if (
lineNumberTable != null) {
// 6 header bytes and 2 bytes for line_number_table_length.
size += 8 +
lineNumberTable.
length;
++
codeAttributeCount;
}
if (
localVariableTable != null) {
// 6 header bytes and 2 bytes for local_variable_table_length.
size += 8 +
localVariableTable.
length;
++
codeAttributeCount;
}
if (
localVariableTypeTable != null) {
// 6 header bytes and 2 bytes for local_variable_type_table_length.
size += 8 +
localVariableTypeTable.
length;
++
codeAttributeCount;
}
if (
lastCodeRuntimeVisibleTypeAnnotation != null) {
size +=
lastCodeRuntimeVisibleTypeAnnotation.
computeAnnotationsSize(
Constants.
RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
++
codeAttributeCount;
}
if (
lastCodeRuntimeInvisibleTypeAnnotation != null) {
size +=
lastCodeRuntimeInvisibleTypeAnnotation.
computeAnnotationsSize(
Constants.
RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
++
codeAttributeCount;
}
if (
firstCodeAttribute != null) {
size +=
firstCodeAttribute.
computeAttributesSize(
symbolTable,
code.
data,
code.
length,
maxStack,
maxLocals);
codeAttributeCount +=
firstCodeAttribute.
getAttributeCount();
}
output
.
putShort(
symbolTable.
addConstantUtf8(
Constants.
CODE))
.
putInt(
size)
.
putShort(
maxStack)
.
putShort(
maxLocals)
.
putInt(
code.
length)
.
putByteArray(
code.
data, 0,
code.
length);
Handler.
putExceptionTable(
firstHandler,
output);
output.
putShort(
codeAttributeCount);
if (
stackMapTableEntries != null) {
boolean
useStackMapTable =
symbolTable.
getMajorVersion() >=
Opcodes.
V1_6;
output
.
putShort(
symbolTable.
addConstantUtf8(
useStackMapTable ?
Constants.
STACK_MAP_TABLE : "StackMap"))
.
putInt(2 +
stackMapTableEntries.
length)
.
putShort(
stackMapTableNumberOfEntries)
.
putByteArray(
stackMapTableEntries.
data, 0,
stackMapTableEntries.
length);
}
if (
lineNumberTable != null) {
output
.
putShort(
symbolTable.
addConstantUtf8(
Constants.
LINE_NUMBER_TABLE))
.
putInt(2 +
lineNumberTable.
length)
.
putShort(
lineNumberTableLength)
.
putByteArray(
lineNumberTable.
data, 0,
lineNumberTable.
length);
}
if (
localVariableTable != null) {
output
.
putShort(
symbolTable.
addConstantUtf8(
Constants.
LOCAL_VARIABLE_TABLE))
.
putInt(2 +
localVariableTable.
length)
.
putShort(
localVariableTableLength)
.
putByteArray(
localVariableTable.
data, 0,
localVariableTable.
length);
}
if (
localVariableTypeTable != null) {
output
.
putShort(
symbolTable.
addConstantUtf8(
Constants.
LOCAL_VARIABLE_TYPE_TABLE))
.
putInt(2 +
localVariableTypeTable.
length)
.
putShort(
localVariableTypeTableLength)
.
putByteArray(
localVariableTypeTable.
data, 0,
localVariableTypeTable.
length);
}
if (
lastCodeRuntimeVisibleTypeAnnotation != null) {
lastCodeRuntimeVisibleTypeAnnotation.
putAnnotations(
symbolTable.
addConstantUtf8(
Constants.
RUNTIME_VISIBLE_TYPE_ANNOTATIONS),
output);
}
if (
lastCodeRuntimeInvisibleTypeAnnotation != null) {
lastCodeRuntimeInvisibleTypeAnnotation.
putAnnotations(
symbolTable.
addConstantUtf8(
Constants.
RUNTIME_INVISIBLE_TYPE_ANNOTATIONS),
output);
}
if (
firstCodeAttribute != null) {
firstCodeAttribute.
putAttributes(
symbolTable,
code.
data,
code.
length,
maxStack,
maxLocals,
output);
}
}
if (
numberOfExceptions > 0) {
output
.
putShort(
symbolTable.
addConstantUtf8(
Constants.
EXCEPTIONS))
.
putInt(2 + 2 *
numberOfExceptions)
.
putShort(
numberOfExceptions);
for (int
exceptionIndex :
exceptionIndexTable) {
output.
putShort(
exceptionIndex);
}
}
if ((
accessFlags &
Opcodes.
ACC_SYNTHETIC) != 0 &&
useSyntheticAttribute) {
output.
putShort(
symbolTable.
addConstantUtf8(
Constants.
SYNTHETIC)).
putInt(0);
}
if (
signatureIndex != 0) {
output
.
putShort(
symbolTable.
addConstantUtf8(
Constants.
SIGNATURE))
.
putInt(2)
.
putShort(
signatureIndex);
}
if ((
accessFlags &
Opcodes.
ACC_DEPRECATED) != 0) {
output.
putShort(
symbolTable.
addConstantUtf8(
Constants.
DEPRECATED)).
putInt(0);
}
if (
lastRuntimeVisibleAnnotation != null) {
lastRuntimeVisibleAnnotation.
putAnnotations(
symbolTable.
addConstantUtf8(
Constants.
RUNTIME_VISIBLE_ANNOTATIONS),
output);
}
if (
lastRuntimeInvisibleAnnotation != null) {
lastRuntimeInvisibleAnnotation.
putAnnotations(
symbolTable.
addConstantUtf8(
Constants.
RUNTIME_INVISIBLE_ANNOTATIONS),
output);
}
if (
lastRuntimeVisibleParameterAnnotations != null) {
AnnotationWriter.
putParameterAnnotations(
symbolTable.
addConstantUtf8(
Constants.
RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS),
lastRuntimeVisibleParameterAnnotations,
visibleAnnotableParameterCount == 0
?
lastRuntimeVisibleParameterAnnotations.length
:
visibleAnnotableParameterCount,
output);
}
if (
lastRuntimeInvisibleParameterAnnotations != null) {
AnnotationWriter.
putParameterAnnotations(
symbolTable.
addConstantUtf8(
Constants.
RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS),
lastRuntimeInvisibleParameterAnnotations,
invisibleAnnotableParameterCount == 0
?
lastRuntimeInvisibleParameterAnnotations.length
:
invisibleAnnotableParameterCount,
output);
}
if (
lastRuntimeVisibleTypeAnnotation != null) {
lastRuntimeVisibleTypeAnnotation.
putAnnotations(
symbolTable.
addConstantUtf8(
Constants.
RUNTIME_VISIBLE_TYPE_ANNOTATIONS),
output);
}
if (
lastRuntimeInvisibleTypeAnnotation != null) {
lastRuntimeInvisibleTypeAnnotation.
putAnnotations(
symbolTable.
addConstantUtf8(
Constants.
RUNTIME_INVISIBLE_TYPE_ANNOTATIONS),
output);
}
if (
defaultValue != null) {
output
.
putShort(
symbolTable.
addConstantUtf8(
Constants.
ANNOTATION_DEFAULT))
.
putInt(
defaultValue.
length)
.
putByteArray(
defaultValue.
data, 0,
defaultValue.
length);
}
if (
parameters != null) {
output
.
putShort(
symbolTable.
addConstantUtf8(
Constants.
METHOD_PARAMETERS))
.
putInt(1 +
parameters.
length)
.
putByte(
parametersCount)
.
putByteArray(
parameters.
data, 0,
parameters.
length);
}
if (
firstAttribute != null) {
firstAttribute.
putAttributes(
symbolTable,
output);
}
}
/**
* Collects the attributes of this method into the given set of attribute prototypes.
*
* @param attributePrototypes a set of attribute prototypes.
*/
final void
collectAttributePrototypes(final
Attribute.
Set attributePrototypes) {
attributePrototypes.
addAttributes(
firstAttribute);
attributePrototypes.
addAttributes(
firstCodeAttribute);
}
}