/*
* 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);
}
}
}
}
}
}
|