/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* 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 jdk.internal.org.objectweb.asm;
A MethodVisitor
that generates methods in bytecode form. Each visit method of this class appends the bytecode corresponding to the visited instruction to a byte vector, in the order these methods are called. Author: Eric Bruneton, Eugene Kuleshov
/**
* A {@link MethodVisitor} that generates methods in bytecode form. Each visit
* method of this class appends the bytecode corresponding to the visited
* instruction to a byte vector, in the order these methods are called.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
class MethodWriter extends MethodVisitor {
Pseudo access flag used to denote constructors.
/**
* Pseudo access flag used to denote constructors.
*/
static final int ACC_CONSTRUCTOR = 0x80000;
Frame has exactly the same locals as the previous stack map frame and
number of stack items is zero.
/**
* Frame has exactly the same locals as the previous stack map frame and
* number of stack items is zero.
*/
static final int SAME_FRAME = 0; // to 63 (0-3f)
Frame has exactly the same locals as the previous stack map frame and
number of stack items is 1
/**
* Frame has exactly the same locals as the previous stack map frame and
* number of stack items is 1
*/
static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)
Reserved for future use
/**
* Reserved for future use
*/
static final int RESERVED = 128;
Frame has exactly the same locals as the previous stack map frame and
number of stack items is 1. Offset is bigger then 63;
/**
* Frame has exactly the same locals as the previous stack map frame and
* number of stack items is 1. Offset is bigger then 63;
*/
static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7
Frame where current locals are the same as the locals in the previous
frame, except that the k last locals are absent. The value of k is given
by the formula 251-frame_type.
/**
* Frame where current locals are the same as the locals in the previous
* frame, except that the k last locals are absent. The value of k is given
* by the formula 251-frame_type.
*/
static final int CHOP_FRAME = 248; // to 250 (f8-fA)
Frame has exactly the same locals as the previous stack map frame and
number of stack items is zero. Offset is bigger then 63;
/**
* Frame has exactly the same locals as the previous stack map frame and
* number of stack items is zero. Offset is bigger then 63;
*/
static final int SAME_FRAME_EXTENDED = 251; // fb
Frame where current locals are the same as the locals in the previous
frame, except that k additional locals are defined. The value of k is
given by the formula frame_type-251.
/**
* Frame where current locals are the same as the locals in the previous
* frame, except that k additional locals are defined. The value of k is
* given by the formula frame_type-251.
*/
static final int APPEND_FRAME = 252; // to 254 // fc-fe
Full frame
/**
* Full frame
*/
static final int FULL_FRAME = 255; // ff
Indicates that the stack map frames must be recomputed from scratch. In
this case the maximum stack size and number of local variables is also
recomputed from scratch.
See Also: - compute
/**
* Indicates that the stack map frames must be recomputed from scratch. In
* this case the maximum stack size and number of local variables is also
* recomputed from scratch.
*
* @see #compute
*/
static final int FRAMES = 0;
Indicates that the stack map frames of type F_INSERT must be computed.
The other frames are not (re)computed. They should all be of type F_NEW
and should be sufficient to compute the content of the F_INSERT frames,
together with the bytecode instructions between a F_NEW and a F_INSERT
frame - and without any knowledge of the type hierarchy (by definition of
F_INSERT).
See Also: - compute
/**
* Indicates that the stack map frames of type F_INSERT must be computed.
* The other frames are not (re)computed. They should all be of type F_NEW
* and should be sufficient to compute the content of the F_INSERT frames,
* together with the bytecode instructions between a F_NEW and a F_INSERT
* frame - and without any knowledge of the type hierarchy (by definition of
* F_INSERT).
*
* @see #compute
*/
static final int INSERTED_FRAMES = 1;
Indicates that the maximum stack size and number of local variables must
be automatically computed.
See Also: - compute
/**
* Indicates that the maximum stack size and number of local variables must
* be automatically computed.
*
* @see #compute
*/
static final int MAXS = 2;
Indicates that nothing must be automatically computed.
See Also: - compute
/**
* Indicates that nothing must be automatically computed.
*
* @see #compute
*/
static final int NOTHING = 3;
The class writer to which this method must be added.
/**
* The class writer to which this method must be added.
*/
final ClassWriter cw;
Access flags of this method.
/**
* Access flags of this method.
*/
private int access;
The index of the constant pool item that contains the name of this
method.
/**
* The index of the constant pool item that contains the name of this
* method.
*/
private final int name;
The index of the constant pool item that contains the descriptor of this
method.
/**
* The index of the constant pool item that contains the descriptor of this
* method.
*/
private final int desc;
The descriptor of this method.
/**
* The descriptor of this method.
*/
private final String descriptor;
The signature of this method.
/**
* The signature of this method.
*/
String signature;
If not zero, indicates that the code of this method must be copied from
the ClassReader associated to this writer in cw.cr
. More
precisely, this field gives the index of the first byte to copied from
cw.cr.b
.
/**
* If not zero, indicates that the code of this method must be copied from
* the ClassReader associated to this writer in <code>cw.cr</code>. More
* precisely, this field gives the index of the first byte to copied from
* <code>cw.cr.b</code>.
*/
int classReaderOffset;
If not zero, indicates that the code of this method must be copied from
the ClassReader associated to this writer in cw.cr
. More
precisely, this field gives the number of bytes to copied from
cw.cr.b
.
/**
* If not zero, indicates that the code of this method must be copied from
* the ClassReader associated to this writer in <code>cw.cr</code>. More
* precisely, this field gives the number of bytes to copied from
* <code>cw.cr.b</code>.
*/
int classReaderLength;
Number of exceptions that can be thrown by this method.
/**
* Number of exceptions that can be thrown by this method.
*/
int exceptionCount;
The exceptions that can be thrown by this method. More precisely, this
array contains the indexes of the constant pool items that contain the
internal names of these exception classes.
/**
* The exceptions that can be thrown by this method. More precisely, this
* array contains the indexes of the constant pool items that contain the
* internal names of these exception classes.
*/
int[] exceptions;
The annotation default attribute of this method. May be null.
/**
* The annotation default attribute of this method. May be <tt>null</tt>.
*/
private ByteVector annd;
The runtime visible annotations of this method. May be null.
/**
* The runtime visible annotations of this method. May be <tt>null</tt>.
*/
private AnnotationWriter anns;
The runtime invisible annotations of this method. May be null.
/**
* The runtime invisible annotations of this method. May be <tt>null</tt>.
*/
private AnnotationWriter ianns;
The runtime visible type annotations of this method. May be null
.
/**
* The runtime visible type annotations of this method. May be <tt>null</tt>
* .
*/
private AnnotationWriter tanns;
The runtime invisible type annotations of this method. May be
null.
/**
* The runtime invisible type annotations of this method. May be
* <tt>null</tt>.
*/
private AnnotationWriter itanns;
The runtime visible parameter annotations of this method. May be
null.
/**
* The runtime visible parameter annotations of this method. May be
* <tt>null</tt>.
*/
private AnnotationWriter[] panns;
The runtime invisible parameter annotations of this method. May be
null.
/**
* The runtime invisible parameter annotations of this method. May be
* <tt>null</tt>.
*/
private AnnotationWriter[] ipanns;
The number of synthetic parameters of this method.
/**
* The number of synthetic parameters of this method.
*/
private int synthetics;
The non standard attributes of the method.
/**
* The non standard attributes of the method.
*/
private Attribute attrs;
The bytecode of this method.
/**
* The bytecode of this method.
*/
private ByteVector code = new ByteVector();
Maximum stack size of this method.
/**
* Maximum stack size of this method.
*/
private int maxStack;
Maximum number of local variables for this method.
/**
* Maximum number of local variables for this method.
*/
private int maxLocals;
Number of local variables in the current stack map frame.
/**
* Number of local variables in the current stack map frame.
*/
private int currentLocals;
Number of stack map frames in the StackMapTable attribute.
/**
* Number of stack map frames in the StackMapTable attribute.
*/
int frameCount;
The StackMapTable attribute.
/**
* The StackMapTable attribute.
*/
private ByteVector stackMap;
The offset of the last frame that was written in the StackMapTable
attribute.
/**
* The offset of the last frame that was written in the StackMapTable
* attribute.
*/
private int previousFrameOffset;
The last frame that was written in the StackMapTable attribute.
See Also: - frame
/**
* The last frame that was written in the StackMapTable attribute.
*
* @see #frame
*/
private int[] previousFrame;
The current stack map frame. The first element contains the 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 values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] = nStack, frame[3] = nLocal. All types are encoded as integers, with the same format as the one used in Label
, but limited to BASE types. /**
* The current stack map frame. The first element contains the 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
* values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] =
* nStack, frame[3] = nLocal. All types are encoded as integers, with the
* same format as the one used in {@link Label}, but limited to BASE types.
*/
private int[] frame;
Number of elements in the exception handler list.
/**
* Number of elements in the exception handler list.
*/
private int handlerCount;
The first element in the exception handler list.
/**
* The first element in the exception handler list.
*/
private Handler firstHandler;
The last element in the exception handler list.
/**
* The last element in the exception handler list.
*/
private Handler lastHandler;
Number of entries in the MethodParameters attribute.
/**
* Number of entries in the MethodParameters attribute.
*/
private int methodParametersCount;
The MethodParameters attribute.
/**
* The MethodParameters attribute.
*/
private ByteVector methodParameters;
Number of entries in the LocalVariableTable attribute.
/**
* Number of entries in the LocalVariableTable attribute.
*/
private int localVarCount;
The LocalVariableTable attribute.
/**
* The LocalVariableTable attribute.
*/
private ByteVector localVar;
Number of entries in the LocalVariableTypeTable attribute.
/**
* Number of entries in the LocalVariableTypeTable attribute.
*/
private int localVarTypeCount;
The LocalVariableTypeTable attribute.
/**
* The LocalVariableTypeTable attribute.
*/
private ByteVector localVarType;
Number of entries in the LineNumberTable attribute.
/**
* Number of entries in the LineNumberTable attribute.
*/
private int lineNumberCount;
The LineNumberTable attribute.
/**
* The LineNumberTable attribute.
*/
private ByteVector lineNumber;
The start offset of the last visited instruction.
/**
* The start offset of the last visited instruction.
*/
private int lastCodeOffset;
The runtime visible type annotations of the code. May be null.
/**
* The runtime visible type annotations of the code. May be <tt>null</tt>.
*/
private AnnotationWriter ctanns;
The runtime invisible type annotations of the code. May be null.
/**
* The runtime invisible type annotations of the code. May be <tt>null</tt>.
*/
private AnnotationWriter ictanns;
The non standard attributes of the method's code.
/**
* The non standard attributes of the method's code.
*/
private Attribute cattrs;
The number of subroutines in this method.
/**
* The number of subroutines in this method.
*/
private int subroutines;
// ------------------------------------------------------------------------
/*
* Fields for the control flow graph analysis algorithm (used to compute the
* maximum stack size). A control flow graph contains one node per "basic
* block", and one edge per "jump" from one basic block to another. Each
* node (i.e., each basic block) is represented by the Label object that
* corresponds to the first instruction of this basic block. Each node also
* stores the list of its successors in the graph, as a linked list of Edge
* objects.
*/
Indicates what must be automatically computed.
See Also:
/**
* Indicates what must be automatically computed.
*
* @see #FRAMES
* @see #INSERTED_FRAMES
* @see #MAXS
* @see #NOTHING
*/
private final int compute;
A list of labels. This list is the list of basic blocks in the method, i.e. a list of Label objects linked to each other by their Label.successor
field, in the order they are visited by MethodVisitor.visitLabel
, and starting with the first basic block. /**
* A list of labels. This list is the list of basic blocks in the method,
* i.e. a list of Label objects linked to each other by their
* {@link Label#successor} field, in the order they are visited by
* {@link MethodVisitor#visitLabel}, and starting with the first basic
* block.
*/
private Label labels;
The previous basic block.
/**
* The previous basic block.
*/
private Label previousBlock;
The current basic block.
/**
* The current basic block.
*/
private Label currentBlock;
The (relative) stack size after the last visited instruction. This size is relative to the beginning of the current basic block, i.e., the true stack size after the last visited instruction is equal to the beginStackSize
of the current basic block plus stackSize.
/**
* The (relative) stack size after the last visited instruction. This size
* is relative to the beginning of the current basic block, i.e., the true
* stack size after the last visited instruction is equal to the
* {@link Label#inputStackTop beginStackSize} of the current basic block
* plus <tt>stackSize</tt>.
*/
private int stackSize;
The (relative) maximum stack size after the last visited instruction. This size is relative to the beginning of the current basic block, i.e., the true maximum stack size after the last visited instruction is equal to the beginStackSize
of the current basic block plus stackSize.
/**
* The (relative) maximum stack size after the last visited instruction.
* This size is relative to the beginning of the current basic block, i.e.,
* the true maximum stack size after the last visited instruction is equal
* to the {@link Label#inputStackTop beginStackSize} of the current basic
* block plus <tt>stackSize</tt>.
*/
private int maxStackSize;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
Constructs a new MethodWriter
. Params: - cw –
the class writer in which the method must be added.
- access – the method's access flags (see
Opcodes
). - name –
the method's name.
- desc – the method's descriptor (see
Type
). - signature –
the method's signature. May be null.
- exceptions –
the internal names of the method's exceptions. May be
null.
- compute –
Indicates what must be automatically computed (see #compute).
/**
* Constructs a new {@link MethodWriter}.
*
* @param cw
* the class writer in which the method must be added.
* @param access
* the method's access flags (see {@link Opcodes}).
* @param name
* the method's name.
* @param desc
* the method's descriptor (see {@link Type}).
* @param signature
* the method's signature. May be <tt>null</tt>.
* @param exceptions
* the internal names of the method's exceptions. May be
* <tt>null</tt>.
* @param compute
* Indicates what must be automatically computed (see #compute).
*/
MethodWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature,
final String[] exceptions, final int compute) {
super(Opcodes.ASM6);
if (cw.firstMethod == null) {
cw.firstMethod = this;
} else {
cw.lastMethod.mv = this;
}
cw.lastMethod = this;
this.cw = cw;
this.access = access;
if ("<init>".equals(name)) {
this.access |= ACC_CONSTRUCTOR;
}
this.name = cw.newUTF8(name);
this.desc = cw.newUTF8(desc);
this.descriptor = desc;
this.signature = signature;
if (exceptions != null && exceptions.length > 0) {
exceptionCount = exceptions.length;
this.exceptions = new int[exceptionCount];
for (int i = 0; i < exceptionCount; ++i) {
this.exceptions[i] = cw.newClass(exceptions[i]);
}
}
this.compute = compute;
if (compute != NOTHING) {
// updates maxLocals
int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2;
if ((access & Opcodes.ACC_STATIC) != 0) {
--size;
}
maxLocals = size;
currentLocals = size;
// creates and visits the label for the first basic block
labels = new Label();
labels.status |= Label.PUSHED;
visitLabel(labels);
}
}
// ------------------------------------------------------------------------
// Implementation of the MethodVisitor abstract class
// ------------------------------------------------------------------------
@Override
public void visitParameter(String name, int access) {
if (methodParameters == null) {
methodParameters = new ByteVector();
}
++methodParametersCount;
methodParameters.putShort((name == null) ? 0 : cw.newUTF8(name))
.putShort(access);
}
@Override
public AnnotationVisitor visitAnnotationDefault() {
annd = new ByteVector();
return new AnnotationWriter(cw, false, annd, null, 0);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
ByteVector bv = new ByteVector();
// write type, and reserve space for values count
bv.putShort(cw.newUTF8(desc)).putShort(0);
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
if (visible) {
aw.next = anns;
anns = aw;
} else {
aw.next = ianns;
ianns = aw;
}
return aw;
}
@Override
public AnnotationVisitor visitTypeAnnotation(final int typeRef,
final TypePath typePath, final String desc, final boolean visible) {
ByteVector bv = new ByteVector();
// write target_type and target_info
AnnotationWriter.putTarget(typeRef, typePath, bv);
// write type, and reserve space for values count
bv.putShort(cw.newUTF8(desc)).putShort(0);
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
bv.length - 2);
if (visible) {
aw.next = tanns;
tanns = aw;
} else {
aw.next = itanns;
itanns = aw;
}
return aw;
}
@Override
public AnnotationVisitor visitParameterAnnotation(final int parameter,
final String desc, final boolean visible) {
ByteVector bv = new ByteVector();
if ("Ljava/lang/Synthetic;".equals(desc)) {
// workaround for a bug in javac with synthetic parameters
// see ClassReader.readParameterAnnotations
synthetics = Math.max(synthetics, parameter + 1);
return new AnnotationWriter(cw, false, bv, null, 0);
}
// write type, and reserve space for values count
bv.putShort(cw.newUTF8(desc)).putShort(0);
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
if (visible) {
if (panns == null) {
panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
}
aw.next = panns[parameter];
panns[parameter] = aw;
} else {
if (ipanns == null) {
ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
}
aw.next = ipanns[parameter];
ipanns[parameter] = aw;
}
return aw;
}
@Override
public void visitAttribute(final Attribute attr) {
if (attr.isCodeAttribute()) {
attr.next = cattrs;
cattrs = attr;
} else {
attr.next = attrs;
attrs = attr;
}
}
@Override
public void visitCode() {
}
@Override
public void visitFrame(final int type, final int nLocal,
final Object[] local, final int nStack, final Object[] stack) {
if (compute == FRAMES) {
return;
}
if (compute == INSERTED_FRAMES) {
if (currentBlock.frame == null) {
// This should happen only once, for the implicit first frame
// (which is explicitly visited in ClassReader if the
// EXPAND_ASM_INSNS option is used).
currentBlock.frame = new CurrentFrame();
currentBlock.frame.owner = currentBlock;
currentBlock.frame.initInputFrame(cw, access,
Type.getArgumentTypes(descriptor), nLocal);
visitImplicitFirstFrame();
} else {
if (type == Opcodes.F_NEW) {
currentBlock.frame.set(cw, nLocal, local, nStack, stack);
} else {
// In this case type is equal to F_INSERT by hypothesis, and
// currentBlock.frame contains the stack map frame at the
// current instruction, computed from the last F_NEW frame
// and the bytecode instructions in between (via calls to
// CurrentFrame#execute).
}
visitFrame(currentBlock.frame);
}
} else if (type == Opcodes.F_NEW) {
if (previousFrame == null) {
visitImplicitFirstFrame();
}
currentLocals = nLocal;
int frameIndex = startFrame(code.length, nLocal, nStack);
for (int i = 0; i < nLocal; ++i) {
if (local[i] instanceof String) {
String desc = Type.getObjectType((String) local[i]).getDescriptor();
frame[frameIndex++] = Frame.type(cw, desc);
} else if (local[i] instanceof Integer) {
frame[frameIndex++] = Frame.BASE | ((Integer) local[i]).intValue();
} else {
frame[frameIndex++] = Frame.UNINITIALIZED
| cw.addUninitializedType("",
((Label) local[i]).position);
}
}
for (int i = 0; i < nStack; ++i) {
if (stack[i] instanceof String) {
String desc = Type.getObjectType((String) stack[i]).getDescriptor();
frame[frameIndex++] = Frame.type(cw, desc);
} else if (stack[i] instanceof Integer) {
frame[frameIndex++] = Frame.BASE | ((Integer) stack[i]).intValue();
} else {
frame[frameIndex++] = Frame.UNINITIALIZED
| cw.addUninitializedType("",
((Label) stack[i]).position);
}
}
endFrame();
} else {
int delta;
if (stackMap == null) {
stackMap = new ByteVector();
delta = code.length;
} else {
delta = code.length - previousFrameOffset - 1;
if (delta < 0) {
if (type == Opcodes.F_SAME) {
return;
} else {
throw new IllegalStateException();
}
}
}
switch (type) {
case Opcodes.F_FULL:
currentLocals = nLocal;
stackMap.putByte(FULL_FRAME).putShort(delta).putShort(nLocal);
for (int i = 0; i < nLocal; ++i) {
writeFrameType(local[i]);
}
stackMap.putShort(nStack);
for (int i = 0; i < nStack; ++i) {
writeFrameType(stack[i]);
}
break;
case Opcodes.F_APPEND:
currentLocals += nLocal;
stackMap.putByte(SAME_FRAME_EXTENDED + nLocal).putShort(delta);
for (int i = 0; i < nLocal; ++i) {
writeFrameType(local[i]);
}
break;
case Opcodes.F_CHOP:
currentLocals -= nLocal;
stackMap.putByte(SAME_FRAME_EXTENDED - nLocal).putShort(delta);
break;
case Opcodes.F_SAME:
if (delta < 64) {
stackMap.putByte(delta);
} else {
stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);
}
break;
case Opcodes.F_SAME1:
if (delta < 64) {
stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
} else {
stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
.putShort(delta);
}
writeFrameType(stack[0]);
break;
}
previousFrameOffset = code.length;
++frameCount;
}
maxStack = Math.max(maxStack, nStack);
maxLocals = Math.max(maxLocals, currentLocals);
}
@Override
public void visitInsn(final int opcode) {
lastCodeOffset = code.length;
// adds the instruction to the bytecode of the method
code.putByte(opcode);
// update currentBlock
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, 0, null, null);
} else {
// updates current and max stack sizes
int size = stackSize + Frame.SIZE[opcode];
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
// if opcode == ATHROW or xRETURN, ends current block (no successor)
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
|| opcode == Opcodes.ATHROW) {
noSuccessor();
}
}
}
@Override
public void visitIntInsn(final int opcode, final int operand) {
lastCodeOffset = code.length;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, operand, null, null);
} else if (opcode != Opcodes.NEWARRAY) {
// updates current and max stack sizes only for NEWARRAY
// (stack size variation = 0 for BIPUSH or SIPUSH)
int size = stackSize + 1;
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
}
// adds the instruction to the bytecode of the method
if (opcode == Opcodes.SIPUSH) {
code.put12(opcode, operand);
} else { // BIPUSH or NEWARRAY
code.put11(opcode, operand);
}
}
@Override
public void visitVarInsn(final int opcode, final int var) {
lastCodeOffset = code.length;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, var, null, null);
} else {
// updates current and max stack sizes
if (opcode == Opcodes.RET) {
// no stack change, but end of current block (no successor)
currentBlock.status |= Label.RET;
// save 'stackSize' here for future use
// (see {@link #findSubroutineSuccessors})
currentBlock.inputStackTop = stackSize;
noSuccessor();
} else { // xLOAD or xSTORE
int size = stackSize + Frame.SIZE[opcode];
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
}
}
if (compute != NOTHING) {
// updates max locals
int n;
if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD
|| opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) {
n = var + 2;
} else {
n = var + 1;
}
if (n > maxLocals) {
maxLocals = n;
}
}
// adds the instruction to the bytecode of the method
if (var < 4 && opcode != Opcodes.RET) {
int opt;
if (opcode < Opcodes.ISTORE) {
/* ILOAD_0 */
opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var;
} else {
/* ISTORE_0 */
opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var;
}
code.putByte(opt);
} else if (var >= 256) {
code.putByte(196 /* WIDE */).put12(opcode, var);
} else {
code.put11(opcode, var);
}
if (opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) {
visitLabel(new Label());
}
}
@Override
public void visitTypeInsn(final int opcode, final String type) {
lastCodeOffset = code.length;
Item i = cw.newStringishItem(ClassWriter.CLASS, type);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, code.length, cw, i);
} else if (opcode == Opcodes.NEW) {
// updates current and max stack sizes only if opcode == NEW
// (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF)
int size = stackSize + 1;
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
}
// adds the instruction to the bytecode of the method
code.put12(opcode, i.index);
}
@Override
public void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc) {
lastCodeOffset = code.length;
Item i = cw.newFieldItem(owner, name, desc);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, 0, cw, i);
} else {
int size;
// computes the stack size variation
char c = desc.charAt(0);
switch (opcode) {
case Opcodes.GETSTATIC:
size = stackSize + (c == 'D' || c == 'J' ? 2 : 1);
break;
case Opcodes.PUTSTATIC:
size = stackSize + (c == 'D' || c == 'J' ? -2 : -1);
break;
case Opcodes.GETFIELD:
size = stackSize + (c == 'D' || c == 'J' ? 1 : 0);
break;
// case Constants.PUTFIELD:
default:
size = stackSize + (c == 'D' || c == 'J' ? -3 : -2);
break;
}
// updates current and max stack sizes
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
}
// adds the instruction to the bytecode of the method
code.put12(opcode, i.index);
}
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc, final boolean itf) {
lastCodeOffset = code.length;
Item i = cw.newMethodItem(owner, name, desc, itf);
int argSize = i.intVal;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, 0, cw, i);
} else {
/*
* computes the stack size variation. In order not to recompute
* several times this variation for the same Item, we use the
* intVal field of this item to store this variation, once it
* has been computed. More precisely this intVal field stores
* the sizes of the arguments and of the return value
* corresponding to desc.
*/
if (argSize == 0) {
// the above sizes have not been computed yet,
// so we compute them...
argSize = Type.getArgumentsAndReturnSizes(desc);
// ... and we save them in order
// not to recompute them in the future
i.intVal = argSize;
}
int size;
if (opcode == Opcodes.INVOKESTATIC) {
size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
} else {
size = stackSize - (argSize >> 2) + (argSize & 0x03);
}
// updates current and max stack sizes
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
}
// adds the instruction to the bytecode of the method
if (opcode == Opcodes.INVOKEINTERFACE) {
if (argSize == 0) {
argSize = Type.getArgumentsAndReturnSizes(desc);
i.intVal = argSize;
}
code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
} else {
code.put12(opcode, i.index);
}
}
@Override
public void visitInvokeDynamicInsn(final String name, final String desc,
final Handle bsm, final Object... bsmArgs) {
lastCodeOffset = code.length;
Item i = cw.newInvokeDynamicItem(name, desc, bsm, bsmArgs);
int argSize = i.intVal;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i);
} else {
/*
* computes the stack size variation. In order not to recompute
* several times this variation for the same Item, we use the
* intVal field of this item to store this variation, once it
* has been computed. More precisely this intVal field stores
* the sizes of the arguments and of the return value
* corresponding to desc.
*/
if (argSize == 0) {
// the above sizes have not been computed yet,
// so we compute them...
argSize = Type.getArgumentsAndReturnSizes(desc);
// ... and we save them in order
// not to recompute them in the future
i.intVal = argSize;
}
int size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
// updates current and max stack sizes
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
}
// adds the instruction to the bytecode of the method
code.put12(Opcodes.INVOKEDYNAMIC, i.index);
code.putShort(0);
}
@Override
public void visitJumpInsn(int opcode, final Label label) {
boolean isWide = opcode >= 200; // GOTO_W
opcode = isWide ? opcode - 33 : opcode;
lastCodeOffset = code.length;
Label nextInsn = null;
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(opcode, 0, null, null);
// 'label' is the target of a jump instruction
label.getFirst().status |= Label.TARGET;
// adds 'label' as a successor of this basic block
addSuccessor(Edge.NORMAL, label);
if (opcode != Opcodes.GOTO) {
// creates a Label for the next basic block
nextInsn = new Label();
}
} else if (compute == INSERTED_FRAMES) {
currentBlock.frame.execute(opcode, 0, null, null);
} else {
if (opcode == Opcodes.JSR) {
if ((label.status & Label.SUBROUTINE) == 0) {
label.status |= Label.SUBROUTINE;
++subroutines;
}
currentBlock.status |= Label.JSR;
addSuccessor(stackSize + 1, label);
// creates a Label for the next basic block
nextInsn = new Label();
/*
* note that, by construction in this method, a JSR block
* has at least two successors in the control flow graph:
* the first one leads the next instruction after the JSR,
* while the second one leads to the JSR target.
*/
} else {
// updates current stack size (max stack size unchanged
// because stack size variation always negative in this
// case)
stackSize += Frame.SIZE[opcode];
addSuccessor(stackSize, label);
}
}
}
// adds the instruction to the bytecode of the method
if ((label.status & Label.RESOLVED) != 0
&& label.position - 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 (i.e., IFNE for IFEQ) and where <L>
* designates the instruction just after the GOTO_W.
*/
if (opcode == Opcodes.GOTO) {
code.putByte(200); // GOTO_W
} else if (opcode == Opcodes.JSR) {
code.putByte(201); // JSR_W
} else {
// if the IF instruction is transformed into IFNOT GOTO_W the
// next instruction becomes the target of the IFNOT instruction
if (nextInsn != null) {
nextInsn.status |= Label.TARGET;
}
code.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1
: opcode ^ 1);
code.putShort(8); // jump offset
// ASM pseudo GOTO_W insn, see ClassReader. We don't use a real
// GOTO_W because we might need to insert a frame just after (as
// the target of the IFNOTxxx jump instruction).
code.putByte(220);
cw.hasAsmInsns = true;
}
label.put(this, code, code.length - 1, true);
} else if (isWide) {
/*
* case of a GOTO_W or JSR_W specified by the user (normally
* ClassReader when used to resize instructions). In this case we
* keep the original instruction.
*/
code.putByte(opcode + 33);
label.put(this, code, code.length - 1, true);
} else {
/*
* case of a backward jump with an offset >= -32768, or of a forward
* jump with, of course, an unknown offset. In these cases we store
* the offset in 2 bytes (which will be increased in
* resizeInstructions, if needed).
*/
code.putByte(opcode);
label.put(this, code, code.length - 1, false);
}
if (currentBlock != null) {
if (nextInsn != null) {
// if the jump instruction is not a GOTO, the next instruction
// is also a successor of this instruction. Calling visitLabel
// adds the label of this next instruction as a successor of the
// current block, and starts a new basic block
visitLabel(nextInsn);
}
if (opcode == Opcodes.GOTO) {
noSuccessor();
}
}
}
@Override
public void visitLabel(final Label label) {
// resolves previous forward references to label, if any
cw.hasAsmInsns |= label.resolve(this, code.length, code.data);
// updates currentBlock
if ((label.status & Label.DEBUG) != 0) {
return;
}
if (compute == FRAMES) {
if (currentBlock != null) {
if (label.position == currentBlock.position) {
// successive labels, do not start a new basic block
currentBlock.status |= (label.status & Label.TARGET);
label.frame = currentBlock.frame;
return;
}
// ends current block (with one new successor)
addSuccessor(Edge.NORMAL, label);
}
// begins a new current block
currentBlock = label;
if (label.frame == null) {
label.frame = new Frame();
label.frame.owner = label;
}
// updates the basic block list
if (previousBlock != null) {
if (label.position == previousBlock.position) {
previousBlock.status |= (label.status & Label.TARGET);
label.frame = previousBlock.frame;
currentBlock = previousBlock;
return;
}
previousBlock.successor = label;
}
previousBlock = label;
} else if (compute == INSERTED_FRAMES) {
if (currentBlock == null) {
// This case should happen only once, for the visitLabel call in
// the constructor. Indeed, if compute is equal to
// INSERTED_FRAMES currentBlock can not be set back to null (see
// #noSuccessor).
currentBlock = label;
} else {
// Updates the frame owner so that a correct frame offset is
// computed in visitFrame(Frame).
currentBlock.frame.owner = label;
}
} else if (compute == MAXS) {
if (currentBlock != null) {
// ends current block (with one new successor)
currentBlock.outputStackMax = maxStackSize;
addSuccessor(stackSize, label);
}
// begins a new current block
currentBlock = label;
// resets the relative current and max stack sizes
stackSize = 0;
maxStackSize = 0;
// updates the basic block list
if (previousBlock != null) {
previousBlock.successor = label;
}
previousBlock = label;
}
}
@Override
public void visitLdcInsn(final Object cst) {
lastCodeOffset = code.length;
Item i = cw.newConstItem(cst);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);
} else {
int size;
// computes the stack size variation
if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) {
size = stackSize + 2;
} else {
size = stackSize + 1;
}
// updates current and max stack sizes
if (size > maxStackSize) {
maxStackSize = size;
}
stackSize = size;
}
}
// adds the instruction to the bytecode of the method
int index = i.index;
if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) {
code.put12(20 /* LDC2_W */, index);
} else if (index >= 256) {
code.put12(19 /* LDC_W */, index);
} else {
code.put11(Opcodes.LDC, index);
}
}
@Override
public void visitIincInsn(final int var, final int increment) {
lastCodeOffset = code.length;
if (currentBlock != null) {
if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(Opcodes.IINC, var, null, null);
}
}
if (compute != NOTHING) {
// updates max locals
int n = var + 1;
if (n > maxLocals) {
maxLocals = n;
}
}
// adds the instruction to the bytecode of the method
if ((var > 255) || (increment > 127) || (increment < -128)) {
code.putByte(196 /* WIDE */).put12(Opcodes.IINC, var)
.putShort(increment);
} else {
code.putByte(Opcodes.IINC).put11(var, increment);
}
}
@Override
public void visitTableSwitchInsn(final int min, final int max,
final Label dflt, final Label... labels) {
lastCodeOffset = code.length;
// adds the instruction to the bytecode of the method
int source = code.length;
code.putByte(Opcodes.TABLESWITCH);
code.putByteArray(null, 0, (4 - code.length % 4) % 4);
dflt.put(this, code, source, true);
code.putInt(min).putInt(max);
for (int i = 0; i < labels.length; ++i) {
labels[i].put(this, code, source, true);
}
// updates currentBlock
visitSwitchInsn(dflt, labels);
}
@Override
public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
final Label[] labels) {
lastCodeOffset = code.length;
// adds the instruction to the bytecode of the method
int source = code.length;
code.putByte(Opcodes.LOOKUPSWITCH);
code.putByteArray(null, 0, (4 - code.length % 4) % 4);
dflt.put(this, code, source, true);
code.putInt(labels.length);
for (int i = 0; i < labels.length; ++i) {
code.putInt(keys[i]);
labels[i].put(this, code, source, true);
}
// updates currentBlock
visitSwitchInsn(dflt, labels);
}
private void visitSwitchInsn(final Label dflt, final Label[] labels) {
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES) {
currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null);
// adds current block successors
addSuccessor(Edge.NORMAL, dflt);
dflt.getFirst().status |= Label.TARGET;
for (int i = 0; i < labels.length; ++i) {
addSuccessor(Edge.NORMAL, labels[i]);
labels[i].getFirst().status |= Label.TARGET;
}
} else {
// updates current stack size (max stack size unchanged)
--stackSize;
// adds current block successors
addSuccessor(stackSize, dflt);
for (int i = 0; i < labels.length; ++i) {
addSuccessor(stackSize, labels[i]);
}
}
// ends current block
noSuccessor();
}
}
@Override
public void visitMultiANewArrayInsn(final String desc, final int dims) {
lastCodeOffset = code.length;
Item i = cw.newStringishItem(ClassWriter.CLASS, desc);
// Label currentBlock = this.currentBlock;
if (currentBlock != null) {
if (compute == FRAMES || compute == INSERTED_FRAMES) {
currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i);
} else {
// updates current stack size (max stack size unchanged because
// stack size variation always negative or null)
stackSize += 1 - dims;
}
}
// adds the instruction to the bytecode of the method
code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims);
}
@Override
public AnnotationVisitor visitInsnAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
ByteVector bv = new ByteVector();
// write target_type and target_info
typeRef = (typeRef & 0xFF0000FF) | (lastCodeOffset << 8);
AnnotationWriter.putTarget(typeRef, typePath, bv);
// write type, and reserve space for values count
bv.putShort(cw.newUTF8(desc)).putShort(0);
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
bv.length - 2);
if (visible) {
aw.next = ctanns;
ctanns = aw;
} else {
aw.next = ictanns;
ictanns = aw;
}
return aw;
}
@Override
public void visitTryCatchBlock(final Label start, final Label end,
final Label handler, final String type) {
++handlerCount;
Handler h = new Handler();
h.start = start;
h.end = end;
h.handler = handler;
h.desc = type;
h.type = type != null ? cw.newClass(type) : 0;
if (lastHandler == null) {
firstHandler = h;
} else {
lastHandler.next = h;
}
lastHandler = h;
}
@Override
public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
ByteVector bv = new ByteVector();
// write target_type and target_info
AnnotationWriter.putTarget(typeRef, typePath, bv);
// write type, and reserve space for values count
bv.putShort(cw.newUTF8(desc)).putShort(0);
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
bv.length - 2);
if (visible) {
aw.next = ctanns;
ctanns = aw;
} else {
aw.next = ictanns;
ictanns = aw;
}
return aw;
}
@Override
public void visitLocalVariable(final String name, final String desc,
final String signature, final Label start, final Label end,
final int index) {
if (signature != null) {
if (localVarType == null) {
localVarType = new ByteVector();
}
++localVarTypeCount;
localVarType.putShort(start.position)
.putShort(end.position - start.position)
.putShort(cw.newUTF8(name)).putShort(cw.newUTF8(signature))
.putShort(index);
}
if (localVar == null) {
localVar = new ByteVector();
}
++localVarCount;
localVar.putShort(start.position)
.putShort(end.position - start.position)
.putShort(cw.newUTF8(name)).putShort(cw.newUTF8(desc))
.putShort(index);
if (compute != NOTHING) {
// updates max locals
char c = desc.charAt(0);
int n = index + (c == 'J' || c == 'D' ? 2 : 1);
if (n > maxLocals) {
maxLocals = n;
}
}
}
@Override
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
TypePath typePath, Label[] start, Label[] end, int[] index,
String desc, boolean visible) {
ByteVector bv = new ByteVector();
// write target_type and target_info
bv.putByte(typeRef >>> 24).putShort(start.length);
for (int i = 0; i < start.length; ++i) {
bv.putShort(start[i].position)
.putShort(end[i].position - start[i].position)
.putShort(index[i]);
}
if (typePath == null) {
bv.putByte(0);
} else {
int length = typePath.b[typePath.offset] * 2 + 1;
bv.putByteArray(typePath.b, typePath.offset, length);
}
// write type, and reserve space for values count
bv.putShort(cw.newUTF8(desc)).putShort(0);
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
bv.length - 2);
if (visible) {
aw.next = ctanns;
ctanns = aw;
} else {
aw.next = ictanns;
ictanns = aw;
}
return aw;
}
@Override
public void visitLineNumber(final int line, final Label start) {
if (lineNumber == null) {
lineNumber = new ByteVector();
}
++lineNumberCount;
lineNumber.putShort(start.position);
lineNumber.putShort(line);
}
@Override
public void visitMaxs(final int maxStack, final int maxLocals) {
if (compute == FRAMES) {
// completes the control flow graph with exception handler blocks
Handler handler = firstHandler;
while (handler != null) {
Label l = handler.start.getFirst();
Label h = handler.handler.getFirst();
Label e = handler.end.getFirst();
// computes the kind of the edges to 'h'
String t = handler.desc == null ? "java/lang/Throwable"
: handler.desc;
int kind = Frame.OBJECT | cw.addType(t);
// h is an exception handler
h.status |= Label.TARGET;
// adds 'h' as a successor of labels between 'start' and 'end'
while (l != e) {
// creates an edge to 'h'
Edge b = new Edge();
b.info = kind;
b.successor = h;
// adds it to the successors of 'l'
b.next = l.successors;
l.successors = b;
// goes to the next label
l = l.successor;
}
handler = handler.next;
}
// creates and visits the first (implicit) frame
Frame f = labels.frame;
f.initInputFrame(cw, access, Type.getArgumentTypes(descriptor),
this.maxLocals);
visitFrame(f);
/*
* fix point algorithm: mark the first basic block as 'changed'
* (i.e. put it in the 'changed' list) and, while there are changed
* basic blocks, choose one, mark it as unchanged, and update its
* successors (which can be changed in the process).
*/
int max = 0;
Label changed = labels;
while (changed != null) {
// removes a basic block from the list of changed basic blocks
Label l = changed;
changed = changed.next;
l.next = null;
f = l.frame;
// a reachable jump target must be stored in the stack map
if ((l.status & Label.TARGET) != 0) {
l.status |= Label.STORE;
}
// all visited labels are reachable, by definition
l.status |= Label.REACHABLE;
// updates the (absolute) maximum stack size
int blockMax = f.inputStack.length + l.outputStackMax;
if (blockMax > max) {
max = blockMax;
}
// updates the successors of the current basic block
Edge e = l.successors;
while (e != null) {
Label n = e.successor.getFirst();
boolean change = f.merge(cw, n.frame, e.info);
if (change && n.next == null) {
// if n has changed and is not already in the 'changed'
// list, adds it to this list
n.next = changed;
changed = n;
}
e = e.next;
}
}
// visits all the frames that must be stored in the stack map
Label l = labels;
while (l != null) {
f = l.frame;
if ((l.status & Label.STORE) != 0) {
visitFrame(f);
}
if ((l.status & Label.REACHABLE) == 0) {
// finds start and end of dead basic block
Label k = l.successor;
int start = l.position;
int end = (k == null ? code.length : k.position) - 1;
// if non empty basic block
if (end >= start) {
max = Math.max(max, 1);
// replaces instructions with NOP ... NOP ATHROW
for (int i = start; i < end; ++i) {
code.data[i] = Opcodes.NOP;
}
code.data[end] = (byte) Opcodes.ATHROW;
// emits a frame for this unreachable block
int frameIndex = startFrame(start, 0, 1);
frame[frameIndex] = Frame.OBJECT
| cw.addType("java/lang/Throwable");
endFrame();
// removes the start-end range from the exception
// handlers
firstHandler = Handler.remove(firstHandler, l, k);
}
}
l = l.successor;
}
handler = firstHandler;
handlerCount = 0;
while (handler != null) {
handlerCount += 1;
handler = handler.next;
}
this.maxStack = max;
} else if (compute == MAXS) {
// completes the control flow graph with exception handler blocks
Handler handler = firstHandler;
while (handler != null) {
Label l = handler.start;
Label h = handler.handler;
Label e = handler.end;
// adds 'h' as a successor of labels between 'start' and 'end'
while (l != e) {
// creates an edge to 'h'
Edge b = new Edge();
b.info = Edge.EXCEPTION;
b.successor = h;
// adds it to the successors of 'l'
if ((l.status & Label.JSR) == 0) {
b.next = l.successors;
l.successors = b;
} else {
// if l is a JSR block, adds b after the first two edges
// to preserve the hypothesis about JSR block successors
// order (see {@link #visitJumpInsn})
b.next = l.successors.next.next;
l.successors.next.next = b;
}
// goes to the next label
l = l.successor;
}
handler = handler.next;
}
if (subroutines > 0) {
// completes the control flow graph with the RET successors
/*
* first step: finds the subroutines. This step determines, for
* each basic block, to which subroutine(s) it belongs.
*/
// finds the basic blocks that belong to the "main" subroutine
int id = 0;
labels.visitSubroutine(null, 1, subroutines);
// finds the basic blocks that belong to the real subroutines
Label l = labels;
while (l != null) {
if ((l.status & Label.JSR) != 0) {
// the subroutine is defined by l's TARGET, not by l
Label subroutine = l.successors.next.successor;
// if this subroutine has not been visited yet...
if ((subroutine.status & Label.VISITED) == 0) {
// ...assigns it a new id and finds its basic blocks
id += 1;
subroutine.visitSubroutine(null, (id / 32L) << 32
| (1L << (id % 32)), subroutines);
}
}
l = l.successor;
}
// second step: finds the successors of RET blocks
l = labels;
while (l != null) {
if ((l.status & Label.JSR) != 0) {
Label L = labels;
while (L != null) {
L.status &= ~Label.VISITED2;
L = L.successor;
}
// the subroutine is defined by l's TARGET, not by l
Label subroutine = l.successors.next.successor;
subroutine.visitSubroutine(l, 0, subroutines);
}
l = l.successor;
}
}
/*
* control flow analysis algorithm: while the block stack is not
* empty, pop a block from this stack, update the max stack size,
* compute the true (non relative) begin stack size of the
* successors of this block, and push these successors onto the
* stack (unless they have already been pushed onto the stack).
* Note: by hypothesis, the {@link Label#inputStackTop} of the
* blocks in the block stack are the true (non relative) beginning
* stack sizes of these blocks.
*/
int max = 0;
Label stack = labels;
while (stack != null) {
// pops a block from the stack
Label l = stack;
stack = stack.next;
// computes the true (non relative) max stack size of this block
int start = l.inputStackTop;
int blockMax = start + l.outputStackMax;
// updates the global max stack size
if (blockMax > max) {
max = blockMax;
}
// analyzes the successors of the block
Edge b = l.successors;
if ((l.status & Label.JSR) != 0) {
// ignores the first edge of JSR blocks (virtual successor)
b = b.next;
}
while (b != null) {
l = b.successor;
// if this successor has not already been pushed...
if ((l.status & Label.PUSHED) == 0) {
// computes its true beginning stack size...
l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start
+ b.info;
// ...and pushes it onto the stack
l.status |= Label.PUSHED;
l.next = stack;
stack = l;
}
b = b.next;
}
}
this.maxStack = Math.max(maxStack, max);
} else {
this.maxStack = maxStack;
this.maxLocals = maxLocals;
}
}
@Override
public void visitEnd() {
}
// ------------------------------------------------------------------------
// Utility methods: control flow analysis algorithm
// ------------------------------------------------------------------------
Adds a successor to the currentBlock
block. Params: - info –
information about the control flow edge to be added.
- successor –
the successor block to be added to the current block.
/**
* Adds a successor to the {@link #currentBlock currentBlock} block.
*
* @param info
* information about the control flow edge to be added.
* @param successor
* the successor block to be added to the current block.
*/
private void addSuccessor(final int info, final Label successor) {
// creates and initializes an Edge object...
Edge b = new Edge();
b.info = info;
b.successor = successor;
// ...and adds it to the successor list of the currentBlock block
b.next = currentBlock.successors;
currentBlock.successors = b;
}
Ends the current basic block. This method must be used in the case where
the current basic block does not have any successor.
/**
* Ends the current basic block. This method must be used in the case where
* the current basic block does not have any successor.
*/
private void noSuccessor() {
if (compute == FRAMES) {
Label l = new Label();
l.frame = new Frame();
l.frame.owner = l;
l.resolve(this, code.length, code.data);
previousBlock.successor = l;
previousBlock = l;
} else {
currentBlock.outputStackMax = maxStackSize;
}
if (compute != INSERTED_FRAMES) {
currentBlock = null;
}
}
// ------------------------------------------------------------------------
// Utility methods: stack map frames
// ------------------------------------------------------------------------
Visits a frame that has been computed from scratch.
Params: - f –
the frame that must be visited.
/**
* Visits a frame that has been computed from scratch.
*
* @param f
* the frame that must be visited.
*/
private void visitFrame(final Frame f) {
int i, t;
int nTop = 0;
int nLocal = 0;
int nStack = 0;
int[] locals = f.inputLocals;
int[] stacks = f.inputStack;
// computes the number of locals (ignores TOP types that are just after
// a LONG or a DOUBLE, and all trailing TOP types)
for (i = 0; i < locals.length; ++i) {
t = locals[i];
if (t == Frame.TOP) {
++nTop;
} else {
nLocal += nTop + 1;
nTop = 0;
}
if (t == Frame.LONG || t == Frame.DOUBLE) {
++i;
}
}
// computes the stack size (ignores TOP types that are just after
// a LONG or a DOUBLE)
for (i = 0; i < stacks.length; ++i) {
t = stacks[i];
++nStack;
if (t == Frame.LONG || t == Frame.DOUBLE) {
++i;
}
}
// visits the frame and its content
int frameIndex = startFrame(f.owner.position, nLocal, nStack);
for (i = 0; nLocal > 0; ++i, --nLocal) {
t = locals[i];
frame[frameIndex++] = t;
if (t == Frame.LONG || t == Frame.DOUBLE) {
++i;
}
}
for (i = 0; i < stacks.length; ++i) {
t = stacks[i];
frame[frameIndex++] = t;
if (t == Frame.LONG || t == Frame.DOUBLE) {
++i;
}
}
endFrame();
}
Visit the implicit first frame of this method.
/**
* Visit the implicit first frame of this method.
*/
private void visitImplicitFirstFrame() {
// There can be at most descriptor.length() + 1 locals
int frameIndex = startFrame(0, descriptor.length() + 1, 0);
if ((access & Opcodes.ACC_STATIC) == 0) {
if ((access & ACC_CONSTRUCTOR) == 0) {
frame[frameIndex++] = Frame.OBJECT | cw.addType(cw.thisName);
} else {
frame[frameIndex++] = Frame.UNINITIALIZED_THIS;
}
}
int i = 1;
loop: while (true) {
int j = i;
switch (descriptor.charAt(i++)) {
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
frame[frameIndex++] = Frame.INTEGER;
break;
case 'F':
frame[frameIndex++] = Frame.FLOAT;
break;
case 'J':
frame[frameIndex++] = Frame.LONG;
break;
case 'D':
frame[frameIndex++] = Frame.DOUBLE;
break;
case '[':
while (descriptor.charAt(i) == '[') {
++i;
}
if (descriptor.charAt(i) == 'L') {
++i;
while (descriptor.charAt(i) != ';') {
++i;
}
}
frame[frameIndex++] = Frame.type(cw, descriptor.substring(j, ++i));
break;
case 'L':
while (descriptor.charAt(i) != ';') {
++i;
}
frame[frameIndex++] = Frame.OBJECT
| cw.addType(descriptor.substring(j + 1, i++));
break;
default:
break loop;
}
}
frame[1] = frameIndex - 3;
endFrame();
}
Starts the visit of a stack map frame.
Params: - offset –
the offset of the instruction to which the frame corresponds.
- nLocal –
the number of local variables in the frame.
- nStack –
the number of stack elements in the frame.
Returns: the index of the next element to be written in this frame.
/**
* Starts the visit of a stack map frame.
*
* @param offset
* the offset of the instruction to which the frame corresponds.
* @param nLocal
* the number of local variables in the frame.
* @param nStack
* the number of stack elements in the frame.
* @return the index of the next element to be written in this frame.
*/
private int startFrame(final int offset, final int nLocal, final int nStack) {
int n = 3 + nLocal + nStack;
if (frame == null || frame.length < n) {
frame = new int[n];
}
frame[0] = offset;
frame[1] = nLocal;
frame[2] = nStack;
return 3;
}
Checks if the visit of the current frame frame
is finished, and if yes, write it in the StackMapTable attribute. /**
* Checks if the visit of the current frame {@link #frame} is finished, and
* if yes, write it in the StackMapTable attribute.
*/
private void endFrame() {
if (previousFrame != null) { // do not write the first frame
if (stackMap == null) {
stackMap = new ByteVector();
}
writeFrame();
++frameCount;
}
previousFrame = frame;
frame = null;
}
Compress and writes the current frame frame
in the StackMapTable attribute. /**
* Compress and writes the current frame {@link #frame} in the StackMapTable
* attribute.
*/
private void writeFrame() {
int clocalsSize = frame[1];
int cstackSize = frame[2];
if ((cw.version & 0xFFFF) < Opcodes.V1_6) {
stackMap.putShort(frame[0]).putShort(clocalsSize);
writeFrameTypes(3, 3 + clocalsSize);
stackMap.putShort(cstackSize);
writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize);
return;
}
int localsSize = previousFrame[1];
int type = FULL_FRAME;
int k = 0;
int delta;
if (frameCount == 0) {
delta = frame[0];
} else {
delta = frame[0] - previousFrame[0] - 1;
}
if (cstackSize == 0) {
k = clocalsSize - localsSize;
switch (k) {
case -3:
case -2:
case -1:
type = CHOP_FRAME;
localsSize = clocalsSize;
break;
case 0:
type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED;
break;
case 1:
case 2:
case 3:
type = APPEND_FRAME;
break;
}
} else if (clocalsSize == localsSize && cstackSize == 1) {
type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME
: SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
}
if (type != FULL_FRAME) {
// verify if locals are the same
int l = 3;
for (int j = 0; j < localsSize; j++) {
if (frame[l] != previousFrame[l]) {
type = FULL_FRAME;
break;
}
l++;
}
}
switch (type) {
case SAME_FRAME:
stackMap.putByte(delta);
break;
case SAME_LOCALS_1_STACK_ITEM_FRAME:
stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);
break;
case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort(
delta);
writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);
break;
case SAME_FRAME_EXTENDED:
stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);
break;
case CHOP_FRAME:
stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
break;
case APPEND_FRAME:
stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
writeFrameTypes(3 + localsSize, 3 + clocalsSize);
break;
// case FULL_FRAME:
default:
stackMap.putByte(FULL_FRAME).putShort(delta).putShort(clocalsSize);
writeFrameTypes(3, 3 + clocalsSize);
stackMap.putShort(cstackSize);
writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize);
}
}
Writes some types of the current frame frame
into the StackMapTableAttribute. This method converts types from the format used in Label
to the format used in StackMapTable attributes. In particular, it converts type table indexes to constant pool indexes. Params:
/**
* Writes some types of the current frame {@link #frame} into the
* StackMapTableAttribute. This method converts types from the format used
* in {@link Label} to the format used in StackMapTable attributes. In
* particular, it converts type table indexes to constant pool indexes.
*
* @param start
* index of the first type in {@link #frame} to write.
* @param end
* index of last type in {@link #frame} to write (exclusive).
*/
private void writeFrameTypes(final int start, final int end) {
for (int i = start; i < end; ++i) {
int t = frame[i];
int d = t & Frame.DIM;
if (d == 0) {
int v = t & Frame.BASE_VALUE;
switch (t & Frame.BASE_KIND) {
case Frame.OBJECT:
stackMap.putByte(7).putShort(
cw.newClass(cw.typeTable[v].strVal1));
break;
case Frame.UNINITIALIZED:
stackMap.putByte(8).putShort(cw.typeTable[v].intVal);
break;
default:
stackMap.putByte(v);
}
} else {
StringBuilder sb = new StringBuilder();
d >>= 28;
while (d-- > 0) {
sb.append('[');
}
if ((t & Frame.BASE_KIND) == Frame.OBJECT) {
sb.append('L');
sb.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1);
sb.append(';');
} else {
switch (t & 0xF) {
case 1:
sb.append('I');
break;
case 2:
sb.append('F');
break;
case 3:
sb.append('D');
break;
case 9:
sb.append('Z');
break;
case 10:
sb.append('B');
break;
case 11:
sb.append('C');
break;
case 12:
sb.append('S');
break;
default:
sb.append('J');
}
}
stackMap.putByte(7).putShort(cw.newClass(sb.toString()));
}
}
}
private void writeFrameType(final Object type) {
if (type instanceof String) {
stackMap.putByte(7).putShort(cw.newClass((String) type));
} else if (type instanceof Integer) {
stackMap.putByte(((Integer) type).intValue());
} else {
stackMap.putByte(8).putShort(((Label) type).position);
}
}
// ------------------------------------------------------------------------
// Utility methods: dump bytecode array
// ------------------------------------------------------------------------
Returns the size of the bytecode of this method.
Returns: the size of the bytecode of this method.
/**
* Returns the size of the bytecode of this method.
*
* @return the size of the bytecode of this method.
*/
final int getSize() {
if (classReaderOffset != 0) {
return 6 + classReaderLength;
}
int size = 8;
if (code.length > 0) {
if (code.length > 65535) {
throw new RuntimeException("Method code too large!");
}
cw.newUTF8("Code");
size += 18 + code.length + 8 * handlerCount;
if (localVar != null) {
cw.newUTF8("LocalVariableTable");
size += 8 + localVar.length;
}
if (localVarType != null) {
cw.newUTF8("LocalVariableTypeTable");
size += 8 + localVarType.length;
}
if (lineNumber != null) {
cw.newUTF8("LineNumberTable");
size += 8 + lineNumber.length;
}
if (stackMap != null) {
boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
cw.newUTF8(zip ? "StackMapTable" : "StackMap");
size += 8 + stackMap.length;
}
if (ctanns != null) {
cw.newUTF8("RuntimeVisibleTypeAnnotations");
size += 8 + ctanns.getSize();
}
if (ictanns != null) {
cw.newUTF8("RuntimeInvisibleTypeAnnotations");
size += 8 + ictanns.getSize();
}
if (cattrs != null) {
size += cattrs.getSize(cw, code.data, code.length, maxStack,
maxLocals);
}
}
if (exceptionCount > 0) {
cw.newUTF8("Exceptions");
size += 8 + 2 * exceptionCount;
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
cw.newUTF8("Synthetic");
size += 6;
}
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
cw.newUTF8("Deprecated");
size += 6;
}
if (signature != null) {
cw.newUTF8("Signature");
cw.newUTF8(signature);
size += 8;
}
if (methodParameters != null) {
cw.newUTF8("MethodParameters");
size += 7 + methodParameters.length;
}
if (annd != null) {
cw.newUTF8("AnnotationDefault");
size += 6 + annd.length;
}
if (anns != null) {
cw.newUTF8("RuntimeVisibleAnnotations");
size += 8 + anns.getSize();
}
if (ianns != null) {
cw.newUTF8("RuntimeInvisibleAnnotations");
size += 8 + ianns.getSize();
}
if (tanns != null) {
cw.newUTF8("RuntimeVisibleTypeAnnotations");
size += 8 + tanns.getSize();
}
if (itanns != null) {
cw.newUTF8("RuntimeInvisibleTypeAnnotations");
size += 8 + itanns.getSize();
}
if (panns != null) {
cw.newUTF8("RuntimeVisibleParameterAnnotations");
size += 7 + 2 * (panns.length - synthetics);
for (int i = panns.length - 1; i >= synthetics; --i) {
size += panns[i] == null ? 0 : panns[i].getSize();
}
}
if (ipanns != null) {
cw.newUTF8("RuntimeInvisibleParameterAnnotations");
size += 7 + 2 * (ipanns.length - synthetics);
for (int i = ipanns.length - 1; i >= synthetics; --i) {
size += ipanns[i] == null ? 0 : ipanns[i].getSize();
}
}
if (attrs != null) {
size += attrs.getSize(cw, null, 0, -1, -1);
}
return size;
}
Puts the bytecode of this method in the given byte vector.
Params: - out –
the byte vector into which the bytecode of this method must be
copied.
/**
* Puts the bytecode of this method in the given byte vector.
*
* @param out
* the byte vector into which the bytecode of this method must be
* copied.
*/
final void put(final ByteVector out) {
final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC;
int mask = ACC_CONSTRUCTOR | Opcodes.ACC_DEPRECATED
| ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
| ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR);
out.putShort(access & ~mask).putShort(name).putShort(desc);
if (classReaderOffset != 0) {
out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength);
return;
}
int attributeCount = 0;
if (code.length > 0) {
++attributeCount;
}
if (exceptionCount > 0) {
++attributeCount;
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
++attributeCount;
}
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
++attributeCount;
}
if (signature != null) {
++attributeCount;
}
if (methodParameters != null) {
++attributeCount;
}
if (annd != null) {
++attributeCount;
}
if (anns != null) {
++attributeCount;
}
if (ianns != null) {
++attributeCount;
}
if (tanns != null) {
++attributeCount;
}
if (itanns != null) {
++attributeCount;
}
if (panns != null) {
++attributeCount;
}
if (ipanns != null) {
++attributeCount;
}
if (attrs != null) {
attributeCount += attrs.getCount();
}
out.putShort(attributeCount);
if (code.length > 0) {
int size = 12 + code.length + 8 * handlerCount;
if (localVar != null) {
size += 8 + localVar.length;
}
if (localVarType != null) {
size += 8 + localVarType.length;
}
if (lineNumber != null) {
size += 8 + lineNumber.length;
}
if (stackMap != null) {
size += 8 + stackMap.length;
}
if (ctanns != null) {
size += 8 + ctanns.getSize();
}
if (ictanns != null) {
size += 8 + ictanns.getSize();
}
if (cattrs != null) {
size += cattrs.getSize(cw, code.data, code.length, maxStack,
maxLocals);
}
out.putShort(cw.newUTF8("Code")).putInt(size);
out.putShort(maxStack).putShort(maxLocals);
out.putInt(code.length).putByteArray(code.data, 0, code.length);
out.putShort(handlerCount);
if (handlerCount > 0) {
Handler h = firstHandler;
while (h != null) {
out.putShort(h.start.position).putShort(h.end.position)
.putShort(h.handler.position).putShort(h.type);
h = h.next;
}
}
attributeCount = 0;
if (localVar != null) {
++attributeCount;
}
if (localVarType != null) {
++attributeCount;
}
if (lineNumber != null) {
++attributeCount;
}
if (stackMap != null) {
++attributeCount;
}
if (ctanns != null) {
++attributeCount;
}
if (ictanns != null) {
++attributeCount;
}
if (cattrs != null) {
attributeCount += cattrs.getCount();
}
out.putShort(attributeCount);
if (localVar != null) {
out.putShort(cw.newUTF8("LocalVariableTable"));
out.putInt(localVar.length + 2).putShort(localVarCount);
out.putByteArray(localVar.data, 0, localVar.length);
}
if (localVarType != null) {
out.putShort(cw.newUTF8("LocalVariableTypeTable"));
out.putInt(localVarType.length + 2).putShort(localVarTypeCount);
out.putByteArray(localVarType.data, 0, localVarType.length);
}
if (lineNumber != null) {
out.putShort(cw.newUTF8("LineNumberTable"));
out.putInt(lineNumber.length + 2).putShort(lineNumberCount);
out.putByteArray(lineNumber.data, 0, lineNumber.length);
}
if (stackMap != null) {
boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap"));
out.putInt(stackMap.length + 2).putShort(frameCount);
out.putByteArray(stackMap.data, 0, stackMap.length);
}
if (ctanns != null) {
out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
ctanns.put(out);
}
if (ictanns != null) {
out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
ictanns.put(out);
}
if (cattrs != null) {
cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out);
}
}
if (exceptionCount > 0) {
out.putShort(cw.newUTF8("Exceptions")).putInt(
2 * exceptionCount + 2);
out.putShort(exceptionCount);
for (int i = 0; i < exceptionCount; ++i) {
out.putShort(exceptions[i]);
}
}
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
out.putShort(cw.newUTF8("Synthetic")).putInt(0);
}
}
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
out.putShort(cw.newUTF8("Deprecated")).putInt(0);
}
if (signature != null) {
out.putShort(cw.newUTF8("Signature")).putInt(2)
.putShort(cw.newUTF8(signature));
}
if (methodParameters != null) {
out.putShort(cw.newUTF8("MethodParameters"));
out.putInt(methodParameters.length + 1).putByte(
methodParametersCount);
out.putByteArray(methodParameters.data, 0, methodParameters.length);
}
if (annd != null) {
out.putShort(cw.newUTF8("AnnotationDefault"));
out.putInt(annd.length);
out.putByteArray(annd.data, 0, annd.length);
}
if (anns != null) {
out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
anns.put(out);
}
if (ianns != null) {
out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
ianns.put(out);
}
if (tanns != null) {
out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
tanns.put(out);
}
if (itanns != null) {
out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
itanns.put(out);
}
if (panns != null) {
out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations"));
AnnotationWriter.put(panns, synthetics, out);
}
if (ipanns != null) {
out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations"));
AnnotationWriter.put(ipanns, synthetics, out);
}
if (attrs != null) {
attrs.put(cw, null, 0, -1, -1, out);
}
}
}