/* * * * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ 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; boolean doNativesTableOnly = true; // we are interested only in building NativesTable.cpp for the VM 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") || clist[i].equals(/*NOI18N*/"-writer") ){ 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")); if (!doNativesTableOnly) { writeNativesHeaders( nativeTypes, c, nclasses ); writeNativesHeaders( extraHeaders, c, nclasses ); } if ( ROMout ){ return writeROMFile( outName, c, romAttributes ); } 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. * >>IMPL_NOTE: consider whether it should be fixed<<. */ VMClassFactory classMaker = new EVMClassFactory(); public ClassClass[] finalizeClasses(){ ClassClass classes[] = ClassClass.getClassVector(classMaker); int n = classes.length; // 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 (!doNativesTableOnly) { 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(); } } } } // 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); } } } } } }