FileDocCategorySizeDatePackage
DirectClassFile.javaAPI DocAndroid 1.5 API20760Wed May 06 22:41:02 BST 2009com.android.dx.cf.direct

DirectClassFile

public class DirectClassFile extends Object implements com.android.dx.cf.iface.ClassFile
Class file with info taken from a byte[] or slice thereof.

Fields Summary
private static final int
CLASS_FILE_MAGIC
the expected value of the ClassFile.magic field
private static final int
CLASS_FILE_MIN_MAJOR_VERSION
minimum .class file major version The class file definition (vmspec/2nd-edition) says: "Implementations of version 1.2 of the Java 2 platform can support class file formats of versions in the range 45.0 through 46.0 inclusive." The class files generated by the build are currently (as of 11/2006) reporting version 49.0 (0x31.0x00), however, so we use that as our upper bound. 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_VERSION
maximum .class file major version
private static final int
CLASS_FILE_MAX_MINOR_VERSION
maximum .class file minor version
private final String
filePath
non-null; the file path for the class, excluding any base directory specification
private final com.android.dx.util.ByteArray
bytes
non-null; the bytes of the file
private final boolean
strictParse
whether to be strict about parsing; if 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
null-ok; the constant pool; only ever null before the constant pool is successfully parsed
private int
accessFlags
the class file field access_flags; will be -1 before the file is successfully parsed
private com.android.dx.rop.cst.CstType
thisClass
null-ok; the class file field this_class, interpreted as a type constant; only ever null before the file is successfully parsed
private com.android.dx.rop.cst.CstType
superClass
null-ok; the class file field super_class, interpreted as a type constant if non-zero
private com.android.dx.rop.type.TypeList
interfaces
null-ok; the class file field interfaces; only ever null before the file is successfully parsed
private com.android.dx.cf.iface.FieldList
fields
null-ok; the class file field fields; only ever null before the file is successfully parsed
private com.android.dx.cf.iface.MethodList
methods
null-ok; the class file field methods; only ever null before the file is successfully parsed
private com.android.dx.cf.iface.StdAttributeList
attributes
null-ok; the class file field attributes; only ever null before the file is successfully parsed
private AttributeFactory
attributeFactory
null-ok; attribute factory, if any
private com.android.dx.cf.iface.ParseObserver
observer
null-ok; parse observer, if any
Constructors Summary
public DirectClassFile(com.android.dx.util.ByteArray bytes, String filePath, boolean strictParse)
Constructs an instance.

param
bytes non-null; the bytes of the file
param
filePath non-null; the file path for the class, excluding any base directory specification
param
strictParse whether to be strict about parsing; if false, this avoids doing checks that only exist for purposes of verification (such as magic number matching and path-package consistency checking)

        if (bytes == null) {
            throw new NullPointerException("bytes == null");
        }

        if (filePath == null) {
            throw new NullPointerException("filePath == null");
        }

        this.filePath = filePath;
        this.bytes = bytes;
        this.strictParse = strictParse;
        this.accessFlags = -1;
    
public DirectClassFile(byte[] bytes, String filePath, boolean strictParse)
Constructs an instance.

param
bytes non-null; the bytes of the file
param
filePath non-null; the file path for the class, excluding any base directory specification
param
strictParse whether to be strict about parsing; if false, this avoids doing checks that only exist for purposes of verification (such as magic number matching and path-package consistency checking)

        this(new ByteArray(bytes), filePath, strictParse);
    
Methods Summary
public intgetAccessFlags()
{@inheritDoc}

        parseToInterfacesIfNecessary();
        return accessFlags;
    
public com.android.dx.cf.iface.AttributeListgetAttributes()
{@inheritDoc}

        parseToEndIfNecessary();
        return attributes;
    
public com.android.dx.util.ByteArraygetBytes()
Gets the {@link ByteArray} that this instance's data comes from.

return
non-null; the bytes

        return bytes;
    
public com.android.dx.rop.cst.ConstantPoolgetConstantPool()
{@inheritDoc}

        parseToInterfacesIfNecessary();
        return pool;
    
public com.android.dx.cf.iface.FieldListgetFields()
{@inheritDoc}

        parseToEndIfNecessary();
        return fields;
    
public com.android.dx.rop.type.TypeListgetInterfaces()
{@inheritDoc}

        parseToInterfacesIfNecessary();
        return interfaces;
    
public intgetMagic()
{@inheritDoc}

        parseToInterfacesIfNecessary();
        return getMagic0();
    
public intgetMagic0()
Gets the class file field magic, but without doing any checks or parsing first.

return
the magic value

        return bytes.getInt(0);
    
public intgetMajorVersion()
{@inheritDoc}

        parseToInterfacesIfNecessary();
        return getMajorVersion0();
    
public intgetMajorVersion0()
Gets the class file field major_version, but without doing any checks or parsing first.

return
the major version

        return bytes.getUnsignedShort(6);
    
public com.android.dx.cf.iface.MethodListgetMethods()
{@inheritDoc}

        parseToEndIfNecessary();
        return methods;
    
public intgetMinorVersion()
{@inheritDoc}

        parseToInterfacesIfNecessary();
        return getMinorVersion0();
    
public intgetMinorVersion0()
Gets the class file field minor_version, but without doing any checks or parsing first.

return
the minor version

        return bytes.getUnsignedShort(4);
    
public com.android.dx.rop.cst.CstUtf8getSourceFile()
{@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.CstTypegetSuperclass()
{@inheritDoc}

        parseToInterfacesIfNecessary();
        return superClass;
    
public com.android.dx.rop.cst.CstTypegetThisClass()
{@inheritDoc}

        parseToInterfacesIfNecessary();
        return thisClass;
    
private booleanisGoodVersion(int magic, int minorVersion, int majorVersion)
Sees if the .class file header magic/version are within range.

param
magic the value of a classfile "magic" field
param
minorVersion the value of a classfile "minor_version" field
param
majorVersion the value of a classfile "major_version" field
return
true iff the parameters are valid and 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.TypeListmakeTypeList(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 Class.

param
offset offset into {@link #bytes} for the start of the data
param
size number of elements in the list (not number of bytes)
return
non-null; an appropriately-constructed class list

        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 voidparse()
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 voidparse0()
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 voidparseToEndIfNecessary()
Runs {@link #parse} if it has not yet been run successfully.

        if (attributes == null) {
            parse();
        }
    
private voidparseToInterfacesIfNecessary()
Runs {@link #parse} if it has not yet been run to cover up to the interfaces list.

        if (accessFlags == -1) {
            parse();
        }
    
public voidsetAttributeFactory(AttributeFactory attributeFactory)
Sets the attribute factory to use.

param
attributeFactory non-null; the attribute factory

        if (attributeFactory == null) {
            throw new NullPointerException("attributeFactory == null");
        }

        this.attributeFactory = attributeFactory;
    
public voidsetObserver(com.android.dx.cf.iface.ParseObserver observer)
Sets the parse observer for this instance.

param
observer null-ok; the observer

        this.observer = observer;
    
public static java.lang.StringstringOrNone(java.lang.Object obj)
Returns the string form of an object or "(none)" (rather than "null") for null.

param
obj null-ok; the object to stringify
return
non-null; the appropriate string form


                                     
         
        if (obj == null) {
            return "(none)";
        }

        return obj.toString();