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.mdr.util;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author	Martin Matula
 * @version	0.1
 */
public abstract class ImplGenerator {

    /* 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;
    
    /* Type codes */
    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_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_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_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_invokevirtual = 182;
    public static final int opc_invokespecial = 183;
    public static final int opc_invokestatic = 184;
    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_athrow = 191;
    public static final int opc_checkcast = 192;
    public static final int opc_wide = 196;
    public static final int opc_ifnull = 198;

    /** prefix for field names */
    protected static final String FIELD_PREFIX = "field$";

    /** superclass of generated class */
    protected Class superclass;
    
    /** superclass dotToSlash name */
    protected String superclassName;

    /** name of generated class */
    protected String className;

    /** JMI generated interface */ 
    protected Class ifc;

    /** constant pool of class being generated */
    protected ConstantPool cp = new ConstantPool();

    /** FieldInfo struct for each field of generated class */
    protected HashSet fields = new HashSet();

    /** MethodInfo struct for each method of generated class */
    protected List methods = new ArrayList();

    /**
     * for each method to be generated, maps method name and parameter
     * descriptor to HandlerMethod object
     */
    protected Map classMethods = new HashMap(11);

    /**
     * Construct a HandlerGenerator to generate a handler class with the
     * specified name and for the given interfaces.
     */
    protected ImplGenerator(String className,Class ifc,Class handlerClass) {
	this.className = className;
	this.ifc = ifc;
        this.superclass = handlerClass;
        this.superclassName = dotToSlash(superclass.getName());
    }

    /**
     * Generate a class file for the handler.  This method drives the
     * class file generation process.
     */
    final protected byte[] generateClassFile() {
        HashSet methodSet = new HashSet();
//        Method[] allMethods = superclass.getMethods();

        // collect all methods from the ancestor
/*        for (int i = 0; i < allMethods.length; i++) {
            methodSet.add(allMethods[i].getName() + getMethodDescriptor(allMethods[i].getParameterTypes(), allMethods[i].getReturnType()));
        }
*/         
        // add the methods of interface which are not implemented in ancestor to the list of methods we should generate
        {
            // collect methods from descedant implementations of ImplGenerator
            Method[] methods = getMethodsToImplement();
            for (int i = 0; i < methods.length; i++) {
//                if (!methodSet.contains(methods[i].getName() + getMethodDescriptor(methods[i].getParameterTypes(), methods[i].getReturnType()))) {
                    addClassMethod(methods[i], ifc);
//                }
            }
        }

        // collect field info and method info structs
	try {
            // generate MethodInfo for constructor
	    methods.add(generateConstructor());

            // generate MethodInfo and FieldInfo for all methods
	    for (Iterator it = classMethods.values().iterator(); it.hasNext();) {
		ClassMethod cm = (ClassMethod) it.next();

		// generate MethodInfo for handler method
		methods.add(cm.generateMethod());
	    }

            // generate MethodInfo for static initializer
	    methods.add(generateStaticInitializer());
	} catch (IOException e) {
	    throw new InternalError("unexpected I/O Exception");
	}

        // make sure these classes are in the constant pool
	cp.getClass(dotToSlash(className));
	cp.getClass(dotToSlash(superclass.getName()));
        cp.getClass(dotToSlash(ifc.getName()));
	cp.setReadOnly();

        // write the class file
	ByteArrayOutputStream bout = new ByteArrayOutputStream();
	DataOutputStream dout = new DataOutputStream(bout);

	try {
            // 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(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
            // u2 this_class;
	    dout.writeShort(cp.getClass(dotToSlash(className)));
            // u2 super_class;
	    dout.writeShort(cp.getClass(dotToSlash(superclass.getName())));

            // u2 interfaces_count;
	    dout.writeShort(1);
            // u2 interfaces[interfaces_count];
            dout.writeShort(cp.getClass(dotToSlash(ifc.getName())));

            // u2 fields_count;
	    dout.writeShort(fields.size());
            // field_info fields[fields_count];
	    for (Iterator it = fields.iterator(); it.hasNext();) {
		FieldInfo f = (FieldInfo) it.next();
		f.write(dout);
	    }

            // u2 methods_count;
	    dout.writeShort(methods.size());
            // method_info methods[methods_count];
	    for (Iterator it = methods.iterator(); it.hasNext();) {
		MethodInfo m = (MethodInfo) it.next();
		m.write(dout);
	    }

            // u2 attributes_count;
	    dout.writeShort(0);
	} catch (IOException e) {
	    throw new InternalError("unexpected I/O Exception");
	}

	return bout.toByteArray();
    }

    /**
     * Add a method to be generated
     */
    protected void addClassMethod(Method m, Class fromClass) {
        String name = m.getName();
        Class[] parameterTypes = m.getParameterTypes();
        String key = name + getParameterDescriptors(parameterTypes);
        
        if (classMethods.get(key) == null) {
            ClassMethod cm = getClassMethod(m, fromClass);
            classMethods.put(key, cm);
        }
    }
    
    protected abstract Method[] getMethodsToImplement();

    protected ClassMethod getClassMethod(Method m, Class fromClass) {
        // this method should never be called (it is called only if descedant cannot recognize the method)
        throw new InternalError("Unrecognized method: " + m.getName() + " from class: " + fromClass.getName());  
    }

    /**
     * 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 {
	public int accessFlags;
	public String name;
	public String descriptor;

	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);
	}

	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;
	    out.writeShort(0);	// (no field_info attributes for proxy classes)
	}

        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 {
	public int accessFlags;
	public String name;
	public String descriptor;
	public short maxStack;
	public short maxLocals;
	public ByteArrayOutputStream code = new ByteArrayOutputStream();
	public List exceptionTable = new ArrayList();
	public short[] declaredExceptions;

	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);
	    cp.getUtf8("Code");
	    cp.getUtf8("Exceptions");
	}

	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;
	    out.writeShort(2);	// (two method_info attributes:)

	    // Write "Code" attribute. See JVMS section 4.7.3.

					// u2 attribute_name_index;
	    out.writeShort(cp.getUtf8("Code"));
					// 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.

					// u2 attribute_name_index;
	    out.writeShort(cp.getUtf8("Exceptions"));
					// 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]);
	    }
	}

    }

    /**
     * A HandlerMethod object represents a proxy method in the proxy class
     * being generated: a method whose implementation will encode and
     * dispatch invocations to the proxy instance's invocation handler.
     */
    protected class ClassMethod {

	public String methodName;
	public Class[] parameterTypes;
	public Class returnType;
	public Class[] exceptionTypes;
	public Class fromClass;
	public String methodFieldName;
        public short delegateMethod;
        private boolean hasField;

	public ClassMethod(Method method, short delegate, String methodFieldName)
	{
	    this.methodName = method.getName();
	    this.parameterTypes = method.getParameterTypes();
	    this.returnType = method.getReturnType();
	    this.exceptionTypes = method.getExceptionTypes();
	    this.fromClass = method.getDeclaringClass();
            if (methodFieldName == null) {
                this.methodFieldName = null;
                hasField = false;
            } else {
                this.methodFieldName = FIELD_PREFIX + methodFieldName;
                // add static field for feature name method operates with
                hasField = fields.add(new FieldInfo(this.methodFieldName, "Ljava/lang/String;", ACC_PRIVATE | ACC_STATIC));
            }
            delegateMethod = delegate;
	}

	/**
	 * Return a MethodInfo object for this method, including generating
	 * the code.
	 */
	public MethodInfo generateMethod() throws IOException {
	    String desc = getMethodDescriptor(parameterTypes, returnType);
	    MethodInfo minfo = new MethodInfo(methodName, desc, ACC_PUBLIC | ACC_FINAL);
	    int[] parameterSlot = new int[parameterTypes.length];
	    int nextSlot = 1;
	    for (int i = 0; i < parameterSlot.length; i++) {
		parameterSlot[i] = nextSlot;
		nextSlot += getWordsPerType(parameterTypes[i]);
	    }
	    int localSlot0 = nextSlot;

	    DataOutputStream out = new DataOutputStream(minfo.code);

            // I'll pass this instance as the first parameter
	    code_aload(0, out);

            if (methodFieldName != null) {
                // feature name stored in the static variable as the second parameter
                out.writeByte(opc_dup);
                out.writeByte(opc_getstatic);
                out.writeShort(cp.getFieldRef(dotToSlash(className), methodFieldName, "Ljava/lang/String;"));
            }

            // The rest of parameters follows
            for (int i = 0; i < parameterTypes.length; i++) {
                codeWrapArgument(parameterTypes[i], parameterSlot[i], out);
            }

            // call the delegate method
	    out.writeByte(opc_invokespecial);
	    out.writeShort(delegateMethod);

	    if (returnType == void.class) {
//		out.writeByte(opc_pop);
		out.writeByte(opc_return);
	    } else {
		codeUnwrapReturnValue(returnType, out);
	    }

	    minfo.maxStack = 10;
	    minfo.maxLocals = (short) (localSlot0 + 1);
	    minfo.declaredExceptions = new short[0];

	    return minfo;
	}

        /**
         * Generate code for wrapping a parameter of the given type and whose
         * value can be found at the specified local variable index to be
         * passed to the invocation handler's "invoke" method (as an Object).
         * The code is written to the supplied stream.
         */
        public void codeWrapArgument(Class type, int slot, DataOutputStream out) throws IOException {
            if (type.isPrimitive()) {
                PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);

                out.writeByte(opc_new);
                out.writeShort(cp.getClass(prim.wrapperClassName));

                out.writeByte(opc_dup);

                if (type == int.class || type == boolean.class || type == byte.class || type == char.class || type == short.class) {
                    code_iload(slot, out);
                } else if (type == long.class) {
                    code_lload(slot, out);
                } else if (type == float.class) {
                    code_fload(slot, out);
                } else if (type == double.class) {
                    code_dload(slot, out);
                } else {
                    assertTrue(false);
                }

                out.writeByte(opc_invokespecial);
                out.writeShort(cp.getMethodRef(prim.wrapperClassName, "", prim.wrapperConstructorDesc));
            } else {
                code_aload(slot, out);
            }
        }

	/**
	 * Generate code for unwrapping the return value of the given type
	 * from the invocation handler's "invoke" method (of type Object) to
	 * its correct type.  The code is written to the supplied stream.
	 */
	public void codeUnwrapReturnValue(Class type, DataOutputStream out) throws IOException {
	    if (type.isPrimitive()) {
		PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
                
                out.writeByte(opc_dup);
                out.writeByte(opc_ifnull);
                out.writeShort(10);

		out.writeByte(opc_checkcast);
		out.writeShort(cp.getClass(prim.wrapperClassName));

		out.writeByte(opc_invokevirtual);
		out.writeShort(cp.getMethodRef(
		    prim.wrapperClassName,
		    prim.unwrapMethodName, prim.unwrapMethodDesc));

		if (type == int.class ||
		    type == boolean.class ||
		    type == byte.class ||
		    type == char.class ||
		    type == short.class)
		{
		    out.writeByte(opc_ireturn);
                    out.writeByte(opc_iconst_0);
                    out.writeByte(opc_ireturn);
		} else if (type == long.class) {
		    out.writeByte(opc_lreturn);
                    out.writeByte(opc_lconst_0);
		    out.writeByte(opc_lreturn);
		} else if (type == float.class) {
		    out.writeByte(opc_freturn);
                    out.writeByte(opc_fconst_0);
		    out.writeByte(opc_freturn);
		} else if (type == double.class) {
		    out.writeByte(opc_dreturn);
                    out.writeByte(opc_dconst_0);
		    out.writeByte(opc_dreturn);
		} else {
		    assertTrue(false);
		}

	    } else {

		out.writeByte(opc_checkcast);
		out.writeShort(cp.getClass(dotToSlash(type.getName())));

		out.writeByte(opc_areturn);
	    }
	}
        
        public int getBytesForUnwrapReturn(Class type) {
	    if (type.isPrimitive()) {
                return 13;
	    } else {
                return 4;
            }
        }

        /**
         * Generate code for initializing the static field that stores
         * the Method object for this proxy method.  The code is written
         * to the supplied stream.
         */
        public void codeFieldInitialization(DataOutputStream out) throws IOException {
            if (hasField) {
                out.writeByte(opc_new);            // new
                out.writeShort(cp.getClass("java/lang/String"));    //      java.lang.String
                out.writeByte(opc_dup);            // dup

                // get the string bytes to pass to the string constructor
                byte[] bytes = strip(methodFieldName, FIELD_PREFIX).getBytes();

                code_ipush(bytes.length, out);                      // \
                out.writeByte(opc_newarray);       // - array = new byte[bytes.length]
                out.writeByte(T_BYTE);             // /

                // populate array with bytes
                for (int i = 0; i < bytes.length; i++) {
                    out.writeByte(opc_dup);        // \
                    code_ipush(i, out);                             // - array[i] = bytes[i]
                    code_ipush(bytes[i], out);                      // /
                    out.writeByte(opc_bastore);    // /
                }

                // now the stack content is: reference to new String,
                //      reference to new String, reference to array of bytes
                // we'll call the String(byte[]) constructor
                out.writeByte(opc_invokespecial);
                out.writeShort(cp.getMethodRef("java/lang/String", "", "([B)V"));

                // stack content: reference to new String
                // I'll assign this reference to the field
                out.writeByte(opc_putstatic);
                out.writeShort(cp.getFieldRef(dotToSlash(className), methodFieldName, "Ljava/lang/String;"));
            }
        }
    }

    /**
     * Generate the constructor method for the proxy class.
     */
    protected abstract MethodInfo generateConstructor() throws IOException;

    /**
     * Generate the static initializer method for the proxy class.
     */
    protected MethodInfo generateStaticInitializer() throws IOException {
	MethodInfo minfo = new MethodInfo(
	    "", "()V", ACC_STATIC);

	int localSlot0 = 1;
	short pc;

	DataOutputStream out = new DataOutputStream(minfo.code);

	for (Iterator it = classMethods.values().iterator(); it.hasNext();) {
	    ClassMethod cm = (ClassMethod) it.next();
	    cm.codeFieldInitialization(out);
	}

	out.writeByte(opc_return);

	minfo.maxStack = 10;
	minfo.maxLocals = (short) (localSlot0 + 1);
	minfo.declaredExceptions = new short[0];

	return minfo;
    }

    /*
     * =============== 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
    {
	assertTrue(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
    {
	assertTrue(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 {
	    assertTrue(false);
	}
    }

    /**
     * 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",
	    "forName", "(Ljava/lang/String;)Ljava/lang/Class;"));
    }
    /*
     * ==================== General Utility Methods ====================
     */

    /** Strips prefix and returns the name of feature or proxy
    */
    protected static String strip( String methodName, String prefix ) {
        return methodName.substring( prefix.length() );
    }

    /** Strips prefix and returns the name of feature or proxy
    */
    protected static String strip(String methodName, String prefix, String suffix) {
        return methodName.substring(prefix.length(), methodName.length() - suffix.length());
    }

    protected static String firstUpper(String text) {
        try {
            return text.substring(0, 1).toUpperCase() + text.substring(1);
        } catch (IndexOutOfBoundsException e) {
            return "";
        }
    }

    protected static String firstLower(String text) {
        try {
            return text.substring(0, 1).toLowerCase() + text.substring(1);
        } catch (IndexOutOfBoundsException e) {
            return "";
        }
    }

    /**
     * Assert that an assertion is true: throw InternalError if it is not.
     */
    protected static void assertTrue(boolean assertion) {
	if (assertion != true) {
	    throw new InternalError("assertion failure");
	}
    }

    /**
     * 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(Class[] parameterTypes,
					      Class returnType)
    {
	return getParameterDescriptors(parameterTypes) +
	    ((returnType == void.class) ? "V" : getFieldType(returnType));
    }

    /**
     * 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(Class[] parameterTypes) {
	StringBuffer desc = new StringBuffer("(");
	for (int i = 0; i < parameterTypes.length; i++) {
	    desc.append(getFieldType(parameterTypes[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(Class type) {
	if (type.isPrimitive()) {
	    return PrimitiveTypeInfo.get(type).baseTypeString;
	} else if (type.isArray()) {
	    /*
	     * According to JLS 20.3.2, the getName() method on Class does
	     * return the VM type descriptor format for array classes (only);
	     * using that should be quicker than the otherwise obvious code:
	     *
	     *     return "[" + getTypeDescriptor(type.getComponentType());
	     */
	    return type.getName().replace('.', '/');
	} else {
	    return "L" + dotToSlash(type.getName()) + ";";
	}
    }

    /**
     * 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(Class type) {
	if (type == long.class || type == double.class) {
	    return 2;
	} else {
	    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.class, new PrimitiveTypeInfo(
		"I", "java/lang/Integer", "(I)V", "intValue",     "()I"));
	    table.put(boolean.class, new PrimitiveTypeInfo(
		"Z", "java/lang/Boolean", "(Z)V", "booleanValue", "()Z"));
	    table.put(byte.class, new PrimitiveTypeInfo(
		"B", "java/lang/Byte",    "(B)V", "byteValue",    "()B"));
	    table.put(char.class, new PrimitiveTypeInfo(
		"C", "java/lang/Char",    "(C)V", "charValue",    "()C"));
	    table.put(short.class, new PrimitiveTypeInfo(
		"S", "java/lang/Short",   "(S)V", "shortValue",   "()S"));
	    table.put(long.class, new PrimitiveTypeInfo(
		"J", "java/lang/Long",    "(J)V", "longValue",    "()J"));
	    table.put(float.class, new PrimitiveTypeInfo(
		"F", "java/lang/Float",   "(F)V", "floatValue",   "()F"));
	    table.put(double.class, new PrimitiveTypeInfo(
		"D", "java/lang/Double",  "(D)V", "doubleValue",  "()D"));
	}

	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(Class cl) {
	    return (PrimitiveTypeInfo) table.get(cl);
	}
    }


    /**
     * 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));
	}

	/**
	 * 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);
		}
		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");
		}
		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);
		}
	    }
	}

	/**
	 * 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.