FileDocCategorySizeDatePackage
ClassDef.javaAPI DocAndroid 1.5 API25871Wed May 06 22:41:16 BST 2009com.vladium.jcd.cls

ClassDef

public final class ClassDef extends Object implements Cloneable, IAccessFlags, com.vladium.jcd.compiler.IClassFormatOutput
This class represents the abstract syntax table (AST) that {@link com.vladium.jcd.parser.ClassDefParser} produces from bytecode. Most elements are either settable or extendible. This class also implements {@link com.vladium.jcd.compiler.IClassFormatOutput} and works with {@link com.vladium.jcd.compiler.ClassWriter} to produce bytecode without an external compiler.

MT-safety: this class and all interfaces used by it are not safe for access from multiple concurrent threads.

author
(C) 2001, Vlad Roubtsov

Fields Summary
private long
m_magic
private int[]
m_version
private int
m_access_flags
private int
m_this_class_index
private int
m_super_class_index
private IConstantCollection
m_constants
private IInterfaceCollection
m_interfaces
private IFieldCollection
m_fields
private IMethodCollection
m_methods
private IAttributeCollection
m_attributes
private long
m_declaredSUID
private static final boolean
DEBUG_SUID
Constructors Summary
public ClassDef()

        m_version = new int [2];

        m_constants = ElementFactory.newConstantCollection (-1);
        m_interfaces = ElementFactory.newInterfaceCollection (-1);
        m_fields = ElementFactory.newFieldCollection (-1);
        m_methods = ElementFactory.newMethodCollection (-1);
        m_attributes = ElementFactory.newAttributeCollection (-1);
    
Methods Summary
public voidaccept(IClassDefVisitor visitor, java.lang.Object ctx)

        visitor.visit (this, ctx);
    
public intaddCONSTANT_Utf8(java.lang.String value, boolean keepUnique)

        if (keepUnique)
        {
            final int existing = m_constants.findCONSTANT_Utf8 (value);
            if (existing > 0)
            {
                return existing;
            }
                
            // [else fall through]
        }

        return m_constants.add (new CONSTANT_Utf8_info (value));
    
public intaddClassref(java.lang.String classJVMName)

        final int name_index = addCONSTANT_Utf8 (classJVMName, true);
        // TODO: this should do uniqueness checking:
        
        return m_constants.add (new CONSTANT_Class_info (name_index));
    
public intaddField(java.lang.String name, java.lang.String descriptor, int access_flags)
Adds a new declared field to this class [with no attributes]

        // TODO: support Fields with initializer attributes?
        // TODO: no "already exists" check done here
        
        final int name_index = addCONSTANT_Utf8 (name, true);
        final int descriptor_index = addCONSTANT_Utf8 (descriptor, true);
        
        final Field_info field = new Field_info (access_flags, name_index, descriptor_index,
            ElementFactory.newAttributeCollection (0));
        
        return m_fields.add (field);
    
public intaddField(java.lang.String name, java.lang.String descriptor, int access_flags, IAttributeCollection attributes)
Adds a new declared field to this class [with given attributes]

        // TODO: support Fields with initializer attributes?
        // TODO: no "already exists" check done here
        
        final int name_index = addCONSTANT_Utf8 (name, true);
        final int descriptor_index = addCONSTANT_Utf8 (descriptor, true);
        
        final Field_info field = new Field_info (access_flags, name_index, descriptor_index, attributes);
        
        return m_fields.add (field);
    
public intaddFieldref(Field_info field)
Adds a reference to a field declared by this class.

return
constant pool index of the reference

        // TODO: keepUnique flag
        
        final CONSTANT_NameAndType_info nametype = new CONSTANT_NameAndType_info (field.m_name_index, field.m_descriptor_index);
        final int nametype_index = m_constants.add (nametype); // TODO: unique logic
        
        return m_constants.add (new CONSTANT_Fieldref_info (m_this_class_index, nametype_index));
    
public intaddFieldref(int offset)
Adds a reference to a field declared by this class.

return
constant pool index of the reference

        // TODO: keepUnique flag
        
        final Field_info field = m_fields.get (offset); 
        
        final CONSTANT_NameAndType_info nametype = new CONSTANT_NameAndType_info (field.m_name_index, field.m_descriptor_index);
        final int nametype_index = m_constants.add (nametype); // TODO: unique logic
        
        return m_constants.add (new CONSTANT_Fieldref_info (m_this_class_index, nametype_index));
    
public intaddMethod(Method_info method)

        return m_methods.add (method);
    
public intaddNameType(java.lang.String name, java.lang.String typeDescriptor)

        final int name_index = addCONSTANT_Utf8 (name, true);
        final int descriptor_index = addCONSTANT_Utf8 (typeDescriptor, true);
        
        return m_constants.add (new CONSTANT_NameAndType_info (name_index, descriptor_index));
    
public intaddStringConstant(java.lang.String value)

        final int value_index = addCONSTANT_Utf8 (value, true);
        
        // TODO: const uniqueness
        return m_constants.add (new CONSTANT_String_info (value_index));
    
public java.lang.Objectclone()
Performs a deep copy.

        try
        {
            final ClassDef _clone = (ClassDef) super.clone ();
            
            // do deep copy:
            _clone.m_version = (int []) m_version.clone ();
            _clone.m_constants = (IConstantCollection) m_constants.clone ();
            _clone.m_interfaces = (IInterfaceCollection) m_interfaces.clone ();
            _clone.m_fields = (IFieldCollection) m_fields.clone ();
            _clone.m_methods = (IMethodCollection) m_methods.clone ();
            _clone.m_attributes = (IAttributeCollection) m_attributes.clone ();
            
            return _clone;
        }
        catch (CloneNotSupportedException e)
        {
            throw new InternalError (e.toString ());
        }
    
public final longcomputeSUID(boolean skipCLINIT)
This follows the spec at http://java.sun.com/j2se/1.4.1/docs/guide/serialization/spec/class.doc6.html#4100 as well as undocumented hacks used by Sun's 1.4.2 J2SDK

        long result = m_declaredSUID;
        if (result != 0L)
            return result;
        else
        {
            try
            {
                final ByteArrayOStream bout = new ByteArrayOStream (1024); // TODO: reuse these 
                final DataOutputStream dout = new DataOutputStream (bout);
                
                // (1) The class name written using UTF encoding: 

                dout.writeUTF (Types.vmNameToJavaName (getName ())); // [in Java format]
                
                // (2) The class modifiers written as a 32-bit integer:
                
                // ACC_STATIC is never written for nested classes/interfaces;
                // however, ACC_SUPER must be ignored:
                {
                    // this is tricky: for static/non-static nested classes that
                    // were declared protected in the source the usual access flags
                    // will have ACC_PUBLIC set; the only way to achieve J2SDK
                    // compatibility is to recover the source access flags
                    // from the InnerClasses attribute:
                    
                    final int [] nestedAccessFlags = new int [1];
                    
                    final int modifiers = (isNested (nestedAccessFlags)
                            ? nestedAccessFlags [0]
                            : getAccessFlags ())
                        & (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT);

                    // if/when emma decides to instrument interfaces for <clinit>
                    // coverage, compensate for javac bug in which ABSTRACT bit
                    // was set for an interface only if the interface declared methods
                    // [Sun's J2SDK]:
                    
                    dout.writeInt (modifiers);
                }
                
                // not doing another J2SDK compensation for arrays, because
                // we never load/instrument those 
                
                // (3) The name of each interface sorted by name written using UTF encoding
                {
                    final IInterfaceCollection interfaces = getInterfaces ();
                    final String [] ifcs = new String [interfaces.size ()];
                    
                    final int iLimit = ifcs.length;
                    for (int i = 0; i < iLimit; ++ i)
                    {
                        // [in Java format]
                        ifcs [i] = Types.vmNameToJavaName (((CONSTANT_Class_info) m_constants.get (interfaces.get (i))).getName (this));
                    }
                    
                    Arrays.sort (ifcs);
                    for (int i = 0; i < iLimit; ++ i)
                    {
                        dout.writeUTF (ifcs [i]);
                    }
                }
                
                // (4) For each field of the class sorted by field name (except
                // private static and private transient fields):
                //      a. The name of the field in UTF encoding. 
                //      b. The modifiers of the field written as a 32-bit integer. 
                //      c. The descriptor of the field in UTF encoding 
                {
                    final IFieldCollection fields = getFields ();
                    final FieldDescriptor [] fds = new FieldDescriptor [fields.size ()];
                    
                    int fcount = 0;
                    for (int f = 0, fLimit = fds.length; f < fLimit; ++ f)
                    {
                        final Field_info field = fields.get (f);
                        final int modifiers = field.getAccessFlags ();
                        
                        if (((modifiers & ACC_PRIVATE) == 0) ||
                            ((modifiers & (ACC_STATIC | ACC_TRANSIENT)) == 0))
                            fds [fcount ++] = new FieldDescriptor (field.getName (this), modifiers, field.getDescriptor (this));
                    }
                    
                    if (fcount > 0)
                    {
                        Arrays.sort (fds, 0, fcount);
                        for (int i = 0; i < fcount; ++ i)
                        {
                            final FieldDescriptor fd = fds [i];
                            
                            dout.writeUTF (fd.m_name);
                            dout.writeInt (fd.m_modifiers);
                            dout.writeUTF (fd.m_descriptor);
                        }
                    }
                }
                
                // (5) If a class initializer exists, write out the following: 
                //      a. The name of the method, <clinit>, in UTF encoding. 
                //      b. The modifier of the method, ACC_STATIC, written as a 32-bit integer. 
                //      c. The descriptor of the method, ()V, in UTF encoding. 
                // (6) For each non-private constructor sorted by method name and signature: 
                //      a. The name of the method, <init>, in UTF encoding. 
                //      b. The modifiers of the method written as a 32-bit integer. 
                //      c. The descriptor of the method in UTF encoding. 
                // (7) For each non-private method sorted by method name and signature: 
                //      a. The name of the method in UTF encoding. 
                //      b. The modifiers of the method written as a 32-bit integer. 
                //      c. The descriptor of the method in UTF encoding.
                
                // note: although this is not documented, J2SDK code uses '.''s as
                // descriptor separators (this is done for methods only, not for fields)
                {
                    final IMethodCollection methods = getMethods ();
                    
                    boolean hasCLINIT = false;
                    final ConstructorDescriptor [] cds = new ConstructorDescriptor [methods.size ()];
                    final MethodDescriptor [] mds = new MethodDescriptor [cds.length];
                    
                    int ccount = 0, mcount = 0;
                    
                    for (int i = 0, iLimit = cds.length; i < iLimit; ++ i)
                    {
                        final Method_info method = methods.get (i);
                        
                        final String name = method.getName (this);
                        
                        if (! hasCLINIT && IClassDefConstants.CLINIT_NAME.equals (name))
                        {
                            hasCLINIT  = true;
                            continue;
                        }
                        else
                        {
                            final int modifiers = method.getAccessFlags ();
                            if ((modifiers & ACC_PRIVATE) == 0)
                            {
                                if (IClassDefConstants.INIT_NAME.equals (name))
                                    cds [ccount ++] = new ConstructorDescriptor (modifiers, method.getDescriptor (this));
                                else
                                    mds [mcount ++] = new MethodDescriptor (name, modifiers, method.getDescriptor (this));
                            }
                        }
                    }
                    
                    if (hasCLINIT && ! skipCLINIT)
                    {
                        dout.writeUTF (IClassDefConstants.CLINIT_NAME);
                        dout.writeInt (ACC_STATIC);
                        dout.writeUTF (IClassDefConstants.CLINIT_DESCRIPTOR);
                    }
                    
                    if (ccount > 0)
                    {
                        Arrays.sort (cds, 0, ccount);
                        
                        for (int i = 0; i < ccount; ++ i)
                        {
                            final ConstructorDescriptor cd = cds [i];
                        
                            dout.writeUTF (IClassDefConstants.INIT_NAME);
                            dout.writeInt (cd.m_modifiers);
                            dout.writeUTF (cd.m_descriptor.replace ('/", '."));
                        }
                    }
                    
                    if (mcount > 0)
                    {
                        Arrays.sort (mds, 0, mcount);
                        
                        for (int i = 0; i < mcount; ++ i)
                        {
                            final MethodDescriptor md = mds [i];
                        
                            dout.writeUTF (md.m_name);
                            dout.writeInt (md.m_modifiers);
                            dout.writeUTF (md.m_descriptor.replace ('/", '."));
                        }
                    }
                }
        
                dout.flush();
                
                
                if (DEBUG_SUID)
                {
                    byte [] dump = bout.copyByteArray ();
                    for (int x = 0; x < dump.length; ++ x)
                    {
                        System.out.println ("DUMP[" + x + "] = " + dump [x] + "\t" + (char) dump[x]);
                    }
                }
                
                final MessageDigest md = MessageDigest.getInstance ("SHA");
                
                md.update (bout.getByteArray (), 0, bout.size ());
                final byte [] hash = md.digest ();

                if (DEBUG_SUID)
                {                    
                    for (int x = 0; x < hash.length; ++ x)
                    {
                        System.out.println ("HASH[" + x + "] = " + hash [x]);
                    }
                }
                
//                    final int hash0 = hash [0];
//                    final int hash1 = hash [1];
//                    result = ((hash0 >>> 24) & 0xFF) | ((hash0 >>> 16) & 0xFF) << 8 | ((hash0 >>> 8) & 0xFF) << 16 | ((hash0 >>> 0) & 0xFF) << 24 |
//                             ((hash1 >>> 24) & 0xFF) << 32 | ((hash1 >>> 16) & 0xFF) << 40 | ((hash1 >>> 8) & 0xFF) << 48 | ((hash1 >>> 0) & 0xFF) << 56;

                for (int i = Math.min (hash.length, 8) - 1; i >= 0; -- i)
                {
                    result = (result << 8) | (hash [i] & 0xFF);
                }
                
                return result;
            }
            catch (IOException ioe)
            {
                throw new Error (ioe.getMessage ());
            }
            catch (NoSuchAlgorithmException nsae)
            {
                throw new SecurityException (nsae.getMessage());
            }
        }
    
public final intgetAccessFlags()

        return m_access_flags;
    
public IAttributeCollectiongetAttributes()

        return m_attributes;
    
public IConstantCollectiongetConstants()

        return m_constants;
    
public final longgetDeclaredSUID()

        return m_declaredSUID;
    
public IFieldCollectiongetFields()

        return m_fields;
    
public int[]getFields(java.lang.String name)

        return m_fields.get (this, name);
    
public IInterfaceCollectiongetInterfaces()

        return m_interfaces;
    
public longgetMagic()

        return m_magic;
    
public IMethodCollectiongetMethods()

        return m_methods;
    
public int[]getMethods(java.lang.String name)

        return m_methods.get (this, name);
    
public java.lang.StringgetName()

        return getThisClass ().getName (this);
    
public com.vladium.jcd.cls.constant.CONSTANT_Class_infogetSuperClass()

        return (CONSTANT_Class_info) m_constants.get (m_super_class_index);
    
public intgetSuperClassIndex()

        return m_super_class_index;
    
public com.vladium.jcd.cls.constant.CONSTANT_Class_infogetThisClass()

        return (CONSTANT_Class_info) m_constants.get (m_this_class_index);
    
public intgetThisClassIndex()

        return m_this_class_index;
    
public int[]getVersion()

        return m_version;
    
public booleanisInterface()

        return (m_access_flags & ACC_INTERFACE) != 0;
    
public booleanisNested(int[] nestedAccessFlags)

        final InnerClassesAttribute_info innerClassesAttribute = m_attributes.getInnerClassesAttribute ();
        
        if (innerClassesAttribute == null)
            return false;
        else
            return innerClassesAttribute.makesClassNested (m_this_class_index, nestedAccessFlags);
    
public booleanisSynthetic()

        return m_attributes.hasSynthetic ();
    
public Method_infonewEmptyMethod(java.lang.String name, java.lang.String descriptor, int access_flags)

        // TODO: flag for making synthetic etc
        final int attribute_name_index = addCONSTANT_Utf8 (Attribute_info.ATTRIBUTE_CODE, true);
        final int name_index = addCONSTANT_Utf8 (name, true);
        final int descriptor_index = addCONSTANT_Utf8 (descriptor, true);
        
        final IAttributeCollection attributes = ElementFactory.newAttributeCollection (0);
        final CodeAttribute_info code = new CodeAttribute_info (attribute_name_index, 0, 0,
            CodeAttribute_info.EMPTY_BYTE_ARRAY,
            AttributeElementFactory.newExceptionHandlerTable (0),
            ElementFactory.newAttributeCollection (0));
            
        attributes.add (code);
        
        final Method_info method = new Method_info (access_flags, name_index, descriptor_index, attributes);
        
        return method;
    
public final voidsetAccessFlags(int flags)

        m_access_flags = flags;
    
public final voidsetDeclaredSUID(long suid)

        m_declaredSUID = suid;
    
public voidsetMagic(long magic)

        m_magic = magic;
    
public voidsetSuperClassIndex(int super_class_index)

        m_super_class_index = super_class_index;
    
public voidsetThisClassIndex(int this_class_index)

        m_this_class_index = this_class_index;
    
public voidsetVersion(int[] version)

        m_version [0] = version [0];
        m_version [1] = version [1];
    
public voidwriteInClassFormat(com.vladium.jcd.lib.UDataOutputStream out)

        if (out == null) throw new IllegalArgumentException ("null input: out");
        
        out.writeU4 (m_magic);
        
        out.writeU2 (m_version [1]);
        out.writeU2 (m_version [0]);
        
        m_constants.writeInClassFormat (out);
        
        out.writeU2 (m_access_flags);
        
        out.writeU2 (m_this_class_index);
        out.writeU2 (m_super_class_index);
        
        m_interfaces.writeInClassFormat (out);
        m_fields.writeInClassFormat (out);
        m_methods.writeInClassFormat (out);
        m_attributes.writeInClassFormat (out);