/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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-2007 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
* 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.
*/
/**
* A Java class parser to make a {@link ClassVisitor} visit an existing class.
* This class parses a byte array conforming to the Java class file format and
* calls the appropriate visit methods of a given class visitor for each field,
* method and bytecode instruction encountered.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
public class ClassReader {
/**
* True to enable signatures support.
*/
static final boolean SIGNATURES = true;
/**
* True to enable annotations support.
*/
static final boolean ANNOTATIONS = true;
/**
* True to enable stack map frames support.
*/
static final boolean FRAMES = true;
/**
* True to enable bytecode writing support.
*/
static final boolean WRITER = true;
/**
* True to enable JSR_W and GOTO_W support.
*/
static final boolean RESIZE = true;
/**
* Flag to skip method code. If this class is set <code>CODE</code>
* attribute won't be visited. This can be used, for example, to retrieve
* annotations for methods and method parameters.
*/
/**
* Flag to skip the debug information in the class. If this flag is set the
* debug information of the class is not visited, i.e. the
* {@link MethodVisitor#visitLocalVariable visitLocalVariable} and
* {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be
* called.
*/
/**
* Flag to skip the stack map frames in the class. If this flag is set the
* stack map frames of the class is not visited, i.e. the
* {@link MethodVisitor#visitFrame visitFrame} method will not be called.
* This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is
* used: it avoids visiting frames that will be ignored and recomputed from
* scratch in the class writer.
*/
/**
* Flag to expand the stack map frames. By default stack map frames are
* visited in their original format (i.e. "expanded" for classes whose
* version is less than V1_6, and "compressed" for the other classes). If
* this flag is set, stack map frames are always visited in expanded format
* (this option adds a decompression/recompression step in ClassReader and
* ClassWriter which degrades performances quite a lot).
*/
/**
* The class to be parsed. <i>The content of this array must not be
* modified. This field is intended for {@link Attribute} sub classes, and
* is normally not needed by class generators or adapters.</i>
*/
public final byte[] b;
/**
* The start index of each constant pool item in {@link #b b}, plus one.
* The one byte offset skips the constant pool item tag that indicates its
* type.
*/
private final int[] items;
/**
* The String objects corresponding to the CONSTANT_Utf8 items. This cache
* avoids multiple parsing of a given CONSTANT_Utf8 constant pool item,
* which GREATLY improves performances (by a factor 2 to 3). This caching
* strategy could be extended to all constant pool items, but its benefit
* would not be so great for these items (because they are much less
* expensive to parse than CONSTANT_Utf8 items).
*/
/**
* Maximum length of the strings contained in the constant pool of the
* class.
*/
private final int maxStringLength;
/**
* Start index of the class header information (access, name...) in
* {@link #b b}.
*/
public final int header;
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Constructs a new {@link ClassReader} object.
*
* @param b the bytecode of the class to be read.
*/
public ClassReader(final byte[] b) {
this(b, 0, b.length);
}
/**
* Constructs a new {@link ClassReader} object.
*
* @param b the bytecode of the class to be read.
* @param off the start offset of the class data.
* @param len the length of the class data.
*/
this.b = b;
// parses the constant pool
int max = 0;
for (int i = 1; i < n; ++i) {
int size;
switch (b[index]) {
case ClassWriter.FIELD:
case ClassWriter.METH:
case ClassWriter.IMETH:
case ClassWriter.INT:
case ClassWriter.FLOAT:
case ClassWriter.NAME_TYPE:
size = 5;
break;
case ClassWriter.LONG:
case ClassWriter.DOUBLE:
size = 9;
++i;
break;
case ClassWriter.UTF8:
}
break;
// case ClassWriter.CLASS:
// case ClassWriter.STR:
default:
size = 3;
break;
}
}
// the class header information starts just after the constant pool
}
/**
* Returns the class's access flags (see {@link Opcodes}). This value may
* not reflect Deprecated and Synthetic flags when bytecode is before 1.5
* and those flags are represented by attributes.
*
* @return the class access flags
*
* @see ClassVisitor#visit(int, int, String, String, String, String[])
*/
public int getAccess() {
return readUnsignedShort(header);
}
/**
* Returns the internal name of the class (see
* {@link Type#getInternalName() getInternalName}).
*
* @return the internal class name
*
* @see ClassVisitor#visit(int, int, String, String, String, String[])
*/
}
/**
* Returns the internal of name of the super class (see
* {@link Type#getInternalName() getInternalName}). For interfaces, the
* super class is {@link Object}.
*
* @return the internal name of super class, or <tt>null</tt> for
* {@link Object} class.
*
* @see ClassVisitor#visit(int, int, String, String, String, String[])
*/
}
/**
* Returns the internal names of the class's interfaces (see
* {@link Type#getInternalName() getInternalName}).
*
* @return the array of internal names for all implemented interfaces or
* <tt>null</tt>.
*
* @see ClassVisitor#visit(int, int, String, String, String, String[])
*/
int n = readUnsignedShort(index);
if (n > 0) {
char[] buf = new char[maxStringLength];
for (int i = 0; i < n; ++i) {
index += 2;
}
}
return interfaces;
}
/**
* Copies the constant pool data into the given {@link ClassWriter}. Should
* be called before the {@link #accept(ClassVisitor,int)} method.
*
* @param classWriter the {@link ClassWriter} to copy constant pool into.
*/
char[] buf = new char[maxStringLength];
for (int i = 1; i < ll; i++) {
int nameType;
switch (tag) {
case ClassWriter.FIELD:
case ClassWriter.METH:
case ClassWriter.IMETH:
break;
case ClassWriter.INT:
break;
case ClassWriter.FLOAT:
break;
case ClassWriter.NAME_TYPE:
null);
break;
case ClassWriter.LONG:
++i;
break;
case ClassWriter.DOUBLE:
++i;
break;
case ClassWriter.UTF8: {
if (s == null) {
buf);
}
}
break;
// case ClassWriter.STR:
// case ClassWriter.CLASS:
default:
break;
}
}
}
/**
* Constructs a new {@link ClassReader} object.
*
* @param is an input stream from which to read the class.
* @throws IOException if a problem occurs during reading.
*/
}
/**
* Constructs a new {@link ClassReader} object.
*
* @param name the fully qualified name of the class to be read.
* @throws IOException if an exception occurs during reading.
*/
+ ".class"));
}
/**
* Reads the bytecode of a class.
*
* @param is an input stream from which to read the class.
* @return the bytecode read from the given input stream.
* @throws IOException if a problem occurs during reading.
*/
throw new IOException("Class not found");
}
int len = 0;
while (true) {
if (n == -1) {
byte[] c = new byte[len];
b = c;
}
return b;
}
len += n;
byte[] c = new byte[b.length + 1000];
b = c;
}
}
}
// ------------------------------------------------------------------------
// Public methods
// ------------------------------------------------------------------------
/**
* Makes the given visitor visit the Java class of this {@link ClassReader}.
* This class is the one specified in the constructor (see
* {@link #ClassReader(byte[]) ClassReader}).
*
* @param classVisitor the visitor that must visit this class.
* @param flags option flags that can be used to modify the default behavior
* of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES},
* {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
*/
}
/**
* Makes the given visitor visit the Java class of this {@link ClassReader}.
* This class is the one specified in the constructor (see
* {@link #ClassReader(byte[]) ClassReader}).
*
* @param classVisitor the visitor that must visit this class.
* @param attrs prototypes of the attributes that must be parsed during the
* visit of the class. Any attribute whose type is not equal to the
* type of one the prototypes will not be parsed: its byte array
* value will be passed unchanged to the ClassWriter. <i>This may
* corrupt it if this value contains references to the constant pool,
* or has syntactic or semantic links with a class element that has
* been transformed by a class adapter between the reader and the
* writer</i>.
* @param flags option flags that can be used to modify the default behavior
* of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES},
* {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
*/
public void accept(
final ClassVisitor classVisitor,
final int flags)
{
byte[] b = this.b; // the bytecode array
char[] c = new char[maxStringLength]; // buffer used to read strings
int i, j, k; // loop variables
int u, v, w; // indexes in b
int access;
int anns = 0;
int ianns = 0;
// visits the header
u = header;
access = readUnsignedShort(u);
w = 0;
u += 8;
implementedItfs[i] = readClass(u, c);
u += 2;
}
// skips fields and methods
v = u;
i = readUnsignedShort(v);
v += 2;
for (; i > 0; --i) {
j = readUnsignedShort(v + 6);
v += 8;
for (; j > 0; --j) {
}
}
i = readUnsignedShort(v);
v += 2;
for (; i > 0; --i) {
j = readUnsignedShort(v + 6);
v += 8;
for (; j > 0; --j) {
}
}
// reads the class's attributes
i = readUnsignedShort(v);
v += 2;
for (; i > 0; --i) {
// tests are sorted in decreasing frequency order
// (based on frequencies observed on typical classes)
w = v + 6;
if (item != 0) {
}
anns = v + 6;
ianns = v + 6;
} else {
v + 6,
readInt(v + 2),
c,
-1,
null);
}
}
}
// calls the visit method
name,
// calls the visitSource method
}
// calls the visitOuterClass method
if (enclosingOwner != null) {
}
// visits the class annotations
if (ANNOTATIONS) {
for (i = 1; i >= 0; --i) {
if (v != 0) {
j = readUnsignedShort(v);
v += 2;
for (; j > 0; --j) {
v = readAnnotationValues(v + 2,
c,
true,
}
}
}
}
// visits the class attributes
}
// calls the visitInnerClass method
if (w != 0) {
i = readUnsignedShort(w);
w += 2;
for (; i > 0; --i) {
? null
? null
? null
w += 8;
}
}
// visits the fields
i = readUnsignedShort(u);
u += 2;
for (; i > 0; --i) {
access = readUnsignedShort(u);
// visits the field's attributes and looks for a ConstantValue
// attribute
int fieldValueItem = 0;
anns = 0;
ianns = 0;
j = readUnsignedShort(u + 6);
u += 8;
for (; j > 0; --j) {
// tests are sorted in decreasing frequency order
// (based on frequencies observed on typical classes)
anns = u + 6;
ianns = u + 6;
} else {
u + 6,
readInt(u + 2),
c,
-1,
null);
}
}
}
// visits the field
name,
desc,
// visits the field annotations and attributes
if (ANNOTATIONS) {
for (j = 1; j >= 0; --j) {
if (v != 0) {
k = readUnsignedShort(v);
v += 2;
for (; k > 0; --k) {
v = readAnnotationValues(v + 2,
c,
true,
}
}
}
}
}
}
}
// visits the methods
i = readUnsignedShort(u);
u += 2;
for (; i > 0; --i) {
int u0 = u + 6;
access = readUnsignedShort(u);
anns = 0;
ianns = 0;
int dann = 0;
int mpanns = 0;
int impanns = 0;
v = 0;
w = 0;
// looks for Code and Exceptions attributes
j = readUnsignedShort(u + 6);
u += 8;
for (; j > 0; --j) {
u += 6;
// tests are sorted in decreasing frequency order
// (based on frequencies observed on typical classes)
if (!skipCode) {
v = u;
}
w = u;
anns = u;
dann = u;
ianns = u;
{
mpanns = u;
{
impanns = u;
} else {
u,
c,
-1,
null);
}
}
u += attrSize;
}
// reads declared exceptions
String[] exceptions;
if (w == 0) {
exceptions = null;
} else {
w += 2;
exceptions[j] = readClass(w, c);
w += 2;
}
}
// visits the method's code, if any
name,
desc,
/*
* if the returned MethodVisitor is in fact a MethodWriter, it
* means there is no method adapter between the reader and the
* writer. If, in addition, the writer's constant pool was
* copied from this reader (mw.cw.cr == this), and the signature
* and exceptions of the method have not been changed, then it
* is possible to skip all visit events and just copy the
* original code of the method to the writer (the access, name
* and descriptor can have been changed, this is not important
* since they are not copied as is from the reader).
*/
boolean sameExceptions = false;
if (exceptions == null) {
} else {
sameExceptions = true;
{
w -= 2;
{
sameExceptions = false;
break;
}
}
}
}
if (sameExceptions) {
/*
* we do not copy directly the code into
* MethodWriter to save a byte array copy
* operation. The real copy will be done in
* ClassWriter.toByteArray().
*/
continue;
}
}
}
}
}
}
if (ANNOTATIONS) {
for (j = 1; j >= 0; --j) {
if (w != 0) {
k = readUnsignedShort(w);
w += 2;
for (; k > 0; --k) {
w = readAnnotationValues(w + 2,
c,
true,
}
}
}
}
}
}
}
}
int maxStack = readUnsignedShort(v);
v += 8;
int codeStart = v;
int codeEnd = v + codeLength;
// 1st phase: finds the labels
int label;
while (v < codeEnd) {
w = v - codeStart;
int opcode = b[v] & 0xFF;
case ClassWriter.NOARG_INSN:
case ClassWriter.IMPLVAR_INSN:
v += 1;
break;
case ClassWriter.LABEL_INSN:
v += 3;
break;
case ClassWriter.LABELW_INSN:
v += 5;
break;
case ClassWriter.WIDE_INSN:
v += 6;
} else {
v += 4;
}
break;
case ClassWriter.TABL_INSN:
// skips 0 to 3 padding bytes*
v = v + 4 - (w & 3);
// reads instruction
v += 12;
for (; j > 0; --j) {
v += 4;
}
break;
case ClassWriter.LOOK_INSN:
// skips 0 to 3 padding bytes*
v = v + 4 - (w & 3);
// reads instruction
j = readInt(v + 4);
v += 8;
for (; j > 0; --j) {
v += 8;
}
break;
case ClassWriter.VAR_INSN:
case ClassWriter.SBYTE_INSN:
case ClassWriter.LDC_INSN:
v += 2;
break;
case ClassWriter.SHORT_INSN:
case ClassWriter.LDCW_INSN:
case ClassWriter.FIELDORMETH_INSN:
case ClassWriter.TYPE_INSN:
case ClassWriter.IINC_INSN:
v += 3;
break;
case ClassWriter.ITFMETH_INSN:
v += 5;
break;
// case MANA_INSN:
default:
v += 4;
break;
}
}
// parses the try catch entries
j = readUnsignedShort(v);
v += 2;
for (; j > 0; --j) {
if (type == 0) {
} else {
end,
}
v += 8;
}
// parses the local variable, line number tables, and code
// attributes
int varTable = 0;
int varTypeTable = 0;
int stackMap = 0;
int frameCount = 0;
int frameMode = 0;
int frameOffset = 0;
int frameLocalCount = 0;
int frameLocalDiff = 0;
int frameStackCount = 0;
boolean zip = true;
j = readUnsignedShort(v);
v += 2;
for (; j > 0; --j) {
if (!skipDebug) {
varTable = v + 6;
k = readUnsignedShort(v + 6);
w = v + 8;
for (; k > 0; --k) {
label = readUnsignedShort(w);
}
}
w += 10;
}
}
varTypeTable = v + 6;
if (!skipDebug) {
k = readUnsignedShort(v + 6);
w = v + 8;
for (; k > 0; --k) {
label = readUnsignedShort(w);
}
w += 4;
}
}
stackMap = v + 8;
}
/*
* here we do not extract the labels corresponding to
* the attribute content. This would require a full
* parsing of the attribute, which would need to be
* repeated in the second phase (see below). Instead the
* content of the attribute is read one frame at a time
* (i.e. after a frame has been visited, the next frame
* is read), and the labels it contains are also
* extracted one frame at a time. Thanks to the ordering
* of frames, having only a "one frame lookahead" is not
* a problem, i.e. it is not possible to see an offset
* smaller than the offset of the current insn and for
* which no Label exist.
*/
// TODO true for frame offsets,
// but for UNINITIALIZED type offsets?
stackMap = v + 8;
zip = false;
}
/*
* IMPORTANT! here we assume that the frames are
* ordered, as in the StackMapTable attribute, although
* this is not guaranteed by the attribute format.
*/
} else {
v + 6,
readInt(v + 2),
c,
codeStart - 8,
labels);
}
}
}
}
}
// 2nd phase: visits each instruction
// creates the very first (implicit) frame from the method
// descriptor
if (unzip) {
int local = 0;
} else {
}
}
j = 1;
loop: while (true) {
k = j;
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
break;
case 'F':
break;
case 'J':
break;
case 'D':
break;
case '[':
++j;
}
++j;
++j;
}
}
break;
case 'L':
++j;
}
j++);
break;
default:
break loop;
}
}
}
/*
* for the first explicit frame the offset is not
* offset_delta + 1 but only offset_delta; setting the
* implicit frame offset to -1 allow the use of the
* "offset_delta + 1" rule in all cases
*/
frameOffset = -1;
}
v = codeStart;
Label l;
while (v < codeEnd) {
w = v - codeStart;
l = labels[w];
if (l != null) {
mv.visitLabel(l);
}
}
{
// if there is a frame for this offset,
// makes the visitor visit it,
// and reads the next frame if there is one.
} else if (frameOffset != -1) {
}
if (frameCount > 0) {
if (zip) {
} else {
frameOffset = -1;
}
frameLocalDiff = 0;
{
frameStackCount = 0;
0,
c,
labels);
frameStackCount = 1;
} else {
stackMap += 2;
{
0,
c,
labels);
frameStackCount = 1;
{
- tag;
frameStackCount = 0;
{
frameStackCount = 0;
for (k = tag
{
j++,
c,
labels);
}
frameStackCount = 0;
} else { // if (tag == FULL_FRAME) {
stackMap += 2;
for (j = 0; n > 0; n--) {
j++,
c,
labels);
}
stackMap += 2;
for (j = 0; n > 0; n--) {
j++,
c,
labels);
}
}
}
--frameCount;
} else {
frameLocal = null;
}
}
int opcode = b[v] & 0xFF;
case ClassWriter.NOARG_INSN:
v += 1;
break;
case ClassWriter.IMPLVAR_INSN:
opcode & 0x3);
} else {
opcode & 0x3);
}
v += 1;
break;
case ClassWriter.LABEL_INSN:
+ readShort(v + 1)]);
v += 3;
break;
case ClassWriter.LABELW_INSN:
+ readInt(v + 1)]);
v += 5;
break;
case ClassWriter.WIDE_INSN:
readShort(v + 4));
v += 6;
} else {
readUnsignedShort(v + 2));
v += 4;
}
break;
case ClassWriter.TABL_INSN:
// skips 0 to 3 padding bytes
v = v + 4 - (w & 3);
// reads instruction
v += 12;
v += 4;
}
max,
table);
break;
case ClassWriter.LOOK_INSN:
// skips 0 to 3 padding bytes
v = v + 4 - (w & 3);
// reads instruction
j = readInt(v + 4);
v += 8;
int[] keys = new int[j];
v += 8;
}
keys,
values);
break;
case ClassWriter.VAR_INSN:
v += 2;
break;
case ClassWriter.SBYTE_INSN:
v += 2;
break;
case ClassWriter.SHORT_INSN:
v += 3;
break;
case ClassWriter.LDC_INSN:
v += 2;
break;
case ClassWriter.LDCW_INSN:
c));
v += 3;
break;
case ClassWriter.FIELDORMETH_INSN:
case ClassWriter.ITFMETH_INSN:
} else {
}
v += 5;
} else {
v += 3;
}
break;
case ClassWriter.TYPE_INSN:
v += 3;
break;
case ClassWriter.IINC_INSN:
v += 3;
break;
// case MANA_INSN:
default:
b[v + 3] & 0xFF);
v += 4;
break;
}
}
if (l != null) {
mv.visitLabel(l);
}
// visits the local variable tables
if (varTypeTable != 0) {
w = varTypeTable + 2;
typeTable = new int[k];
while (k > 0) {
w += 10;
}
}
k = readUnsignedShort(varTable);
w = varTable + 2;
for (; k > 0; --k) {
int start = readUnsignedShort(w);
{
break;
}
}
}
readUTF8(w + 6, c),
index);
w += 10;
}
}
// visits the other attributes
}
// visits the max stack and max locals values
}
}
}
// visits the end of the class
}
/**
* Reads parameter annotations and makes the given visitor visit them.
*
* @param v start offset in {@link #b b} of the annotations to be read.
* @param desc the method descriptor.
* @param buf buffer to be used to call {@link #readUTF8 readUTF8},
* {@link #readClass(int,char[]) readClass} or
* {@link #readConst readConst}.
* @param visible <tt>true</tt> if the annotations to be read are visible
* at runtime.
* @param mv the visitor that must visit the annotations.
*/
private void readParameterAnnotations(
int v,
final char[] buf,
final boolean visible,
final MethodVisitor mv)
{
int i;
int n = b[v++] & 0xFF;
// workaround for a bug in javac (javac compiler generates a parameter
// annotation array whose size is equal to the number of parameters in
// the Java source file, while it should generate an array whose size is
// equal to the number of parameters in the method descriptor - which
// includes the synthetic parameters added by the compiler). This work-
// around supposes that the synthetic parameters are the first ones.
for (i = 0; i < synthetics; ++i) {
// virtual annotation to detect synthetic parameters in MethodWriter
}
}
for (; i < n + synthetics; ++i) {
int j = readUnsignedShort(v);
v += 2;
for (; j > 0; --j) {
}
}
}
/**
* Reads the values of an annotation and makes the given visitor visit them.
*
* @param v the start offset in {@link #b b} of the values to be read
* (including the unsigned short that gives the number of values).
* @param buf buffer to be used to call {@link #readUTF8 readUTF8},
* {@link #readClass(int,char[]) readClass} or
* {@link #readConst readConst}.
* @param named if the annotation values are named or not.
* @param av the visitor that must visit the values.
* @return the end offset of the annotation values.
*/
private int readAnnotationValues(
int v,
final char[] buf,
final boolean named,
final AnnotationVisitor av)
{
int i = readUnsignedShort(v);
v += 2;
if (named) {
for (; i > 0; --i) {
}
} else {
for (; i > 0; --i) {
}
}
}
return v;
}
/**
* Reads a value of an annotation and makes the given visitor visit it.
*
* @param v the start offset in {@link #b b} of the value to be read (<i>not
* including the value name constant pool index</i>).
* @param buf buffer to be used to call {@link #readUTF8 readUTF8},
* {@link #readClass(int,char[]) readClass} or
* {@link #readConst readConst}.
* @param name the name of the value to be read.
* @param av the visitor that must visit the value.
* @return the end offset of the annotation value.
*/
private int readAnnotationValue(
int v,
final char[] buf,
final AnnotationVisitor av)
{
int i;
switch (b[v] & 0xFF) {
case 'e': // enum_const_value
return v + 5;
case '@': // annotation_value
case '[': // array_value
default:
return v + 3;
}
}
switch (b[v++] & 0xFF) {
case 'I': // pointer to CONSTANT_Integer
case 'J': // pointer to CONSTANT_Long
case 'F': // pointer to CONSTANT_Float
case 'D': // pointer to CONSTANT_Double
v += 2;
break;
case 'B': // pointer to CONSTANT_Byte
v += 2;
break;
case 'Z': // pointer to CONSTANT_Boolean
v += 2;
break;
case 'S': // pointer to CONSTANT_Short
v += 2;
break;
case 'C': // pointer to CONSTANT_Char
v += 2;
break;
case 's': // pointer to CONSTANT_Utf8
v += 2;
break;
case 'e': // enum_const_value
v += 4;
break;
case 'c': // class_info
v += 2;
break;
case '@': // annotation_value
v = readAnnotationValues(v + 2,
buf,
true,
break;
case '[': // array_value
int size = readUnsignedShort(v);
v += 2;
if (size == 0) {
return readAnnotationValues(v - 2,
buf,
false,
}
switch (this.b[v++] & 0xFF) {
case 'B':
for (i = 0; i < size; i++) {
v += 3;
}
--v;
break;
case 'Z':
for (i = 0; i < size; i++) {
v += 3;
}
--v;
break;
case 'S':
for (i = 0; i < size; i++) {
v += 3;
}
--v;
break;
case 'C':
for (i = 0; i < size; i++) {
v += 3;
}
--v;
break;
case 'I':
for (i = 0; i < size; i++) {
v += 3;
}
--v;
break;
case 'J':
for (i = 0; i < size; i++) {
v += 3;
}
--v;
break;
case 'F':
for (i = 0; i < size; i++) {
v += 3;
}
--v;
break;
case 'D':
for (i = 0; i < size; i++) {
v += 3;
}
--v;
break;
default:
v = readAnnotationValues(v - 3,
buf,
false,
}
}
return v;
}
private int readFrameType(
final int index,
int v,
final char[] buf,
{
int type = b[v++] & 0xFF;
switch (type) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7: // Object
v += 2;
break;
default: // Uninitialized
v += 2;
}
return v;
}
/**
* Returns the label corresponding to the given offset. The default
* implementation of this method creates a label for the given offset if it
* has not been already created.
*
* @param offset a bytecode offset in a method.
* @param labels the already created labels, indexed by their offset. If a
* label already exists for offset this method must not create a new
* one. Otherwise it must store the new label in this array.
* @return a non null Label, which must be equal to labels[offset].
*/
}
}
/**
* Reads an attribute in {@link #b b}.
*
* @param attrs prototypes of the attributes that must be parsed during the
* visit of the class. Any attribute whose type is not equal to the
* type of one the prototypes is ignored (i.e. an empty
* {@link Attribute} instance is returned).
* @param type the type of the attribute.
* @param off index of the first byte of the attribute's content in
* {@link #b b}. The 6 attribute header bytes, containing the type
* and the length of the attribute, are not taken into account here
* (they have already been read).
* @param len the length of the attribute's content.
* @param buf buffer to be used to call {@link #readUTF8 readUTF8},
* {@link #readClass(int,char[]) readClass} or
* {@link #readConst readConst}.
* @param codeOff index of the first byte of code's attribute content in
* {@link #b b}, or -1 if the attribute to be read is not a code
* attribute. The 6 attribute header bytes, containing the type and
* the length of the attribute, are not taken into account here.
* @param labels the labels of the method's code, or <tt>null</tt> if the
* attribute to be read is not a code attribute.
* @return the attribute that has been read, or <tt>null</tt> to skip this
* attribute.
*/
final int off,
final int len,
final char[] buf,
final int codeOff,
{
}
}
}
// ------------------------------------------------------------------------
// Utility methods: low level parsing
// ------------------------------------------------------------------------
/**
* Returns the start index of the constant pool item in {@link #b b}, plus
* one. <i>This method is intended for {@link Attribute} sub classes, and is
* normally not needed by class generators or adapters.</i>
*
* @param item the index a constant pool item.
* @return the start index of the constant pool item in {@link #b b}, plus
* one.
*/
}
/**
* Reads a byte value in {@link #b b}. <i>This method is intended for
* {@link Attribute} sub classes, and is normally not needed by class
* generators or adapters.</i>
*
* @param index the start index of the value to be read in {@link #b b}.
* @return the read value.
*/
return b[index] & 0xFF;
}
/**
* Reads an unsigned short value in {@link #b b}. <i>This method is
* intended for {@link Attribute} sub classes, and is normally not needed by
* class generators or adapters.</i>
*
* @param index the start index of the value to be read in {@link #b b}.
* @return the read value.
*/
byte[] b = this.b;
}
/**
* Reads a signed short value in {@link #b b}. <i>This method is intended
* for {@link Attribute} sub classes, and is normally not needed by class
* generators or adapters.</i>
*
* @param index the start index of the value to be read in {@link #b b}.
* @return the read value.
*/
byte[] b = this.b;
}
/**
* Reads a signed int value in {@link #b b}. <i>This method is intended for
* {@link Attribute} sub classes, and is normally not needed by class
* generators or adapters.</i>
*
* @param index the start index of the value to be read in {@link #b b}.
* @return the read value.
*/
byte[] b = this.b;
}
/**
* Reads a signed long value in {@link #b b}. <i>This method is intended
* for {@link Attribute} sub classes, and is normally not needed by class
* generators or adapters.</i>
*
* @param index the start index of the value to be read in {@link #b b}.
* @return the read value.
*/
}
/**
* Reads an UTF8 string constant pool item in {@link #b b}. <i>This method
* is intended for {@link Attribute} sub classes, and is normally not needed
* by class generators or adapters.</i>
*
* @param index the start index of an unsigned short value in {@link #b b},
* whose value is the index of an UTF8 constant pool item.
* @param buf buffer to be used to read the item. This buffer must be
* sufficiently large. It is not automatically resized.
* @return the String corresponding to the specified UTF8 item.
*/
if (s != null) {
return s;
}
}
/**
* Reads UTF8 string in {@link #b b}.
*
* @param index start offset of the UTF8 string to be read.
* @param utfLen length of the UTF8 string to be read.
* @param buf buffer to be used to read the string. This buffer must be
* sufficiently large. It is not automatically resized.
* @return the String corresponding to the specified UTF8 string.
*/
byte[] b = this.b;
int strLen = 0;
int c, d, e;
c = b[index++] & 0xFF;
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
// 0xxxxxxx
break;
case 12:
case 13:
// 110x xxxx 10xx xxxx
d = b[index++];
break;
default:
// 1110 xxxx 10xx xxxx 10xx xxxx
d = b[index++];
e = b[index++];
| ((d & 0x3F) << 6) | (e & 0x3F));
break;
}
}
}
/**
* Reads a class constant pool item in {@link #b b}. <i>This method is
* intended for {@link Attribute} sub classes, and is normally not needed by
* class generators or adapters.</i>
*
* @param index the start index of an unsigned short value in {@link #b b},
* whose value is the index of a class constant pool item.
* @param buf buffer to be used to read the item. This buffer must be
* sufficiently large. It is not automatically resized.
* @return the String corresponding to the specified class item.
*/
// computes the start index of the CONSTANT_Class item in b
// and reads the CONSTANT_Utf8 item designated by
// the first two bytes of this CONSTANT_Class item
}
/**
* Reads a numeric or string constant pool item in {@link #b b}. <i>This
* method is intended for {@link Attribute} sub classes, and is normally not
* needed by class generators or adapters.</i>
*
* @param item the index of a constant pool item.
* @param buf buffer to be used to read the item. This buffer must be
* sufficiently large. It is not automatically resized.
* @return the {@link Integer}, {@link Float}, {@link Long},
* {@link Double}, {@link String} or {@link Type} corresponding to
* the given constant pool item.
*/
switch (b[index - 1]) {
case ClassWriter.INT:
case ClassWriter.FLOAT:
case ClassWriter.LONG:
case ClassWriter.DOUBLE:
case ClassWriter.CLASS:
// case ClassWriter.STR:
default:
}
}
}