/* * @(#)JavaCodeCompact.java 1.96 06/11/07 * * Copyright 1990-2008 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 consts.*; import components.*; import vm.*; import runtime.*; import util.Assert; import util.*; import jcc.*; import java.io.FileReader; import java.io.BufferedReader; import java.io.PrintStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.File; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.Set; import java.util.Vector; import components.ClassLoader; public class JavaCodeCompact extends LinkerUtil { int verbosity = 0; //ConstantPool classNameConstants = new ConstantPool(); ClassFileFinder searchPath; String firstFileName; static String outName; /** If true, JCC should include class debug info in generated class data. * This includes line number tables, local var info, etc. */ boolean classDebug = false; boolean outSet = false; String archName = "CVM"; boolean archSet = false; /** If true, JCC will attempt to share the same constant pool for all the * classes that it romizes. */ boolean useSharedCP = false; boolean validate = false; boolean unresolvedOk = false; Vector romAttributes = new Vector(); ClassReader rdr; Hashtable headerDirs = new Hashtable(31); String stubDestName; boolean stubTraceMode = false; ClassnameFilterList nativeTypes = new ClassnameFilterList(); ClassnameFilterList extraHeaders = new ClassnameFilterList(); int maxSegmentSize = -1; boolean firstTimeOnly = true; /* arguments for JavaAPILister */ static String APIListerArgs = null; 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) + ".c"; } private boolean readFile(String fileName, Vector classesRead) { if (rdr == null){ rdr = new ClassReader(verbosity); } try { if (fileName.endsWith(".zip") || fileName.endsWith(".jar")){ rdr.readZip(fileName, classesRead); } else { rdr.readFile(fileName, classesRead); } fileFound(fileName); } catch (IOException e) { System.out.println(Localizer.getString( "javacodecompact.could_not_read_file", fileName)); e.printStackTrace(); return false; } return true; } /* Return the outName string. */ public static String getOutName() { return outName; } /** * Iterate through the classes looking for unresolved * class references. */ protected Set findUnresolvedClassNames(Enumeration e){ Set undefinedClassNames = new HashSet(); ClassInfo cinfo; while (e.hasMoreElements()){ cinfo = (ClassInfo)e.nextElement(); cinfo.findUndefinedClasses(undefinedClassNames); } return undefinedClassNames; } /* * Find the unresolved class names. * Make a list of them, as java Strings. */ public String[] unresolvedClassNames(Set undefinedClassNames){ int nUndefined; nUndefined = undefinedClassNames.size(); if (nUndefined == 0) return null; String names[] = new String[nUndefined]; names = (String[])undefinedClassNames.toArray(names); return names; } Vector classesProcessed = new Vector(); int nclasses = 0; /** If true, JCC is not allowed to do lossy quickening. */ boolean qlossless = false; /** If true, JCC needs to generate JIT friendly output. */ boolean jitOn = false; /** If true, JCC is not allowed to apply optimizations that can compact * the code. Example of such compaction includes removing nop bytecodes, * and useless gotos. */ boolean noCodeCompaction = false; /** * Processes all the JCC command line arguments into internal flags. Also * checks for the validity of the arguments. * * NOTE: processOptions() also loads and processes all the classfiles of * the initial set to be romized. Later on, process() will pull in all * other classes needed for the full transitive closure of class * references from the constant pools of the classes. processOptions() * does not resolve any of the constant pool entries (not even those in * the initial set of classes). The only CP entries that can be resolved * here are the UTF8 and value constants that do not require binding to * and other classes. * * @return false if an error was encountered. */ private boolean processOptions(String clist[]) throws Exception { boolean success = true; for( int i = 0; i < clist.length; i++ ){ if ( clist[i].equals(/*NOI18N*/"-jit") ){ jitOn = true; // Generate JIT friendly output. continue; } else if ( clist[i].equals(/*NOI18N*/"-qlossless") ){ qlossless = true; // Disallow lossy quickening. continue; } else if ( clist[i].equals(/*NOI18N*/"-noCodeCompaction") ){ noCodeCompaction = true; // Disallow code compaction. continue; } else if ( clist[i].equals(/*NOI18N*/"-g") ){ classDebug = true; // Include class debug info. 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*/"-o") ){ outName = clist[ ++i ]; } else if ( clist[i].equals(/*NOI18N*/"-classpath") ){ if ( searchPath == null ) { searchPath = new ClassFileFinder(); ClassTable.setSearchPath(searchPath); } 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("-target")){ if (!VMConfig.setJDKVersion(clist[++i])) { System.err.println(Localizer.getString( "javacodecompact.invalid_target_version", clist[i])); return false; } } else if (clist[i].equals("-sharedCP")){ useSharedCP = true; } else if ( clist[i].equals("-headersDir") ){ String type = clist[++i]; String dir = clist[++i]; headerDirs.put(type, dir); /* These not supported by CVM } 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 = Integer.parseInt(arg); } catch (NumberFormatException ex) { System.err.println(Localizer.getString( "javacodecompact.invalid_max_segment_size")); success = false; } } else if ( clist[i].equals("-validate") ){ // validate data structures before writing output validate = true; } else if ( clist[i].equals("-excludeFile") ){ // A file containing a list of methods & fields to exclude readExcludeFile(clist[++i]); } else if (clist[i].equals("-allowUnresolved")){ unresolvedOk = true; } else if (clist[i].startsWith("-cl:")){ process(false); searchPath = null; // user classloader String arg = clist[i].substring("-cl:".length()); String name; ClassLoader parent = null; int sepIndex = arg.indexOf(':'); if (sepIndex > 0) { name = arg.substring(0, sepIndex); String parentName = arg.substring(sepIndex + 1, arg.length()); parent = ClassTable.getClassLoader(parentName); if (parent == null) { /* parent classloader not defined */ System.err.println(Localizer.getString( "javacodecompact.parent_classloader_not_defined", parentName) ); return false; } } else { name = arg; parent = ClassTable.getClassLoader(); } if (ClassTable.getClassLoader(name) != null) { /* classloader name already used */ System.err.println(Localizer.getString("javacodecompact.classloader_already_defined", name) ); return false; } ClassLoader l = new components.ClassLoader(name, parent); ClassTable.setClassLoader(l); } else if (clist[i].startsWith("-listapi:")) { APIListerArgs = clist[i]; } else { Vector classesRead = new Vector(); /* Read in classes that need to be ROMized. */ classesRead.clear(); if (!readFile(clist[i], classesRead)) { success = false; // but keep going to process rest of options anyway. } classesProcessed.addAll(classesRead); ClassTable.initIfNeeded(verbosity); if (!ClassTable.enterClasses(classesRead.elements())) { success = false; } } } // Default classname filter for natives nativeTypes.addTypePatterns( "JNI", "-*" ); return success; } private boolean loadClass(ClassLoader cl, String classname, Vector oneClass) throws Exception { ClassLoader parent = cl.getParent(); if (parent != null) { if (loadClass(parent, classname, oneClass)) { return true; } } ClassFileFinder searchPath = cl.getSearchPath(); if (searchPath == null) { // If the search path is empty, then there is no place else // to look for the class. So, fail: return false; } int nfound = rdr.readClass(classname, searchPath, oneClass); if (nfound == 1) { // Add class to the appropriate classloader ClassInfo ci = (ClassInfo)oneClass.elementAt(0); if (!ClassTable.enterClass(ci, cl)) { throw new Exception(); } return true; } else { return false; } } /** * Do closure on CP entry class constant references until no unresolved * constants remain. * NOTE: doClosure() only loads ClassInfos for each class that is * referenced from unresolved CP entries. After doClosure() is done * (assuming full transitive closure is desired), then all the CP * entries for class constants will reference a ClassInfo object. * NOTE: resolving CP entry class constants to ClassInfo does not * include quickening of bytecodes and other types of VM specific * processing associated with resolution yet. That part is done * later. */ private boolean doClosure() { // do closure on references until none remain. while (true) { Assert.disallowClassloading(); Set undefinedClassNames = findUnresolvedClassNames(classesProcessed.elements()); String unresolved[] = unresolvedClassNames(undefinedClassNames); Assert.allowClassloading(); if (unresolved == null) break; // none left! int nfound = 0; Vector processedThisTime = new Vector(); for( int i=0; i < unresolved.length; i++){ try { Vector oneClass = new Vector(); loadClass(ClassTable.getClassLoader(), unresolved[i], oneClass); processedThisTime.addAll(oneClass); } catch (Exception e) { return false; } } // If we have gone through an iteration when we weren't able to // resolve any more classes, then we have resolved everything // we can. Hence, break out of here. if (processedThisTime.isEmpty()) { break; } // Otherwise, continue to resolve the new classes that we've found. classesProcessed.addAll(processedThisTime); } return true; } /** * Performs the work for JCC. This is the root processing method in JCC * which in turn will call all other processing methods. * NOTE: process() is responsible for resolving all the constant pool * entries of the loaded classes if possible. If full transitive closure * is required, then this is the place where the work will be done. * * @return false if an error was discovered. */ private boolean process(boolean doWrite) throws Exception { /* Do closure on references until none remain. * NOTE: doClosure() only loads ClassInfos for each class that is * referenced from unresolved CP entries. After doClosure() is done * (assuming full transitive closure is desired), then all the CP * entries for class constants will reference a ClassInfo object. * NOTE: resolving CP entry class constants to ClassInfo does not * include quickening of bytecodes and other types of VM specific * processing associated with resolution yet. That part is done * later. */ if (!doClosure()) { return false; } ClassInfo c[] = ClassTable.allClasses(); nclasses = c.length; 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++){ ClassInfo cinfo = c[i]; if (!(cinfo instanceof PrimitiveClassInfo) && !(cinfo instanceof ArrayClassInfo)) { if (verbosity != 0) System.out.println(Localizer.getString( "javacodecompact.building_tables_for_class", cinfo.className)); cinfo.buildFieldtable(); cinfo.buildMethodtable(); } } // Warn if fields or methods marked for exclusion were not found checkExcludedClassEntries(); if (!doWrite) { return true; } // now write the output if (verbosity != 0) System.out.println(Localizer.getString( "javacodecompact.writing_output_file")); writeNativeHeaders( nativeTypes, c, nclasses ); writeNativeHeaders( extraHeaders, c, nclasses ); if (stubDestName != null){ writeCStubs( c, nclasses ); } boolean good = true; if (firstTimeOnly) { // For CVM, make sure that all arrays of basic types // are instantiated! good = instantiateBasicArrayClasses(verbosity > 1); firstTimeOnly = false; } // prepareClasses() is responsible for quickening bytecodes, // optimizing the bytecode, constantpools, etc. This is where // CP resolution as the VM knows it is done. if (!prepareClasses(c) || !good) { return false; } if (doWrite) { makeOutfileName(); } good = writeROMFile( outName, c, romAttributes, doWrite ); /* Don't destroy class vector. The JavaAPILister * needs to access class typeids, which come * from CVMClass and CVMClass come from the class * vector. */ //ClassClass.destroyClassVector(); return good; } public static void main( String clist[] ){ boolean success = false; try { try { JavaCodeCompact jcc = new JavaCodeCompact(); // Parse the command line arguments and check for malformed // arguments or a file read error? // Note that processOptions also loads all the initial // classes to be romized specified at the JCC command line. // These classes will be loaded, but their constant pool // entries wil not be resolved yet. if (jcc.processOptions(clist)) { // If we got here, then the arguments were parsed // successfully. Now, we are ready to do some // additional work. This includes resolving the // constant pool entries, quickening bytecodes, etc. success = jcc.process(true); } /* We are done with processing ROMized classes. Now run * JavaAPILister, which need to access some of the data * such as class and member typeid that we created during * Romizing classes. */ if (APIListerArgs != null) { new JavaAPILister().process(APIListerArgs); } }finally{ System.out.flush(); System.err.flush(); } }catch (Throwable t){ t.printStackTrace(); } if (!success){ // process threw error or failed System.exit(1); } return; } /* * ALL THIS IS FOR ROMIZATION */ public boolean instantiateBasicArrayClasses(boolean verbose) { boolean good = true; // For CVM, 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 (!ArrayClassInfo.collectArrayClass(basicArray[ino], ClassTable.getClassLoader("boot"), verbose)) { good = false; } } return good; } /* * 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; // Now dredge through all class constant pools. for ( int cno = 0; cno < nclasses; cno++ ){ ClassInfo c = classTable[cno]; ConstantObject ctable[] = c.getConstantPool().getConstants(); if ( ctable == null ) continue; int n = ctable.length; for( int i = 1; 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 (!vm.ArrayClassInfo.collectArrayClass(cname, c.loader, verbose)) { good = false; } cc.forget(); // forget the fact that we couldn't find it } } // We might just want to check the code as well. for (int j = 0; j < c.methods.length; j++) { MethodInfo m = c.methods[j]; m.collectArrayForAnewarray(ctable, c.className); } } return good; } /* * We attempt to factor out VM specific code * by subclassing ClassClass. Perhaps we should be subclassing * components.ClassInfo itself. * Anyway, this is the CVM-specific class factory. This * would better be dependent on a runtime switch. */ VMClassFactory classMaker = new CVMClassFactory(); public ClassClass[] finalizeClasses() throws Exception{ ClassClass classes[] = ClassClass.getClassVector(classMaker); int numberOfClasses = classes.length; CodeHacker ch = new CodeHacker( qlossless, jitOn, verbosity >= 2 ); for (int i = 0; i < numberOfClasses; i++) { if (verbosity != 0) { System.out.println(Localizer.getString( "javacodecompact.quickening_code_of_class", classes[i].classInfo.className)); } if (!ch.quickenAllMethodsInClass(classes[i].classInfo)) { throw new Exception(Localizer.getString( "javacodecompact.quickening_code_of_class", classes[i].classInfo.className)); } } // constant pool smashing has to be done after quickening, // else it doesn't make much difference! for (int i = 0; i < numberOfClasses; i++) { ClassInfo cinfo = classes[i].classInfo; if (verbosity != 0) { System.out.println(Localizer.getString( "javacodecompact.reducing_constant_pool_of_class", cinfo.className)); } cinfo.countReferences(false); cinfo.smashConstantPool(); cinfo.relocateReferences(); } /* * This last-minute preparation step might be generalized * to something more useful. */ if (!noCodeCompaction && !qlossless) { for (int i = 0; i < numberOfClasses; i++) { classes[i].getInlining(); } } return classes; } private void validateClasses(ClassClass classes[], Vector sharedConstantPools){ int totalclasses = classes.length; for (int i = 0; i < totalclasses; i++){ ClassInfo ci = classes[i].classInfo; ci.validate(null); } } Vector sharedConstantPools = null; private boolean prepareClasses(ClassInfo classTable[]) throws Exception { UnresolvedReferenceList missingObjects = new UnresolvedReferenceList(); boolean anyMissingConstants = false; boolean good = instantiateArrayClasses( classTable, verbosity>1 ); // 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... ConstantPool sharedConstant; int i; if (useSharedCP) { int idx = 0; // create a shared constant pool sharedConstantPools = new Vector(); sharedConstant = new ConstantPool(); sharedConstantPools.add(sharedConstant); for (i = 0; i < classTable.length; i++) { boolean needSplit = false; needSplit = mergeConstantsIntoSharedPool( classTable[i], sharedConstant, idx); if (needSplit) { idx ++; sharedConstant = new ConstantPool(); sharedConstantPools.add(sharedConstant); mergeConstantsIntoSharedPool( classTable[i], sharedConstant, idx); } } // sort the reference count for (i = 0; i < sharedConstantPools.size(); i++) { sharedConstant = (ConstantPool)sharedConstantPools.get(i); sharedConstant.doSort(); // run via the shared constant pool once. if (ClassClass.isPartiallyResolved(sharedConstant)) { sharedConstant = classMaker.makeResolvable( sharedConstant, missingObjects, "shared constant pool"); } sharedConstantPools.set(i, sharedConstant); } } else { for (i = 0; i < totalclasses; i++){ if (! classes[i].adjustSymbolicConstants(missingObjects)) anyMissingConstants = true; } if ( anyMissingConstants == true ){ System.err.println(Localizer.getString( "javacodecompact.classes_referred_to_missing_classes")); for (i = 0; i < totalclasses; i++){ if ( classes[i].impureConstants ){ System.err.println(" " + classes[i].classInfo.className); } } } } if (missingObjects.hasUnresolvedReferences()){ if (verbosity > 0 || !unresolvedOk) { missingObjects.print(System.err); } if (!unresolvedOk){ System.err.println(Localizer.getString( "javacodecompact.unresolved_references_not_allowed")); return false; } } for (i = 0; i < totalclasses; i++) { classes[i].classInfo.relocateAndPackCode(noCodeCompaction); if (useSharedCP) { classes[i].classInfo.setConstantPool(sharedConstantPools); } } if ( ! good ) return false; if ( validate ){ if (useSharedCP){ for (i = 0; i < sharedConstantPools.size(); i++) { sharedConstant = (ConstantPool)sharedConstantPools.get(i); sharedConstant.validate(); } } validateClasses(classes, sharedConstantPools); } return true; } private boolean writeROMFile( String outName, ClassInfo classTable[], Vector attributes, boolean doWrite) throws Exception { 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; } } // // NOTE: mb's marked always mutable for now, since the stackmaps // need to be written into the mb. It's possible to fix // if there is a stackmaps cache, and the mb->stackmapsX field // is removed. // w.init(classDebug, qlossless, nativeTypes, verbosity>0, maxSegmentSize, true); 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)); return false; } } if (doWrite && ! w.open(outName)){ w.printError(System.out); return false; } else { boolean good = w.writeClasses(sharedConstantPools, doWrite); w.printSpaceStats(System.out); if (doWrite) { 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 writeNativeHeaders( 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) { e.printStackTrace(); 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 updates the reference count and puts all constants // associated with a ClassInfo to the shared constant pool. private boolean mergeConstantsIntoSharedPool(ClassInfo cinfo, ConstantPool sharedCP, int sharedCPIdx) { ConstantPool cp = cinfo.getConstantPool(); if (sharedCP.getLength() + cp.getLength() > 0xffff) { return true; } cinfo.setSharedCPIdx(sharedCPIdx); // Get the CP that is to be added to the shared CP: ConstantObject[] constants = cinfo.getConstantPool().getConstants(); // For each CP entry that is referenced, add it to the shared CP: for (int j = 0; j < constants.length; j++) { ConstantObject thisConst = constants[j]; if (thisConst == null) continue; int count = thisConst.getReferences(); if (count > 0) { // Add into sharedCP, and replace the original in the cinfo: constants[j] = sharedCP.add(thisConst); } } // For each constant in the interface array, add it to the shared CP // and update the interface array with the new constant values: if (cinfo.interfaces != null) { for (int k = 0 ; k < cinfo.interfaces.length; k++) { cinfo.interfaces[k] = (ClassConstant) sharedCP.add(cinfo.interfaces[k]); } } // update exception table (catchType) // and also thrown exceptions table for (int i = 0; i < cinfo.methods.length; i++) { MethodInfo mi = cinfo.methods[i]; // NOTE: Investigate the possibility of removing this scan // of the exception tables. Since references to exception // classes from the exception tables are already counted in // the individual class' constant pool, adding the constants // from the class' constant pool should be adequate. We // might not need to scan these exception tables anymore. if ( mi.exceptionTable != null) { for (int j = 0; j < mi.exceptionTable.length; j++) { ClassConstant cc = mi.exceptionTable[j].catchType; if (cc != null) { mi.exceptionTable[j].catchType = (ClassConstant)sharedCP.add(cc); } } } if (mi.exceptionsThrown != null) { for (int j = 0; j < mi.exceptionsThrown.length; j++) { ClassConstant cc = mi.exceptionsThrown[j]; mi.exceptionsThrown[j] = (ClassConstant) sharedCP.add(cc); } } } if (cinfo.innerClassAttr != null) { cinfo.innerClassAttr.mergeConstantsIntoSharedPool(sharedCP); } /* TODO :: BEGIN experimental code for future signature support. if (cinfo.signatureAttr != null) { cinfo.signatureAttr.mergeConstantsIntoSharedPool(sharedCP); } // TODO :: END */ /* Just to make sure we are not exceeding the limit */ if (sharedCP.getLength() > 0xffff) { throw new Error("Constant pool overflow: 64k constants"+ " allowed, have " + sharedCP.getLength()); } return false; /* not need to split the shared constnat pool */ } /* * Read the exclude file, if any, to determine fields and * methods to exclude. File format is one entry per line * (lines beginning with '#' are ignored), a keyword of * "METHOD" or "FIELD" followed by the signature: * METHOD classname.methodname(type) * FIELD classname.fieldname * Only record the lists of fields and methods if we read the * file without incident. */ Vector fieldVector = new Vector(); Vector methodVector = new Vector(); private void readExcludeFile( String fileName ) { BufferedReader r; try { r = new BufferedReader(new FileReader(fileName)); String line, sig, clazz, method, field, type; int dotIndex, typeIndex, closeIndex; while((line = r.readLine()) != null) { if (line.length() == 0 || line.startsWith("#")) { continue; } else if (line.startsWith("METHOD ")) { sig = line.substring("METHOD ".length()); dotIndex = sig.indexOf('.'); typeIndex = sig.indexOf('(', dotIndex); closeIndex = sig.indexOf(')',typeIndex); if (dotIndex > 0 && typeIndex > dotIndex && closeIndex > typeIndex) { clazz = sig.substring(0, dotIndex); method = sig.substring(dotIndex+1, typeIndex); type = sig.substring(typeIndex, closeIndex+1); methodVector.add( new MemberNameTriple(clazz, method, type)); } else { System.err.println(Localizer.getString("javacodecompact.exclude_parse_error",line)); } } else if (line.startsWith("FIELD ")) { sig = line.substring("FIELD ".length()); dotIndex = sig.indexOf('.'); if (dotIndex > 0 ) { clazz = sig.substring(0, dotIndex); field = sig.substring(dotIndex+1); fieldVector.add( new MemberNameTriple(clazz, field, null)); } else { System.err.println(Localizer.getString("javacodecompact.exclude_parse_error",line)); } } } r.close(); // We may be repetitively handing the same vector // to ClassInfo if multiple exclude files are // specified, but this shouldn't hurt. ClassInfo.setExcludeLists( methodVector, fieldVector ); } catch ( IOException ioe ) { System.err.println(Localizer.getString("javacodecompact.bad_exclude_file",fileName)); } return; } // Ensure that we don't have any fields or methods marked for // exclusion which weren't found. private void checkExcludedClassEntries() { int i; for (i = 0 ; i < methodVector.size(); i++) { System.err.println(Localizer.getString("javacodecompact.excluded_method_not_found",((MemberNameTriple)methodVector.get(i)).toString())); } for (i = 0 ; i < fieldVector.size(); i++) { System.err.println(Localizer.getString("javacodecompact.excluded_field_not_found",((MemberNameTriple)fieldVector.get(i)).toString())); } return; } }