alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

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

 

new blog posts

 

Copyright 1998-2021 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.