0001: /*
0002: * AdaptorGen.java --
0003: *
0004: * Generates adaptor class for the java::bind command to handle
0005: * JavaBean events.
0006: *
0007: * Copyright (c) 1997 Sun Microsystems, Inc.
0008: *
0009: * See the file "license.terms" for information on usage and
0010: * redistribution of this file, and for a DISCLAIMER OF ALL
0011: * WARRANTIES.
0012: *
0013: * RCS: @(#) $Id: AdaptorGen.java,v 1.3 2000/01/25 03:42:26 mo Exp $
0014: */
0015:
0016: package tcl.lang;
0017:
0018: import java.util.*;
0019: import java.lang.reflect.*;
0020: import java.beans.*;
0021: import java.io.*;
0022:
0023: /**
0024: * AdaptorGen is the event adaptor class generator. It can generate an
0025: * event adaptor class that implements any given JavaBean event
0026: * interface. The generated class is used to provide a callback
0027: * mechanism for JavaBeans to invoke Tcl scripts.
0028: *
0029: * The program in src/test/AdaptorGenTest.java can be used to test the
0030: * operation of the AdaptorGen class -- it saves the data generated by
0031: * the AdaptorGen class into a .class file, which can then be examined
0032: * using tools such as javap.
0033: */
0034:
0035: class AdaptorGen {
0036:
0037: // Constant pool types.
0038:
0039: private static final int CONSTANT_Class = 7;
0040: private static final int CONSTANT_FieldRef = 9;
0041: private static final int CONSTANT_MethodRef = 10;
0042: private static final int CONSTANT_InterfaceMethodRef = 11;
0043: private static final int CONSTANT_String = 8;
0044: private static final int CONSTANT_Integer = 3;
0045: private static final int CONSTANT_Float = 4;
0046: private static final int CONSTANT_Long = 5;
0047: private static final int CONSTANT_Double = 6;
0048: private static final int CONSTANT_NameAndType = 12;
0049: private static final int CONSTANT_Utf8 = 1;
0050:
0051: // Java op-codes used by the adaptor class.
0052:
0053: private static final int ALOAD = 0x19;
0054: private static final int ALOAD_0 = 0x2a;
0055: private static final int ALOAD_1 = 0x2b;
0056: private static final int ICONST_0 = 0x3;
0057: private static final int ICONST_1 = 0x4;
0058: private static final int ANEWARRAY = 0xbd;
0059: private static final int DUP = 0x59;
0060: private static final int AASTORE = 0x53;
0061: private static final int RETURN = 0xb1;
0062: private static final int ARETURN = 0xb0;
0063: private static final int DRETURN = 0xaf;
0064: private static final int FRETURN = 0xae;
0065: private static final int IRETURN = 0xac;
0066: private static final int LRETURN = 0xad;
0067: private static final int SIPUSH = 0x11;
0068: private static final int ASTORE = 0x3a;
0069: private static final int NEW = 0xbb;
0070: private static final int ILOAD = 0x15;
0071: private static final int LLOAD = 0x16;
0072: private static final int FLOAD = 0x17;
0073: private static final int DLOAD = 0x18;
0074: private static final int INVOKESP = 0xb7;
0075: private static final int INVOKEVT = 0xb6;
0076: private static final int WIDE = 0xc4;
0077: private static final int LDC_W = 0x13;
0078: private static final int INSTNCOF = 0xc1;
0079: private static final int CHKCAST = 0xc0;
0080: private static final int IFEQ = 0x99;
0081: private static final int ATHROW = 0xbf;
0082: private static final int GOTO_W = 0xc8;
0083:
0084: // Access modifiers.
0085:
0086: private static final int ACC_PUBLIC = 0x0001;
0087: private static final int ACC_SUPER = 0x0020;
0088:
0089: // These are internal variable shared among the methods of this
0090: // class. We declare them as member variables so that we don't need to
0091: // pass them explicitly to all methods.
0092:
0093: private DataOutputStream ostream;
0094: private Class listenerCls;
0095: private Method methods[];
0096: private String clsName;
0097: private Class super Cls;
0098:
0099: // The number of items that have been added into the constant pool so
0100: // far. It starts at 1 because there is always an implicit item #0
0101: // in the constant pool.
0102:
0103: int cpSize;
0104:
0105: // This Vector is used to hold temporarily the constant pool elements
0106: // when we are counting the number of elements in the constant pool.
0107:
0108: Vector constPool;
0109:
0110: // Stores all the UTF string constants that are currently in the
0111: // constant pool. We use this information to avoid having duplicate
0112: // copies of the same string in the constant pool.
0113:
0114: Hashtable utf8Tab;
0115:
0116: // The hashtable stores the Class objects of all the Object types
0117: // referenced by the adaptor class, including:
0118: //
0119: // + Object types passed in as parameters to the methods of
0120: // the adaptor class.
0121: // + Object types returned by the methods of the adaptor class.
0122: // + Wrapper Object types used to pass event parameters
0123: // to _processEvent().
0124: // + Exception types thrown by the methods of the adaptor
0125: // class.
0126:
0127: Hashtable allClasses;
0128:
0129: // This hashtable contains all the Class objects of the primitive
0130: // types used in the adaptor class.
0131:
0132: Hashtable primClasses;
0133:
0134: // This hashtable contains all the primitive types returned by the
0135: // methods of the interface. It will also contain Object.class if
0136: // there is a method that returns an object (of any class).
0137:
0138: Hashtable returnTypes;
0139:
0140: // This hashtable contains the constant pool IDs for the _return_<type>
0141: // methods.
0142:
0143: Hashtable returnMethodRef;
0144:
0145: // This hashtable stores the constant pool IDs for the constructors of
0146: // the wrapper classes that are used to pass parameters of primitive
0147: // types to the _processEvent() method.
0148:
0149: Hashtable wrapperConsRef;
0150:
0151: // This hashtable contains the constant pool IDs for all the classes
0152: // referenced by the adaptor class.
0153:
0154: Hashtable clsRef;
0155:
0156: // This hashtable contains the constant pool IDs for all the strings
0157: // referenced by the adaptor class.
0158:
0159: Hashtable stringRef;
0160:
0161: // The constant pool ID of the adaptor class.
0162:
0163: short cp_this _class;
0164:
0165: // The constant pool ID of the super class of the adaptor class
0166: // (tcl.lang.EventAdaptor).
0167:
0168: short cp_super _class;
0169:
0170: // The constant pool ID of the event interface that the adaptor class
0171: // implements.
0172:
0173: short cp_listener_interface;
0174:
0175: // The constant pool ID of the "Code" string, which is used to
0176: // identify a section of executable code in the class file.
0177:
0178: short cp_code;
0179:
0180: // The constant pool ID of the constructor of the super class.
0181:
0182: short cp_super _cons;
0183:
0184: // The constant pool ID of the _processEvent() method in the super
0185: // class.
0186:
0187: short cp_processEvent;
0188:
0189: // The constant pool ID of the _wrongException() method in the super
0190: // class.
0191:
0192: short cp_wrongException;
0193:
0194: // Stores information about each method in the adaptor class.
0195: // cp_methodDesc[i] contains info about method[i].
0196:
0197: MethodDesc cp_methodDesc[];
0198:
0199: // Store information about the constructor of the adaptor class.
0200:
0201: MethodDesc cp_consDesc;
0202:
0203: /*
0204: *----------------------------------------------------------------------
0205: *
0206: * generate --
0207: *
0208: * Generate the byte code of an adaptor class that implements the
0209: * event interface of the given event.
0210: *
0211: * Results:
0212: * A byte array that contains the byte code of the adaptor class.
0213: *
0214: * Side effects:
0215: * None.
0216: *
0217: *----------------------------------------------------------------------
0218: */
0219:
0220: byte[] generate(EventSetDescriptor desc, Class super Class,
0221: String className) {
0222: // Copy these arguments into member variables so that they don't need
0223: // to be passed into the internal methods called by generateByteCode().
0224:
0225: super Cls = super Class;
0226: clsName = className;
0227: listenerCls = desc.getListenerType();
0228: methods = listenerCls.getMethods();
0229:
0230: // Initialize other member variables used to generate the byte code.
0231: // These variables must be re-initialize each time a new class is to
0232: // be generated.
0233:
0234: allClasses = new Hashtable();
0235: primClasses = new Hashtable();
0236: returnTypes = new Hashtable();
0237:
0238: returnMethodRef = new Hashtable();
0239: wrapperConsRef = new Hashtable();
0240: clsRef = new Hashtable();
0241: stringRef = new Hashtable();
0242: utf8Tab = new Hashtable();
0243: cp_methodDesc = new MethodDesc[methods.length];
0244: analyzeListener();
0245: cpSize = 1;
0246:
0247: // Generate the data of the adaptor class that implements the
0248: // event interface given by desc.
0249:
0250: try {
0251: // Prepare the output streams.
0252:
0253: ByteArrayOutputStream baos = new ByteArrayOutputStream();
0254: DataOutputStream dos = new DataOutputStream(baos);
0255: ostream = dos;
0256:
0257: // Generate the data.
0258:
0259: generateByteCode();
0260:
0261: return baos.toByteArray();
0262:
0263: } catch (IOException e) {
0264: throw new TclRuntimeError("Unexcepted IOException " + e);
0265: }
0266: }
0267:
0268: /*
0269: *----------------------------------------------------------------------
0270: *
0271: * analizeListener --
0272: *
0273: * Find out information about the listener class:
0274: *
0275: * - How many method are there.
0276: * - What exceptions (if any) are thrown by the methods.
0277: * - The argument types of the methods
0278: * - The return types of the methods.
0279: *
0280: * From this information, we can determine what to put in the constant
0281: * pools:
0282: *
0283: * - All the classes referred to by the arguments, checked exception
0284: * and return types.
0285: * - All the primitive wrapper classes needed to pass primitive
0286: * arguments to the super.processEvent()
0287: * - All the super.return<type> methods needed by the return types
0288: * of the methods.
0289: *
0290: * One reason for obtaining the information is to reduce the size
0291: * of the constant pool. E.g., if we know that none of the methods
0292: * returns int values, then we don't need to put the method
0293: * _return_int() into the constant pool.
0294: *
0295: * Results:
0296: * None.
0297: *
0298: * Side effects:
0299: * Infomation about the listener class are recorded in various member
0300: * variables.
0301: *
0302: *----------------------------------------------------------------------
0303: */
0304:
0305: private void analyzeListener() {
0306: int i, j;
0307: boolean paramsDefined = false;
0308:
0309: for (i = 0; i < methods.length; i++) {
0310: Class params[] = methods[i].getParameterTypes();
0311:
0312: // Record all classes (including wrapper classes) that will
0313: // be used to pass parameters to _processEvent().
0314:
0315: for (j = 0; j < params.length; j++) {
0316: if (params[j].isPrimitive()) {
0317: if (params[j] == Void.TYPE) {
0318: // Looks likes the JVM has loaded a bad interface class:
0319: // one of the parameter of an interface is of type void.
0320:
0321: throw new ClassFormatError(
0322: "Parameter type cannot be void");
0323: }
0324: Class wrapper = getWrapperClass(params[j]);
0325: allClasses.put(wrapper, wrapper);
0326: primClasses.put(params[j], params[j]);
0327: } else {
0328: allClasses.put(params[j], params[j]);
0329: }
0330: paramsDefined = true;
0331: }
0332:
0333: // Record all exceptions thrown by the methods.
0334:
0335: Class exceptions[] = methods[i].getExceptionTypes();
0336: for (j = 0; j < exceptions.length; j++) {
0337: allClasses.put(exceptions[j], exceptions[j]);
0338: }
0339:
0340: // Record information about the return types of the methods.
0341:
0342: Class retType = methods[i].getReturnType();
0343: if (retType != Void.TYPE) {
0344: if (!retType.isPrimitive()) {
0345: allClasses.put(retType, retType);
0346: }
0347: returnTypes.put(retType, retType);
0348: }
0349: }
0350:
0351: if (paramsDefined) {
0352: allClasses.put(Object.class, Object.class);
0353: }
0354:
0355: allClasses.put(Throwable.class, Throwable.class);
0356: }
0357:
0358: /*
0359: *----------------------------------------------------------------------
0360: *
0361: * generateByteCode --
0362: *
0363: * Writes out the byte code into the ostream.
0364: *
0365: * Results:
0366: *
0367: * None.
0368: *
0369: * Side effects:
0370: * Byte code is written into the ostream.
0371: *
0372: *----------------------------------------------------------------------
0373: */
0374:
0375: private void generateByteCode() throws IOException // This exception may never happen. We
0376: // declare it just to avoid putting
0377: // catch statements everywhere.
0378: {
0379: // u4 magic.
0380: // u2 minor_version
0381: // u2 major_version
0382:
0383: ostream.writeInt(0xCAFEBABE);
0384: ostream.writeShort(3);
0385: ostream.writeShort(45);
0386:
0387: // u2 constant_pool_count
0388: // cp_info constant_pool[constant_pool_count-1]
0389:
0390: generateConstantPool();
0391:
0392: // u2 access_flags
0393: // u2 this_class
0394: // u2 super_class
0395:
0396: ostream.writeShort(ACC_SUPER | ACC_PUBLIC);
0397: ostream.writeShort(cp_this _class);
0398: ostream.writeShort(cp_super _class);
0399:
0400: // u2 interfaces_count
0401: // u2 interfaces[interfaces_count]
0402:
0403: ostream.writeShort(1);
0404: ostream.writeShort(cp_listener_interface);
0405:
0406: // u2 fields_count
0407: // u2 field_info fields[fields_count]
0408:
0409: ostream.writeShort(0);
0410:
0411: // u2 methods_count
0412: // u2 method_info methods[methods_count]
0413:
0414: ostream.writeShort(1 + methods.length);
0415: generateConstructor();
0416:
0417: for (int i = 0; i < methods.length; i++) {
0418: generateMethod(i);
0419: }
0420:
0421: // u2 attributes_count
0422: // u2 attribute_info attributes[attributes_count]
0423:
0424: ostream.writeShort(0);
0425: }
0426:
0427: /*
0428: *----------------------------------------------------------------------
0429: *
0430: * generateConstantPool --
0431: *
0432: * Generate the constant pool.
0433: *
0434: * Results:
0435: * None.
0436: *
0437: * Side effects:
0438: * The constant pool elements are written into the byte array stream.
0439: *
0440: *----------------------------------------------------------------------
0441: */
0442:
0443: private void generateConstantPool() throws IOException // This exception may never happen because
0444: // we are only writing to byte array
0445: // streams. We declare it just to
0446: // avoid putting catch statements
0447: // everywhere.
0448: {
0449: // We do this in three stages because inside the byte code, the
0450: // constant_pool_count appears in front of the constant pool
0451: // elements.
0452: //
0453: // (1) Generate the constant pool elements and store them into a
0454: // Vector. When we are done we know the total number of
0455: // constants we have.
0456: //
0457: // (2) Find out how many elements we have written, and write
0458: // constant_pool_count into the byte array stream.
0459: //
0460: // (3) Write the constant pool elements into the byte array
0461: // stream.
0462: //
0463:
0464: constPool = new Vector();
0465:
0466: // Names of this class, its super class and the interface that
0467: // this class implements.
0468:
0469: cp_this _class = cp_putClass(clsName);
0470: cp_super _class = cp_putClass(super Cls.getName());
0471:
0472: cp_listener_interface = cp_putClass(listenerCls.getName());
0473:
0474: // The UTF8 string "Code" is used to generate the body of methods.
0475:
0476: cp_code = cp_putUtf8("Code");
0477:
0478: // All the methods that the generated class calls.
0479:
0480: cp_super _cons = cp_putMethodRef(cp_super _class, "<init>", "()V");
0481: cp_processEvent = cp_putMethodRef(cp_super _class,
0482: "_processEvent",
0483: "([Ljava/lang/Object;Ljava/lang/String;)V");
0484: cp_wrongException = cp_putMethodRef(cp_super _class,
0485: "_wrongException", "()V");
0486:
0487: for (Enumeration e = returnTypes.keys(); e.hasMoreElements();) {
0488: Class retType = (Class) e.nextElement();
0489: short ref;
0490:
0491: if (retType.isPrimitive()) {
0492: ref = cp_putMethodRef(cp_super _class, "_return_"
0493: + retType.getName(), "()"
0494: + getTypeDesc(retType));
0495: } else {
0496: cp_putString(retType.getName());
0497: ref = cp_putMethodRef(cp_super _class, "_return_Object",
0498: "(Ljava/lang/String;)" + getTypeDesc(retType));
0499: }
0500:
0501: hashPutShort(returnMethodRef, retType, ref);
0502: }
0503:
0504: // The constructor and methods that are defined in the generated
0505: // class.
0506:
0507: cp_consDesc = cp_putMethodDesc("<init>", "()V", false);
0508:
0509: for (int i = 0; i < methods.length; i++) {
0510: cp_methodDesc[i] = cp_putMethodDesc(methods[i].getName(),
0511: getMethodDescriptor(methods[i]), true);
0512: }
0513:
0514: // All the classes referred to by the generated class.
0515:
0516: for (Enumeration e = allClasses.keys(); e.hasMoreElements();) {
0517: Class type = (Class) e.nextElement();
0518:
0519: short ref = cp_putClass(type.getName());
0520: hashPutShort(clsRef, type, ref);
0521: }
0522:
0523: // If the methods in the generated class receives parameter of
0524: // primitive types, they must be wrapped in wrapper classes such
0525: // as java.lang.Integer before they are passed to
0526: // super._processEvent().
0527:
0528: for (Enumeration e = primClasses.keys(); e.hasMoreElements();) {
0529: // FIXME : javac compiler bug workaround
0530: //
0531: // This loop works around a compiler bug in JAVAC 1.1.4. For
0532: // For some reasons, if this loop is not here, AdaptorGen.class will
0533: // contain incorrect byte code and causes a NullPoniterException.
0534: //
0535: // This compiler bug happens only in JAVAC. MS JVC apparently
0536: // works fine.
0537:
0538: e.nextElement();
0539: }
0540:
0541: for (Enumeration e = primClasses.keys(); e.hasMoreElements();) {
0542: Class primType = (Class) e.nextElement();
0543: short class_index = cp_getClass(getWrapperClass(primType));
0544: short ref = cp_putMethodRef(class_index, "<init>", "("
0545: + getTypeDesc(primType) + ")V");
0546:
0547: hashPutShort(wrapperConsRef, primType, ref);
0548: }
0549:
0550: // Now we know the count. Let's write into the byte array.
0551:
0552: ostream.writeShort(constPool.size() + 1);
0553: for (int i = 0; i < constPool.size(); i++) {
0554: Object obj = constPool.elementAt(i);
0555:
0556: if (obj instanceof ConstUtf) {
0557: ConstUtf cutf = (ConstUtf) obj;
0558:
0559: ostream.writeByte(CONSTANT_Utf8);
0560: ostream.writeUTF(cutf.string);
0561:
0562: } else if (obj instanceof ConstString) {
0563: ConstString cstr = (ConstString) obj;
0564:
0565: ostream.writeByte(CONSTANT_String);
0566: ostream.writeShort(cstr.string_index);
0567:
0568: } else if (obj instanceof ConstClass) {
0569: ConstClass ccls = (ConstClass) obj;
0570:
0571: ostream.writeByte(CONSTANT_Class);
0572: ostream.writeShort(ccls.name_index);
0573:
0574: } else if (obj instanceof ConstMethodRef) {
0575: ConstMethodRef cmref = (ConstMethodRef) obj;
0576:
0577: ostream.writeByte(CONSTANT_MethodRef);
0578: ostream.writeShort(cmref.class_index);
0579: ostream.writeShort(cmref.name_and_type_index);
0580:
0581: } else {
0582: ConstNameAndType cnat = (ConstNameAndType) obj;
0583:
0584: ostream.writeByte(CONSTANT_NameAndType);
0585: ostream.writeShort(cnat.name_index);
0586: ostream.writeShort(cnat.desc_index);
0587: }
0588: }
0589: }
0590:
0591: /*
0592: *----------------------------------------------------------------------
0593: *
0594: * generateConstructor --
0595: *
0596: * Generates the constructor method -- it just chains to the
0597: * constructor of the super class. E.g.
0598: *
0599: * public Adaptor0() {
0600: * super();
0601: * }
0602: *
0603: * Note we need to generate such an "implicit constructor"
0604: * anyways. The JVM requires this. We can omit implicit
0605: * constructor in Java source code because JAVAC makes one for us
0606: * behind the scene.
0607: *
0608: * Results:
0609: * None.
0610: *
0611: * Side effects:
0612: * The byte code of the constructor is written into the ostream.
0613: *
0614: *----------------------------------------------------------------------
0615: */
0616:
0617: void generateConstructor() throws IOException // This exception may never happen. We
0618: // declare it just to avoid putting
0619: // catch statements everywhere.
0620: {
0621: ostream.writeShort(ACC_PUBLIC); // access
0622: ostream.writeShort(cp_consDesc.name_index);
0623: ostream.writeShort(cp_consDesc.descriptor_index);
0624: ostream.writeShort(1); // attr count
0625:
0626: ostream.writeShort(cp_code); // attr_name_index = "Code"
0627: ostream.writeInt(17); // attr_length,excluding first 6 bytes
0628: ostream.writeShort(2); // max_stacks
0629: ostream.writeShort(1); // max_locals
0630: ostream.writeInt(5); // code_length
0631:
0632: ostream.writeByte(ALOAD_0); // aload_0
0633:
0634: ostream.writeByte(INVOKESP); // invokespecial super.<init>()
0635: ostream.writeShort(cp_super _cons);
0636:
0637: ostream.writeByte(RETURN); // return
0638:
0639: ostream.writeShort(0); // exception_table_length
0640: ostream.writeShort(0); // attribute_count
0641: }
0642:
0643: /*
0644: *----------------------------------------------------------------------
0645: *
0646: * generateMethod --
0647: *
0648: * Generates an event handling method. This procedure is more
0649: * complex because the method can receive any parameters, throw
0650: * any exceptions and return any value. Here is a canonical form
0651: * of the kind of method generated:
0652: *
0653: * public int someEvent(int p0, double p1, byte p2, Object p3)
0654: * throws Exception1, Exception2
0655: * {
0656: * Object params[] = new Object[4];
0657: * params[0] = new Integer(p0);
0658: * params[1] = new Double(p1);
0659: * params[2] = new Byte(p2);
0660: * params[3] = p3;
0661: *
0662: * try {
0663: * _processEvent(params, "someEvent");
0664: * } catch (Throwable exception) {
0665: * if (exception instanceof Exception1) {
0666: * throw (Exception1)exception;
0667: * } else if (exception instanceof Exception2) {
0668: * throw (Exception2)exception;
0669: * } else {
0670: * _wrongException();
0671: * }
0672: * }
0673: *
0674: * return _return_int();
0675: * }
0676: *
0677: *
0678: * If the return type is any Object type, the final statement
0679: * will be modified as in the following:
0680: *
0681: * public xyz.pkg.FooBar someEvent(...)
0682: * {
0683: * ....
0684: * return (FooBar)_return_Object("xyz.pkg.FooBar");
0685: * }
0686: *
0687: * The actual work of converting interp.getResult() to the
0688: * appropriate return type is done in the _return_<type> methods
0689: * in the EventAdaptor class.
0690: *
0691: * Results:
0692: * None.
0693: *
0694: * Side effects:
0695: * The byte code of the method is written into the ostream.
0696: *
0697: *----------------------------------------------------------------------
0698: */
0699:
0700: void generateMethod(int methodIdx) // Generate the method described by
0701: // methods[methodIdx].
0702: throws IOException {
0703:
0704: int max_stacks;
0705: int max_locals;
0706: int paramVarIdx; // index of the "param" variable.
0707: int exceptionVarIdx; // index of the "exception" variable.
0708: int exStartPC, exEndPC; // Exception start and end PC.
0709: int exHandlerPC = 0; // Exception handler PC.
0710:
0711: // Calculate the max_stacks and max_locals variables for the
0712: // method.
0713:
0714: max_stacks = 6;
0715:
0716: Class paramTypes[] = methods[methodIdx].getParameterTypes();
0717: int numParams = paramTypes.length;
0718:
0719: max_locals = 1; // "this" pointer
0720: for (int i = 0; i < numParams; i++) {
0721: if ((paramTypes[i] == Double.TYPE)
0722: || (paramTypes[i] == Long.TYPE)) {
0723: max_locals += 2;
0724: } else {
0725: max_locals += 1;
0726: }
0727: }
0728:
0729: max_locals += 2; // param[], exception.
0730: paramVarIdx = max_locals - 2;
0731: exceptionVarIdx = max_locals - 1;
0732:
0733: ostream.writeShort(ACC_PUBLIC); // access = "public"
0734: ostream.writeShort(cp_methodDesc[methodIdx].name_index);
0735: ostream.writeShort(cp_methodDesc[methodIdx].descriptor_index);
0736: ostream.writeShort(1); // attr count
0737:
0738: // Generate the body of the code.
0739:
0740: ByteArrayOutputStream baos = new ByteArrayOutputStream();
0741: DataOutputStream code = new DataOutputStream(baos);
0742:
0743: // [1] Create an array for passing parameters to super.processEvent():
0744: //
0745: // Object params[] = new Object[numParams];
0746: //
0747: // NOTE:
0748: //
0749: // - In all the generated code, it is sufficient to represent
0750: // a local variable using a short because a Java method may
0751: // have no more than 65535 local variables (including paramaters).
0752: //
0753: // - This means the side of the params array is no more than 65535,
0754: // so we can specify its size using sipush.
0755:
0756: code.writeByte(SIPUSH); // sipush #<numParams>
0757: code.writeShort(numParams);
0758:
0759: code.writeByte(ANEWARRAY); // anewarray Object
0760: code.writeShort(cp_getClass(Object.class));
0761:
0762: writeLoadStore(code, ASTORE, // astore param[]
0763: paramVarIdx);
0764:
0765: // [2] Copy the parameters into an object array:
0766: //
0767: // params[0] = new Integer(p0);
0768: // params[1] = new Double(p0);
0769: // params[2] = (Object)p2;
0770: // // .... etc
0771:
0772: // We start at local variable index 1, which is the first parameter.
0773: // (index 0 is the "this" pointer).
0774:
0775: int paramIdx = 1;
0776:
0777: for (int i = 0; i < numParams; i++) {
0778: writeLoadStore(code, ALOAD, // aload param[]
0779: paramVarIdx);
0780:
0781: code.writeByte(SIPUSH); // sipush #<i>
0782: code.writeShort(i);
0783:
0784: if (paramTypes[i].isPrimitive()) {
0785: Class prim = paramTypes[i];
0786: Class wrapper = getWrapperClass(paramTypes[i]);
0787: int loadOpcode, numWords;
0788:
0789: if (prim == Double.TYPE) {
0790: loadOpcode = DLOAD;
0791: numWords = 2;
0792: } else if (prim == Float.TYPE) {
0793: loadOpcode = FLOAD;
0794: numWords = 1;
0795: } else if (prim == Long.TYPE) {
0796: loadOpcode = LLOAD;
0797: numWords = 2;
0798: } else {
0799: loadOpcode = ILOAD;
0800: numWords = 1;
0801: }
0802:
0803: code.writeByte(NEW); // new <Wrapper>
0804: code.writeShort(cp_getClass(wrapper));
0805:
0806: code.writeByte(DUP); // dup
0807:
0808: writeLoadStore(code, // <?>load p_<i>
0809: loadOpcode, paramIdx);
0810:
0811: code.writeByte(INVOKESP); // invokespecial <Wrapper>(<type>);
0812: code.writeShort(cp_getWrapperConstructor(prim));
0813:
0814: paramIdx += numWords;
0815: } else {
0816: writeLoadStore(code, ALOAD, // aload p_<i>
0817: paramIdx);
0818:
0819: paramIdx += 1;
0820: }
0821:
0822: code.writeByte(AASTORE); // aastore
0823: }
0824:
0825: // [3] Call super.processEvent():
0826: //
0827: // try {
0828: // super.processEvent(params, <nameOfMethod>);
0829: // }
0830:
0831: exStartPC = code.size();
0832:
0833: code.writeByte(ALOAD_0); // aload_0 this
0834:
0835: writeLoadStore(code, ALOAD, // aload param[]
0836: paramVarIdx);
0837:
0838: code.writeByte(LDC_W); // ldc_w <name_of_method>
0839: code.writeShort(cp_getString(methods[methodIdx].getName()));
0840:
0841: code.writeByte(INVOKEVT); // invokevirtual processEvent()
0842: code.writeShort(cp_processEvent);
0843:
0844: exEndPC = code.size();
0845:
0846: // [4] Handle any exceptions thrown by processEvent():
0847: //
0848: // catch (Throwable exception) {
0849: // ....
0850: // }
0851: //
0852: // Note, we use WIDE version of load/store in all subsequent
0853: // byte codes so that it's easy to calculate the offset
0854: // for jumping to the "normal" return statement.)
0855:
0856: Class exceptions[] = methods[methodIdx].getExceptionTypes();
0857:
0858: int offset = 5 + 4 + exceptions.length * 18 + 4;
0859: code.writeByte(GOTO_W); // goto_w #<offset>
0860: code.writeInt(offset);
0861:
0862: exHandlerPC = code.size();
0863:
0864: code.writeByte(WIDE); // astore exception
0865: code.writeByte(ASTORE);
0866: code.writeShort(exceptionVarIdx);
0867:
0868: for (int i = 0; i < exceptions.length; i++) {
0869: // Write the exception handler for each of the checked exception
0870: // types. Each handler is 16 bytes long.
0871:
0872: code.writeByte(WIDE); // aload exception
0873: code.writeByte(ALOAD);
0874: code.writeShort(exceptionVarIdx);
0875:
0876: code.writeByte(INSTNCOF); // instanceof <exceptions[i]>
0877: code.writeShort(cp_getClass(exceptions[i]));
0878:
0879: code.writeByte(IFEQ); // ifeq #<nextException>
0880: code.writeShort(11);
0881:
0882: code.writeByte(WIDE); // aload exception
0883: code.writeByte(ALOAD);
0884: code.writeShort(exceptionVarIdx);
0885:
0886: code.writeByte(CHKCAST); // checkcast <exceptions[i]>
0887: code.writeShort(cp_getClass(exceptions[i]));
0888:
0889: code.writeByte(ATHROW); // athrow
0890: }
0891:
0892: code.writeByte(ALOAD_0); // aload_0 this
0893:
0894: code.writeByte(INVOKEVT); // invokevirtual _wrongExceptionError()
0895: code.writeShort(cp_wrongException);
0896:
0897: // [5] Normal return from this method.
0898:
0899: Class retType = methods[methodIdx].getReturnType();
0900:
0901: if (retType == Void.TYPE) {
0902: code.writeByte(RETURN);
0903: } else if (retType.isPrimitive()) {
0904: code.writeByte(ALOAD_0); // aload_0 this
0905:
0906: code.writeByte(INVOKEVT); // invokevirtual return_<type>
0907: code.writeShort(cp_getReturnMethodRef(retType));
0908:
0909: if (retType == Double.TYPE) {
0910: code.writeByte(DRETURN); // dreturn
0911: } else if (retType == Float.TYPE) {
0912: code.writeByte(FRETURN); // freturn
0913: } else if (retType == Long.TYPE) {
0914: code.writeByte(LRETURN); // lreturn
0915: } else {
0916: // IRETURN is used for boolean,
0917: // byte, char, int and short.
0918:
0919: code.writeByte(IRETURN); // ireturn
0920: }
0921: } else {
0922: code.writeByte(ALOAD_0); // aload_0 this
0923:
0924: code.writeByte(LDC_W); // ldc_w <retType.getName()>
0925: code.writeShort(cp_getString(retType.getName()));
0926:
0927: code.writeByte(INVOKEVT); // invokevirtual return_<type>
0928: code.writeShort(cp_getReturnMethodRef(retType));
0929:
0930: code.writeByte(CHKCAST); // checkcast <retType>
0931: code.writeShort(cp_getClass(retType));
0932: code.writeByte(ARETURN); // areturn
0933: }
0934:
0935: int codeLength = code.size();
0936:
0937: // [6] Write the exception table: we catch all Throwable
0938: // classes.
0939:
0940: code.writeShort(1); // exception_table_length
0941:
0942: code.writeShort(exStartPC); // start_pc
0943: code.writeShort(exEndPC); // end_pc
0944: code.writeShort(exHandlerPC); // handler_pc
0945: code.writeShort( // catch_type
0946: cp_getClass(Throwable.class));
0947:
0948: // [7] The attributes table (empty)
0949:
0950: code.writeShort(0); // attribute_count
0951:
0952: // [8] Now we are done. Emit the code section into the output
0953: // stream.
0954:
0955: code.close();
0956: byte codeBytes[] = baos.toByteArray();
0957:
0958: ostream.writeShort(cp_code); // attr_name_index = "Code"
0959: ostream.writeInt(codeBytes.length // attr_length
0960: + 8);
0961:
0962: ostream.writeShort(max_stacks);
0963: ostream.writeShort(max_locals);
0964:
0965: ostream.writeInt(codeLength); // code_length
0966: ostream.write(codeBytes);
0967: }
0968:
0969: /*
0970: *----------------------------------------------------------------------
0971: *
0972: * internalClassName --
0973: *
0974: * Returns the "internal" class name of a Java class: E.g. the
0975: * internal name for "java.lang.Integer" is "java/lang/Integer".
0976: *
0977: * Results:
0978: * The "internal" class name.
0979: *
0980: * Side effects:
0981: * None.
0982: *
0983: *----------------------------------------------------------------------
0984: */
0985:
0986: private final static String internalClassName(String className) // "Normal" name of the class.
0987: {
0988: return className.replace('.', '/');
0989: }
0990:
0991: /*
0992: *----------------------------------------------------------------------
0993: *
0994: * hashPutShort --
0995: *
0996: * Puts a short value into a hash table.
0997: *
0998: * Results:
0999: * None.
1000: *
1001: * Side effects:
1002: * The short value is wrapped in a Short object and stored in the
1003: * hashtable.
1004: *
1005: *----------------------------------------------------------------------
1006: */
1007:
1008: private final static void hashPutShort(Hashtable hashtable, // The hashtable.
1009: Object key, // The key.
1010: short num) // Put this number under the given key
1011: // in the hashtable.
1012: {
1013: Short shortObj = new Short(num);
1014: hashtable.put(key, shortObj);
1015: }
1016:
1017: /*
1018: *----------------------------------------------------------------------
1019: *
1020: * hashGetShort --
1021: *
1022: * Gets the short value corresponding to the key from the hash
1023: * table.
1024: *
1025: * Results:
1026: * The short value corresponding to the key.
1027: *
1028: * Side effects:
1029: * None.
1030: *
1031: *----------------------------------------------------------------------
1032: */
1033:
1034: private final static short hashGetShort(Hashtable hashtable, // The hashtable.
1035: Object key) // The key.
1036: {
1037: return ((Short) hashtable.get(key)).shortValue();
1038: }
1039:
1040: /*
1041: *----------------------------------------------------------------------
1042: *
1043: * getWrapperClass --
1044: *
1045: * Given a primitive type (e.g. int), returns its wrapper class
1046: * (e.g., java.lang.Integer).
1047: *
1048: * Results:
1049: * The wrapper class for the primitive type.
1050: *
1051: * Side effects:
1052: * None.
1053: *
1054: *----------------------------------------------------------------------
1055: */
1056:
1057: private final static Class getWrapperClass(Class primType) // The Class object that represents the
1058: // primitive type.
1059: {
1060: if (primType == Boolean.TYPE) {
1061: return Boolean.class;
1062: } else if (primType == Byte.TYPE) {
1063: return Byte.class;
1064: } else if (primType == Character.TYPE) {
1065: return Character.class;
1066: } else if (primType == Double.TYPE) {
1067: return Double.class;
1068: } else if (primType == Float.TYPE) {
1069: return Float.class;
1070: } else if (primType == Integer.TYPE) {
1071: return Integer.class;
1072: } else if (primType == Long.TYPE) {
1073: return Long.class;
1074: } else {
1075: return Short.class;
1076: }
1077: }
1078:
1079: /*
1080: *----------------------------------------------------------------------
1081: *
1082: * getTypeDesc --
1083: *
1084: * Returns the string that represents a Java type. E.g, "Z" for
1085: * boolean, "Lfoo.Bar;" for foo.Bar.
1086: *
1087: * Results:
1088: * The string that represents a Java type.
1089: *
1090: * Side effects:
1091: * None.
1092: *
1093: *----------------------------------------------------------------------
1094: */
1095:
1096: private final static String getTypeDesc(Class cls) {
1097: if (cls.isPrimitive()) {
1098: if (cls == Boolean.TYPE) {
1099: return "Z";
1100: } else if (cls == Byte.TYPE) {
1101: return "B";
1102: } else if (cls == Character.TYPE) {
1103: return "C";
1104: } else if (cls == Double.TYPE) {
1105: return "D";
1106: } else if (cls == Float.TYPE) {
1107: return "F";
1108: } else if (cls == Integer.TYPE) {
1109: return "I";
1110: } else if (cls == Long.TYPE) {
1111: return "J";
1112: } else if (cls == Short.TYPE) {
1113: return "S";
1114: } else {
1115: return "V";
1116: }
1117: } else {
1118: if (cls.isArray()) {
1119: return "[" + getTypeDesc(cls.getComponentType());
1120: } else {
1121: String s = "L" + cls.getName() + ";";
1122: return s.replace('.', '/');
1123: }
1124: }
1125: }
1126:
1127: /*
1128: *----------------------------------------------------------------------
1129: *
1130: * getMethodDesc --
1131: *
1132: * Returns the string that represents the type of a method. E.g.
1133: * "(Lfoo.Bar;DI)V" for void xxx(foo.Bar,double,int)
1134: *
1135: * Results:
1136: * The string that represents the type of a method.
1137: *
1138: * Side effects:
1139: * None.
1140: *
1141: *----------------------------------------------------------------------
1142: */
1143:
1144: private final static String getMethodDescriptor(Method method) // Returns the desc of this method.
1145: {
1146: StringBuffer sbuf = new StringBuffer();
1147: sbuf.append('(');
1148:
1149: Class params[] = method.getParameterTypes();
1150:
1151: for (int i = 0; i < params.length; i++) {
1152: sbuf.append(getTypeDesc(params[i]));
1153: }
1154: sbuf.append(')');
1155: sbuf.append(getTypeDesc(method.getReturnType()));
1156:
1157: return sbuf.toString();
1158: }
1159:
1160: /*
1161: *----------------------------------------------------------------------
1162: *
1163: * writeLoadStore --
1164: *
1165: * Writes the byte-code for a load or store operation. To reduce
1166: * the size of the byte code, the WIDE prefix is used only when
1167: * necessary (i.e., the address is greater than 255)
1168: *
1169: * Results:
1170: * None.
1171: *
1172: * Side effects:
1173: * the load/store byte codes are written into the
1174: * DataOutputStream.
1175: *
1176: *----------------------------------------------------------------------
1177: */
1178:
1179: void writeLoadStore(DataOutputStream code, // The DataOutputStream to write the byte-code
1180: // into.
1181: int opcode, // The opcode can be ?LOAD or ?STORE
1182: int address) // The target address of the load/store.
1183: throws IOException // This exception may never happen. We
1184: // declare it just to avoid putting
1185: // catch statements everywhere.
1186: {
1187: if (address > 255) {
1188: code.writeByte(WIDE);
1189: code.writeByte(opcode);
1190: code.writeShort(address);
1191: } else {
1192: code.writeByte(opcode);
1193: code.writeByte(address);
1194: }
1195: }
1196:
1197: /*
1198: *----------------------------------------------------------------------
1199: *
1200: * cp_putUtf8 --
1201: *
1202: * Puts a UTF8 string into the constant pool.
1203: *
1204: * Results:
1205: * The index of that UTF8 string in the constant pool.
1206: *
1207: * Side effects:
1208: * The UTF8 string is put into the constPool vector if the same
1209: * string is not already in the constant pool.
1210: *
1211: *----------------------------------------------------------------------
1212: */
1213:
1214: short cp_putUtf8(String string) // The string to put (in UTF8 format) into
1215: // the constant pool.
1216: {
1217: Short shortObj;
1218:
1219: shortObj = (Short) utf8Tab.get(string);
1220:
1221: // Check to make sure that the string is not already in the
1222: // constant pool so that we won't have duplicated entries (which
1223: // wastes space!).
1224:
1225: if (shortObj != null) {
1226: return shortObj.shortValue();
1227: } else {
1228: ConstUtf cutf = new ConstUtf();
1229: cutf.string = string;
1230: constPool.addElement(cutf);
1231:
1232: short id = (short) cpSize++;
1233: hashPutShort(utf8Tab, string, id);
1234: return id;
1235: }
1236: }
1237:
1238: /*
1239: *----------------------------------------------------------------------
1240: *
1241: * cp_putString --
1242: *
1243: * Puts a string into the constant pool. N.B., this is stored as
1244: * a CONSTANT_String element. In contrast, UTF8 strings are
1245: * stored as CONSTANT_Utf8. Read "The Java Virtual Machine
1246: * Specification" for details.
1247: *
1248: * Results:
1249: * The index of the string in the constant pool.
1250: *
1251: * Side effects:
1252: * The string is put into the constPool vector.
1253: *
1254: *----------------------------------------------------------------------
1255: */
1256:
1257: private short cp_putString(String string) // The string to put (in CONSTANT_String
1258: // format) into the constant pool.
1259: {
1260: ConstString cstr = new ConstString();
1261: cstr.string_index = cp_putUtf8(string);
1262: constPool.addElement(cstr);
1263:
1264: short id = (short) cpSize++;
1265: hashPutShort(stringRef, string, id);
1266: return id;
1267: }
1268:
1269: /*
1270: *----------------------------------------------------------------------
1271: *
1272: * cp_putClass --
1273: *
1274: * Puts a CONSTANT_Class element into the constant pool.
1275: *
1276: * Results:
1277: * The index of the class.
1278: *
1279: * Side effects:
1280: * The class is put into the constPool vector.
1281: *
1282: *----------------------------------------------------------------------
1283: */
1284:
1285: private short cp_putClass(String className) // Fully qualified name of the class.
1286: {
1287: ConstClass ccls = new ConstClass();
1288: ccls.name_index = cp_putUtf8(internalClassName(className));
1289: constPool.addElement(ccls);
1290:
1291: return (short) cpSize++;
1292: }
1293:
1294: /*
1295: *----------------------------------------------------------------------
1296: *
1297: * cp_putNameAndType --
1298: *
1299: * Puts a CONSTANT_NameAndType element into the constant pool.
1300: *
1301: * Results:
1302: * The index of the NameAndType.
1303: *
1304: * Side effects:
1305: * The NameAndType is put into the constPool vector.
1306: *
1307: *----------------------------------------------------------------------
1308: */
1309:
1310: short cp_putNameAndType(String name, // The name of the method.
1311: String type) // The type of the method.
1312: {
1313: ConstNameAndType cnat = new ConstNameAndType();
1314: cnat.name_index = cp_putUtf8(name);
1315: cnat.desc_index = cp_putUtf8(type);
1316: constPool.addElement(cnat);
1317:
1318: return (short) cpSize++;
1319: }
1320:
1321: /*
1322: *----------------------------------------------------------------------
1323: *
1324: * cp_putMethodRef --
1325: *
1326: * Puts a CONSTANT_MethodRef element into the constant pool.
1327: *
1328: * Results:
1329: * The index of the MethodRef.
1330: *
1331: * Side effects:
1332: * The MethodRef is put into the constPool vector.
1333: *
1334: *----------------------------------------------------------------------
1335: */
1336:
1337: short cp_putMethodRef(short class_index, // Index of the class.
1338: String name, // Name of the method.
1339: String desc) // Descriptor of the method.
1340: {
1341: ConstMethodRef cmref = new ConstMethodRef();
1342: cmref.class_index = class_index;
1343: cmref.name_and_type_index = cp_putNameAndType(name, desc);
1344:
1345: constPool.addElement(cmref);
1346:
1347: return (short) cpSize++;
1348: }
1349:
1350: /*
1351: *----------------------------------------------------------------------
1352: *
1353: * cp_putMethodDesc --
1354: *
1355: * Puts the UTF8 strings needed to describe a method defined in
1356: * the generated class.
1357: *
1358: * Results:
1359: * A MethodDesc object that contains the index of the name and
1360: * descriptor of the method.
1361: *
1362: * Side effects:
1363: * UTF8 strings may be put into the constPool vector.
1364: *
1365: *----------------------------------------------------------------------
1366: */
1367:
1368: MethodDesc cp_putMethodDesc(String name, // Name of the method.
1369: String descriptor, // Descriptor of the method.
1370: boolean generateID) // True if we need to generate a string ID
1371: // to pass to _processEvent() for this method.
1372: {
1373: MethodDesc desc = new MethodDesc();
1374: desc.name_index = cp_putUtf8(name);
1375: desc.descriptor_index = cp_putUtf8(descriptor);
1376: if (generateID) {
1377: cp_putString(name);
1378: }
1379:
1380: return desc;
1381: }
1382:
1383: /*
1384: *----------------------------------------------------------------------
1385: *
1386: * cp_getClass --
1387: *
1388: * Returns the index of a class definition in the constant pool
1389: *
1390: * Results:
1391: * The index of the class definition.
1392: *
1393: * Side effects:
1394: * None.
1395: *
1396: *----------------------------------------------------------------------
1397: */
1398:
1399: short cp_getClass(Class cls) // Query the index of this class.
1400: {
1401: return hashGetShort(clsRef, cls);
1402: }
1403:
1404: /*
1405: *----------------------------------------------------------------------
1406: *
1407: * cp_getString --
1408: *
1409: * Returns the index of a CONSTANT_String definition in the
1410: * constant pool
1411: *
1412: * Results:
1413: * The index of the string.
1414: *
1415: * Side effects:
1416: * None.
1417: *
1418: *----------------------------------------------------------------------
1419: */
1420:
1421: short cp_getString(String string) // Query the index of this string.
1422: {
1423: return hashGetShort(stringRef, string);
1424: }
1425:
1426: /*
1427: *----------------------------------------------------------------------
1428: *
1429: * cp_getWrapperConstructor --
1430: *
1431: * Returns the constant pool index of a CONSTANT_MethodRef
1432: * definition for the constructor of the primitive wrapper class.
1433: *
1434: * Results:
1435: * The index of the constructor.
1436: *
1437: * Side effects:
1438: * None.
1439: *
1440: *----------------------------------------------------------------------
1441: */
1442:
1443: short cp_getWrapperConstructor(Class primType) // Query the index of the constructor
1444: // of the wrapper class of this primitive type.
1445: {
1446: return hashGetShort(wrapperConsRef, primType);
1447: }
1448:
1449: /*
1450: *----------------------------------------------------------------------
1451: *
1452: * cp_getReturnMethodRef --
1453: *
1454: * Returns the constant pool index of a CONSTANT_MethodRef
1455: * definition of methods such super._return_int(); these methods
1456: * are used to return values from the binding script.
1457: *
1458: * Results:
1459: * The index of the method.
1460: *
1461: * Side effects:
1462: * None.
1463: *
1464: *----------------------------------------------------------------------
1465: */
1466:
1467: short cp_getReturnMethodRef(Class retType) // Query the index of the method that
1468: // returns this type.
1469: {
1470: if (retType.isPrimitive()) {
1471: return hashGetShort(returnMethodRef, retType);
1472: } else {
1473: return hashGetShort(returnMethodRef, Object.class);
1474: }
1475: }
1476:
1477: // The following five inner classes are used to store temporary copies
1478: // of constane pool items in a Vector.
1479:
1480: class ConstUtf {
1481: String string; // The string to put into the constant pool
1482: // in UTF8 format.
1483: }
1484:
1485: class ConstString {
1486: short string_index; // Index of the CONSTANR_Utf8 element that
1487: // defines the string.
1488: }
1489:
1490: class ConstClass {
1491: short name_index; // Index of the CONSTANR_Utf8 element that
1492: // defines the internal name of the class.
1493: }
1494:
1495: class ConstNameAndType {
1496: short name_index; // Index of the CONSTANR_Utf8 element that
1497: // defines the name of a method.
1498: short desc_index; // Index of the CONSTANR_Utf8 element that
1499: // defines the type of a method.
1500: }
1501:
1502: class ConstMethodRef {
1503: short class_index; // Index of the CONSTANR_Utf8 element that
1504: // defines the internal name of the class.
1505: short name_and_type_index; // Index of the CONSTANR_NameAndType element
1506: // that defines the name and type of the
1507: // method.
1508: }
1509:
1510: // This inner class stores the name and descriptor of the method to
1511: // generate.
1512:
1513: class MethodDesc {
1514:
1515: // Index to the name of the method (a CONSTANT_String).
1516:
1517: short name_index;
1518:
1519: // Index to the name of the method (a CONSTANT_String).
1520:
1521: short descriptor_index;
1522:
1523: } // end AdaptorGen.MethodDesc
1524:
1525: } // end AdaptorGen
|