Fields Summary |
---|
private static final int | CLASS_FILE_MAGICthe expected value of the ClassFile.magic field |
private static final int | CLASS_FILE_MIN_MAJOR_VERSIONminimum {@code .class} file major version
See http://en.wikipedia.org/wiki/Java_class_file for an up-to-date
list of version numbers. Currently known (taken from that table) are:
J2SE 7.0 = 51 (0x33 hex),
J2SE 6.0 = 50 (0x32 hex),
J2SE 5.0 = 49 (0x31 hex),
JDK 1.4 = 48 (0x30 hex),
JDK 1.3 = 47 (0x2F hex),
JDK 1.2 = 46 (0x2E hex),
JDK 1.1 = 45 (0x2D hex).
Valid ranges are typically of the form
"A.0 through B.C inclusive" where A <= B and C >= 0,
which is why we don't have a CLASS_FILE_MIN_MINOR_VERSION. |
private static final int | CLASS_FILE_MAX_MAJOR_VERSIONmaximum {@code .class} file major version
Note: if you change this, please change "java.class.version" in System.java. |
private static final int | CLASS_FILE_MAX_MINOR_VERSIONmaximum {@code .class} file minor version |
private final String | filePath{@code non-null;} the file path for the class, excluding any base directory
specification |
private final com.android.dx.util.ByteArray | bytes{@code non-null;} the bytes of the file |
private final boolean | strictParsewhether to be strict about parsing; if
{@code false}, this avoids doing checks that only exist
for purposes of verification (such as magic number matching and
path-package consistency checking) |
private com.android.dx.rop.cst.StdConstantPool | pool{@code null-ok;} the constant pool; only ever {@code null}
before the constant pool is successfully parsed |
private int | accessFlagsthe class file field {@code access_flags}; will be {@code -1}
before the file is successfully parsed |
private com.android.dx.rop.cst.CstType | thisClass{@code null-ok;} the class file field {@code this_class},
interpreted as a type constant; only ever {@code null}
before the file is successfully parsed |
private com.android.dx.rop.cst.CstType | superClass{@code null-ok;} the class file field {@code super_class}, interpreted
as a type constant if non-zero |
private com.android.dx.rop.type.TypeList | interfaces{@code null-ok;} the class file field {@code interfaces}; only
ever {@code null} before the file is successfully
parsed |
private com.android.dx.cf.iface.FieldList | fields{@code null-ok;} the class file field {@code fields}; only ever
{@code null} before the file is successfully parsed |
private com.android.dx.cf.iface.MethodList | methods{@code null-ok;} the class file field {@code methods}; only ever
{@code null} before the file is successfully parsed |
private com.android.dx.cf.iface.StdAttributeList | attributes{@code null-ok;} the class file field {@code attributes}; only
ever {@code null} before the file is successfully
parsed |
private AttributeFactory | attributeFactory{@code null-ok;} attribute factory, if any |
private com.android.dx.cf.iface.ParseObserver | observer{@code null-ok;} parse observer, if any |
Methods Summary |
---|
public int | getAccessFlags(){@inheritDoc}
parseToInterfacesIfNecessary();
return accessFlags;
|
public com.android.dx.cf.iface.AttributeList | getAttributes(){@inheritDoc}
parseToEndIfNecessary();
return attributes;
|
public com.android.dx.util.ByteArray | getBytes()Gets the {@link ByteArray} that this instance's data comes from.
return bytes;
|
public com.android.dx.rop.cst.ConstantPool | getConstantPool(){@inheritDoc}
parseToInterfacesIfNecessary();
return pool;
|
public com.android.dx.cf.iface.FieldList | getFields(){@inheritDoc}
parseToEndIfNecessary();
return fields;
|
public java.lang.String | getFilePath()Gets the path where this class file is located.
return filePath;
|
public com.android.dx.rop.type.TypeList | getInterfaces(){@inheritDoc}
parseToInterfacesIfNecessary();
return interfaces;
|
public int | getMagic(){@inheritDoc}
parseToInterfacesIfNecessary();
return getMagic0();
|
public int | getMagic0()Gets the class file field {@code magic}, but without doing any
checks or parsing first.
return bytes.getInt(0);
|
public int | getMajorVersion(){@inheritDoc}
parseToInterfacesIfNecessary();
return getMajorVersion0();
|
public int | getMajorVersion0()Gets the class file field {@code major_version}, but
without doing any checks or parsing first.
return bytes.getUnsignedShort(6);
|
public com.android.dx.cf.iface.MethodList | getMethods(){@inheritDoc}
parseToEndIfNecessary();
return methods;
|
public int | getMinorVersion(){@inheritDoc}
parseToInterfacesIfNecessary();
return getMinorVersion0();
|
public int | getMinorVersion0()Gets the class file field {@code minor_version}, but
without doing any checks or parsing first.
return bytes.getUnsignedShort(4);
|
public com.android.dx.rop.cst.CstString | getSourceFile(){@inheritDoc}
AttributeList attribs = getAttributes();
Attribute attSf = attribs.findFirst(AttSourceFile.ATTRIBUTE_NAME);
if (attSf instanceof AttSourceFile) {
return ((AttSourceFile) attSf).getSourceFile();
}
return null;
|
public com.android.dx.rop.cst.CstType | getSuperclass(){@inheritDoc}
parseToInterfacesIfNecessary();
return superClass;
|
public com.android.dx.rop.cst.CstType | getThisClass(){@inheritDoc}
parseToInterfacesIfNecessary();
return thisClass;
|
private boolean | isGoodVersion(int magic, int minorVersion, int majorVersion)Sees if the .class file header magic/version are within
range.
/* Valid version ranges are typically of the form
* "A.0 through B.C inclusive" where A <= B and C >= 0,
* which is why we don't have a CLASS_FILE_MIN_MINOR_VERSION.
*/
if (magic == CLASS_FILE_MAGIC && minorVersion >= 0) {
/* Check against max first to handle the case where
* MIN_MAJOR == MAX_MAJOR.
*/
if (majorVersion == CLASS_FILE_MAX_MAJOR_VERSION) {
if (minorVersion <= CLASS_FILE_MAX_MINOR_VERSION) {
return true;
}
} else if (majorVersion < CLASS_FILE_MAX_MAJOR_VERSION &&
majorVersion >= CLASS_FILE_MIN_MAJOR_VERSION) {
return true;
}
}
return false;
|
public com.android.dx.rop.type.TypeList | makeTypeList(int offset, int size)Constructs and returns an instance of {@link TypeList} whose
data comes from the bytes of this instance, interpreted as a
list of constant pool indices for classes, which are in turn
translated to type constants. Instance construction will fail
if any of the (alleged) indices turn out not to refer to
constant pool entries of type {@code Class}.
if (size == 0) {
return StdTypeList.EMPTY;
}
if (pool == null) {
throw new IllegalStateException("pool not yet initialized");
}
return new DcfTypeList(bytes, offset, size, pool, observer);
|
private void | parse()Does the parsing, handing exceptions.
try {
parse0();
} catch (ParseException ex) {
ex.addContext("...while parsing " + filePath);
throw ex;
} catch (RuntimeException ex) {
ParseException pe = new ParseException(ex);
pe.addContext("...while parsing " + filePath);
throw pe;
}
|
private void | parse0()Does the actual parsing.
if (bytes.size() < 10) {
throw new ParseException("severely truncated class file");
}
if (observer != null) {
observer.parsed(bytes, 0, 0, "begin classfile");
observer.parsed(bytes, 0, 4, "magic: " + Hex.u4(getMagic0()));
observer.parsed(bytes, 4, 2,
"minor_version: " + Hex.u2(getMinorVersion0()));
observer.parsed(bytes, 6, 2,
"major_version: " + Hex.u2(getMajorVersion0()));
}
if (strictParse) {
/* Make sure that this looks like a valid class file with a
* version that we can handle.
*/
if (!isGoodVersion(getMagic0(), getMinorVersion0(),
getMajorVersion0())) {
throw new ParseException("bad class file magic (" +
Hex.u4(getMagic0()) +
") or version (" +
Hex.u2(getMajorVersion0()) + "." +
Hex.u2(getMinorVersion0()) + ")");
}
}
ConstantPoolParser cpParser = new ConstantPoolParser(bytes);
cpParser.setObserver(observer);
pool = cpParser.getPool();
pool.setImmutable();
int at = cpParser.getEndOffset();
int accessFlags = bytes.getUnsignedShort(at); // u2 access_flags;
int cpi = bytes.getUnsignedShort(at + 2); // u2 this_class;
thisClass = (CstType) pool.get(cpi);
cpi = bytes.getUnsignedShort(at + 4); // u2 super_class;
superClass = (CstType) pool.get0Ok(cpi);
int count = bytes.getUnsignedShort(at + 6); // u2 interfaces_count
if (observer != null) {
observer.parsed(bytes, at, 2,
"access_flags: " +
AccessFlags.classString(accessFlags));
observer.parsed(bytes, at + 2, 2, "this_class: " + thisClass);
observer.parsed(bytes, at + 4, 2, "super_class: " +
stringOrNone(superClass));
observer.parsed(bytes, at + 6, 2,
"interfaces_count: " + Hex.u2(count));
if (count != 0) {
observer.parsed(bytes, at + 8, 0, "interfaces:");
}
}
at += 8;
interfaces = makeTypeList(at, count);
at += count * 2;
if (strictParse) {
/*
* Make sure that the file/jar path matches the declared
* package/class name.
*/
String thisClassName = thisClass.getClassType().getClassName();
if (!(filePath.endsWith(".class") &&
filePath.startsWith(thisClassName) &&
(filePath.length() == (thisClassName.length() + 6)))) {
throw new ParseException("class name (" + thisClassName +
") does not match path (" +
filePath + ")");
}
}
/*
* Only set the instance variable accessFlags here, since
* that's what signals a successful parse of the first part of
* the file (through the interfaces list).
*/
this.accessFlags = accessFlags;
FieldListParser flParser =
new FieldListParser(this, thisClass, at, attributeFactory);
flParser.setObserver(observer);
fields = flParser.getList();
at = flParser.getEndOffset();
MethodListParser mlParser =
new MethodListParser(this, thisClass, at, attributeFactory);
mlParser.setObserver(observer);
methods = mlParser.getList();
at = mlParser.getEndOffset();
AttributeListParser alParser =
new AttributeListParser(this, AttributeFactory.CTX_CLASS, at,
attributeFactory);
alParser.setObserver(observer);
attributes = alParser.getList();
attributes.setImmutable();
at = alParser.getEndOffset();
if (at != bytes.size()) {
throw new ParseException("extra bytes at end of class file, " +
"at offset " + Hex.u4(at));
}
if (observer != null) {
observer.parsed(bytes, at, 0, "end classfile");
}
|
private void | parseToEndIfNecessary()Runs {@link #parse} if it has not yet been run successfully.
if (attributes == null) {
parse();
}
|
private void | parseToInterfacesIfNecessary()Runs {@link #parse} if it has not yet been run to cover up to
the interfaces list.
if (accessFlags == -1) {
parse();
}
|
public void | setAttributeFactory(AttributeFactory attributeFactory)Sets the attribute factory to use.
if (attributeFactory == null) {
throw new NullPointerException("attributeFactory == null");
}
this.attributeFactory = attributeFactory;
|
public void | setObserver(com.android.dx.cf.iface.ParseObserver observer)Sets the parse observer for this instance.
this.observer = observer;
|
public static java.lang.String | stringOrNone(java.lang.Object obj)Returns the string form of an object or {@code "(none)"}
(rather than {@code "null"}) for {@code null}.
if (obj == null) {
return "(none)";
}
return obj.toString();
|