FileDocCategorySizeDatePackage
DirectClassFile.javaAPI DocAndroid 5.1 API21052Thu Mar 12 22:18:30 GMT 2015com.android.dx.cf.direct

DirectClassFile

public class DirectClassFile extends Object implements com.android.dx.cf.iface.ClassFile
Class file with info taken from a {@code 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 {@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_VERSION
maximum {@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_VERSION
maximum {@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
strictParse
whether 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
accessFlags
the 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
Constructors Summary
public DirectClassFile(com.android.dx.util.ByteArray bytes, String filePath, boolean strictParse)
Constructs an instance.

param
bytes {@code non-null;} the bytes of the file
param
filePath {@code non-null;} the file path for the class, excluding any base directory specification
param
strictParse whether 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)

        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 {@code non-null;} the bytes of the file
param
filePath {@code non-null;} the file path for the class, excluding any base directory specification
param
strictParse whether 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)

        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
{@code 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 java.lang.StringgetFilePath()
Gets the path where this class file is located.

return
{@code non-null;} the filePath

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

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

        parseToInterfacesIfNecessary();
        return getMagic0();
    
public intgetMagic0()
Gets the class file field {@code 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 {@code 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 {@code minor_version}, but without doing any checks or parsing first.

return
the minor version

        return bytes.getUnsignedShort(4);
    
public com.android.dx.rop.cst.CstStringgetSourceFile()
{@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 {@code 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
{@code 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 {@code 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 {@code null-ok;} the observer

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

param
obj {@code null-ok;} the object to stringify
return
{@code non-null;} the appropriate string form


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

        return obj.toString();