/* * @(#)ConstantPool.java 1.20 06/10/10 * * 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. * */ package components; import java.io.DataOutput; import java.io.DataInput; import java.util.Arrays; import java.util.Comparator; import java.util.Hashtable; import java.util.Vector; import java.util.Enumeration; import java.io.IOException; import java.io.PrintStream; import util.ValidationException; /* * A ConstantPool represented a shared collection of ConstantObjects. * They behave just as they do when part of individual Classes, including * all the usual rules about first entry being null, and (for purposes of * our internal bookkeeping) always being a null entry following any two-word * constant: this makes simple indexing much easier. * * Originally, this class was used to form the shared part of a multi-class * output file. But that idea was deprecated long ago. It is now used to form * the shared constant pool used internally in our VM for all ROMized classes * when the -sharedCP command-line flag is given. This is seen as a space saving * device (much less duplication), as well as a time saver (less resolution of * those duplicate entries). Unfortunately, it means that some entries will * be out of range of the usual instructions such as ldc, so the bytecodes have * to be rewritten to accomodate. * In order to minimize the rewriting necessary, constants are sorted by * number of references from the code (see field ConstantObject.ldcReferences) * so that the statically most popular come first and their indices will fit * into the normal, short-form instructions. */ public class ConstantPool implements Comparator { protected Hashtable h; // for "quick" lookup protected Vector enumedEntries; // for enumeration in order protected int n; protected ConstantObject constants[]= null; protected boolean locked = false; private boolean impureConstants = false; private boolean needsTypeTable = false; public ConstantPool(){ h = new Hashtable( 500, 0.7f ); enumedEntries = new Vector(); enumedEntries.addElement(null ); // 0th element is string of length 0. n = 1; } public ConstantPool(ConstantObject[] c) { constants = c; } /** * compare * * @param o1 first object to compare. * @param o2 second object to compare. * @return -1 if obj1 > obj2, 0 if obj1 == obj2, 1 if obj1 < obj2. */ public int compare(java.lang.Object o1, java.lang.Object o2) { ConstantObject obj1 = (ConstantObject) o1; ConstantObject obj2 = (ConstantObject) o2; if (obj1.getLdcReferences() < obj2.getLdcReferences()) { return 1; } else if (obj1.getLdcReferences() == obj2.getLdcReferences()) return 0; return -1; } public void doSort() { int count = 0; int nNew = 1; // Retrieve the ConstantObject array from the hashtable. Enumeration e = h.elements(); ConstantObject arr[] = new ConstantObject[h.size()]; while (e.hasMoreElements()) { arr[count]= (ConstantObject) e.nextElement(); count++; } // Sorting the ConstantObject with descending reference // count. Arrays.sort(arr, this); enumedEntries.removeAllElements(); enumedEntries.addElement(null); for (int i = 0; i < arr.length; i++) { arr[i].index = nNew; nNew += arr[i].nSlots; enumedEntries.addElement(arr[i]); for (int j =arr[i].nSlots; j > 1; j-- ) enumedEntries.addElement( null ); // place holder } constants = null; } /** * Return the ConstantObject in constant table corresponding to * the given ConstantObject s. * Inserts s if it is not already there. * The index member of the returned value (which * may be the object s!) will be set to its index in our * table. There will be no element of index 0. * * The constant's shared flag is set and its containingPool field * points back to this. This is only interesting for validation * at this point. * * This should be the ONLY way to add constants into the pool. * appendElement went away. */ public ConstantObject add( ConstantObject s ) { if ( s.containingPool != null) { if (s.containingPool==this ){ return s; // this very object already ok in this pool } else { throw new InternalError("Shared constant "+s); } } ConstantObject r = (ConstantObject)h.get( s ); if ( r == null ){ if ( locked ){ throw new Error("add on locked ConstantPool"); } r = s; r.index = n; n += r.nSlots; r.containingPool = this; r.shared = true; h.put( r, r ); enumedEntries.addElement( r ); for ( int i =r.nSlots; i > 1; i-- ) enumedEntries.addElement( null ); // place holder. constants = null; // mark any "constants" as obsolete! } else { r.setLdcReferences(r.getLdcReferences() + s.getLdcReferences()); r.setReferences(r.getReferences() + s.getReferences()); } return r; // a similar object in the pool. } public ConstantObject lookup( ConstantObject s ) { if ( s.containingPool == this) { return s; // this very object already ok in this pool } return (ConstantObject)h.get( s ); // may be null } public boolean needsTypeTable() { // unquickened bytecodes return needsTypeTable; } public void setNeedsTypeTable() { needsTypeTable = true; } public ConstantObject elementAt(int i) { return getConstants()[i]; } public ConstantObject[] getConstants(){ if (constants != null) { return constants; } constants = new ConstantObject[enumedEntries.size()]; enumedEntries.copyInto(constants); return constants; } public int getLength() { return getConstants().length; } public Enumeration getEnumeration(){ return enumedEntries.elements(); } public void lock(){ locked = true; } public void unlock(){ locked = false; } private int read(DataInput in) throws IOException { int n = in.readUnsignedShort(); ConstantObject c[] = new ConstantObject[n]; for (int i = 1; i < n; i+=c[i].nSlots) { c[i] = ConstantObject.readObject(in); } //System.err.println("DEBUG CONSTANTPOOL DUMP" ); //for (int i = 1; i < n; i+=c[i].nSlots ){ // System.err.println("\enumedEntries#"+i+"\enumedEntries"+c[i].toString()); //} for (int i = 1; i < n; i+=c[i].nSlots) { c[i].flatten(this); } for (int i = 1; i < n; i+=c[i].nSlots) { add(c[i]); } constants = c; return n; } public void clearAllReferences(){ ConstantObject c; for( int i=1; i< n; i+=c.nSlots){ c = (ConstantObject)enumedEntries.elementAt(i); c.clearReference(); } } /* * If we are not loading entire classes, then there is * some chance that unreferenced constants have sneaked into * this pool. They can be deleted and the table made smaller. * This is a waste of time when partial class loading is not done. * * Naturally, we preserve the null entries. * */ private void smashConstantPool(){ int nNew = 1; ConstantObject o; // first, count and index. for ( int i = 1; i < n; i += o.nSlots ){ o = (ConstantObject)enumedEntries.elementAt(i); if (o.getReferences() == 0) { o.index = -1; h.remove( o ); } else { // we're keeping it. o.index = nNew; nNew += o.nSlots; } } if ( nNew == n ) return; // all done! // copy live ones from old vector to new. Vector newConstants = new Vector( nNew ); newConstants.addElement( null ); for ( int i = 1; i < n; i += o.nSlots ){ o = (ConstantObject)enumedEntries.elementAt(i); if (o.getReferences() != 0) { // we're keeping it. newConstants.addElement(o); for ( int j =o.nSlots; j > 1; j-- ) newConstants.addElement( null ); // place holder. } } enumedEntries = newConstants; n = nNew; constants = null; // mark as obsolete } /* * sweep over the constant pool. For each constant in it, * ensure that * (a) the index is correct. * (b) two-word constants are followed by null entries, * one-word entries are not. * (c) there are no UnicodeConstants. * (d) all constants are flagged as 'shared' and belong in this pool. * (e) no constant is unreferenced */ public void validate(){ ConstantObject[] constantPool = getConstants(); if (constantPool == null){ return; // it probably won't } int nConsts = constantPool.length; if (constantPool[0] != null){ throw new ValidationException( "Shared constant pool entry 0 must be zero"); } for (int i = 1; i < nConsts; i++ ){ ConstantObject c = constantPool[i]; if (c == null){ throw new ValidationException( "Unexpected null constant pool entry in shared pool"); } if (c.index != i){ throw new ValidationException( "Bad constant self-index in shared pool", c); } if (!c.shared){ throw new ValidationException( "Unshared constant in shared pool", c); } if (c.containingPool != this){ throw new ValidationException( "Constant contained in wrong shared pool?", c); } if (c instanceof DoubleValueConstant){ // following element must be zero. i += 1; if ((i >= nConsts) || (constantPool[i] != null)){ throw new ValidationException( "Bad double value constant (no null) in shared pool", c); } }else if (c instanceof UnicodeConstant){ throw new ValidationException( "Shared constant pool contains Unicode constant", c); } if (c.getReferences() + c.getLdcReferences() == 0) { throw new ValidationException( "Shared constant pool contains unreferenced constant", c); } } } /** * Write out the number of constants to write. * Then write out the constants, in order. * Returns total number of constants written. */ public int write( DataOutput o ) throws IOException { o.writeShort(n); ConstantObject ob; for (int i = 1; i < n; i+=ob.nSlots ){ ob = (ConstantObject)enumedEntries.elementAt(i); if ( ob != null ) ob.write(o); } return n; } public void dump( PrintStream o ){ ConstantObject c; for( int i=1; i< n; i+=c.nSlots){ c = (ConstantObject)enumedEntries.elementAt(i); o.println("\t["+c.index+"]\t"+ c.getReferences()+"\t"+c.toString() ); } } }