/*
* 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.3 2000/01/25 03:42:26 mo 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_<type>
// 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<type> 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<methods.length; i++) {
generateMethod(i);
}
// u2 attributes_count
// u2 attribute_info attributes[attributes_count]
ostream.writeShort(0);
}
/*
*----------------------------------------------------------------------
*
* generateConstantPool --
*
* Generate the constant pool.
*
* Results:
* None.
*
* Side effects:
* The constant pool elements are written into the byte array stream.
*
*----------------------------------------------------------------------
*/
private void
generateConstantPool()
throws
IOException // This exception may never happen because
// we are only writing to byte array
// streams. We declare it just to
// avoid putting catch statements
// everywhere.
{
// We do this in three stages because inside the byte code, the
// constant_pool_count appears in front of the constant pool
// elements.
//
// (1) Generate the constant pool elements and store them into a
// Vector. When we are done we know the total number of
// constants we have.
//
// (2) Find out how many elements we have written, and write
// constant_pool_count into the byte array stream.
//
// (3) Write the constant pool elements into the byte array
// stream.
//
constPool = new Vector();
// Names of this class, its super class and the interface that
// this class implements.
cp_this_class = cp_putClass(clsName);
cp_super_class = cp_putClass(superCls.getName());
cp_listener_interface = cp_putClass(listenerCls.getName());
// The UTF8 string "Code" is used to generate the body of methods.
cp_code = cp_putUtf8("Code");
// All the methods that the generated class calls.
cp_super_cons = cp_putMethodRef(cp_super_class, "<init>", "()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("<init>", "()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(); ) {
// FIXME : javac compiler bug workaround
//
// 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 = cp_putMethodRef(class_index, "<init>",
"(" + 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<constPool.size(); i++) {
Object obj = constPool.elementAt(i);
if (obj instanceof ConstUtf) {
ConstUtf cutf = (ConstUtf)obj;
ostream.writeByte(CONSTANT_Utf8);
ostream.writeUTF(cutf.string);
} else if (obj instanceof ConstString) {
ConstString cstr = (ConstString)obj;
ostream.writeByte(CONSTANT_String);
ostream.writeShort(cstr.string_index);
} else if (obj instanceof ConstClass) {
ConstClass ccls = (ConstClass)obj;
ostream.writeByte(CONSTANT_Class);
ostream.writeShort(ccls.name_index);
} else if (obj instanceof ConstMethodRef) {
ConstMethodRef cmref = (ConstMethodRef)obj;
ostream.writeByte(CONSTANT_MethodRef);
ostream.writeShort(cmref.class_index);
ostream.writeShort(cmref.name_and_type_index);
} else {
ConstNameAndType cnat = (ConstNameAndType)obj;
ostream.writeByte(CONSTANT_NameAndType);
ostream.writeShort(cnat.name_index);
ostream.writeShort(cnat.desc_index);
}
}
}
/*
*----------------------------------------------------------------------
*
* generateConstructor --
*
* Generates the constructor method -- it just chains to the
* constructor of the super class. E.g.
*
* public Adaptor0() {
* super();
* }
*
* Note we need to generate such an "implicit constructor"
* anyways. The JVM requires this. We can omit implicit
* constructor in Java source code because JAVAC makes one for us
* behind the scene.
*
* Results:
* None.
*
* Side effects:
* The byte code of the constructor is written into the ostream.
*
*----------------------------------------------------------------------
*/
void
generateConstructor()
throws
IOException // This exception may never happen. We
// declare it just to avoid putting
// catch statements everywhere.
{
ostream.writeShort(ACC_PUBLIC); // access
ostream.writeShort(cp_consDesc.name_index);
ostream.writeShort(cp_consDesc.descriptor_index);
ostream.writeShort(1); // attr count
ostream.writeShort(cp_code); // attr_name_index = "Code"
ostream.writeInt(17); // attr_length,excluding first 6 bytes
ostream.writeShort(2); // max_stacks
ostream.writeShort(1); // max_locals
ostream.writeInt(5); // code_length
ostream.writeByte(ALOAD_0); // aload_0
ostream.writeByte(INVOKESP); // invokespecial super.<init>()
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 receive 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_<type> 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 #<numParams>
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 #<i>
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 <Wrapper>
code.writeShort(cp_getClass(wrapper));
code.writeByte(DUP); // dup
writeLoadStore(code, // <?>load p_<i>
loadOpcode,
paramIdx);
code.writeByte(INVOKESP); // invokespecial <Wrapper>(<type>);
code.writeShort(cp_getWrapperConstructor(prim));
paramIdx += numWords;
} else {
writeLoadStore(code, ALOAD, // aload p_<i>
paramIdx);
paramIdx += 1;
}
code.writeByte(AASTORE); // aastore
}
// [3] Call super.processEvent():
//
// try {
// super.processEvent(params, <nameOfMethod>);
// }
exStartPC = code.size();
code.writeByte(ALOAD_0); // aload_0 this
writeLoadStore(code, ALOAD, // aload param[]
paramVarIdx);
code.writeByte(LDC_W); // ldc_w <name_of_method>
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 #<offset>
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 <exceptions[i]>
code.writeShort(cp_getClass(exceptions[i]));
code.writeByte(IFEQ); // ifeq #<nextException>
code.writeShort(11);
code.writeByte(WIDE); // aload exception
code.writeByte(ALOAD);
code.writeShort(exceptionVarIdx);
code.writeByte(CHKCAST); // checkcast <exceptions[i]>
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_<type>
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 <retType.getName()>
code.writeShort(cp_getString(retType.getName()));
code.writeByte(INVOKEVT); // invokevirtual return_<type>
code.writeShort(cp_getReturnMethodRef(
retType));
code.writeByte(CHKCAST); // checkcast <retType>
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