FileDocCategorySizeDatePackage
JavaCodeCompact.javaAPI DocJ2ME CLDC 1.123940Wed Feb 05 15:56:04 GMT 2003None

JavaCodeCompact.java

/*
 *      JavaCodeCompact.java     1.53     03/01/14 SMI
 *
 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

import components.*;
import vm.*;
import runtime.*;
import util.*;
import jcc.*;

import java.io.PrintStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.File;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Hashtable;

public class JavaCodeCompact extends LinkerUtil {
    int             verbosity = 0;
    boolean      fail      = false;
    ConstantPool t = new ConstantPool();
    ClassFileFinder  searchPath;
    String     firstFileName;
    String     outName;

    Vector    arrayClasses = new Vector();
    boolean    classDebug = false;
    boolean    ROMout     = true;
    boolean    outSet   = false;
    String    archName = "EVM";
    boolean    archSet  = false;
    boolean     doShared = false;
    Vector    romAttributes = new Vector();
    ClassReader rdr;
    MemberLoader memberLoader;

    Hashtable    headerDirs = new Hashtable(31);
    String    stubDestName;
    boolean    stubTraceMode = false;
    ClassnameFilterList nativeTypes = new ClassnameFilterList();
    ClassnameFilterList extraHeaders = new ClassnameFilterList();
    int        maxSegmentSize = -1;

    private void
    fileFound( String fname ){
    // currently, the only thing we do with file names
    // is make them into potential output file names.
    if ( firstFileName == null ) firstFileName = fname;
    }

    private void
    makeOutfileName(){
    if ( outName != null ) return; // already done by -o option.
    if (firstFileName==null) firstFileName = "ROMjava.c";
    int sepindex = firstFileName.lastIndexOf( File.separatorChar )+1;
    int suffindex = firstFileName.lastIndexOf( '.' );
    if ( suffindex < 0 ) suffindex = firstFileName.length();
    outName = firstFileName.substring( sepindex, suffindex)
          + (ROMout ? ".c" : ".mclass");
    }

    private void
    readFile( String fileName, Vector classesProcessed ){

    if ( rdr == null ){
        rdr = new ClassReader( t, verbosity );
    }
    try {
        if (fileName.endsWith(".zip") || fileName.endsWith(".jar")){ 
        rdr.readZip( fileName, classesProcessed );
        } else { 
        rdr.readFile( fileName, classesProcessed );
        fileFound( fileName );
        }
    } catch ( IOException e ){
        System.out.println(Localizer.getString("javacodecompact.could_not_read_file", fileName));
        e.printStackTrace();
        fail = true;
    }
    }

    private void
    loadMembers( String filename, Vector classlist ){
    if ( memberLoader == null ){
        if ( rdr == null ){
        rdr = new ClassReader( t, verbosity );
        }
        if ( searchPath == null ){
        searchPath = new ClassFileFinder();
        }
        memberLoader = new MemberLoader( rdr, searchPath );
    }
    try {
        memberLoader.readFromFile( filename, classlist );
    } catch ( IOException e ){
        System.err.println("-memberlist "+filename);
        e.printStackTrace();
        fail = true;
    }
    }

    /*
     * Iterate through the shared constant pool, looking for
     * class constants that cannot be resolved. Make a list
     * of their names, as java Strings
     */
    public Vector
    unresolvedClassNames(){
    Vector names  = new Vector();
    Enumeration symbols = t.getEnumeration();
    while ( symbols.hasMoreElements() ){
        Object o = symbols.nextElement();
        if ( o instanceof ClassConstant ){
        ClassConstant cc = (ClassConstant)o;
        if ( ! cc.isResolved() ){
            names.addElement( cc.name.string );
            cc.forget(); // so 'unresolved' doesn't stick.
        }
        }
    }
    Enumeration classes = ClassInfo.allClasses();
    while ( classes.hasMoreElements() ){
        ClassInfo c = (ClassInfo) classes.nextElement();
        ConstantObject constants[] = c.constants;
        if ( constants == null ) 
        continue;
        int nc = constants.length;
        for ( int i = 0 ; i < nc; i++ ){
        if ( (constants[i] instanceof ClassConstant ) && ! constants[i].isResolved() ){
            //
            // so far, entries in the names array are
            // unique. But because each class has a separate
            // interfaces array, the current candidate
            // may already be on it. Avoid adding it 
            // a second time!
            // Also, don't put arrays on this list!
            ClassConstant intrf = (ClassConstant)constants[i];
            String intname = intrf.name.string;
            if ( (intname.charAt(0) != Const.SIGC_ARRAY ) && ! names.contains( intname ) )
            names.addElement( intname );
            intrf.forget(); // so 'unresolved' doesn't stick.
        }
        }
    }
    return names;
    }

    Vector  classesProcessed = new Vector();
    int     nclasses = 0;
    boolean buildTables = true;
    boolean quicken     = true;
    boolean doClosure   = false;
    boolean qlossless   = false;

    private boolean
    processOptions( String clist[] ){
    boolean success = true;

    for( int i = 0; i < clist.length; i++ ){
        if ( clist[i].equals(/*NOI18N*/"-c") ){
        doClosure = true;
        continue;
        } else if ( clist[i].equals(/*NOI18N*/"-t") ){
        buildTables = true;
        continue;
        } else if ( clist[i].equals(/*NOI18N*/"-q") ){
        quicken     = true;
        continue;
        } else if ( clist[i].equals(/*NOI18N*/"-nt") ){
        buildTables = false;
        continue;
        } else if ( clist[i].equals(/*NOI18N*/"-nq") ){
        quicken     = false;
        continue;
        } else if ( clist[i].equals(/*NOI18N*/"-qlossless") ){
        buildTables = true;
        quicken     = true;
        qlossless   = true;
        continue;
        } else if ( clist[i].equals(/*NOI18N*/"-g") ){
        classDebug = true;
        ClassInfo.classDebug = true;
        continue;
        } else if ( clist[i].equals(/*NOI18N*/"-imageAttribute") ){
        romAttributes.addElement( clist[ ++i ] );
        continue;
        } else if ( clist[i].equals(/*NOI18N*/"-v") ){
        verbosity++;
        continue;
        } else if ( clist[i].equals(/*NOI18N*/"-r") ){
        ROMout = false;
        continue;
        } else if ( clist[i].equals(/*NOI18N*/"-o")  ){
        outName =  clist[ ++i ];
        } else if ( clist[i].equals(/*NOI18N*/"-classpath")  ){
        if ( searchPath == null )
            searchPath = new ClassFileFinder();
        searchPath.addToSearchPath( clist[ ++i ] );
        } else if ( clist[i].equals(/*NOI18N*/"-arch") ){
        String archArg = clist[ ++i ];
        archName = archArg.toUpperCase();
        if ( archSet ){
            System.err.println(Localizer.getString("javacodecompact.too_many_-arch_targetarchname_specifiers"));
            success = false;
        }
        archSet = true;
        continue;
        } else if (clist[i].equals(/*NOI18N*/"-s")){ 
        doShared = true;
        } else if ( clist[i].equals("-memberlist") ){
        loadMembers( clist[++i], classesProcessed );
        } else if ( clist[i].equals("-headersDir") ){
        String type = clist[++i];
        String dir = clist[++i];
        headerDirs.put(type, dir);
        buildTables = true; // got to.
        } else if ( clist[i].equals("-stubs") ){
        stubDestName = clist[++i];
        } else if ( clist[i].equals("-trace") ){
        stubTraceMode = true;
        } else if ( clist[i].equals("-f") ){
        try {
            success = processOptions( parseOptionFile( clist[++i] ) );
        } catch ( java.io.IOException e ){
            e.printStackTrace();
            success = false;
        }
        } else if ( clist[i].equals("-nativesType") ){
        String name = clist[++i];
        String patterns = clist[++i];
        nativeTypes.addTypePatterns( name, patterns );
        } else if ( clist[i].equals("-extraHeaders") ){
        String name = clist[++i];
        String patterns = clist[++i];
        extraHeaders.addTypePatterns( name, patterns );
        } else if ( clist[i].equals("-maxSegmentSize") ){
        String arg = clist[++i];
        try {
            maxSegmentSize = (new Integer(arg)).intValue();
        } catch (NumberFormatException ex) {
            System.err.println(Localizer.getString("javacodecompact.invalid_max_segment_size"));
            success = false;
        }
        } else { 
        readFile( clist[i], classesProcessed );
        }
    }

    // Default classname filter for natives
    nativeTypes.addTypePatterns( "JDKPack", "-*" );

    return success;
    }

    public boolean process( String clist[] ){

    if ( ! processOptions( clist ) )
        return false;
    makeOutfileName();
    if ( !ROMout ){
        quicken = false;
        buildTables = false;
    }
        
        

    if ( doClosure ){
        while ( !fail ){
        Vector unresolved = unresolvedClassNames();
        if ( unresolved.size() == 0 )
            break; // none left!
        Enumeration ulist = unresolved.elements();
        int nfound = 0;
        while ( ulist.hasMoreElements() ){
            String uname = (String)ulist.nextElement();
            nfound += rdr.readClass(uname, searchPath, classesProcessed);
        }
        if ( nfound == 0 ){
            // the list now contains things which could
            // not ever be resolved. Print it out for
            // information and go on.
            // this is not actually an error!
            if ( verbosity > 0 ){
            ulist = unresolved.elements();
            System.out.println(Localizer.getString("javacodecompact.could_not_resolve_these_names"));
            while( ulist.hasMoreElements() ){
                System.out.write('\t');
                System.out.println( (String)ulist.nextElement() );
            }
            }
            break;
        }
        }
    }

    if ( fail ){
        return false;
    }

    if ( memberLoader != null ){
        // then we saw a -memberlist option.
        // go through all classes, hacking away
        // all unmarked, unwanted elements.
        memberLoader.deleteUnwantedMembers( classesProcessed );
    }

    nclasses = classesProcessed.size();
    ClassInfo c[] = new ClassInfo[ nclasses ];
    classesProcessed.copyInto( c );

    if ( buildTables ){
        if ( verbosity != 0 )System.out.println(Localizer.getString("javacodecompact.resolving_superclass_hierarchy") );
        if ( ! ClassInfo.resolveSupers() ){
        return false; // missing superclass is a fatal error.
        }
        for ( int i = 0; i < nclasses; i++ ){
        if ( verbosity != 0 )System.out.println(Localizer.getString("javacodecompact.building_tables_for_class", c[i].className));
        c[i].buildReferenceFieldtable( t );
        c[i].buildReferenceMethodtable( t );
        }
    }
    for ( int i = 0; i < nclasses; i++ ){
        c[i].findReferences();
    }

    //if ( fail ) System.exit(1);

    // now write the output
    if ( verbosity != 0 )System.out.println(Localizer.getString("javacodecompact.writing_output_file"));

    writeNativesHeaders( nativeTypes, c, nclasses );
    writeNativesHeaders( extraHeaders, c, nclasses );

    if ( stubDestName != null ){
        writeCStubs( c, nclasses );
    }

    if ( ROMout ){
        return writeROMFile( outName, c, romAttributes );
    } else {
        MultiClass out = new MultiClass( t, c, verbosity>1 );
        out.writeMultiClass( outName );
    }

    return !fail;

    }

    public static void main( String clist[] ){
    boolean success = new JavaCodeCompact().process( clist );
    if ( !success ){
        System.out.flush();
        System.err.flush();
        System.exit(1);
    }
    return;
    }

    /*
     * ALL THIS IS FOR ROMIZATION
     */

    /*
     * Iterate through all known classes.
     * Iterate through all constant pools.
     * Look at ClassConstants. If they are unbound,
     * and if they are references to array classes,
     * then instantiate the classes and rebind.
     */
    public boolean instantiateArrayClasses( ClassInfo classTable[], boolean verbose ){
    int nclasses = classTable.length;
    boolean good = true;
    // For EVM, make sure that all arrays of basic types
    // are instantiated!
    String basicArray[] = { "[C", "[S", "[Z", "[I", "[J", "[F", "[D", "[B", 
        "[Ljava/lang/Object;" // not strictly basic.
    };
    for ( int ino = 0; ino < basicArray.length; ino++ ){
        if ( ! collectArrayClass( basicArray[ino], verbose )){
        good = false;
        }
    }

    // Now dredge through all class constant pools.
    for ( int cno = 0; cno < nclasses; cno++ ){
        ClassInfo c = classTable[cno];
        ConstantObject ctable[] = c.constants;
        if ( ctable == null ) continue;
        int n = ctable.length;
        for( int i = 0; i < n; i++ ){
        if ( ctable[i] instanceof ClassConstant ){
            ClassConstant cc = (ClassConstant)ctable[i];
            String        cname = cc.name.string;
            if (cname.charAt(0) != Const.SIGC_ARRAY ){
            continue; // not interesting
            }
            if ( cc.isResolved() ){
            continue; // not interesting
            }
            if ( ! collectArrayClass( cname, verbose )){
            good = false;
            }
            cc.forget(); // forget the fact that we couldn't find it
        }
        }
    }
    return good;
    }

    private boolean collectArrayClass( String cname, boolean verbose ){
    // cname is the name of an array class
    // make sure it doesn't exist ( it won't if it came from a 
    // class constant ), and instantiate it. For EVM, do the same with
    // any sub-array types.
    boolean good = true;
    do {
        if ( ClassInfo.lookupClass( cname ) != null ){
        continue; // this one exists. But subclasses may not, so keep going.
        }
        try {
        arrayClasses.addElement( new ArrayClassInfo( verbose, cname ) );
        } catch ( DataFormatException e ){
        e.printStackTrace();
        good = false;
        break; // out of do...while
        }
    } while ( (cname = cname.substring(1) ).charAt(0) == Const.SIGC_ARRAY );
    return good;
    }

    /*
     * For each array class we just made up, process it like
     * all normal classes would have been processed up to this point.
     */
    public void processArrayClasses( boolean verbose, ConstantPool x ){
    Enumeration arrays = arrayClasses.elements();
    while( arrays.hasMoreElements() ){
        ArrayClassInfo a = (ArrayClassInfo)arrays.nextElement();
        if ( verbose ){
        System.out.println(Localizer.getString("javacodecompact.processing_array_class", a.className));
        }
        a.externalize(x);
        //a.countReferences();
        //a.smashConstantPool();
    }
    }

    /*
     * My first attempt at factoring out VM specific code
     * is to subclass ClassClass. Perhaps I should be subclassing
     * components.ClassInfo itself, I don't know.
     * Anyway, this is the EVM-specific class factory. This
     * should be dependent on a runtime switch, obviously.
     * >>FIXME<<.
     */
    VMClassFactory classMaker = new EVMClassFactory();

    public ClassClass[] finalizeClasses(){
    ClassClass classes[] = ClassClass.getClassVector(classMaker);
    int n = classes.length;

    if ( quicken ){
        CodeHacker ch = new CodeHacker( t, qlossless, true, verbosity >= 2 );
        for ( int i = 0; i < n; i++ ){
        if ( verbosity != 0 )System.out.println(Localizer.getString("javacodecompact.quickening_code_of_class", classes[i].ci.className));
        if ( ! ch.quickenCode( classes[i].ci ) )
            fail = true;
        }
    }

    // constant pool smashing has to be done after quickening,
    // else it doesn't make much difference!

    for ( int i = 0; i < n; i++ ){
        ClassInfo c = classes[i].ci;
        if ( verbosity != 0 )System.out.println(Localizer.getString("javacodecompact.reducing_constant_pool_of_class", c.className));
        c.countReferences( !ROMout );
        c.smashConstantPool();
        c.relocateReferences();
    }

    for ( int i = 0; i < n ; i++ )
        classes[i].ci.allocateFieldsFromFieldtable();
    
    /*
     * EVM doesn't do inlining, yet.
     * This last-minute preparation step might be generalized
     * to something more useful.
     */
    /*
    //for ( int i = 0; i < n; i++) 
    //    classes[i].getInlining();
    */
    return classes;
    }

    private boolean writeROMFile( String outName, ClassInfo classTable[], Vector attributes ){
    boolean good = true;
    ConstantPool sharedConstant = null;

    //
    // did arg parsing.
    // now do work.
    //

    good = instantiateArrayClasses( classTable, verbosity>1 );
    processArrayClasses( verbosity>1, t );
    PrimitiveClassInfo.init( verbosity > 1, t);

    // is better to have this after instantiating Array classes, I think.
    ClassClass classes[] = finalizeClasses();
    int       totalclasses = classes.length;
    // at this point, the classes array INCLUDES all the array
    // classes. classTable doesn't include these!
    // Since array classes CANNOT participate in sharing
    // (because of magic offsets) they are excluded from the
    // sharing calculation below. And because they don't have
    // any code...

    if (doShared) {
        // create a shared constant pool
        sharedConstant = new ConstantPool();
            for (int i = 0; i < classTable.length; i++) 
        addConstant2SharedPool(classTable[i], sharedConstant);
           
        // sort the reference count
        sharedConstant.doSort();

            // run via the shared constant pool once.
            if (ClassClass.isPartiallyResolved(sharedConstant.getConstants())) {
                sharedConstant = ClassClass.makeResolvable(sharedConstant);
            }
        } else {
            for (int i = 0; i < totalclasses; i++)
                classes[i].adjustSymbolicConstants();
        }

    for (int i = 0; i < totalclasses; i++) 
            classes[i].ci.relocateAndPackCode();

    if ( ! good ) return false;

    CoreImageWriter w;

    {
        String writername = "runtime."+archName+"Writer";
        Class writerClass = null;
        try {
        writerClass = Class.forName( writername );
        } catch ( ClassNotFoundException ee ){
        System.err.println(Localizer.getString("javacodecompact.not_supported", archName));
        return false;
        }
        try {
        w = (CoreImageWriter)(writerClass.newInstance());
        } catch ( Exception e ){
        System.err.println(Localizer.getString("javacodecompact.could_not_instantiate", writername));
        e.printStackTrace( );
        return false;
        }
    }

    w.init(classDebug, nativeTypes, verbosity>0, maxSegmentSize);

    Enumeration attr = attributes.elements();
    while ( attr.hasMoreElements() ){
        String val = (String)attr.nextElement();
        if ( ! w.setAttribute( val ) ){
        System.err.println(Localizer.getString("javacodecompact.bad_attribute_value",val));
        }
    }

    if (  w.open( outName ) != true ) {
        w.printError( System.out );
        good = false;
    } else {
        good = w.writeClasses(t, sharedConstant);
        w.printSpaceStats( System.out );
        w.close();
    }
    return good;
    }

    /*
     * For writing header files. We just instantiate
     * a runtime.HeaderDump and let it do all the work for us.
     */
    private void
    writeNativesHeaders( ClassnameFilterList groups, ClassInfo c[], int nclasses ){
    Hashtable dumpers = new Hashtable(7);

    for ( int i = 0; i < nclasses; i++ ){
        ClassInfo ci = c[i];
        String classname = ci.className;

        String[] types =  groups.getTypes( classname );
        for ( int j = 0; j < types.length; ++j) {
        String type = types[j];
        HeaderDump hd = type != null ?
            (HeaderDump)dumpers.get(type) : null;
        if (hd == null) {
            try {
            Class dumperClass =
                Class.forName("runtime." + type + "Header");
            hd = (HeaderDump)dumperClass.newInstance();
            dumpers.put(type, hd);
            } catch (Exception e) {
            continue;
            }
        }
        String classFilename = hd.filename( classname );
        String destFilename = classFilename+".h";

        String nativesHeaderDestDir = (String)headerDirs.get(type);
        File nativesDestFile = new File(nativesHeaderDestDir,
                        destFilename);
        File nativesDumpFile;

        boolean didWorkForNatives;

        if ( nativesDestFile.exists() ){
            nativesDumpFile =
            new File( nativesHeaderDestDir, classFilename+".TMP" );
        } else {
            nativesDumpFile = nativesDestFile;
        }

        try {
            PrintStream o = new BufferedPrintStream( new FileOutputStream( nativesDumpFile ) );
            didWorkForNatives = hd.dumpHeader( ci, o );
            o.close();
        } catch ( IOException e ){
            e.printStackTrace();
            continue;
        }

        if ( didWorkForNatives ){
            if ( nativesDestFile != nativesDumpFile ){
            // copy and delete
            FileCompare.conditionalCopy( nativesDumpFile,
                             nativesDestFile );
            nativesDumpFile.delete();
            }
        } else {
            nativesDumpFile.delete();
        }
        }
    }
    }

    /*
     * For writing a C stub file. We just instantiate
     * a runtime.CStubGenerator and let it do all the work for us.
     */
    private void
    writeCStubs( ClassInfo c[], int nclasses ){
    // (conditional file creation copied from above)
    File destFile = new File( stubDestName );
    File dumpFile;

    if ( destFile.exists() ){
        dumpFile = new File( stubDestName+".TMP" );
    } else {
        dumpFile = destFile;
    }
    try {
        PrintStream o = new BufferedPrintStream( new FileOutputStream( dumpFile ) );
        CStubGenerator cs = new CStubGenerator( stubTraceMode, o );
        cs.writeStubs( c, nclasses, nativeTypes );
        o.close();
    } catch ( IOException e ){
        e.printStackTrace();
        return;
    }
    if ( destFile != dumpFile ){
        // copy and delete
        FileCompare.conditionalCopy( dumpFile, destFile );
        dumpFile.delete();
    }
    }
    // This function update the reference count and put constantobject
    // to the shared constant pool.
    private void addConstant2SharedPool(ClassInfo cinfo, ConstantPool cp) {
        for (int j = 0; j < cinfo.constants.length; j++) {
            if (cinfo.constants[j] == null)
                continue;

            int count = cinfo.constants[j].references;
            if (count > 0) {
               cinfo.constants[j] = cp.appendElement(cinfo.constants[j]);
        }
        }

        // update interfaces array
        if (cinfo.interfaces != null) {
            for (int k = 0 ; k < cinfo.interfaces.length; k++) {
               cinfo.interfaces[k] = (ClassConstant)
                               cp.appendElement(cinfo.interfaces[k]);
            }
        }

        // update exception table (catchType)
        for (int i = 0; i < cinfo.methods.length; i++) {
            if ( cinfo.methods[i].exceptionTable != null) {
                for (int j = 0; j < cinfo.methods[i].exceptionTable.length; j++) {
                    ClassConstant cc = cinfo.methods[i].exceptionTable[j].catchType;
                    if (cc != null) {
                        cinfo.methods[i].exceptionTable[j].catchType = (ClassConstant)                                                             cp.appendElement(cc);
                    }
                }
            }    
        }
    }

}