FileDocCategorySizeDatePackage
ClassInfo.javaAPI DocJ2ME CLDC 1.130634Wed Feb 05 15:56:02 GMT 2003components

ClassInfo.java

/*
 *    ClassInfo.java    1.22    99/12/08 SMI
 *
 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package components;
import jcc.Util;
import vm.Const;
import util.*;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;

//
// container for stuff about a class.
// can be in a classfile or in a module.
//

public
class ClassInfo
{
    public String        className;
    public int            access;
    public ClassConstant    thisClass;
    public ClassConstant    superClass;
    public ClassInfo        superClassInfo;

    // Tables for all fields, methods and constants of this class
    public FieldInfo        fields[];
    public MethodInfo        methods[];
    public ConstantObject    constants[];
    private ConstantObject    oldConstants[];
    public ConstantObject    symbols[];
    public ClassConstant    interfaces[];
    public FieldConstant    refFieldtable[];
    public MethodConstant    refMethodtable[];

    // In case we lay it all out here
    public FieldInfo        fieldtable[];
    public MethodInfo        methodtable[];
    public UnicodeConstant    fieldtableName;
    public UnicodeConstant    methodtableName;

    // Class attributes that we do not interpret
    public Attribute[]          classAttributes;
    public SourceFileAttribute  sourceFileAttr;

    public vm.ClassClass    vmClass; // used by in-core output writers

    public Vector        allInterfaces;

    protected boolean        verbose;
    protected PrintStream    log = System.out;
    public  ConstantPool    externalPool;
    public  static boolean      classDebug = false;

    public  int            flags;
    public static final int    INCLUDE_ALL = 1;

    public ClassInfo( boolean v ) {
    verbose = v;
    flags = INCLUDE_ALL; // by default, we want all members.
    // what else should be here?
    }

    private String genericNativeName;

    public final String getGenericNativeName() { 
        if (genericNativeName == null) 
        genericNativeName = createGenericNativeName();
        return genericNativeName;
    }

    // This will be overridden by subclasses
    protected String createGenericNativeName() { 
        return Util.convertToClassName(className );
    }

    // Read in the constants from a classfile
    void readConstantPool( DataInput in ) throws IOException {
    int num = in.readUnsignedShort();

    if(verbose){
        log.println(Localizer.getString("classinfo.reading_entries_in_constant_pool", Integer.toString(num)));
    }
    constants = new ConstantObject[num];
    for (int i = 1; i < num; i+=constants[i].nSlots) {
        constants[i] = ConstantObject.readObject( in );
        constants[i].index = i;
    }
    }

    private void resolveConstants( ) {
    if (verbose){
        log.println(Localizer.getString("classinfo.>>>resolving_constants"));
    }
    for (int i = 1; i < constants.length; i+=constants[i].nSlots) {
        constants[i].resolve( symbols );
    }
    }

    
    protected void externalizeConstants( ConstantPool externalPool ){
    if (verbose){
        log.println(Localizer.getString("classinfo.>>>externalizing_constants"));
    }
    // externalize immediately certain kinds of constants
    // we know to have no direct references.
    for (int i = 1; i < constants.length; i+=constants[i].nSlots) {
        switch ( constants[i].tag ){
        case Const.CONSTANT_UTF8:
        case Const.CONSTANT_NAMEANDTYPE:
        // unquestionably, share these.
        constants[i] = externalPool.add( constants[i] );
        // FALLTHROUGH
        default:
        constants[i].externalize( externalPool );
        break;
        }
    }
    }

    /*
     * If we are using an external string table, then
     * we can make our own table smaller. At this point,
     * all non-code references into it are by object reference, NEVER
     * by index -- everything has been resolved! Thus we can
     * compact our table, deleting all the UnicodeConstants.
     * We adjust each constant's index entry accordingly.
     * Naturally, we preserve the null entries.
     *
     */
    public void smashConstantPool(){
    int nOld = constants.length;
    int nNew = 1;
    ConstantObject o;
    // first, count and index.
    for ( int i = 1; i < nOld; i += o.nSlots ){
        o = constants[i];
        if ( ! o.shared ){
        if ( o.references == 0 ){
            o.index = -1; // trouble.
        } else {
            // we're keeping it.
            o.index = nNew;
            nNew += o.nSlots;
        }
        }
    }
    // now reallocate and copy.
    ConstantObject newConstants[] = new ConstantObject[ nNew ];
    int j = 1;
    for ( int i = 1; i < nOld; i += o.nSlots ){
        o = constants[i];
        if ( (! o.shared ) && ( o.references != 0 ) ){
        // we're keeping it.
        newConstants[j] = o;
        j += o.nSlots;
        }
    }
    oldConstants = constants;
    constants = newConstants;
    }

    // write constants back out, just like we read them in.
    void writeConstantPool( DataOutput out ) throws IOException {
    int num = constants==null ? 0 : constants.length;
    if(verbose){
        log.println(Localizer.getString("classinfo.writing_constant_pool_entries", Integer.toString(num)));
    }
    out.writeShort( num );
    for (int i = 1; i < num; i+=constants[i].nSlots) {
        constants[i].write( out );
    }
    }

    // Read the list of interfaces this class supports
    void readInterfaces( DataInput in ) throws IOException {
    int count = in.readUnsignedShort();
    if(verbose){
        log.println(Localizer.getString("classinfo.reading_interfaces_implemented", Integer.toString(count)));
    }
    interfaces = new ClassConstant[count];
    for (int i = 0; i < count; i++) {
        //interfaces[i] = (ClassConstant) symbols[in.readUnsignedShort()];
        // interfaces not external -- they use own constant pool!
        interfaces[i] = (ClassConstant) constants[in.readUnsignedShort()];
    }
    }

    void externalizeInterfaces( ConstantPool p ){
    int count = interfaces==null ? 0 : interfaces.length;
    if(verbose){
        log.println(Localizer.getString("classinfo.>>>externalizing_interfaces_implemented"));
    }
    for (int i = 0; i < count; i++) {
        interfaces[i] = (ClassConstant)p.dup( interfaces[i] );
    }
    }

    void writeInterfaces( DataOutput out ) throws IOException {
    int count = interfaces==null ? 0 : interfaces.length;
    if(verbose){
        log.println(Localizer.getString("classinfo.writing_interfaces_implemented", Integer.toString(count)));
    }
    out.writeShort( count );
    for (int i = 0; i < count; i++) {
        out.writeShort( interfaces[i].index );
    }
    }

    // Read the list of fields
    void readFields( DataInput in ) throws IOException {
    int count = in.readUnsignedShort();
    fields = new FieldInfo[count];
    if(verbose){
        log.println(Localizer.getString("classinfo.reading_field_members", Integer.toString(count)));
    }

    for (int i = 0; i < count; i++) {
        fields[i] = FieldInfo.readField(in, this);
        fields[i].index = i;
    }
    }

    void externalizeFields( ConstantPool p ){
    int count = fields==null ? 0 : fields.length;
    if(verbose){
        log.println(Localizer.getString("classinfo.>>>externalizing_field_members"));
    }
    for (int i = 0; i < count; i++) {
        fields[i].externalize( p );
    }
    }

    void writeFields( DataOutput out ) throws IOException {
    int count = fields==null ? 0 : fields.length;
    if(verbose){
        log.println(Localizer.getString("classinfo.writing_field_members", Integer.toString(count)));
    }
    out.writeShort( count );
    for (int i = 0; i < count; i++) {
        fields[i].write( out );
    }
    }

    // Read the list of methods from classfile
    void readMethods( DataInput in, boolean readCode ) throws IOException {
    int count = in.readUnsignedShort();
    methods = new MethodInfo[count];

    if(verbose){
        log.println(Localizer.getString(
            "classinfo.reading_methods", Integer.toString(count)));
    }
    for (int i = 0; i < count; i++) {
        methods[i] = MethodInfo.readMethod( in, this, readCode );
        methods[i].index = i;
    }
    }

    void externalizeMethods( ConstantPool p ){
    int count = methods==null ? 0 : methods.length;
    if(verbose){
        log.println(Localizer.getString("classinfo.>>>externalizing_methods"));
    }
    for (int i = 0; i < count; i++) {
        methods[i].externalize( p );
    }
    }

    void writeMethods( DataOutput out) throws IOException {
    int count = methods==null ? 0 : methods.length;
    if(verbose){
        log.println(Localizer.getString("classinfo.writing_methods", Integer.toString(count)));
    }
    out.writeShort(count);
    for (int i = 0; i < count; i++) {
        methods[i].write( out );
    }
    }

    void readAttributes( DataInput in ) throws IOException {
    int count = in.readUnsignedShort();
    Vector clssAttr = new Vector();
    
    if(verbose){
        log.println(Localizer.getString("classinfo.reading_attributes", Integer.toString(count)));
    }

    for (int i = 0; i < count; i++) {
        int nameIndex = in.readUnsignedShort();
        int bytes = in.readInt();
        UnicodeConstant name = (UnicodeConstant)symbols[nameIndex];
        if (name.string.equals(/*NOI18N*/"fieldtable")){
        fieldtableName = name;
        if (verbose){
            log.println(Localizer.getString("classinfo.reading_name",name));
        }
        int n = bytes / 2;
        refFieldtable = new FieldConstant[ n ];
        for( int j = 0; j < n; j++ ){
            refFieldtable[j] = (FieldConstant)symbols[ in.readUnsignedShort() ];
        }
        } else if  (name.string.equals(/*NOI18N*/"methodtable")){
        methodtableName = name;
        if (verbose){
            log.println(Localizer.getString("classinfo.reading_name", name));
        }
        int n = bytes / 2;
        refMethodtable = new MethodConstant[ n ];
        for( int j = 0; j < n; j++ ){
            refMethodtable[j] = (MethodConstant)symbols[ in.readUnsignedShort() ];
        }
        } else if  (name.string.equals(/*NOI18N*/"SourceFile")) {
        if (ClassInfo.classDebug) {
            UnicodeConstant srcName =
            (UnicodeConstant)symbols[in.readUnsignedShort()];
            sourceFileAttr =
            new SourceFileAttribute(name, bytes, srcName);
            clssAttr.addElement(sourceFileAttr);
        } else {
            byte[] b = new byte[bytes];
            in.readFully(b);
            clssAttr.addElement
            (new UninterpretedAttribute(name, bytes, b));
        }
        } else {
        byte[] b = new byte[bytes];
        in.readFully(b);
        clssAttr.addElement
            (new UninterpretedAttribute(name, bytes, b));
        }
    }
    int nattr = clssAttr.size();
    if (nattr > 0) {
        this.classAttributes = new Attribute[nattr];
        clssAttr.copyInto(classAttributes);
    }
    }

    void externalizeAttributes( ConstantPool p ){
    Attribute.externalizeAttributes(classAttributes, p);
    }

    public void
    allocateFieldsFromFieldtable(){
    if ( refFieldtable == null ) return; // no can do.

    int n = refFieldtable.length;
    int nsuper;
    int fieldoff = 0;
    nsuper = ( superClassInfo == null ) ? 0 : superClassInfo.refFieldtable.length;
    if ( nsuper == 0 ){
        fieldoff = 0;
    } else {
        FieldInfo f =  refFieldtable[ nsuper-1 ].find();
        if ( f.instanceOffset < 0 ){
        superClassInfo.allocateFieldsFromFieldtable();
        }
        fieldoff = f.instanceOffset + f.nSlots;
    }
    for ( int i = nsuper ; i < n; i++ ){
        FieldInfo f = refFieldtable[ i ].find();
        if ( f == null ){
        // this is not supposed to happen.
        System.out.println( Localizer.getString("classinfo.cannot_find_field", refFieldtable[i]));
        continue;
        }
        f.instanceOffset = fieldoff;
        fieldoff += f.nSlots;
    }
    //
    // and while we're here, do methodtable as well.
    //
    n = refMethodtable.length;
    int methodoff = 0; // 1=>0 EJVM

    for ( int i = 0; i < n ; i++ ){
        MethodInfo m = refMethodtable[ i ].find();

        if ( m == null ){
        // this is not supposed to happen.
        System.out.println( Localizer.getString("classinfo.cannot_find_field", refMethodtable[i]));
        continue;
        } else if ( m.methodTableIndex != methodoff ){
        if ( m.parent != this ){
            // this method is in a superclass.
            // which apparently hasn't set up its methodtable yet.
            // so go do it.
            superClassInfo.allocateFieldsFromFieldtable();
        } else {
            // we do it here.?
            System.out.println("Inconsistent refMethodtable in class "+this.toString());
            m.methodTableIndex = methodoff;
        }
        }
        methodoff += 1;
    }
    }

    void writeTableAttribute( DataOutput out, UnicodeConstant name, FMIrefConstant table[] ) throws IOException {
    if (verbose){
        log.println(Localizer.getString("classinfo.writing_name", name.string));
    }
    out.writeShort( name.index );
    int n = table.length;
    out.writeInt( 2*n );
    for ( int i = 0; i < n; i++ ){
        out.writeShort( table[i].index );
    }
    }

    void writeAttributes( DataOutput out ) throws IOException {
    int count = 0;
    if ( fieldtableName != null )
        count++;
    if ( methodtableName != null )
        count++;

    if (classAttributes != null) {
        count += classAttributes.length;
    }
    out.writeShort(count);

    if ( fieldtableName != null ){
        writeTableAttribute( out, fieldtableName, refFieldtable );
    }
    if ( methodtableName != null ){
        writeTableAttribute( out, methodtableName, refMethodtable );
    }
    if (classAttributes != null) {
        for (int k = 0; k < classAttributes.length; k++) {
        classAttributes[k].write( out );
        }
    }
    }

    // Read in the entire class
    // assume file is open, magic numbers are o.k.
    // read assumes reading from class file
    // see also readMultiClass
    //
    private void
    doRead( DataInput in, boolean readCode, ConstantPool externalSymbols )
    throws IOException {

    externalPool = externalSymbols; // for convenience, later
    resolveConstants( );

    access = in.readUnsignedShort();
    thisClass = (ClassConstant) symbols[in.readUnsignedShort()];
    int sup = in.readUnsignedShort();
    if ( sup != 0 )
        superClass = (ClassConstant) symbols[sup];
    className = thisClass.name.string;

    // Read the various parts of the class file
    readInterfaces( in );
    readFields( in );
    readMethods( in, readCode );
    readAttributes( in );
    enterClass( className );
    }

    public void
    read( DataInput in, boolean readCode, ConstantPool externalSymbols )
    throws IOException {
    readConstantPool( in );
    symbols = constants;    // symbol table == constant pool
    doRead( in, readCode, externalSymbols );
    }

    //
    // read a whole class from a module file.
    // the only difference is in the treatment of the external
    // symbol table. In this case, it is not built, but USED.
    public void
    readMultiClass( DataInput in, boolean readCode, ConstantPool externalSymbols )
    throws IOException {
    readConstantPool( in );
    symbols = externalSymbols.getConstants(); //symbol table is external.
    doRead( in, readCode, externalSymbols );
    }

    public void
    externalize( ConstantPool p ){
    if (verbose){
        log.println(Localizer.getString("classinfo.externalizing_class", className));
    }
    externalizeConstants( p );
    thisClass = (ClassConstant)p.dup( thisClass );
    //thisClass.externalize(p);//redundant?
    if ( superClass != null ){
        superClass = (ClassConstant)p.dup( superClass );
        //superClass.externalize(p);//redundant?
    }
    //externalizeInterfaces( p ); // interfaces NOT externalized!
    externalizeMethods( p );
    externalizeFields( p );
    externalizeAttributes( p );
    }

    // Compute the fieldtable for a class.  This requires laying
    // out the fieldtable for our parent, then adding any fields
    // that are not inherited.
    public
    void buildFieldtable( ConstantPool cp ){
    if (this.fieldtable != null) return; // already done.
    FieldInfo fieldtable[];
    int n;
    int fieldoff;
    int fieldtableLength = 0;
    FieldInfo candidate[] = this.fields;
    for( int i =0; i < candidate.length; i++ ){
        if ((candidate[i].access & Const.ACC_STATIC) == 0){
        fieldtableLength++;
        }
    }
    if ( superClassInfo != null ){
        superClassInfo.buildFieldtable( cp );
        n = superClassInfo.fieldtable.length;
        fieldtableLength += n;
        fieldoff = (n==0)? 0
            : (superClassInfo.fieldtable[n-1].instanceOffset +
            superClassInfo.fieldtable[n-1].nSlots);
        fieldtable = new FieldInfo[ fieldtableLength ];
        System.arraycopy( superClassInfo.fieldtable, 0, fieldtable, 0, n );
    } else {
        fieldtable = new FieldInfo[ fieldtableLength ];
        n = 0;
        fieldoff = 0;
    }
    for( int i =0; i < candidate.length; i++ ){
        if ((candidate[i].access & Const.ACC_STATIC) == 0){
        fieldtable[n++] = candidate[i];
        candidate[i].instanceOffset = fieldoff;
        fieldoff += candidate[i].nSlots;
        }
    }
    this.fieldtable = fieldtable;
    //
    // here, we make the gross assumption that
    // if we're building a fieldtable, we're using a shared
    // external Constant Pool
    fieldtableName = (UnicodeConstant)cp.add( new UnicodeConstant(/*NOI18N*/"fieldtable") );
        
    }

    private FMIrefConstant buildReference( ClassMemberInfo m, boolean isMethod, ConstantPool cp ){
    ClassConstant c = (ClassConstant) cp.dup( m.parent.thisClass );
    FMIrefConstant x;
    NameAndTypeConstant n = (NameAndTypeConstant) cp.add(
        new NameAndTypeConstant( 
        (UnicodeConstant)cp.add( m.name), 
        (UnicodeConstant)cp.add( m.type )
        )
    );
    if (isMethod){
        x = new MethodConstant( c, n );
    }else{
        x = new FieldConstant( c, n );
    }
    return (FMIrefConstant)cp.add( x );
    }

    public
    void buildReferenceFieldtable( ConstantPool cp ){
    if ( refFieldtable != null ) return; // already done, it says here.
    if ( fieldtableName == null ){
        fieldtableName = (UnicodeConstant)cp.add( new UnicodeConstant(/*NOI18N*/"fieldtable") );
    }
    buildFieldtable( cp );
    int n = fieldtable.length;
    refFieldtable = new FieldConstant[ n ];
    for ( int i = 0; i < n; i++ ){
        refFieldtable[i] = (FieldConstant)buildReference( fieldtable[i], false, cp );
    }
    }

    // Compute the method table for a class.  This requires laying
    // out the method table for our parent, then adding any methods
    // that are not inherited.
    public
    void buildMethodtable( ConstantPool cp ) {
    if ( this.methodtable != null ) return; // already done.
    MethodInfo table[];
    MethodInfo methods[] = this.methods;
    ClassInfo sup = superClassInfo;
    if ((sup != null) && ( (sup.access&Const.ACC_INTERFACE)==(this.access&Const.ACC_INTERFACE) ) ) {
        sup.buildMethodtable( cp );
        table = sup.methodtable;
    } else {
        table = new MethodInfo[0];
    }

    // allocate a temporary table that is certainly large enough.
    MethodInfo newTable[] = new MethodInfo[table.length + methods.length];
    int index = table.length;
    System.arraycopy( table, 0, newTable, 0, index );
    
    if (sup == null) { 
        // finalize() goes into slot 0 of java.lang.Object
        // FY: Removed for KVM.  We have no finalize() in slot 0.
        // index++;
    }

    method_loop:
    for (int i = 0; i < methods.length; i++) {
        if ((methods[i].access & (Const.ACC_STATIC|Const.ACC_PRIVATE)) != 0) {
        continue method_loop;
        } else if (methods[i].name.string.equals(/*NOI18N*/"<init>")) {
        continue method_loop;
        } else if (sup == null && methods[i].name.string.equals(/*NOI18N*/"finalize") 
                           && methods[i].type.string.equals(/*NOI18N*/"()V")) {
            newTable[0] = methods[i];
        newTable[0].methodTableIndex = 0; // 1=>0 EJVM
        continue method_loop;
        } 
        int j;
        int thisID = methods[i].getID();
        for ( j = 0; j < table.length; j++) {
        if (thisID == table[j].getID()) {
            newTable[j] = methods[i];
            newTable[j].methodTableIndex = j + 0; // 1=>0 EJVM
            continue method_loop;
        }
        }
        // If we're not overriding our parent's method we do add
        // a new entry to the method table.
        newTable[index] = methods[i];
        newTable[index].methodTableIndex = index + 0; // 1=>0 EJVM
        index++;
    }

    // now allocate a table of the correct size.
    MethodInfo methodTable[] = new MethodInfo[index];
    System.arraycopy( newTable, 0, methodTable, 0, index );

    this.methodtable =  methodTable;
    //
    // here, we make the gross assumption that
    // if we're building a methodtable, we're using a shared
    // external Constant Pool
    methodtableName = (UnicodeConstant)cp.add( new UnicodeConstant(/*NOI18N*/"methodtable") );
    }

    public
    void buildReferenceMethodtable( ConstantPool cp ){
    if ( refMethodtable != null ) return; // already done, it says here.
    if ( methodtableName == null ){
        methodtableName = (UnicodeConstant)cp.add( new UnicodeConstant(/*NOI18N*/"methodtable") );
    }
    buildMethodtable( cp );
    int n = methodtable.length;
    refMethodtable = new MethodConstant[ n ];
    for ( int i = 0; i < n; i++ ){
        refMethodtable[i] = (MethodConstant)buildReference( methodtable[i], true, cp );
    }
    }

    private static boolean
    conditionalAdd( Vector v, Object o ){
    if ( v.contains( o ) )
        return false;
    v.addElement( o );
    return true;
    }
    /*
     * Compute the vector of all interfaces this class implements (or
     * this interface extends). Not only the interfaced declared in
     * the implements clause, which is what the interfaces[] field
     * represents, but all interfaces, including those of our superclasses
     * and those extended/implemented by any interfaces we implement.
     *
     */
    public void
    findAllInterfaces(){
    /*
     * This works recursively, by computing parent's interface
     * set first. THIS ASSUMES NON-CIRCULARITY, as does the rest
     * of the Java system. This assumption will fail loudly, if
     * at all.
     */
    if ( superClassInfo == null ){
        // we must be java.lang.Object!
        allInterfaces = new Vector(5); // a generous size.
    } else {
        if ( superClassInfo.allInterfaces == null )
        superClassInfo.findAllInterfaces();
        allInterfaces = (Vector)(superClassInfo.allInterfaces.clone());
    }
    if ( interfaces == null ) return; // all done!
    for( int i = 0; i < interfaces.length; i++ ){
        ClassInfo interf = interfaces[i].find();
        if ( ( interf == null ) || ( (interf.access&Const.ACC_INTERFACE) == 0 ) ){
        System.err.println(Localizer.getString("classinfo.class_which_should_be_an_interface_but_is_not", className, interfaces[i]));
        continue;
        }
        if ( interf.allInterfaces == null )
        interf.findAllInterfaces();
        if ( ! conditionalAdd( allInterfaces, interf ) ){
        // if this interface was already in the set,
        // then all the interfaces that it extend/implement
        // will be, too.
        continue;
        }
        Enumeration interfInterf = interf.allInterfaces.elements();
        while( interfInterf.hasMoreElements() ){
        conditionalAdd( allInterfaces, interfInterf.nextElement() );
        }
    }
    }

    public boolean
    findReferences(){
    try {
        for ( int i = 0; i < methods.length; i++ ){
        methods[i].findConstantReferences();
        }
    } catch ( DataFormatException e ){
        return false;
    }
    return true;
    }

    public boolean
    countReferences( boolean isRelocatable ){
    thisClass.incReference();
    if ( superClass != null ) superClass.incReference();
    // count interface references
    if ( interfaces!=null ){
        for ( int i = 0; i < interfaces.length ; i++ ){
        interfaces[i].incReference();
        }
    }
    // then count references from fields.
    if ( fields != null ){
        for ( int i = 0; i < fields.length; i++ ){
        fields[i].countConstantReferences( isRelocatable );
        }
    }
    // then count references from code
    if ( methods != null ){
        for ( int i = 0; i < methods.length; i++ ){
        methods[i].countConstantReferences( constants, isRelocatable );
        }
    }
    Attribute.countConstantReferences(classAttributes, isRelocatable);
    return true;
    }

    public boolean
    relocateReferences(){
    try {
        for ( int i = 0; i < methods.length; i++ ){
        methods[i].relocateConstantReferences( oldConstants );
        }
    } catch ( DataFormatException e ){
        return false;
    }
    return true;
    }

    public void
    clearMemberFlags( int flagsToClear ){
    int mask = ~ flagsToClear;
    int n;
    ClassMemberInfo members[];
    if ( fields != null ){
        members = fields;
        n = members.length;
        for ( int i = 0; i < n; i++ ){
        members[i].flags &= mask;
        }
    }
    if ( fields != null ){
        members = methods;
        n = members.length;
        for ( int i = 0; i < n; i++ ){
        members[i].flags &= mask;
        }
    }
    }

    public void
    write( DataOutput o ) throws IOException {
    writeConstantPool( o );
    o.writeShort( access );
    o.writeShort( thisClass.index );
    o.writeShort( superClass==null? 0 : superClass.index );
    writeInterfaces( o );
    writeFields( o );
    writeMethods( o );
    writeAttributes( o );
    }

    private static void
    dumpComponentTable( PrintStream o, String title, ClassComponent t[] ){
    int n;
    if ( (t == null) || ((n=t.length) == 0) ) return;
    o.print( title ); o.println(/*NOI18N*/"["+n+"]:");
    for( int i = 0; i < n; i++ ){
        if ( t[i] != null )
        o.println(/*NOI18N*/"\t["+i+/*NOI18N*/"]\t"+t[i]);
    }
    }

    private static void
    dumpConstantTable( PrintStream o, String title, ConstantObject t[] ){
    int n;
    if ( (t == null) || ((n=t.length) == 0) ) return;
    o.print( title ); o.println(/*NOI18N*/"["+n+/*NOI18N*/"]:");
    o.println(/*NOI18N*/"\tPosition Index\tNrefs");
    for( int i = 0; i < n; i++ ){
        if ( t[i] != null )
        o.println(/*NOI18N*/"\t["+i+/*NOI18N*/"]\t"+t[i].index+/*NOI18N*/"\t"+t[i].references+/*NOI18N*/"\t"+t[i]);
    }
    }
    private static void
    dumpMemberTable( PrintStream o, String title, ClassMemberInfo t[] ){
    int n;
    if ( (t == null) || ((n=t.length) == 0) ) return;
    o.print( title ); o.println(/*NOI18N*/":");
    for( int i = 0; i < n; i++ ){
        if ( t[i] != null )
        o.println(/*NOI18N*/"\t["+i+/*NOI18N*/"]\t"+t[i].qualifiedName() );
    }
    }
    public void
    dump( PrintStream o ){
    o.print(Util.accessToString(access)+/*NOI18N*/"Class "+thisClass);
    if ( superClass != null )
        o.print(/*NOI18N*/" extends "+superClass);
    if ( interfaces!=null && interfaces.length != 0 ){
        o.print(/*NOI18N*/" implements");
        for( int i = 0; i < interfaces.length ; i++ ){
        o.print(" "+interfaces[i]);
        }
    }
    o.println();
    dumpComponentTable( o, /*NOI18N*/"Methods", methods );
    dumpComponentTable( o, /*NOI18N*/"Fields",  fields  );
    if ( fieldtable != null )
        dumpMemberTable( o, /*NOI18N*/"Fieldtable", fieldtable );
    else
        dumpComponentTable( o, /*NOI18N*/"Fieldtable-by-reference", refFieldtable );
    if ( methodtable != null )
        dumpMemberTable( o, /*NOI18N*/"Methodtable", methodtable );
    else
        dumpComponentTable( o, /*NOI18N*/"Methodtable-by-reference", refMethodtable );
    dumpConstantTable( o, /*NOI18N*/"Constants", constants );
    }

    /**
     * We keep track of classes by hashing them by name when
     * we read them. They can be looked up using lookupClass,
     * which will take a classname string as parameter.
     */
    public static Hashtable classtable = new Hashtable();

    protected void enterClass( String key ){
    // should check to see if a class of this name is already there...
    if ( classtable.containsKey( className ) ){
        System.err.println(Localizer.getString("classinfo.class_table_already_contains", className));
        return;
    }
    classtable.put( key, this );
    // if a classvector has been created, we need to add this.
    // at end should be sufficient.
    vm.ClassClass.appendClassElement( this );
    }

    public static ClassInfo lookupClass( String key ){
    return (ClassInfo)classtable.get( key );
    }

    public static int nClasses(){
    return classtable.size();
    }

    public static Enumeration allClasses(){
    return classtable.elements();
    }

    public static boolean resolveSupers(){
    Enumeration allclasses = allClasses();
    boolean ok = true;
    while( allclasses.hasMoreElements() ){
        ClassInfo c = (ClassInfo)allclasses.nextElement();
        if ( c.superClass==null ){
        // only java.lang.Object can be parentless
        if ( ! c.className.equals( /*NOI18N*/"java/lang/Object" ) ){
            System.out.println(Localizer.getString("classinfo.class_is_parent-less", c.className));
            ok = false;
        }
        } else {
        ClassInfo s = ClassInfo.lookupClass( c.superClass.name.string );
        if ( s == null ){
            System.out.println(Localizer.getString("classinfo.class_is_missing_parent", c.className, c.superClass.name.string ));
            ok = false;
        } else {
            c.superClassInfo = s;
        }
        }
    }
    return ok;
    }

    public String
    toString(){
    return /*NOI18N*/"ClassInfo-\""+className+/*NOI18N*/"\"";
    }

    // Convert ldc to ldc2
    public void relocateAndPackCode() {
        for ( int i = 0; i < methods.length; i++ )
            methods[i].relocateAndPackCode(constants); 
    }
}