|
What this is
Other links
The source code
/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.lib.jmi.util;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
/**
* @author Martin Matula, Dusan Balek
* @version 0.1
*/
public abstract class ClassFileGenerator {
/* Class File Constants */
public static final int JAVA_MAGIC = 0xcafebabe;
/* Generate class file version for 1.1 by default */
public static final int JAVA_DEFAULT_VERSION = 45;
public static final int JAVA_DEFAULT_MINOR_VERSION = 3;
/* Constant table */
public static final int CONSTANT_UTF8 = 1;
public static final int CONSTANT_INTEGER = 3;
public static final int CONSTANT_FLOAT = 4;
public static final int CONSTANT_LONG = 5;
public static final int CONSTANT_DOUBLE = 6;
public static final int CONSTANT_CLASS = 7;
public static final int CONSTANT_STRING = 8;
public static final int CONSTANT_FIELD = 9;
public static final int CONSTANT_METHOD = 10;
public static final int CONSTANT_INTERFACEMETHOD = 11;
public static final int CONSTANT_NAMEANDTYPE = 12;
/* Access and modifier flags */
public static final int ACC_PUBLIC = 0x00000001;
public static final int ACC_PRIVATE = 0x00000002;
public static final int ACC_PROTECTED = 0x00000004;
public static final int ACC_STATIC = 0x00000008;
public static final int ACC_FINAL = 0x00000010;
public static final int ACC_SUPER = 0x00000020;
public static final int ACC_INTERFACE = 0x00000200;
public static final int ACC_ABSTRACT = 0x00000400;
/* Type codes */
public static final int T_BYTE = 0x00000008;
/* Opcodes */
public static final int opc_aconst_null = 1;
public static final int opc_iconst_0 = 3;
public static final int opc_iconst_1 = 4;
public static final int opc_lconst_0 = 9;
public static final int opc_fconst_0 = 11;
public static final int opc_dconst_0 = 14;
public static final int opc_bipush = 16;
public static final int opc_sipush = 17;
public static final int opc_ldc = 18;
public static final int opc_ldc_w = 19;
public static final int opc_iload = 21;
public static final int opc_lload = 22;
public static final int opc_fload = 23;
public static final int opc_dload = 24;
public static final int opc_aload = 25;
public static final int opc_iload_0 = 26;
public static final int opc_lload_0 = 30;
public static final int opc_fload_0 = 34;
public static final int opc_dload_0 = 38;
public static final int opc_aload_0 = 42;
public static final int opc_aload_1 = 43;
public static final int opc_aaload = 50;
public static final int opc_istore = 54;
public static final int opc_lstore = 55;
public static final int opc_fstore = 56;
public static final int opc_dstore = 57;
public static final int opc_astore = 58;
public static final int opc_istore_0 = 59;
public static final int opc_lstore_0 = 63;
public static final int opc_fstore_0 = 67;
public static final int opc_dstore_0 = 71;
public static final int opc_astore_0 = 75;
public static final int opc_aastore = 83;
public static final int opc_bastore = 84;
public static final int opc_pop = 87;
public static final int opc_dup = 89;
public static final int opc_ifeq = 153;
public static final int opc_ifne = 154;
public static final int opc_ifle = 158;
public static final int opc_if_icmpeq = 159;
public static final int opc_if_acmpne = 166;
public static final int opc_goto = 167;
public static final int opc_jsr = 168;
public static final int opc_ret = 169;
public static final int opc_ireturn = 172;
public static final int opc_lreturn = 173;
public static final int opc_freturn = 174;
public static final int opc_dreturn = 175;
public static final int opc_areturn = 176;
public static final int opc_return = 177;
public static final int opc_getstatic = 178;
public static final int opc_putstatic = 179;
public static final int opc_getfield = 180;
public static final int opc_putfield = 181;
public static final int opc_invokevirtual = 182;
public static final int opc_invokespecial = 183;
public static final int opc_invokestatic = 184;
public static final int opc_invokeinterface = 185;
public static final int opc_new = 187;
public static final int opc_newarray = 188;
public static final int opc_anewarray = 189;
public static final int opc_arraylength = 190;
public static final int opc_athrow = 191;
public static final int opc_checkcast = 192;
public static final int opc_instanceof = 193;
public static final int opc_wide = 196;
public static final int opc_ifnull = 198;
/** name of generated class */
protected String className;
/** access flags */
protected int accessFlags;
/** superclass name */
protected String superclassName;
/** interface names */
protected String[] ifaceNames;
/** constant pool of class being generated */
protected ConstantPool cp = new ConstantPool();
/**
* Construct a HandlerGenerator to generate a handler class with the
* specified name and for the given interfaces.
*/
protected ClassFileGenerator(String className, String[] interfaces, String superclass, int accessFlags) {
this.className = className;
this.ifaceNames = interfaces;
this.superclassName = superclass;
this.accessFlags = accessFlags;
}
/**
* Generate a class file for the handler. This method drives the
* class file generation process.
*/
final protected void generateClassFile(OutputStream stream) {
try {
// collect field info and method info structs
MethodInfo[] methods = generateMethods();
FieldInfo[] fields = generateFields();
// make sure these classes are in the constant pool
cp.getClass(dotToSlash(className));
cp.getClass(dotToSlash(superclassName));
for (int i = 0; i < ifaceNames.length; i++)
cp.getClass(dotToSlash(ifaceNames[i]));
cp.setReadOnly();
// write the class file
DataOutputStream dout = new DataOutputStream(stream);
// u4 magic;
dout.writeInt(JAVA_MAGIC);
// u2 major_version;
dout.writeShort(JAVA_DEFAULT_MINOR_VERSION);
// u2 minor_version;
dout.writeShort(JAVA_DEFAULT_VERSION);
// constant pool
cp.write(dout);
// u2 access_flags;
dout.writeShort(accessFlags);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(dotToSlash(superclassName)));
// u2 interfaces_count;
dout.writeShort(ifaceNames.length);
// u2 interfaces[interfaces_count];
for (int i = 0; i < ifaceNames.length; i++)
dout.writeShort(cp.getClass(dotToSlash(ifaceNames[i])));
// u2 fields_count;
dout.writeShort(fields.length);
// field_info fields[fields_count];
for (int i = 0; i < fields.length; i++)
fields[i].write(dout);
// u2 methods_count;
dout.writeShort(methods.length);
// method_info methods[methods_count];
for (int i = 0 ; i < methods.length; i++)
methods[i].write(dout);
// u2 attributes_count;
dout.writeShort(0);
dout.close();
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception"); //NOI18N
}
}
protected abstract MethodInfo[] generateMethods() throws IOException;
protected abstract FieldInfo[] generateFields() throws IOException;
/**
* A FieldInfo object contains information about a particular field
* in the class being generated. The class mirrors the data items of
* the "field_info" structure of the class file format (see JVMS 4.5).
*/
final protected class FieldInfo {
private int accessFlags;
private String name;
private String descriptor;
private Object constValue;
public FieldInfo(String name, String descriptor, int accessFlags) {
this.name = name;
this.descriptor = descriptor;
this.accessFlags = accessFlags;
/*
* Make sure that constant pool indexes are reserved for the
* following items before starting to write the final class file.
*/
cp.getUtf8(name);
cp.getUtf8(descriptor);
}
public void setConstValue(Object value) {
if ((accessFlags & ACC_STATIC) != 0) {
constValue = value;
cp.getUtf8("ConstantValue"); //NOI18N
cp.getUnknownValue(constValue);
}
}
final public void write(DataOutputStream out) throws IOException {
/*
* Write all the items of the "field_info" structure.
* See JVMS section 4.5.
*/
// u2 access_flags;
out.writeShort(accessFlags);
// u2 name_index;
out.writeShort(cp.getUtf8(name));
// u2 descriptor_index;
out.writeShort(cp.getUtf8(descriptor));
// u2 attributes_count;
if (constValue == null)
out.writeShort(0); // (no field_info attributes for proxy classes)
else {
out.writeShort(1);
// u2 attribute_name_index;
out.writeShort(cp.getUtf8("ConstantValue")); //NOI18N
// u4 attribute_length;
out.writeInt(2);
// u2 constantvalue_index
out.writeShort(cp.getUnknownValue(constValue));
}
}
public boolean equals(Object o) {
if (o instanceof FieldInfo) {
return ((FieldInfo) o).name.equalsIgnoreCase(name);
} else {
return false;
}
}
public int hashCode() {
return name.toUpperCase().hashCode();
}
}
/**
* An ExceptionTableEntry object holds values for the data items of
* an entry in the "exception_table" item of the "Code" attribute of
* "method_info" structures (see JVMS 4.7.3).
*/
final protected static class ExceptionTableEntry {
public short startPc;
public short endPc;
public short handlerPc;
public short catchType;
public ExceptionTableEntry(short startPc, short endPc,
short handlerPc, short catchType) {
this.startPc = startPc;
this.endPc = endPc;
this.handlerPc = handlerPc;
this.catchType = catchType;
}
};
/**
* A MethodInfo object contains information about a particular method
* in the class being generated. This class mirrors the data items of
* the "method_info" structure of the class file format (see JVMS 4.6).
*/
final protected class MethodInfo {
private int accessFlags;
private String name;
private String descriptor;
private ByteArrayOutputStream code = new ByteArrayOutputStream();
private short maxStack;
private short maxLocals;
private short[] declaredExceptions;
private List exceptionTable = new ArrayList();
public MethodInfo(String name, String descriptor, int accessFlags) {
this.name = name;
this.descriptor = descriptor;
this.accessFlags = accessFlags;
/*
* Make sure that constant pool indexes are reserved for the
* following items before starting to write the final class file.
*/
cp.getUtf8(name);
cp.getUtf8(descriptor);
}
public void setDeclaredExceptions(short[] exceptions) {
cp.getUtf8("Exceptions"); //NOI18N
declaredExceptions = exceptions;
}
public ByteArrayOutputStream getCodeStream() {
cp.getUtf8("Code"); //NOI18N
return code;
}
public void setMaxStack(short max) {
maxStack = max;
}
public void setMaxLocals(short max) {
maxLocals = max;
}
public List getExceptionTable() {
return exceptionTable;
}
public void write(DataOutputStream out) throws IOException {
/*
* Write all the items of the "method_info" structure.
* See JVMS section 4.6.
*/
// u2 access_flags;
out.writeShort(accessFlags);
// u2 name_index;
out.writeShort(cp.getUtf8(name));
// u2 descriptor_index;
out.writeShort(cp.getUtf8(descriptor));
// u2 attributes_count;
short count = 0;
if (code.size() > 0)
count++;
if (declaredExceptions != null && declaredExceptions.length > 0)
count++;
out.writeShort(count);
// Write "Code" attribute. See JVMS section 4.7.3.
if (code.size() > 0) {
// u2 attribute_name_index;
out.writeShort(cp.getUtf8("Code")); //NOI18N
// u4 attribute_length;
out.writeInt(12 + code.size() + 8 * exceptionTable.size());
// u2 max_stack;
out.writeShort(maxStack);
// u2 max_locals;
out.writeShort(maxLocals);
// u2 code_length;
out.writeInt(code.size());
// u1 code[code_length];
code.writeTo(out);
// u2 exception_table_length;
out.writeShort(exceptionTable.size());
for (Iterator iter = exceptionTable.iterator(); iter.hasNext();) {
ExceptionTableEntry e = (ExceptionTableEntry) iter.next();
// u2 start_pc;
out.writeShort(e.startPc);
// u2 end_pc;
out.writeShort(e.endPc);
// u2 handler_pc;
out.writeShort(e.handlerPc);
// u2 catch_type;
out.writeShort(e.catchType);
}
// u2 attributes_count;
out.writeShort(0);
}
// write "Exceptions" attribute. See JVMS section 4.7.4.
if (declaredExceptions != null && declaredExceptions.length > 0) {
// u2 attribute_name_index;
out.writeShort(cp.getUtf8("Exceptions")); //NOI18N
// u4 attributes_length;
out.writeInt(2 + 2 * declaredExceptions.length);
// u2 number_of_exceptions;
out.writeShort(declaredExceptions.length);
// u2 exception_index_table[number_of_exceptions];
for (int i = 0; i < declaredExceptions.length; i++)
out.writeShort(declaredExceptions[i]);
}
}
}
/*
* =============== Code Generation Utility Methods ===============
*/
/*
* The following methods generate code for the load or store operation
* indicated by their name for the given local variable. The code is
* written to the supplied stream.
*/
protected void code_iload(int lvar, DataOutputStream out)
throws IOException {
codeLocalLoadStore(lvar,
opc_iload, opc_iload_0, out);
}
protected void code_lload(int lvar, DataOutputStream out)
throws IOException {
codeLocalLoadStore(lvar,
opc_lload, opc_lload_0, out);
}
protected void code_fload(int lvar, DataOutputStream out)
throws IOException {
codeLocalLoadStore(lvar,
opc_fload, opc_fload_0, out);
}
protected void code_dload(int lvar, DataOutputStream out)
throws IOException {
codeLocalLoadStore(lvar,
opc_dload, opc_dload_0, out);
}
protected void code_aload(int lvar, DataOutputStream out)
throws IOException {
codeLocalLoadStore(lvar,
opc_aload, opc_aload_0, out);
}
protected void code_istore(int lvar, DataOutputStream out)
throws IOException {
codeLocalLoadStore(lvar,
opc_istore, opc_istore_0, out);
}
protected void code_lstore(int lvar, DataOutputStream out)
throws IOException {
codeLocalLoadStore(lvar,
opc_lstore, opc_lstore_0, out);
}
protected void code_fstore(int lvar, DataOutputStream out)
throws IOException {
codeLocalLoadStore(lvar,
opc_fstore, opc_fstore_0, out);
}
protected void code_dstore(int lvar, DataOutputStream out)
throws IOException {
codeLocalLoadStore(lvar,
opc_dstore, opc_dstore_0, out);
}
protected void code_astore(int lvar, DataOutputStream out)
throws IOException {
codeLocalLoadStore(lvar,
opc_astore, opc_astore_0, out);
}
/**
* Generate code for a load or store instruction for the given local
* variable. The code is written to the supplied stream.
*
* "opcode" indicates the opcode form of the desired load or store
* instruction that takes an explicit local variable index, and
* "opcode_0" indicates the corresponding form of the instruction
* with the implicit index 0.
*/
protected void codeLocalLoadStore(int lvar, int opcode, int opcode_0,
DataOutputStream out)
throws IOException {
_assert(lvar >= 0 && lvar <= 0xFFFF);
if (lvar <= 3) {
out.writeByte(opcode_0 + lvar);
} else if (lvar <= 0xFF) {
out.writeByte(opcode);
out.writeByte(lvar & 0xFF);
} else {
/*
* Use the "wide" instruction modifier for local variable
* indexes that do not fit into an unsigned byte.
*/
out.writeByte(opc_wide);
out.writeByte(opcode);
out.writeShort(lvar & 0xFFFF);
}
}
/**
* Generate code for an "ldc" instruction for the given constant pool
* index (the "ldc_w" instruction is used if the index does not fit
* into an unsigned byte). The code is written to the supplied stream.
*/
protected void code_ldc(int index, DataOutputStream out)
throws IOException {
_assert(index >= 0 && index <= 0xFFFF);
if (index <= 0xFF) {
out.writeByte(opc_ldc);
out.writeByte(index & 0xFF);
} else {
out.writeByte(opc_ldc_w);
out.writeShort(index & 0xFFFF);
}
}
/**
* Generate code to push a constant integer value on to the operand
* stack, using the "iconst_", "bipush", or "sipush" instructions
* depending on the size of the value. The code is written to the
* supplied stream.
*/
protected void code_ipush(int value, DataOutputStream out)
throws IOException {
if (value >= -1 && value <= 5) {
out.writeByte(opc_iconst_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
out.writeByte(opc_bipush);
out.writeByte(value & 0xFF);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
out.writeByte(opc_sipush);
out.writeShort(value & 0xFFFF);
} else {
_assert(false);
}
}
protected void codeReturnFieldValue(String className, String fieldName, String descriptor, boolean isStatic, DataOutputStream out)
throws IOException {
short index = cp.getFieldRef(dotToSlash(className), fieldName, descriptor);
_assert(index >= 0);
if (isStatic)
out.writeByte(opc_getstatic);
else {
code_aload(0, out);
out.writeByte(opc_getfield);
}
out.writeShort(index);
if (descriptor.equals("I") || descriptor.equals("S") || descriptor.equals("C") || //NOI18N
descriptor.equals("B") || descriptor.equals("Z")) //NOI18N
out.writeByte(opc_ireturn);
else if (descriptor.equals("J")) //NOI18N
out.writeByte(opc_lreturn);
else if (descriptor.equals("F")) //NOI18N
out.writeByte(opc_freturn);
else if (descriptor.equals("D")) //NOI18N
out.writeByte(opc_dreturn);
else
out.writeByte(opc_areturn);
}
/**
* Generate code to invoke the Class.forName with the name of the given
* class to get its Class object at runtime. The code is written to
* the supplied stream. Note that the code generated by this method
* may caused the checked ClassNotFoundException to be thrown.
*/
protected void codeClassForName(Class cl, DataOutputStream out)
throws IOException {
code_ldc(cp.getString(cl.getName()), out);
out.writeByte(opc_invokestatic);
out.writeShort(cp.getMethodRef(
"java/lang/Class", //NOI18N
"forName", "(Ljava/lang/String;)Ljava/lang/Class;")); //NOI18N
}
/*
* ==================== General Utility Methods ====================
*/
protected static String firstUpper(String text) {
try {
return text.substring(0, 1).toUpperCase() + text.substring(1);
} catch (IndexOutOfBoundsException e) {
return ""; //NOI18N
}
}
protected static String firstLower(String text) {
try {
return text.substring(0, 1).toLowerCase() + text.substring(1);
} catch (IndexOutOfBoundsException e) {
return ""; //NOI18N
}
}
/**
* Assert that an assertion is true: throw InternalError if it is not.
*/
protected static void _assert(boolean assertion) {
if (assertion != true) {
throw new InternalError("assertion failure"); //NOI18N
}
}
/**
* Convert a fully qualified class name that uses '.' as the package
* separator, the external representation used by the Java language
* and APIs, to a fully qualified class name that uses '/' as the
* package separator, the representation used in the class file
* format (see JVMS section 4.2).
*/
protected static String dotToSlash(String name) {
return name.replace('.', '/');
}
/**
* Return the "method descriptor" string for a method with the given
* parameter types and return type. See JVMS section 4.3.3.
*/
protected static String getMethodDescriptor(String[] parameterTypeNames,
String returnTypeName) {
return getParameterDescriptors(parameterTypeNames) +
((returnTypeName.equals("void")) ? "V" : getFieldType(returnTypeName)); //NOI18N
}
/**
* Return the list of "parameter descriptor" strings enclosed in
* parentheses corresponding to the given parameter types (in other
* words, a method descriptor without a return descriptor). This
* string is useful for constructing string keys for methods without
* regard to their return type.
*/
protected static String getParameterDescriptors(String[] parameterTypeNames) {
StringBuffer desc = new StringBuffer("("); //NOI18N
for (int i = 0; i < parameterTypeNames.length; i++) {
desc.append(getFieldType(parameterTypeNames[i]));
}
desc.append(')');
return desc.toString();
}
/**
* Return the "field type" string for the given type, appropriate for
* a field descriptor, a parameter descriptor, or a return descriptor
* other than "void". See JVMS section 4.3.2.
*/
protected static String getFieldType(String typeName) {
PrimitiveTypeInfo ptInfo = PrimitiveTypeInfo.get(typeName);
if (ptInfo != null)
return ptInfo.baseTypeString;
else if (typeName.endsWith("[]")) //NOI18N
return "[" + getFieldType(typeName.substring(0, typeName.length()-2).trim()); //NOI18N
else
return "L" + dotToSlash(typeName) + ";"; //NOI18N
}
/**
* Return the number of abstract "words", or consecutive local variable
* indexes, required to contain a value of the given type. See JVMS
* section 3.6.1.
*
* Note that the original version of the JVMS contained a definition of
* this abstract notion of a "word" in section 3.4, but that definition
* was removed for the second edition.
*/
protected static int getWordsPerType(String typeName) {
if (typeName.equals("long") || typeName.equals("double")) //NOI18N
return 2;
return 1;
}
/**
* A PrimitiveTypeInfo object contains assorted information about
* a primitive type in its public fields. The struct for a particular
* primitive type can be obtained using the static "get" method.
*/
protected static class PrimitiveTypeInfo {
/** "base type" used in various descriptors (see JVMS section 4.3.2) */
public String baseTypeString;
/** name of corresponding wrapper class */
public String wrapperClassName;
/** method descriptor for wrapper class constructor */
public String wrapperConstructorDesc;
/** name of wrapper class method for retrieving primitive value */
public String unwrapMethodName;
/** descriptor of same method */
public String unwrapMethodDesc;
private static Map table = new HashMap(11);
static {
table.put("int", new PrimitiveTypeInfo( //NOI18N
"I", "java/lang/Integer", "(I)V", "intValue", "()I")); //NOI18N
table.put("boolean", new PrimitiveTypeInfo( //NOI18N
"Z", "java/lang/Boolean", "(Z)V", "booleanValue", "()Z")); //NOI18N
table.put("byte", new PrimitiveTypeInfo( //NOI18N
"B", "java/lang/Byte", "(B)V", "byteValue", "()B")); //NOI18N
table.put("char", new PrimitiveTypeInfo( //NOI18N
"C", "java/lang/Char", "(C)V", "charValue", "()C")); //NOI18N
table.put("short", new PrimitiveTypeInfo( //NOI18N
"S", "java/lang/Short", "(S)V", "shortValue", "()S")); //NOI18N
table.put("long", new PrimitiveTypeInfo( //NOI18N
"J", "java/lang/Long", "(J)V", "longValue", "()J")); //NOI18N
table.put("float", new PrimitiveTypeInfo( //NOI18N
"F", "java/lang/Float", "(F)V", "floatValue", "()F")); //NOI18N
table.put("double", new PrimitiveTypeInfo( //NOI18N
"D", "java/lang/Double", "(D)V", "doubleValue", "()D")); //NOI18N
}
private PrimitiveTypeInfo(String baseTypeString,
String wrapperClassName,
String wrapperConstructorDesc,
String unwrapMethodName,
String unwrapMethodDesc) {
this.baseTypeString = baseTypeString;
this.wrapperClassName = wrapperClassName;
this.wrapperConstructorDesc = wrapperConstructorDesc;
this.unwrapMethodName = unwrapMethodName;
this.unwrapMethodDesc = unwrapMethodDesc;
}
public static PrimitiveTypeInfo get(String name) {
return (PrimitiveTypeInfo) table.get(name);
}
}
/**
* A ConstantPool object represents the constant pool of a class file
* being generated. This representation of a constant pool is designed
* specifically for use by ProxyGenerator; in particular, it assumes
* that constant pool entries will not need to be resorted (for example,
* by their type, as the Java compiler does), so that the final index
* value can be assigned and used when an entry is first created.
*
* Note that new entries cannot be created after the constant pool has
* been written to a class file. To prevent such logic errors, a
* ConstantPool instance can be marked "read only", so that further
* attempts to add new entries will fail with a runtime exception.
*
* See JVMS section 4.4 for more information about the constant pool
* of a class file.
*/
protected static class ConstantPool {
/**
* list of constant pool entries, in constant pool index order.
*
* This list is used when writing the constant pool to a stream
* and for assigning the next index value. Note that element 0
* of this list corresponds to constant pool index 1.
*/
private List pool = new ArrayList(32);
/**
* maps constant pool data of all types to constant pool indexes.
*
* This map is used to look up the index of an existing entry for
* values of all types.
*/
private Map map = new HashMap(16);
/** true if no new constant pool entries may be added */
private boolean readOnly = false;
/**
* Get or assign the index for a CONSTANT_Utf8 entry.
*/
public short getUtf8(String s) {
if (s == null) {
throw new NullPointerException();
}
return getValue(s);
}
/**
* Get or assign the index for a CONSTANT_Integer entry.
*/
public short getInteger(int i) {
return getValue(new Integer(i));
}
/**
* Get or assign the index for a CONSTANT_Float entry.
*/
public short getFloat(float f) {
return getValue(new Float(f));
}
/**
* Get or assign the index for a CONSTANT_Long entry.
*/
public short getLong(long l) {
return getValue(new Long(l));
}
/**
* Get or assign the index for a CONSTANT_Double entry.
*/
public short getDouble(double d) {
return getValue(new Double(d));
}
/**
* Get or assign the index for a CONSTANT_Class entry.
*/
public short getClass(String name) {
short utf8Index = getUtf8(name);
return getIndirect(new IndirectEntry(
CONSTANT_CLASS, utf8Index));
}
/**
* Get or assign the index for a CONSTANT_String entry.
*/
public short getString(String s) {
short utf8Index = getUtf8(s);
return getIndirect(new IndirectEntry(
CONSTANT_STRING, utf8Index));
}
/**
* Get or assign the index for a CONSTANT_FieldRef entry.
*/
public short getFieldRef(String className,
String name, String descriptor) {
short classIndex = getClass(className);
short nameAndTypeIndex = getNameAndType(name, descriptor);
return getIndirect(new IndirectEntry(
CONSTANT_FIELD,
classIndex, nameAndTypeIndex));
}
/**
* Get or assign the index for a CONSTANT_MethodRef entry.
*/
public short getMethodRef(String className,
String name, String descriptor) {
short classIndex = getClass(className);
short nameAndTypeIndex = getNameAndType(name, descriptor);
return getIndirect(new IndirectEntry(
CONSTANT_METHOD,
classIndex, nameAndTypeIndex));
}
/**
* Get or assign the index for a CONSTANT_InterfaceMethodRef entry.
*/
public short getInterfaceMethodRef(String className, String name,
String descriptor) {
short classIndex = getClass(className);
short nameAndTypeIndex = getNameAndType(name, descriptor);
return getIndirect(new IndirectEntry(
CONSTANT_INTERFACEMETHOD,
classIndex, nameAndTypeIndex));
}
/**
* Get or assign the index for a CONSTANT_NameAndType entry.
*/
public short getNameAndType(String name, String descriptor) {
short nameIndex = getUtf8(name);
short descriptorIndex = getUtf8(descriptor);
return getIndirect(new IndirectEntry(
CONSTANT_NAMEANDTYPE,
nameIndex, descriptorIndex));
}
public short getUnknownValue(Object value) {
if (value == null)
throw new NullPointerException();
if (value instanceof String)
return getString((String)value);
if (value instanceof Integer || value instanceof Float ||
value instanceof Long || value instanceof Double)
return getValue(value);
throw new InternalError("bogus value entry: " + value); //NOI18N
}
/**
* Set this ConstantPool instance to be "read only".
*
* After this method has been called, further requests to get
* an index for a non-existent entry will cause an InternalError
* to be thrown instead of creating of the entry.
*/
public void setReadOnly() {
readOnly = true;
}
/**
* Write this constant pool to a stream as part of
* the class file format.
*
* This consists of writing the "constant_pool_count" and
* "constant_pool[]" items of the "ClassFile" structure, as
* described in JVMS section 4.1.
*/
public void write(OutputStream out) throws IOException {
DataOutputStream dataOut = new DataOutputStream(out);
// constant_pool_count: number of entries plus one
dataOut.writeShort(pool.size() + 1);
for (Iterator iter = pool.iterator(); iter.hasNext();) {
Entry e = (Entry) iter.next();
e.write(dataOut);
}
}
/**
* Add a new constant pool entry and return its index.
*/
private short addEntry(Entry entry) {
pool.add(entry);
return (short) pool.size();
}
/**
* Get or assign the index for an entry of a type that contains
* a direct value. The type of the given object determines the
* type of the desired entry as follows:
*
* java.lang.String CONSTANT_Utf8
* java.lang.Integer CONSTANT_Integer
* java.lang.Float CONSTANT_Float
* java.lang.Long CONSTANT_Long
* java.lang.Double CONSTANT_DOUBLE
*/
private short getValue(Object key) {
Short index = (Short) map.get(key);
if (index != null) {
return index.shortValue();
} else {
if (readOnly) {
throw new InternalError(
"late constant pool addition: " + key); //NOI18N
}
short i = addEntry(new ValueEntry(key));
map.put(key, new Short(i));
return i;
}
}
/**
* Get or assign the index for an entry of a type that contains
* references to other constant pool entries.
*/
private short getIndirect(IndirectEntry e) {
Short index = (Short) map.get(e);
if (index != null) {
return index.shortValue();
} else {
if (readOnly) {
throw new InternalError("late constant pool addition"); //NOI18N
}
short i = addEntry(e);
map.put(e, new Short(i));
return i;
}
}
/**
* Entry is the abstact superclass of all constant pool entry types
* that can be stored in the "pool" list; its purpose is to define a
* common method for writing constant pool entries to a class file.
*/
private static abstract class Entry {
public abstract void write(DataOutputStream out)
throws IOException;
}
/**
* ValueEntry represents a constant pool entry of a type that
* contains a direct value (see the comments for the "getValue"
* method for a list of such types).
*
* ValueEntry objects are not used as keys for their entries in the
* Map "map", so no useful hashCode or equals methods are defined.
*/
private static class ValueEntry extends Entry {
private Object value;
public ValueEntry(Object value) {
this.value = value;
}
public void write(DataOutputStream out) throws IOException {
if (value instanceof String) {
out.writeByte(CONSTANT_UTF8);
out.writeUTF((String) value);
} else if (value instanceof Integer) {
out.writeByte(CONSTANT_INTEGER);
out.writeInt(((Integer) value).intValue());
} else if (value instanceof Float) {
out.writeByte(CONSTANT_FLOAT);
out.writeFloat(((Float) value).floatValue());
} else if (value instanceof Long) {
out.writeByte(CONSTANT_LONG);
out.writeLong(((Long) value).longValue());
} else if (value instanceof Double) {
out.writeDouble(CONSTANT_DOUBLE);
out.writeDouble(((Double) value).doubleValue());
} else {
throw new InternalError("bogus value entry: " + value); //NOI18N
}
}
}
/**
* IndirectEntry represents a constant pool entry of a type that
* references other constant pool entries, i.e., the following types:
*
* CONSTANT_Class, CONSTANT_String, CONSTANT_Fieldref,
* CONSTANT_Methodref, CONSTANT_InterfaceMethodref, and
* CONSTANT_NameAndType.
*
* Each of these entry types contains either one or two indexes of
* other constant pool entries.
*
* IndirectEntry objects are used as the keys for their entries in
* the Map "map", so the hashCode and equals methods are overridden
* to allow matching.
*/
private static class IndirectEntry extends Entry {
private int tag;
private short index0;
private short index1;
/**
* Construct an IndirectEntry for a constant pool entry type
* that contains one index of another entry.
*/
public IndirectEntry(int tag, short index) {
this.tag = tag;
this.index0 = index;
this.index1 = 0;
}
/**
* Construct an IndirectEntry for a constant pool entry type
* that contains two indexes for other entries.
*/
public IndirectEntry(int tag, short index0, short index1) {
this.tag = tag;
this.index0 = index0;
this.index1 = index1;
}
public void write(DataOutputStream out) throws IOException {
out.writeByte(tag);
out.writeShort(index0);
/*
* If this entry type contains two indexes, write
* out the second, too.
*/
if (tag == CONSTANT_FIELD ||
tag == CONSTANT_METHOD ||
tag == CONSTANT_INTERFACEMETHOD ||
tag == CONSTANT_NAMEANDTYPE) {
out.writeShort(index1);
}
}
public int hashCode() {
return tag + index0 + index1;
}
public boolean equals(Object obj) {
if (obj instanceof IndirectEntry) {
IndirectEntry other = (IndirectEntry) obj;
if (tag == other.tag &&
index0 == other.index0 && index1 == other.index1) {
return true;
}
}
return false;
}
}
}
}
|
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.