Mirror of the JODE repository
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
jode/jode/src/net/sf/jode/bytecode/GrowableConstantPool.java

339 lines
10 KiB

/* GrowableConstantPool Copyright (C) 1999 Jochen Hoenicke.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package net.sf.jode.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Hashtable;
/**
* This class represent a constant pool, where new constants can be
* added to. Normally you wont need to touch this class, as ClassInfo
* already does all the hard work. You will only need it if you want
* to add your own custom attributes that use the constant pool.
*
* @author Jochen Hoenicke
*/
public class GrowableConstantPool extends ConstantPool {
Hashtable entryToIndex = new Hashtable();
boolean written;
/**
* This class is used as key to the entryToIndex hashtable
*/
private class Key {
int tag;
Object objData;
int intData;
public Key(int tag, Object objData, int intData) {
this.tag = tag;
this.objData = objData;
this.intData = intData;
}
public int hashCode() {
return tag ^ objData.hashCode() ^ intData;
}
public boolean equals(Object o) {
if (o instanceof Key) {
Key k = (Key) o;
return tag == k.tag && intData == k.intData
&& objData.equals(k.objData);
}
return false;
}
}
/**
* Create a new growable constant pool
*/
public GrowableConstantPool () {
count = 1;
tags = new int[128];
indices1 = new int[128];
indices2 = new int[128];
constants = new Object[128];
written = false;
}
private final void grow(int wantedSize) {
if (written)
throw new IllegalStateException("adding to written ConstantPool");
if (wantedSize > 65535)
throw new IllegalArgumentException("Too many constants added");
if (tags.length < wantedSize) {
int newSize = Math.min(65535, Math.max(tags.length*2, wantedSize));
int[] tmpints = new int[newSize];
System.arraycopy(tags, 0, tmpints, 0, count);
tags = tmpints;
tmpints = new int[newSize];
System.arraycopy(indices1, 0, tmpints, 0, count);
indices1 = tmpints;
tmpints = new int[newSize];
System.arraycopy(indices2, 0, tmpints, 0, count);
indices2 = tmpints;
Object[] tmpobjs = new Object[newSize];
System.arraycopy(constants, 0, tmpobjs, 0, count);
constants = tmpobjs;
}
}
private int putConstant(int tag, Object constant) {
Key key = new Key(tag, constant, 0);
Integer index = (Integer) entryToIndex.get(key);
if (index != null)
return index.intValue();
int newIndex = count;
grow(count + 1);
tags[newIndex] = tag;
constants[newIndex] = constant;
entryToIndex.put(key, new Integer(newIndex));
count++;
return newIndex;
}
private int putLongConstant(int tag, Object constant) {
Key key = new Key(tag, constant, 0);
Integer index = (Integer) entryToIndex.get(key);
if (index != null)
return index.intValue();
int newIndex = count;
grow(count + 2);
tags[newIndex] = tag;
tags[newIndex+1] = -tag;
constants[newIndex] = constant;
entryToIndex.put(key, new Integer(newIndex));
count += 2;
return newIndex;
}
private int putIndexed(int tag, Object obj1, int index1, int index2) {
Key key = new Key(tag, obj1, index2);
Integer indexObj = (Integer) entryToIndex.get(key);
if (indexObj != null) {
/* Maybe this was a reserved, but not filled entry */
int index = indexObj.intValue();
indices1[index] = index1;
indices2[index] = index2;
return index;
}
grow(count+1);
tags[count] = tag;
indices1[count] = index1;
indices2[count] = index2;
entryToIndex.put(key, new Integer(count));
return count++;
}
/**
* Adds a new UTF8 entry to the constant pool and returns the index.
* If it already exists it will reuse the previous entry.
* @param utf the UTF8 string.
* @return the index of the pool entry.
* @exception IllegalStateException if the pool was already written,
* but the entry was not added before.
*/
public final int putUTF8(String utf) {
return putConstant(UTF8, utf);
}
/**
* Adds a new class name entry to the constant pool and returns
* the index. If it already exists it will reuse the previous
* entry.
* @param name the dot separated full qualified class name.
* @return the index of the pool entry.
* @exception IllegalArgumentException if the class name is illegal.
* @exception IllegalStateException if the pool was already written,
* but the entry was not added before.
*/
public int putClassName(String name) {
name = name.replace('.','/');
TypeSignature.checkTypeSig("L"+name+";");
return putIndexed(CLASS, name, putUTF8(name), 0);
}
/**
* Adds a new class entry to the constant pool and returns
* the index. If it already exists it will reuse the previous
* entry. This is the same as putClassName, except for the format
* of the parameter and that it can also handle arrays.
* @param name the type signature of the class to add.
* @return the index of the pool entry.
* @exception IllegalArgumentException if the class name is illegal.
* @exception IllegalStateException if the pool was already written,
* but the entry was not added before.
*/
public int putClassType(String name) {
TypeSignature.checkTypeSig(name);
if (name.charAt(0) == 'L')
name = name.substring(1, name.length()-1);
else if (name.charAt(0) != '[')
throw new IllegalArgumentException("wrong class type: "+name);
return putIndexed(CLASS, name, putUTF8(name), 0);
}
/**
* Adds a new field/method or interface reference to the constant
* pool and returns the index. If it already exists it will reuse
* the previous entry.
* @param tag the tag of the reference, one of FIELDREF, METHODREF
* or INTERFACEMETHODREF.
* @param ref the reference.
* @return the index of the pool entry.
* @exception IllegalArgumentException if the reference type or
* class name is illegal.
* @exception IllegalStateException if the pool was already written,
* but the entry was not added before.
*/
public int putRef(int tag, Reference ref) {
String className = ref.getClazz();
String typeSig = ref.getType();
if (tag == FIELDREF)
TypeSignature.checkTypeSig(typeSig);
else
TypeSignature.checkMethodTypeSig(typeSig);
int classIndex = putClassType(className);
int nameIndex = putUTF8(ref.getName());
int typeIndex = putUTF8(typeSig);
int nameTypeIndex = putIndexed(NAMEANDTYPE,
ref.getName(), nameIndex, typeIndex);
return putIndexed(tag, className, classIndex, nameTypeIndex);
}
/**
* Puts a "one slot" constant into this constant pool. If it
* already exists it will reuse the previous entry.
* @param c the constant; must be of type
* Integer, Float or String
* @return the index of the pool entry.
* @exception IllegalArgumentException if the constant is of wrong type.
* @exception IllegalStateException if the pool was already written,
* but the entry was not added before.
*/
public int putConstant(Object c) {
if (c instanceof String) {
return putIndexed(STRING, c, putUTF8((String) c), 0);
} else {
int tag;
if (c instanceof Integer)
tag = INTEGER;
else if (c instanceof Float)
tag = FLOAT;
else
throw new IllegalArgumentException
("illegal constant " + c + " of type: " + c.getClass());
return putConstant(tag, c);
}
}
/**
* Puts a "double slot" constant into this constant pool. If it
* already exists it will reuse the previous entry.
* @param c the constant; must be of type Long or Double
* @return the index of the pool entry.
* @exception IllegalArgumentException if the constant is of wrong type.
* @exception IllegalStateException if the pool was already written,
* but the entry was not added before.
*/
public int putLongConstant(Object c) {
int tag;
if (c instanceof Long)
tag = LONG;
else if (c instanceof Double)
tag = DOUBLE;
else
throw new IllegalArgumentException
("illegal long constant " + c + " of type: " + c.getClass());
return putLongConstant(tag, c);
}
/**
* Reserves an entry in this constant pool for a constant (for ldc).
* The constant must still be put into the pool, before the pool may
* be written.
* @param c the constant, must be of type
* Integer, Float or String
* @return the reserved index into the pool of this constant.
*/
public int reserveConstant(Object c) {
if (c instanceof String) {
return putIndexed(STRING, c, -1, 0);
} else {
return putConstant(c);
}
}
/**
* Writes the constant pool to the stream. This is normally called
* by {@link ClassInfo#write}.
* @param stream the stream to write to.
* @exception IOException if it occured while writing.
*/
public void write(DataOutputStream stream)
throws IOException {
written = true;
stream.writeShort(count);
for (int i=1; i< count; i++) {
int tag = tags[i];
stream.writeByte(tag);
switch (tag) {
case CLASS:
stream.writeShort(indices1[i]);
break;
case FIELDREF:
case METHODREF:
case INTERFACEMETHODREF:
stream.writeShort(indices1[i]);
stream.writeShort(indices2[i]);
break;
case STRING:
stream.writeShort(indices1[i]);
break;
case INTEGER:
stream.writeInt(((Integer)constants[i]).intValue());
break;
case FLOAT:
stream.writeFloat(((Float)constants[i]).floatValue());
break;
case LONG:
stream.writeLong(((Long)constants[i]).longValue());
i++;
break;
case DOUBLE:
stream.writeDouble(((Double)constants[i]).doubleValue());
i++;
break;
case NAMEANDTYPE:
stream.writeShort(indices1[i]);
stream.writeShort(indices2[i]);
break;
case UTF8:
stream.writeUTF((String)constants[i]);
break;
default:
throw new ClassFormatException("unknown constant tag");
}
}
}
}