/******************************************************************************* * Copyright (c) 2002,2006 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package x10.sncode; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import x10.util.CollectionFactory; /** * This class formats and writes class data into JVM format. */ public final class ConstantPool implements SnConstants { /** Inverted CP. Map from objects to index into the CP. */ Map<Object, Integer> inverseMap = CollectionFactory.newHashMap(1); /** List of new CP entries added since last clearing of the list. */ List<Object> cpItems = new ArrayList<Object>(1); /** * Create a blank Sn editor with no members. */ public ConstantPool() { inverseMap = CollectionFactory.newHashMap(); cpItems = new ArrayList<Object>(); cpItems.add(null); } /** * Copy a constant pool from some ClassReader into this class. This must be * done before any entries are allocated in this ClassWriter's constant * pool, and it can only be done once. If and only if this is done, it is * safe to copy "raw" fields, methods and attributes from the ClassReader * into this class, because the constant pool references in those fields, * methods and attributes are guaranteed to point to the same constant pool * items in this new class. */ public void setRawCP(ConstantPoolParser cp) throws InvalidClassFileException, IllegalArgumentException { if (cp == null) { throw new IllegalArgumentException(); } cpItems.clear(); // item 0 cpItems.add(null); int nextCPIndex = cp.getItemCount(); for (int i = 1; i < nextCPIndex; i++) { byte t = cp.getItemType(i); switch (t) { case CONSTANT_Array: addCPEntry(cp.getCPArray(i)); break; case CONSTANT_String: addCPString(cp.getCPString(i)); break; case CONSTANT_Class: addCPClass(cp.getCPClass(i)); break; case CONSTANT_FieldRef: addCPFieldRef(cp.getCPRefClass(i), cp.getCPRefName(i), cp.getCPRefType(i)); break; case CONSTANT_MethodRef: addCPMethodRef(cp.getCPRefClass(i), cp.getCPRefName(i), cp.getCPRefType(i)); break; case CONSTANT_NameAndType: addCPNameAndType(cp.getCPNATName(i), cp.getCPNATType(i)); break; case CONSTANT_Integer: addCPInt(cp.getCPInt(i)); break; case CONSTANT_Boolean: addCPBoolean(cp.getCPBoolean(i)); break; case CONSTANT_Float: addCPFloat(cp.getCPFloat(i)); break; case CONSTANT_Long: addCPLong(cp.getCPLong(i)); break; case CONSTANT_Double: addCPDouble(cp.getCPDouble(i)); break; case CONSTANT_Utf8: addCPUtf8(cp.getCPUtf8(i)); break; case CONSTANT_Constraint: addCPConstraint(cp.getCPConstraint(i)); break; case CONSTANT_Type: addCPType(cp.getCPType(i)); break; case CONSTANT_ByteArray: addCPBytes(cp.getCPBytes(i)); break; } } } public int addCPEntry(Object o) { if (inverseMap == null) { throw new IllegalArgumentException("Cannot add a new constant pool entry during makeBytes() processing!"); } if (o == null) return 0; Integer i = inverseMap.get(o); if (i != null) { return i.intValue(); } else { int index = cpItems.size(); i = new Integer(index); inverseMap.put(o, i); cpItems.add(o); boolean asserts = false; assert asserts = true; if (asserts) copyInto(new ByteBuffer()); return index; } } /** * Add a Utf8 string to the constant pool if necessary. * * @return the index of a constant pool item with the right value */ public int addCPUtf8(String s) { return addCPEntry(s); } /** * Add a Utf8 string to the constant pool if necessary. * * @return the index of a constant pool item with the right value */ public int addCPConstraint(Constraint s) { return addCPEntry(s); } /** * Add an Integer to the constant pool if necessary. * * @return the index of a constant pool item with the right value */ public int addCPBoolean(boolean b) { return addCPEntry(new Boolean(b)); } /** * Add an Integer to the constant pool if necessary. * * @return the index of a constant pool item with the right value */ public int addCPInt(int i) { return addCPEntry(new Integer(i)); } /** * Add a Float to the constant pool if necessary. * * @return the index of a constant pool item with the right value */ public int addCPFloat(float f) { return addCPEntry(new Float(f)); } /** * Add a Float to the constant pool if necessary. * * @return the index of a constant pool item with the right value */ public int addCPBytes(byte[] b) { return addCPEntry(b); } /** * Add a Long to the constant pool if necessary. * * @return the index of a constant pool item with the right value */ public int addCPLong(long l) { return addCPEntry(new Long(l)); } /** * Add a Double to the constant pool if necessary. * * @return the index of a constant pool item with the right value */ public int addCPDouble(double d) { return addCPEntry(new Double(d)); } /** * Add a String to the constant pool if necessary. * * @return the index of a constant pool item with the right value */ public int addCPString(String s) { return addCPEntry(new CWString(s)); } /** * Add a Class to the constant pool if necessary. * * @param s * the class name, in JVM format (e.g., java/lang/Object) * @return the index of a constant pool item with the right value */ public int addCPClass(String s) { if (s == null) { throw new IllegalArgumentException("null s: " + s); } return addCPEntry(new CWClass(s)); } /** * Add a Type to the constant pool if necessary. * * @param s * the class name, in JVM format (e.g., java/lang/Object) * @return the index of a constant pool item with the right value */ public int addCPType(String s) { if (s == null) { throw new IllegalArgumentException("null s: " + s); } try { return addCPEntry(Type.parse(s)); } catch (InvalidClassFileException e) { throw new IllegalArgumentException(e); } } /** * Add a Type to the constant pool if necessary. * * @param s * the class name, in JVM format (e.g., java/lang/Object) * @return the index of a constant pool item with the right value */ public int addCPType(Type s) { if (s == null) { throw new IllegalArgumentException("null s: " + s); } return addCPEntry(s); } /** * Add a FieldRef to the constant pool if necessary. * * @param c * the class name, in JVM format (e.g., java/lang/Object) * @param n * the field name * @param t * the field type, in JVM format (e.g., I, Z, or * Ljava/lang/Object;) * @return the index of a constant pool item with the right value */ public int addCPFieldRef(String c, String n, String t) { return addCPEntry(new CWRef(CONSTANT_FieldRef, c, n, t)); } /** * Add a MethodRef to the constant pool if necessary. * * @param c * the class name, in JVM format (e.g., java/lang/Object) * @param n * the method name * @param t * the method type, in JVM format (e.g., V(ILjava/lang/Object;) ) * @return the index of a constant pool item with the right value */ public int addCPMethodRef(String c, String n, String t) { return addCPEntry(new CWRef(CONSTANT_MethodRef, c, n, t)); } /** * Add a NameAndType to the constant pool if necessary. * * @param n * the name * @param t * the type, in JVM format * @return the index of a constant pool item with the right value */ public int addCPNameAndType(String n, String t) { return addCPEntry(new CWNameAndType(n, t)); } void copyInto(ByteBuffer w) { int countOffset = w.offset(); // Reserve a place for the number of entries. w.addInt(0); // BE CAREFUL: the newCPEntries array grows during this loop. for (int i = 1; i < cpItems.size(); i++) { Object o = getCPEntry(i); w.addInt(0xdeadbeef); if (o instanceof CWItem) { CWItem item = (CWItem) o; byte t = item.getType(); switch (t) { case CONSTANT_Class: w.addByte(t); w.addInt(addCPUtf8(((CWClass) item).name)); break; case CONSTANT_String: w.addByte(t); w.addInt(addCPUtf8(((CWString) item).str)); break; case CONSTANT_NameAndType: { CWNameAndType nat = (CWNameAndType) item; w.addByte(t); w.addInt(addCPUtf8(nat.name)); w.addInt(addCPType(nat.type)); break; } case CONSTANT_MethodRef: case CONSTANT_FieldRef: { CWRef ref = (CWRef) item; w.addByte(t); w.addInt(addCPClass(ref.container)); w.addInt(addCPNameAndType(ref.name, ref.type)); break; } default: throw new Error("Invalid type: " + t); } } else if (o instanceof String) { String s = (String) o; w.addByte(CONSTANT_Utf8); int lenOffset = w.offset(); w.addInt(0); int startOffset = w.offset(); for (int j = 0; j < s.length(); j++) { char ch = s.charAt(j); if (ch == 0) { w.addUShort(0xC080); } else if (ch < 0x80) { w.addByte((byte) ch); } else if (ch < 0x800) { w.addByte((byte) ((ch >> 6) | 0xC0)); w.addByte((byte) ((ch & 0x3F) | 0x80)); } else { w.addByte((byte) ((ch >> 12) | 0xE0)); w.addByte((byte) (((ch >> 6) & 0x3F) | 0x80)); w.addByte((byte) ((ch & 0x3F) | 0x80)); } } int bytes = w.offset() - startOffset; w.setInt(lenOffset, bytes); } else if (o instanceof Boolean) { w.addByte(CONSTANT_Boolean); w.addByte(((Boolean) o).booleanValue() ? 1 : 0); } else if (o instanceof Integer) { w.addByte(CONSTANT_Integer); w.addInt(((Integer) o).intValue()); } else if (o instanceof Long) { w.addByte(CONSTANT_Long); w.addLong(((Long) o).longValue()); } else if (o instanceof Float) { w.addByte(CONSTANT_Float); w.addFloat(((Float) o).floatValue()); } else if (o instanceof Double) { w.addByte(CONSTANT_Double); w.addDouble(((Double) o).doubleValue()); } else if (o instanceof byte[]) { w.addByte(CONSTANT_ByteArray); w.addLength(((byte[]) o).length); w.addBytes((byte[]) o); } else if (o instanceof Object[]) { w.addByte(CONSTANT_Array); Object[] a = (Object[]) o; w.addLength(a.length*CP_INDEX_SIZE); for (int k = 0; k < a.length; k++) { w.addCPIndex(addCPEntry(a[k])); } } else if (o instanceof int[]) { w.addByte(CONSTANT_Array); int[] a = (int[]) o; w.addLength(a.length*CP_INDEX_SIZE); for (int k = 0; k < a.length; k++) { w.addCPIndex(addCPEntry(a[k])); } } else if (o instanceof long[]) { w.addByte(CONSTANT_Array); long[] a = (long[]) o; w.addLength(a.length*CP_INDEX_SIZE); for (int k = 0; k < a.length; k++) { w.addCPIndex(addCPEntry(a[k])); } } else if (o instanceof float[]) { w.addByte(CONSTANT_Array); float[] a = (float[]) o; w.addLength(a.length*CP_INDEX_SIZE); for (int k = 0; k < a.length; k++) { w.addCPIndex(addCPEntry(a[k])); } } else if (o instanceof double[]) { w.addByte(CONSTANT_Array); double[] a = (double[]) o; w.addLength(a.length*CP_INDEX_SIZE); for (int k = 0; k < a.length; k++) { w.addCPIndex(addCPEntry(a[k])); } } else if (o instanceof Constraint) { Constraint c = (Constraint) o; w.addByte(CONSTANT_Constraint); c.write(w); } else if (o instanceof Type) { Type c = (Type) o; w.addByte(CONSTANT_Type); c.writeInto(this, w); } else { throw new IllegalArgumentException("bad cp item " + o + ": " + o.getClass().getName() + " at " + i); } } // go back and write the count w.setInt(countOffset, cpItems.size()); } /** * @return the number of constant pool items (maximum item index plus one) */ public int getItemCount() { return cpItems.size(); } public Object getCPEntry(int i) { return cpItems.get(i); } /** * @return the type of constant pool item i, or 0 if i is an unused constant * pool item */ public byte getItemType(int i) throws IllegalArgumentException { Object o = getCPEntry(i); if (o == null) return CONSTANT_Null; if (o instanceof String) return CONSTANT_Utf8; if (o instanceof Boolean) return CONSTANT_Boolean; if (o instanceof Integer) return CONSTANT_Integer; if (o instanceof Long) return CONSTANT_Long; if (o instanceof Float) return CONSTANT_Float; if (o instanceof Double) return CONSTANT_Double; if (o instanceof byte[]) return CONSTANT_ByteArray; if (o instanceof Object[]) return CONSTANT_Array; if (o instanceof CWItem) return ((CWItem) o).getType(); throw new IllegalArgumentException("bad constant at " + i); } /** * @return the name of the Class at constant pool item i, in JVM format * (e.g., java/lang/Object) */ public String getCPClass(int i) { return ((CWClass) getCPEntry(i)).name; } /** * @return the name of the class part of the FieldRef, MethodRef, or * InterfaceMethodRef at constant pool item i */ public Type getCPType(int i) { return (Type) getCPEntry(i); } /** * @return the String at constant pool item i */ public String getCPString(int i) { return ((CWString) getCPEntry(i)).str; } private static boolean isRef(byte b) { switch (b) { case CONSTANT_MethodRef: case CONSTANT_FieldRef: return true; default: return false; } } /** * @return the name of the class part of the FieldRef, MethodRef, or * InterfaceMethodRef at constant pool item i */ public String getCPRefClass(int i) { return ((CWRef) getCPEntry(i)).container; } /** * @return the name part of the FieldRef, MethodRef, or InterfaceMethodRef * at constant pool item i */ public String getCPRefName(int i) { return ((CWRef) getCPEntry(i)).name; } /** * @return the type part of the FieldRef, MethodRef, or InterfaceMethodRef * at constant pool item i, in JVM format (e.g., I, Z, or * Ljava/lang/Object;) */ public String getCPRefType(int i) { return ((CWRef) getCPEntry(i)).type; } /** * @return the name part of the NameAndType at constant pool item i */ public String getCPNATName(int i) { return ((CWNameAndType) getCPEntry(i)).name; } /** * @return the type part of the NameAndType at constant pool item i, in JVM * format (e.g., I, Z, or Ljava/lang/Object;) */ public String getCPNATType(int i) { return ((CWNameAndType) getCPEntry(i)).type; } /** * @return the value of the Integer at constant pool item i */ public int getCPInt(int i) { return (Integer) getCPEntry(i); } /** * @return the value of the Float at constant pool item i */ public float getCPFloat(int i) { return (Float) getCPEntry(i); } /** * @return the value of the Long at constant pool item i */ public long getCPLong(int i) { return (Long) getCPEntry(i); } /** * @return the value of the Double at constant pool item i */ public double getCPDouble(int i) { return (Double) getCPEntry(i); } /** * @return the value of the byte array at constant pool item i */ public byte[] getCPBytes(int i) { return (byte[]) getCPEntry(i); } public String getCPUtf8(int i) { return (String) getCPEntry(i); } public <T> T[] getCPArray(int i) { return (T[]) getCPEntry(i); } /** * @return the value of the Constraint at constant pool item i */ public Constraint getCPConstraint(int i) { return (Constraint) getCPEntry(i); } static abstract class CWItem { abstract byte getType(); } static class CWString extends CWItem { final String str; CWString(String s) { this.str = s; } @Override public boolean equals(Object o) { return o instanceof CWString && ((CWString) o).str.equals(str); } @Override public int hashCode() { return str.hashCode() + 3901; } @Override byte getType() { return CONSTANT_String; } } static class CWClass extends CWItem { final String name; CWClass(String c) { this.name = c; } @Override public boolean equals(Object o) { return o instanceof CWClass && ((CWClass) o).name.equals(name); } @Override public int hashCode() { return name.hashCode() + 1431; } @Override byte getType() { return CONSTANT_Class; } } static class CWRef extends CWItem { final String container; final String name; final String type; final byte kind; CWRef(byte k, String c, String n, String t) { this.kind = k; this.container = c; this.name = n; this.type = t; } @Override public boolean equals(Object o) { if (o instanceof CWRef) { CWRef r = (CWRef) o; return r.kind == kind && r.container.equals(container) && r.name.equals(name) && r.type.equals(type); } else { return false; } } @Override public int hashCode() { return kind + (container.hashCode() << 5) + (name.hashCode() << 3) + type.hashCode(); } @Override byte getType() { return kind; } } static class CWNameAndType extends CWItem { final String name; final String type; CWNameAndType(String n, String t) { this.name = n; this.type = t; } @Override public boolean equals(Object o) { if (o instanceof CWNameAndType) { CWNameAndType r = (CWNameAndType) o; return r.name.equals(name) && r.type.equals(type); } else { return false; } } @Override public int hashCode() { return (name.hashCode() << 3) + type.hashCode(); } @Override byte getType() { return CONSTANT_NameAndType; } } public void dump(PrintStream out) { for (int i = 0; i < cpItems.size(); i++) { out.print(i + ": "); Object item = cpItems.get(i); if (item instanceof Object[]) { out.print("array "); item = Arrays.asList((Object[]) item); } if (item instanceof Integer) out.print("int "); if (item instanceof Long) out.print("long "); if (item instanceof Float) out.print("float "); if (item instanceof Double) out.print("double "); if (item instanceof Type) out.print("type "); if (item instanceof String) out.print("utf8 "); if (item instanceof CWNameAndType) out.print("nat "); if (item instanceof CWString) out.print("string "); out.print(item); out.println(); } } }