/* * AdaptorGen.java -- * * Generates adaptor class for the java::bind command to handle * JavaBean events. * * Copyright (c) 1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and * redistribution of this file, and for a DISCLAIMER OF ALL * WARRANTIES. * * RCS: @(#) $Id: AdaptorGen.java,v 1.1.1.1 1998/10/14 21:09:14 cvsadmin Exp $ */ package tcl.lang; import java.util.*; import java.lang.reflect.*; import java.beans.*; import java.io.*; /* * AdaptorGen is the event adaptor class generator. It can generate an * event adaptor class that implements any given JavaBean event * interface. The generated class is used to provide a callback * mechanism for JavaBeans to invoke Tcl scripts. * * The program in src/test/AdaptorGenTest.java can be used to test the * operation of the AdaptorGen class -- it saves the data generated by * the AdaptorGen class into a .class file, which can then be examined * using tools such as javap. */ class AdaptorGen { /* * Constant pool types. */ private static final int CONSTANT_Class = 7; private static final int CONSTANT_FieldRef = 9; private static final int CONSTANT_MethodRef = 10; private static final int CONSTANT_InterfaceMethodRef = 11; private static final int CONSTANT_String = 8; private static final int CONSTANT_Integer = 3; private static final int CONSTANT_Float = 4; private static final int CONSTANT_Long = 5; private static final int CONSTANT_Double = 6; private static final int CONSTANT_NameAndType = 12; private static final int CONSTANT_Utf8 = 1; /* * Java op-codes used by the adaptor class. */ private static final int ALOAD = 0x19; private static final int ALOAD_0 = 0x2a; private static final int ALOAD_1 = 0x2b; private static final int ICONST_0 = 0x3; private static final int ICONST_1 = 0x4; private static final int ANEWARRAY = 0xbd; private static final int DUP = 0x59; private static final int AASTORE = 0x53; private static final int RETURN = 0xb1; private static final int ARETURN = 0xb0; private static final int DRETURN = 0xaf; private static final int FRETURN = 0xae; private static final int IRETURN = 0xac; private static final int LRETURN = 0xad; private static final int SIPUSH = 0x11; private static final int ASTORE = 0x3a; private static final int NEW = 0xbb; private static final int ILOAD = 0x15; private static final int LLOAD = 0x16; private static final int FLOAD = 0x17; private static final int DLOAD = 0x18; private static final int INVOKESP = 0xb7; private static final int INVOKEVT = 0xb6; private static final int WIDE = 0xc4; private static final int LDC_W = 0x13; private static final int INSTNCOF = 0xc1; private static final int CHKCAST = 0xc0; private static final int IFEQ = 0x99; private static final int ATHROW = 0xbf; private static final int GOTO_W = 0xc8; /* * Access modifiers. */ private static final int ACC_PUBLIC = 0x0001; private static final int ACC_SUPER = 0x0020; /* * These are internal variable shared among the methods of this * class. We declare them as member variables so that we don't need to * pass them explicitly to all methods. */ private DataOutputStream ostream; private Class listenerCls; private Method methods[]; private String clsName; private Class superCls; /* * The number of items that have been added into the constant pool so * far. It starts at 1 because there is always an implicit item #0 * in the constant pool. */ int cpSize; /* * This Vector is used to hold temporarily the constant pool elements * when we are counting the number of elements in the constant pool. */ Vector constPool; /* * Stores all the UTF string constants that are currently in the * constant pool. We use this information to avoid having duplicate * copies of the same string in the constant pool. */ Hashtable utf8Tab; /* * The hashtable stores the Class objects of all the Object types * referenced by the adaptor class, including: * * + Object types passed in as parameters to the methods of * the adaptor class. * + Object types returned by the methods of the adaptor class. * + Wrapper Object types used to pass event parameters * to _processEvent(). * + Exception types thrown by the methods of the adaptor * class. */ Hashtable allClasses; /* * This hashtable contains all the Class objects of the primitive * types used in the adaptor class. */ Hashtable primClasses; /* * This hashtable contains all the primitive types returned by the * methods of the interface. It will also contain Object.class if * there is a method that returns an object (of any class). */ Hashtable returnTypes; /* * This hashtable contains the constant pool IDs for the _return_ * methods. */ Hashtable returnMethodRef; /* * This hashtable stores the constant pool IDs for the constructors of * the wrapper classes that are used to pass parameters of primitive * types to the _processEvent() method. */ Hashtable wrapperConsRef; /* * This hashtable contains the constant pool IDs for all the classes * referenced by the adaptor class. */ Hashtable clsRef; /* * This hashtable contains the constant pool IDs for all the strings * referenced by the adaptor class. */ Hashtable stringRef; /* * The constant pool ID of the adaptor class. */ short cp_this_class; /* * The constant pool ID of the super class of the adaptor class * (tcl.lang.EventAdaptor). */ short cp_super_class; /* * The constant pool ID of the event interface that the adaptor class * implements. */ short cp_listener_interface; /* * The constant pool ID of the "Code" string, which is used to * identify a section of executable code in the class file. */ short cp_code; /* * The constant pool ID of the constructor of the super class. */ short cp_super_cons; /* * The constant pool ID of the _processEvent() method in the super * class. */ short cp_processEvent; /* * The constant pool ID of the _wrongException() method in the super * class. */ short cp_wrongException; /* * Stores information about each method in the adaptor class. * cp_methodDesc[i] contains info about method[i]. */ MethodDesc cp_methodDesc[]; /* * Store information about the constructor of the adaptor class. */ MethodDesc cp_consDesc; /* *---------------------------------------------------------------------- * * generate -- * * Generate the byte code of an adaptor class that implements the * event interface of the given event. * * Results: * A byte array that contains the byte code of the adaptor class. * * Side effects: * None. * *---------------------------------------------------------------------- */ byte[] generate( EventSetDescriptor desc, Class superClass, String className) { /* * Copy these arguments into member variables so that they don't need * to be passed into the internal methods called by generateByteCode(). */ superCls = superClass; clsName = className; listenerCls = desc.getListenerType(); methods = listenerCls.getMethods(); /* * Initialize other member variables used to generate the byte code. * These variables must be re-initialize each time a new class is to * be generated. */ allClasses = new Hashtable(); primClasses = new Hashtable(); returnTypes = new Hashtable(); returnMethodRef = new Hashtable(); wrapperConsRef = new Hashtable(); clsRef = new Hashtable(); stringRef = new Hashtable(); utf8Tab = new Hashtable(); cp_methodDesc = new MethodDesc[methods.length]; analyzeListener(); cpSize = 1; /* * Generate the data of the adaptor class that implements the * event interface given by desc. */ try { /* * Prepare the output streams. */ ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); ostream = dos; /* * Generate the data. */ generateByteCode(); return baos.toByteArray(); } catch (IOException e) { throw new TclRuntimeError("Unexcepted IOException " + e); } } /* *---------------------------------------------------------------------- * * analizeListener -- * * Find out information about the listener class: * * - How many method are there. * - What exceptions (if any) are thrown by the methods. * - The argument types of the methods * - The return types of the methods. * * From this information, we can determine what to put in the constant * pools: * * - All the classes referred to by the arguments, checked exception * and return types. * - All the primitive wrapper classes needed to pass primitive * arguments to the super.processEvent() * - All the super.return methods needed by the return types * of the methods. * * One reason for obtaining the information is to reduce the size * of the constant pool. E.g., if we know that none of the methods * returns int values, then we don't need to put the method * _return_int() into the constant pool. * * Results: * None. * * Side effects: * Infomation about the listener class are recorded in various member * variables. * *---------------------------------------------------------------------- */ private void analyzeListener() { int i, j; boolean paramsDefined = false; for (i = 0; i < methods.length; i++) { Class params[] = methods[i].getParameterTypes(); /* * Record all classes (including wrapper classes) that will * be used to pass parameters to _processEvent(). */ for (j = 0; j < params.length; j++) { if (params[j].isPrimitive()) { if (params[j] == Void.TYPE) { /* * Looks likes the JVM has loaded a bad interface class: * one of the parameter of an interface is of type void. */ throw new ClassFormatError( "Parameter type cannot be void"); } Class wrapper = getWrapperClass(params[j]); allClasses.put(wrapper, wrapper); primClasses.put(params[j], params[j]); } else { allClasses.put(params[j], params[j]); } paramsDefined = true; } /* * Record all exceptions thrown by the methods. */ Class exceptions[] = methods[i].getExceptionTypes(); for (j = 0; j < exceptions.length; j++) { allClasses.put(exceptions[j], exceptions[j]); } /* * Record information about the return types of the methods. */ Class retType = methods[i].getReturnType(); if (retType != Void.TYPE) { if (!retType.isPrimitive()) { allClasses.put(retType, retType); } returnTypes.put(retType, retType); } } if (paramsDefined) { allClasses.put(Object.class, Object.class); } allClasses.put(Throwable.class, Throwable.class); } /* *---------------------------------------------------------------------- * * generateByteCode -- * * Writes out the byte code into the ostream. * * Results: * * None. * * Side effects: * Byte code is written into the ostream. * *---------------------------------------------------------------------- */ private void generateByteCode() throws IOException // This exception may never happen. We // declare it just to avoid putting // catch statements everywhere. { /* * u4 magic. * u2 minor_version * u2 major_version */ ostream.writeInt(0xCAFEBABE); ostream.writeShort(3); ostream.writeShort(45); /* * u2 constant_pool_count * cp_info constant_pool[constant_pool_count-1] */ generateConstantPool(); /* * u2 access_flags * u2 this_class * u2 super_class */ ostream.writeShort(ACC_SUPER|ACC_PUBLIC); ostream.writeShort(cp_this_class); ostream.writeShort(cp_super_class); /* * u2 interfaces_count * u2 interfaces[interfaces_count] */ ostream.writeShort(1); ostream.writeShort(cp_listener_interface); /* * u2 fields_count * u2 field_info fields[fields_count] */ ostream.writeShort(0); /* * u2 methods_count * u2 method_info methods[methods_count] */ ostream.writeShort(1 + methods.length); generateConstructor(); for (int i=0; i", "()V"); cp_processEvent = cp_putMethodRef(cp_super_class, "_processEvent", "([Ljava/lang/Object;Ljava/lang/String;)V"); cp_wrongException = cp_putMethodRef(cp_super_class, "_wrongException", "()V"); for (Enumeration e = returnTypes.keys(); e.hasMoreElements(); ) { Class retType = (Class)e.nextElement(); short ref; if (retType.isPrimitive()) { ref = cp_putMethodRef(cp_super_class, "_return_" + retType.getName(), "()" + getTypeDesc(retType)); } else { cp_putString(retType.getName()); ref = cp_putMethodRef(cp_super_class, "_return_Object", "(Ljava/lang/String;)" + getTypeDesc(retType)); } hashPutShort(returnMethodRef, retType, ref); } /* * The constructor and methods that are defined in the generated * class. */ cp_consDesc = cp_putMethodDesc("", "()V", false); for (int i = 0; i < methods.length; i++) { cp_methodDesc[i] = cp_putMethodDesc(methods[i].getName(), getMethodDescriptor(methods[i]), true); } /* * All the classes referred to by the generated class. */ for (Enumeration e = allClasses.keys(); e.hasMoreElements(); ) { Class type = (Class)e.nextElement(); short ref = cp_putClass(type.getName()); hashPutShort(clsRef, type, ref); } /* * If the methods in the generated class receives parameter of * primitive types, they must be wrapped in wrapper classes such * as java.lang.Integer before they are passed to * super._processEvent(). */ for (Enumeration e = primClasses.keys(); e.hasMoreElements(); ) { /* * **** KLUDGE **** * * This loop works around a compiler bug in JAVAC 1.1.4. For * For some reasons, if this loop is not here, AdaptorGen.class will * contain incorrect byte code and causes a NullPoniterException. * * This compiler bug happens only in JAVAC. MS JVC apparently * works fine. */ e.nextElement(); } for (Enumeration e = primClasses.keys(); e.hasMoreElements(); ) { Class primType = (Class)e.nextElement(); short class_index = cp_getClass(getWrapperClass(primType)); short ref = ref = cp_putMethodRef(class_index, "", "(" + getTypeDesc(primType) + ")V"); hashPutShort(wrapperConsRef, primType, ref); } /* * Now we know the count. Let's write into the byte array. */ ostream.writeShort(constPool.size() + 1); for (int i=0; i() ostream.writeShort(cp_super_cons); ostream.writeByte(RETURN); // return ostream.writeShort(0); // exception_table_length ostream.writeShort(0); // attribute_count } /* *---------------------------------------------------------------------- * * generateMethod -- * * Generates an event handling method. This procedure is more * complex because the method can reveice any parameters, throw * any exceptions and return any value. Here is a canonical form * of the kind of method generated: * * public int someEvent(int p0, double p1, byte p2, Object p3) * throws Exception1, Exception2 * { * Object params[] = new Object[4]; * params[0] = new Integer(p0); * params[1] = new Double(p1); * params[2] = new Byte(p2); * params[3] = p3; * * try { * _processEvent(params, "someEvent"); * } catch (Throwable exception) { * if (exception instanceof Exception1) { * throw (Exception1)exception; * } else if (exception instanceof Exception2) { * throw (Exception2)exception; * } else { * _wrongException(); * } * } * * return _return_int(); * } * * * If the return type is any Object type, the final statement * will be modified as in the following: * * public xyz.pkg.FooBar someEvent(...) * { * .... * return (FooBar)_return_Object("xyz.pkg.FooBar"); * } * * The actual work of converting interp.getResult() to the * appropriate return type is done in the _return_ methods * in the EventAdaptor class. * * Results: * None. * * Side effects: * The byte code of the method is written into the ostream. * *---------------------------------------------------------------------- */ void generateMethod(int methodIdx) // Generate the method described by // methods[methodIdx]. throws IOException { int max_stacks; int max_locals; int paramVarIdx; // index of the "param" variable. int exceptionVarIdx; // index of the "exception" variable. int exStartPC, exEndPC; // Exception start and end PC. int exHandlerPC = 0; // Exception handler PC. /* * Calculate the max_stacks and max_locals variables for the * method. */ max_stacks = 6; Class paramTypes[] = methods[methodIdx].getParameterTypes(); int numParams = paramTypes.length; max_locals = 1; // "this" pointer for (int i = 0; i < numParams; i++) { if ((paramTypes[i] == Double.TYPE) || (paramTypes[i] == Long.TYPE)) { max_locals += 2; } else { max_locals += 1; } } max_locals += 2; // param[], exception. paramVarIdx = max_locals-2; exceptionVarIdx = max_locals-1; ostream.writeShort(ACC_PUBLIC); // access = "public" ostream.writeShort(cp_methodDesc[methodIdx].name_index); ostream.writeShort(cp_methodDesc[methodIdx].descriptor_index); ostream.writeShort(1); // attr count /* * Generate the body of the code. */ ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream code = new DataOutputStream(baos); /* * [1] Create an array for passing parameters to super.processEvent(): * * Object params[] = new Object[numParams]; * * NOTE: * * - In all the generated code, it is sufficient to represent * a local variable using a short because a Java method may * have no more than 65535 local variables (including paramaters). * * - This means the side of the params array is no more than 65535, * so we can specify its size using sipush. */ code.writeByte(SIPUSH); // sipush # code.writeShort(numParams); code.writeByte(ANEWARRAY); // anewarray Object code.writeShort(cp_getClass(Object.class)); writeLoadStore(code, ASTORE, // astore param[] paramVarIdx); /* * [2] Copy the parameters into an object array: * * params[0] = new Integer(p0); * params[1] = new Double(p0); * params[2] = (Object)p2; * // .... etc */ /* * We start at local variable index 1, which is the first parameter. * (index 0 is the "this" pointer). */ int paramIdx = 1; for (int i = 0; i < numParams; i++) { writeLoadStore(code, ALOAD, // aload param[] paramVarIdx); code.writeByte(SIPUSH); // sipush # code.writeShort(i); if (paramTypes[i].isPrimitive()) { Class prim = paramTypes[i]; Class wrapper = getWrapperClass(paramTypes[i]); int loadOpcode, numWords; if (prim == Double.TYPE) { loadOpcode = DLOAD; numWords = 2; } else if (prim == Float.TYPE) { loadOpcode = FLOAD; numWords = 1; } else if (prim == Long.TYPE) { loadOpcode = LLOAD; numWords = 2; } else { loadOpcode = ILOAD; numWords = 1; } code.writeByte(NEW); // new code.writeShort(cp_getClass(wrapper)); code.writeByte(DUP); // dup writeLoadStore(code, // load p_ loadOpcode, paramIdx); code.writeByte(INVOKESP); // invokespecial (); code.writeShort(cp_getWrapperConstructor(prim)); paramIdx += numWords; } else { writeLoadStore(code, ALOAD, // aload p_ paramIdx); paramIdx += 1; } code.writeByte(AASTORE); // aastore } /* * [3] Call super.processEvent(): * * try { * super.processEvent(params, ); * } */ exStartPC = code.size(); code.writeByte(ALOAD_0); // aload_0 this writeLoadStore(code, ALOAD, // aload param[] paramVarIdx); code.writeByte(LDC_W); // ldc_w code.writeShort(cp_getString(methods[methodIdx].getName())); code.writeByte(INVOKEVT); // invokevirtual processEvent() code.writeShort(cp_processEvent); exEndPC = code.size(); /* * [4] Handle any exceptions thrown by processEvent(): * * catch (Throwable exception) { * .... * } * * Note, we use WIDE version of load/store in all subsequent * byte codes so that it's easy to calculate the offset * for jumping to the "normal" return statement.) */ Class exceptions[] = methods[methodIdx].getExceptionTypes(); int offset = 5 + 4 + exceptions.length * 18 + 4 ; code.writeByte(GOTO_W); // goto_w # code.writeInt(offset); exHandlerPC = code.size(); code.writeByte(WIDE); // astore exception code.writeByte(ASTORE); code.writeShort(exceptionVarIdx); for (int i = 0; i < exceptions.length; i++) { /* * Write the exception handler for each of the checked exception * types. Each handler is 16 bytes long. */ code.writeByte(WIDE); // aload exception code.writeByte(ALOAD); code.writeShort(exceptionVarIdx); code.writeByte(INSTNCOF); // instanceof code.writeShort(cp_getClass(exceptions[i])); code.writeByte(IFEQ); // ifeq # code.writeShort(11); code.writeByte(WIDE); // aload exception code.writeByte(ALOAD); code.writeShort(exceptionVarIdx); code.writeByte(CHKCAST); // checkcast code.writeShort(cp_getClass(exceptions[i])); code.writeByte(ATHROW); // athrow } code.writeByte(ALOAD_0); // aload_0 this code.writeByte(INVOKEVT); // invokevirtual _wrongExceptionError() code.writeShort(cp_wrongException); /* * [5] Normal return from this method. */ Class retType = methods[methodIdx].getReturnType(); if (retType == Void.TYPE) { code.writeByte(RETURN); } else if (retType.isPrimitive()) { code.writeByte(ALOAD_0); // aload_0 this code.writeByte(INVOKEVT); // invokevirtual return_ code.writeShort(cp_getReturnMethodRef( retType)); if (retType == Double.TYPE) { code.writeByte(DRETURN); // dreturn } else if (retType == Float.TYPE) { code.writeByte(FRETURN); // freturn } else if (retType == Long.TYPE) { code.writeByte(LRETURN); // lreturn } else { /* * IRETURN is used for boolean, * byte, char, int and short. */ code.writeByte(IRETURN); // ireturn } } else { code.writeByte(ALOAD_0); // aload_0 this code.writeByte(LDC_W); // ldc_w code.writeShort(cp_getString(retType.getName())); code.writeByte(INVOKEVT); // invokevirtual return_ code.writeShort(cp_getReturnMethodRef( retType)); code.writeByte(CHKCAST); // checkcast code.writeShort(cp_getClass(retType)); code.writeByte(ARETURN); // areturn } int codeLength = code.size(); /* * [6] Write the exception table: we catch all Throwable * classes. */ code.writeShort(1); // exception_table_length code.writeShort(exStartPC); // start_pc code.writeShort(exEndPC); // end_pc code.writeShort(exHandlerPC); // handler_pc code.writeShort( // catch_type cp_getClass(Throwable.class)); /* * [7] The attributes table (empty) */ code.writeShort(0); // attribute_count /* * [8] Now we are done. Emit the code section into the output * stream. */ code.close(); byte codeBytes[] = baos.toByteArray(); ostream.writeShort(cp_code); // attr_name_index = "Code" ostream.writeInt(codeBytes.length // attr_length + 8); ostream.writeShort(max_stacks); ostream.writeShort(max_locals); ostream.writeInt(codeLength); // code_length ostream.write(codeBytes); } /* *---------------------------------------------------------------------- * * internalClassName -- * * Returns the "internal" class name of a Java class: E.g. the * internal name for "java.lang.Integer" is "java/lang/Integer". * * Results: * The "internal" class name. * * Side effects: * None. * *---------------------------------------------------------------------- */ private final static String internalClassName( String className) // "Normal" name of the class. { return className.replace('.', '/'); } /* *---------------------------------------------------------------------- * * hashPutShort -- * * Puts a short value into a hash table. * * Results: * None. * * Side effects: * The short value is wrapped in a Short object and stored in the * hashtable. * *---------------------------------------------------------------------- */ private final static void hashPutShort( Hashtable hashtable, // The hashtable. Object key, // The key. short num) // Put this number under the given key // in the hashtable. { Short shortObj = new Short(num); hashtable.put(key, shortObj); } /* *---------------------------------------------------------------------- * * hashGetShort -- * * Gets the short value corresponding to the key from the hash * table. * * Results: * The short value corresponding to the key. * * Side effects: * None. * *---------------------------------------------------------------------- */ private final static short hashGetShort( Hashtable hashtable, // The hashtable. Object key) // The key. { return ((Short)hashtable.get(key)).shortValue(); } /* *---------------------------------------------------------------------- * * getWrapperClass -- * * Given a primitive type (e.g. int), returns its wrapper class * (e.g., java.lang.Integer). * * Results: * The wrapper class for the primitive type. * * Side effects: * None. * *---------------------------------------------------------------------- */ private final static Class getWrapperClass( Class primType) // The Class object that represents the // primitive type. { if (primType == Boolean.TYPE) { return Boolean.class; } else if (primType == Byte.TYPE) { return Byte.class; } else if (primType == Character.TYPE) { return Character.class; } else if (primType == Double.TYPE) { return Double.class; } else if (primType == Float.TYPE) { return Float.class; } else if (primType == Integer.TYPE) { return Integer.class; } else if (primType == Long.TYPE) { return Long.class; } else { return Short.class; } } /* *---------------------------------------------------------------------- * * getTypeDesc -- * * Returns the string that represents a Java type. E.g, "Z" for * boolean, "Lfoo.Bar;" for foo.Bar. * * Results: * The string that represents a Java type. * * Side effects: * None. * *---------------------------------------------------------------------- */ private final static String getTypeDesc(Class cls) { if (cls.isPrimitive()) { if (cls == Boolean.TYPE) { return "Z"; } else if (cls == Byte.TYPE) { return "B"; } else if (cls == Character.TYPE) { return "C"; } else if (cls == Double.TYPE) { return "D"; } else if (cls == Float.TYPE) { return "F"; } else if (cls == Integer.TYPE) { return "I"; } else if (cls == Long.TYPE) { return "J"; } else if (cls == Short.TYPE) { return "S"; } else { return "V"; } } else { if (cls.isArray()) { return "[" + getTypeDesc(cls.getComponentType()); } else { String s = "L" + cls.getName() + ";"; return s.replace('.', '/'); } } } /* *---------------------------------------------------------------------- * * getMethodDesc -- * * Returns the string that represents the type of a method. E.g. * "(Lfoo.Bar;DI)V" for void xxx(foo.Bar,double,int) * * Results: * The string that represents the type of a method. * * Side effects: * None. * *---------------------------------------------------------------------- */ private final static String getMethodDescriptor( Method method) // Returns the desc of this method. { StringBuffer sbuf = new StringBuffer(); sbuf.append('('); Class params[] = method.getParameterTypes(); for (int i = 0; i < params.length; i++) { sbuf.append(getTypeDesc(params[i])); } sbuf.append(')'); sbuf.append(getTypeDesc(method.getReturnType())); return sbuf.toString(); } /* *---------------------------------------------------------------------- * * writeLoadStore -- * * Writes the byte-code for a load or store operation. To reduce * the size of the byte code, the WIDE prefix is used only when * necessary (i.e., the address is greater than 255) * * Results: * None. * * Side effects: * the load/store byte codes are written into the * DataOutputStream. * *---------------------------------------------------------------------- */ void writeLoadStore( DataOutputStream code, // The DataOutputStream to write the byte-code // into. int opcode, // The opcode can be ?LOAD or ?STORE int address) // The target address of the load/store. throws IOException // This exception may never happen. We // declare it just to avoid putting // catch statements everywhere. { if (address > 255) { code.writeByte(WIDE); code.writeByte(opcode); code.writeShort(address); } else { code.writeByte(opcode); code.writeByte(address); } } /* *---------------------------------------------------------------------- * * cp_putUtf8 -- * * Puts a UTF8 string into the constant pool. * * Results: * The index of that UTF8 string in the constant pool. * * Side effects: * The UTF8 string is put into the constPool vector if the same * string is not already in the constant pool. * *---------------------------------------------------------------------- */ short cp_putUtf8( String string) // The string to put (in UTF8 format) into // the constant pool. { Short shortObj; shortObj = (Short)utf8Tab.get(string); /* * Check to make sure that the string is not already in the * constant pool so that we won't have duplicated entries (which * wastes space!). */ if (shortObj != null) { return shortObj.shortValue(); } else { ConstUtf cutf = new ConstUtf(); cutf.string = string; constPool.addElement(cutf); short id = (short)cpSize++; hashPutShort(utf8Tab, string, id); return id; } } /* *---------------------------------------------------------------------- * * cp_putString -- * * Puts a string into the constant pool. N.B., this is stored as * a CONSTANT_String element. In contrast, UTF8 strings are * stored as CONSTANT_Utf8. Read "The Java Virtual Machine * Specification" for details. * * Results: * The index of the string in the constant pool. * * Side effects: * The string is put into the constPool vector. * *---------------------------------------------------------------------- */ private short cp_putString( String string) // The string to put (in CONSTANT_String // format) into the constant pool. { ConstString cstr = new ConstString(); cstr.string_index = cp_putUtf8(string); constPool.addElement(cstr); short id = (short)cpSize++; hashPutShort(stringRef, string, id); return id; } /* *---------------------------------------------------------------------- * * cp_putClass -- * * Puts a CONSTANT_Class element into the constant pool. * * Results: * The index of the class. * * Side effects: * The class is put into the constPool vector. * *---------------------------------------------------------------------- */ private short cp_putClass( String className) // Fully qualified name of the class. { ConstClass ccls = new ConstClass(); ccls.name_index = cp_putUtf8(internalClassName(className)); constPool.addElement(ccls); return (short)cpSize++; } /* *---------------------------------------------------------------------- * * cp_putNameAndType -- * * Puts a CONSTANT_NameAndType element into the constant pool. * * Results: * The index of the NameAndType. * * Side effects: * The NameAndType is put into the constPool vector. * *---------------------------------------------------------------------- */ short cp_putNameAndType( String name, // The name of the method. String type) // The type of the method. { ConstNameAndType cnat = new ConstNameAndType(); cnat.name_index = cp_putUtf8(name); cnat.desc_index = cp_putUtf8(type); constPool.addElement(cnat); return (short)cpSize++; } /* *---------------------------------------------------------------------- * * cp_putMethodRef -- * * Puts a CONSTANT_MethodRef element into the constant pool. * * Results: * The index of the MethodRef. * * Side effects: * The MethodRef is put into the constPool vector. * *---------------------------------------------------------------------- */ short cp_putMethodRef( short class_index, // Index of the class. String name, // Name of the method. String desc) // Descriptor of the method. { ConstMethodRef cmref = new ConstMethodRef(); cmref.class_index = class_index; cmref.name_and_type_index = cp_putNameAndType(name, desc); constPool.addElement(cmref); return (short)cpSize++; } /* *---------------------------------------------------------------------- * * cp_putMethodDesc -- * * Puts the UTF8 strings needed to describe a method defined in * the generated class. * * Results: * A MethodDesc object that contains the index of the name and * descriptor of the method. * * Side effects: * UTF8 strings may be put into the constPool vector. * *---------------------------------------------------------------------- */ MethodDesc cp_putMethodDesc( String name, // Name of the method. String descriptor, // Descriptor of the method. boolean generateID) // True if we need to generate a string ID // to pass to _processEvent() for this method. { MethodDesc desc = new MethodDesc(); desc.name_index = cp_putUtf8(name); desc.descriptor_index = cp_putUtf8(descriptor); if (generateID) { cp_putString(name); } return desc; } /* *---------------------------------------------------------------------- * * cp_getClass -- * * Returns the index of a class definition in the constant pool * * Results: * The index of the class definition. * * Side effects: * None. * *---------------------------------------------------------------------- */ short cp_getClass( Class cls) // Query the index of this class. { return hashGetShort(clsRef, cls); } /* *---------------------------------------------------------------------- * * cp_getString -- * * Returns the index of a CONSTANT_String definition in the * constant pool * * Results: * The index of the string. * * Side effects: * None. * *---------------------------------------------------------------------- */ short cp_getString( String string) // Query the index of this string. { return hashGetShort(stringRef, string); } /* *---------------------------------------------------------------------- * * cp_getWrapperConstructor -- * * Returns the constant pool index of a CONSTANT_MethodRef * definition for the constructor of the primitive wrapper class. * * Results: * The index of the constructor. * * Side effects: * None. * *---------------------------------------------------------------------- */ short cp_getWrapperConstructor( Class primType) // Query the index of the constructor // of the wrapper class of this primitive type. { return hashGetShort(wrapperConsRef, primType); } /* *---------------------------------------------------------------------- * * cp_getReturnMethodRef -- * * Returns the constant pool index of a CONSTANT_MethodRef * definition of methods such super._return_int(); these methods * are used to return values from the binding script. * * Results: * The index of the method. * * Side effects: * None. * *---------------------------------------------------------------------- */ short cp_getReturnMethodRef( Class retType) // Query the index of the method that // returns this type. { if (retType.isPrimitive()) { return hashGetShort(returnMethodRef, retType); } else { return hashGetShort(returnMethodRef, Object.class); } } /* * The following five inner classes are used to store temporary copies * of constane pool items in a Vector. */ class ConstUtf { String string; // The string to put into the constant pool // in UTF8 format. } class ConstString { short string_index; // Index of the CONSTANR_Utf8 element that // defines the string. } class ConstClass { short name_index; // Index of the CONSTANR_Utf8 element that // defines the internal name of the class. } class ConstNameAndType { short name_index; // Index of the CONSTANR_Utf8 element that // defines the name of a method. short desc_index; // Index of the CONSTANR_Utf8 element that // defines the type of a method. } class ConstMethodRef { short class_index; // Index of the CONSTANR_Utf8 element that // defines the internal name of the class. short name_and_type_index; // Index of the CONSTANR_NameAndType element // that defines the name and type of the // method. } /* * This inner class stores the name and descriptor of the method to * generate. */ class MethodDesc { /* * Index to the name of the method (a CONSTANT_String). */ short name_index; /* * Index to the name of the method (a CONSTANT_String). */ short descriptor_index; } // end AdaptorGen.MethodDesc } // end AdaptorGen