From: brian Date: Sun, 30 May 2004 21:31:56 +0000 (+0000) Subject: misc cleanup and javadoc comments X-Git-Url: http://git.megacz.com/?p=org.ibex.classgen.git;a=commitdiff_plain;h=94cebd4c12247ae7fd0a4b0cc66609fead0efece misc cleanup and javadoc comments darcs-hash:20040530213156-24bed-baadb0715b22ee43eb469ca0e918596c49a0bca9.gz --- diff --git a/Makefile b/Makefile index 2c9e9b5..c0b0454 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,13 @@ test: $(classes) clean: rm -rf build/* +.PHONY: doc +doc: doc/index.html + +doc/index.html: $(sources) src/org/ibex/classgen/package.html + mkdir -p doc + javadoc -d doc $(sources) + sizecheck: @for c in $(jar_classes); do \ for f in `echo $$c|sed 's,\.class$$,,;'`*.class; do gzip -c $$f; done | wc -c | tr -d '\n'; \ diff --git a/src/org/ibex/classgen/ClassGen.java b/src/org/ibex/classgen/ClassGen.java index 1ae9741..d579b99 100644 --- a/src/org/ibex/classgen/ClassGen.java +++ b/src/org/ibex/classgen/ClassGen.java @@ -3,23 +3,30 @@ package org.ibex.classgen; import java.util.*; import java.io.*; +/** Class generation object representing the whole classfile */ public class ClassGen implements CGConst { - private Type.Object thisType; - private Type.Object superType; - int flags; - private String sourceFile; + private final Type.Object thisType; + private final Type.Object superType; + final int flags; - private Vector interfaces = new Vector(); - private Vector fields = new Vector(); - private Vector methods = new Vector(); + private String sourceFile; + private final Vector interfaces = new Vector(); + private final Vector fields = new Vector(); + private final Vector methods = new Vector(); final CPGen cp; private final AttrGen attributes; + /** @see #ClassGen(Type.Object,Type.Object,int) */ public ClassGen(String name, String superName, int flags) { this(new Type.Object(name),new Type.Object(superName),flags); } + /** Creates a new ClassGen object + @param thisType The type of the class to generate + @param superType The superclas of the generated class (commonly Type.OBJECT) + @param flags The access flags for this class (ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_INTERFACE, and ACC_ABSTRACT) + */ public ClassGen(Type.Object thisType,Type.Object superType, int flags) { if((flags & ~(ACC_PUBLIC|ACC_FINAL|ACC_SUPER|ACC_INTERFACE|ACC_ABSTRACT)) != 0) throw new IllegalArgumentException("invalid flags"); @@ -31,21 +38,53 @@ public class ClassGen implements CGConst { attributes = new AttrGen(cp); } + /** Adds a new method to this class + @param name The name of the method (not the signature, just the name) + @param ret The return type of the method + @param args The arguments to the method + @param flags The flags for the method + (ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT) + @return A new MethodGen object for the method + @exception IllegalArgumentException if illegal flags are specified + @see MethodGen + @see CGConst + */ public final MethodGen addMethod(String name, Type ret, Type[] args, int flags) { MethodGen mg = new MethodGen(this,name,ret,args,flags); methods.addElement(mg); return mg; } + /** Adds a new field to this class + @param name The name of the filed (not the signature, just the name) + @param type The type of the field + @param flags The flags for the field + (ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE, ACC_TRANSIENT) + @return A new FieldGen object for the method + @exception IllegalArgumentException if illegal flags are specified + @see FieldGen + @see CGConst + */ public final FieldGen addField(String name, Type type, int flags) { FieldGen fg = new FieldGen(this,name,type,flags); fields.addElement(fg); return fg; } + /** Sets the source value of the SourceFile attribute of this class + @param sourceFile The string to be uses as the SourceFile of this class + */ public void setSourceFile(String sourceFile) { this.sourceFile = sourceFile; } - public void dump(String s) throws IOException { dump(new File(s)); } + /** Writes the classfile data to the file specifed + @see ClassGen#dump(OutputStream) + */ + public void dump(String file) throws IOException { dump(new File(file)); } + + /** Writes the classfile data to the file specified + If f is a directory directory components under it are created for the package the class is in (like javac's -d option) + @see ClassGen#dump(OutputStream) + */ public void dump(File f) throws IOException { if(f.isDirectory()) { String[] a = thisType.components(); @@ -58,7 +97,13 @@ public class ClassGen implements CGConst { } dump(new FileOutputStream(f)); } - + + /** Writes the classfile data to the outputstream specified + @param os The stream to write the class to + @exception IOException if an IOException occures while writing the class data + @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc) + @exception Exn if the classfile could not be written for any other reason (constant pool full, etc) + */ public void dump(OutputStream os) throws IOException { DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(os)); _dump(dos); @@ -102,10 +147,18 @@ public class ClassGen implements CGConst { attributes.dump(o); // attributes } + /** Thrown when class generation fails for a reason not under the control of the user + (IllegalStateExceptions are thrown in those cases */ public static class Exn extends RuntimeException { public Exn(String s) { super(s); } } + /** A class representing a field or method reference. This is used as an argument to the INVOKE*, GET*, and PUT* bytecodes + @see MethodRef + @see FieldRef + @see MethodRef.I + @see FieldRef + */ public static abstract class FieldOrMethodRef { Type.Object klass; String name; diff --git a/src/org/ibex/classgen/FieldGen.java b/src/org/ibex/classgen/FieldGen.java index b2d25bc..826eb78 100644 --- a/src/org/ibex/classgen/FieldGen.java +++ b/src/org/ibex/classgen/FieldGen.java @@ -2,6 +2,8 @@ package org.ibex.classgen; import java.io.*; +/** Class representing a field in a generated classfile + @see ClassGen#addField */ public class FieldGen implements CGConst { private final CPGen cp; private final String name; diff --git a/src/org/ibex/classgen/FieldRef.java b/src/org/ibex/classgen/FieldRef.java index 6c2d8cf..10b1b1e 100644 --- a/src/org/ibex/classgen/FieldRef.java +++ b/src/org/ibex/classgen/FieldRef.java @@ -1,6 +1,17 @@ package org.ibex.classgen; +/** This class represents Field references. It is used as an argument to the +GETFIELD, PUTFIELD, GETSTATIC, and PUTSTATCI bytecodes +@see CGConst#GETFIELD +@see CGConst#PUTFIELD +@see CGConst#GETSTATIC +@see CGConst#PUTSTATIC +*/ public class FieldRef extends ClassGen.FieldOrMethodRef { + /** Create a reference to field name of class c with the type t */ public FieldRef(Type.Object c, String name, Type t) { super(c,name,t.getDescriptor()); } + /** Equivalent to FieldRef(new Type.Object(s),...) + @see #FieldRef(Type.Object,String,Type,) + */ public FieldRef(String s, String name, Type t) { this(new Type.Object(s),name,t); } } diff --git a/src/org/ibex/classgen/MethodGen.java b/src/org/ibex/classgen/MethodGen.java index 0fc8606..383ed57 100644 --- a/src/org/ibex/classgen/MethodGen.java +++ b/src/org/ibex/classgen/MethodGen.java @@ -3,6 +3,10 @@ package org.ibex.classgen; import java.io.*; import java.util.*; +// FEATURE: Support WIDE bytecodes + +/** A class representing a method in a generated classfile + @see ClassGen#addMethod */ public class MethodGen implements CGConst { private final static boolean EMIT_NOPS = false; @@ -47,6 +51,7 @@ public class MethodGen implements CGConst { maxLocals = Math.max(args.length + (flags&ACC_STATIC)==0 ? 1 : 0,4); } + /** Returns the descriptor string for this method */ public String getDescriptor() { return MethodRef.getDescriptor(ret,args); } private class ExnTableEnt { @@ -68,10 +73,20 @@ public class MethodGen implements CGConst { } } - public final void addExceptionHandler(int startPC, int endPC, int handlerPC, Type.Object type) { - exnTable.put(type, new ExnTableEnt(startPC,endPC,handlerPC,cp.add(type))); + /** Adds an exception handler for the range [start,end) pointing to handler + @param start The instruction to start at (inclusive) + @param end The instruction to end at (exclusive) + @param handler The instruction of the excepton handler + @param type The type of exception that is to be handled (MUST inherit from Throwable) + */ + public final void addExceptionHandler(int start, int end, int handler, Type.Object type) { + exnTable.put(type, new ExnTableEnt(start,end,handler,cp.add(type))); } + /** Adds a exception type that can be thrown from this method + NOTE: This isn't enforced by the JVM. This is for reference only. A method can throw exceptions not declared to be thrown + @param type The type of exception that can be thrown + */ public final void addThrow(Type.Object type) { thrownExceptions.put(type,cp.add(type)); } @@ -93,9 +108,14 @@ public class MethodGen implements CGConst { capacity = newCap; } + + /** Returns the size (in instructions) of this method + @return The size of the method (in instructions) + */ public final int size() { return size; } // These two are optimized for speed, they don't call set() below + /** Add a bytecode (with no argument) to the method */ public final int add(byte op) { int s = size; if(s == capacity) grow(); @@ -103,22 +123,57 @@ public class MethodGen implements CGConst { size++; return s; } + /** Set the bytecode at position pos to op */ public final void set(int pos, byte op) { this.op[pos] = op; } + /** Adds a bytecode, op, with argument arg to the method + @return The position of the new bytecode + */ public final int add(byte op, Object arg) { if(capacity == size) grow(); set(size,op,arg); return size++; } + /** Adds a bytecode with a boolean argument - equivalent to add(op,arg?1:0); + @return The position of the new bytecode + @see #add(byte,int) + */ public final int add(byte op, boolean arg) { if(capacity == size) grow(); set(size,op,arg); return size++; } + /** Adds a bytecode with an integer argument. This is equivalent to add(op,new Integer(arg)), but optimized to prevent the allocation when possible + @return The position of the new bytecode + @see #add(byte,Object) + */ public final int add(byte op, int arg) { if(capacity == size) grow(); set(size,op,arg); return size++; } + /** Gets the bytecode at position pos + @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() + */ public final byte get(int pos) { return op[pos]; } + + /** Gets the bytecode at position pos. NOTE: This isn't necessarily the same object that was set with add or set. + Arguments for instructions which access the constant pool (LDC, INVOKEVIRTUAL, etc) are converted to a more efficient + interal form when they are added. The value returned from this method for these instruction can be reused, but there + is no way to retrieve the original object + @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() + */ public final Object getArg(int pos) { return arg[pos]; } + /** Sets the argument for pos to arg. This is equivalent to set(pos,op,new Integer(arg)), but optimized to prevent the allocation when possible. + @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() + @see #setArg(int,Object) */ public final void setArg(int pos, int arg) { set(pos,op[pos],N(arg)); } + /** Sets the argument for pos to arg. + @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() + */ public final void setArg(int pos, Object arg) { set(pos,op[pos],arg); } - - public final void set(int pos, byte op, boolean b) { set(pos,op,b?1:0); } + /** Sets the bytecode and argument at pos to op and arg respectivly. + This is equivalent to set(pos,op,arg?1:0) + @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() + */ + public final void set(int pos, byte op, boolean arg) { set(pos,op,arg?1:0); } // This MUST handle x{LOAD,STORE} and LDC with an int arg WITHOUT falling back to set(int,byte,Object) + /** Sets the bytecode and argument at pos to op and n respectivly. + This is equivalent to set(pos,op, new Integer(n)), but optimized to prevent the allocation when possible. + @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() + */ public final void set(int pos, byte op, int n) { Object arg = null; OUTER: switch(op) { @@ -166,6 +221,9 @@ public class MethodGen implements CGConst { this.arg[pos] = arg; } + /** Sets the bytecode and argument at pos to op and arg respectivly. + @exception ArrayIndexOutOfBoundException if pos < 0 || pos >= size() + */ public final void set(int pos, byte op, Object arg) { switch(op) { case ILOAD: case ISTORE: case LLOAD: case LSTORE: case FLOAD: @@ -199,6 +257,10 @@ public class MethodGen implements CGConst { this.arg[pos] = arg; } + /** This class represents the arguments to the TABLESWITH and LOOKUPSWITCH bytecodes + @see MethodGen.TSI + @see MethodGen.LSI + */ public static class SI { public final Object[] targets; public Object defaultTarget; @@ -214,6 +276,7 @@ public class MethodGen implements CGConst { public int getDefaultTarget() { return ((Integer)defaultTarget).intValue(); } } + /** This class represents the arguments to the TABLESWITCH bytecode */ public static class TSI extends SI { public final int lo; public final int hi; @@ -226,6 +289,7 @@ public class MethodGen implements CGConst { public void setTargetForVal(int val, int n) { setTarget(val-lo,n); } } + /** This class represents the arguments to the LOOKUPSWITCH bytecode */ public static class LSI extends SI { public final int[] vals; public LSI(int size) { @@ -235,15 +299,23 @@ public class MethodGen implements CGConst { public final void setVal(int pos, int val) { vals[pos] = val; } } + /** This class represents the arguments to byecodes that take two integer arguments. */ public static class Pair { public int i1; public int i2; public Pair(int i1, int i2) { this.i1 = i1; this.i2 = i2; } } + /** Sets the maximum number of locals in the function to maxLocals. NOTE: This defaults to 0 and is automatically increased as + necessary when *LOAD/*STORE bytecodes are added. You do not need to call this function in most cases */ public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; } + /** Sets the maxinum size of th stack for this function to maxStack. This defaults to 16< */ public void setMaxStack(int maxStack) { this.maxStack = maxStack; } + /** Computes the final bytecode for this method. + @exception IllegalStateException if the data for a method is in an inconsistent state (required arguments missing, etc) + @exception Exn if the byteocode could not be generated for any other reason (constant pool full, etc) + */ public void finish() { try { _finish(); @@ -479,7 +551,7 @@ public class MethodGen implements CGConst { size = capacity = FINISHED; } - public void dump(DataOutput o) throws IOException { + void dump(DataOutput o) throws IOException { o.writeShort(flags); o.writeShort(cp.getUtf8Index(name)); o.writeShort(cp.getUtf8Index(getDescriptor())); @@ -487,6 +559,8 @@ public class MethodGen implements CGConst { attrs.dump(o); } + /** Negates the IF* instruction, op (IF_ICMPGT -> IF_ICMPLE, IFNE -> IFEQ, etc) + @exception IllegalArgumentException if op isn't an IF* instruction */ public static byte negate(byte op) { switch(op) { case IFEQ: return IFNE; @@ -509,6 +583,8 @@ public class MethodGen implements CGConst { } } + /** Class that represents a target that isn't currently know. The target MUST be set with setTarget() before the classfile is written. + This class is more or less a mutable integer */ public static class PhantomTarget { private int target = -1; public void setTarget(int target) { this.target = target; } diff --git a/src/org/ibex/classgen/MethodRef.java b/src/org/ibex/classgen/MethodRef.java index c866adc..75d58f3 100644 --- a/src/org/ibex/classgen/MethodRef.java +++ b/src/org/ibex/classgen/MethodRef.java @@ -1,9 +1,21 @@ package org.ibex.classgen; +/** This class represents Method references. It is used as an argument to the + INVOKESTATIC, INVOKEVIRTUAL, INVOKESPEICAL, and INVOKEINTERFACE bytecodes + @see CGConst#INVOKESTATIC + @see CGConst#INVOKEVIRTUAL + @see CGConst#INVOKESPECIAL + @see CGConst#INVOKEINTERFACE +*/ public class MethodRef extends ClassGen.FieldOrMethodRef { + /** Create a reference to method name of class c with the return type ret and the + arguments args */ public MethodRef(Type.Object c, String name, Type ret, Type[] args) { super(c,name,getDescriptor(ret,args)); } + /** Equivalent to MethodRef(new Type.Object(s),...) + @see #MethodRef(Type.Object,String,Type,Type[]) + */ public MethodRef(String s, String name, Type ret, Type[] args) { this(new Type.Object(s),name,ret,args); } @@ -18,6 +30,10 @@ public class MethodRef extends ClassGen.FieldOrMethodRef { return sb.toString(); } + /** MethodRef class used for the INVOKEINTERFACE bytecode. Although this contains the same + data as a normal MethodRef, these are treated differently in the byte code format. In general, + users don't need to be concerned with this though because MethodRef's are automatically converted + to MethodRef.I's when they are applied to an INVOKEINTERFACE bytecode */ public static class I extends MethodRef { public I(Type.Object c, String name, Type ret, Type[] args) { super(c,name,ret,args); } public I(String s, String name, Type ret, Type[] args) { super(s,name,ret,args); } diff --git a/src/org/ibex/classgen/Type.java b/src/org/ibex/classgen/Type.java index 33af24d..ec6d834 100644 --- a/src/org/ibex/classgen/Type.java +++ b/src/org/ibex/classgen/Type.java @@ -9,6 +9,9 @@ public class Type { public static final Type BOOLEAN = new Type("Z"); public static final Type DOUBLE = new Type("D"); public static final Type FLOAT = new Type("F"); + public static final Type BYTE = new Type("B"); + public static final Type CHAR = new Type("C"); + public static final Type SHORT = new Type("S"); public static final Type.Object OBJECT = new Type.Object("java.lang.Object"); public static final Type.Object STRING = new Type.Object("java.lang.String"); @@ -17,17 +20,28 @@ public class Type { public static final Type.Object DOUBLE_OBJECT = new Type.Object("java.lang.Double"); public static final Type.Object FLOAT_OBJECT = new Type.Object("java.lang.Float"); + /** A zero element Type[] array (can be passed as the "args" param when a method takes no arguments */ public static final Type[] NO_ARGS = new Type[0]; final String descriptor; Type(String descriptor) { this.descriptor = descriptor; } + /** Returns the Java descriptor string for this object ("I", or "Ljava/lang/String", "[[J", etc */ public final String getDescriptor() { return descriptor; } public int hashCode() { return descriptor.hashCode(); } public boolean equals(java.lang.Object o) { return o instanceof Type && ((Type)o).descriptor.equals(descriptor); } + /** Returns a one dimensional array type for the base type base + @param base The base type + @return A one dimensional array of the base type + */ public static Type arrayType(Type base) { return arrayType(base,1); } + /** Returns a dim dimensional array type for the base type base + @param base The base type + @param dim Number if dimensions + @return A one dimensional array of the base type + */ public static Type arrayType(Type base, int dim) { StringBuffer sb = new StringBuffer(base.descriptor.length() + dim); for(int i=0;is can be a string in the form + "java.lang.String", "java/lang/String", or "Ljava/lang/String;". + @param s The type */ public Object(String s) { super(_initHelper(s)); } private static String _initHelper(String s) { @@ -44,14 +62,14 @@ public class Type { return s; } - public String[] components() { + String[] components() { StringTokenizer st = new StringTokenizer(descriptor.substring(1,descriptor.length()-1),"/"); String[] a = new String[st.countTokens()]; for(int i=0;st.hasMoreTokens();i++) a[i] = st.nextToken(); return a; } - public String internalForm() { return descriptor.substring(1,descriptor.length()-1); } + String internalForm() { return descriptor.substring(1,descriptor.length()-1); } // FEATURE: Do a proper check here (invalid chars, etc) static boolean validDescriptorString(String s) { diff --git a/src/org/ibex/classgen/package.html b/src/org/ibex/classgen/package.html new file mode 100644 index 0000000..c921dab --- /dev/null +++ b/src/org/ibex/classgen/package.html @@ -0,0 +1,83 @@ + +Example Usage:
+ +The following:
+
+        ClassGen cg = new ClassGen("Test1","java.lang.Object",ACC_PUBLIC|ACC_SUPER);
+        
+        FieldGen fg = cg.addField("message",Type.STRING,ACC_PRIVATE|ACC_STATIC);
+        fg.setConstantValue("Hello, World");
+        
+        MethodGen mg = cg.addMethod("main",Type.VOID,new Type[]{Type.arrayType(Type.STRING)},ACC_STATIC|ACC_PUBLIC);
+        mg.add(LDC,10);
+        mg.add(ISTORE_0);
+        
+        int top = mg.size();
+        mg.add(GETSTATIC,new FieldRef("java.lang.System","out",new Type.Object("java.io.PrintStream")));
+        mg.add(NEW,Type.STRINGBUFFER);
+        mg.add(DUP);
+        mg.add(INVOKESPECIAL,new MethodRef(Type.STRINGBUFFER,"",Type.VOID,Type.NO_ARGS));
+        mg.add(GETSTATIC,new FieldRef("Test1","message",Type.STRING));
+        mg.add(INVOKEVIRTUAL,new MethodRef(Type.STRINGBUFFER,"append",Type.STRINGBUFFER,new Type[]{Type.STRING}));
+        mg.add(LDC," - ");
+        mg.add(INVOKEVIRTUAL,new MethodRef(Type.STRINGBUFFER,"append",Type.STRINGBUFFER,new Type[]{Type.STRING}));
+        mg.add(ILOAD_0);
+        mg.add(INVOKEVIRTUAL,new MethodRef(Type.STRINGBUFFER,"append",Type.STRINGBUFFER,new Type[]{Type.INT}));
+        
+        mg.add(ILOAD_0);
+        mg.add(ICONST_1);
+        mg.add(IAND);
+        int b1 = mg.add(IFNE);
+        mg.add(LDC,'!');
+        mg.add(INVOKEVIRTUAL,new MethodRef(Type.STRINGBUFFER,"append",Type.STRINGBUFFER,new Type[]{Type.CHAR}));
+        mg.setArg(b1,mg.size());
+        
+        mg.add(INVOKEVIRTUAL,new MethodRef(Type.STRINGBUFFER,"toString",Type.STRING,Type.NO_ARGS));
+        mg.add(INVOKEVIRTUAL,new MethodRef("java.io.PrintStream","println",Type.VOID,new Type[]{Type.STRING}));
+        
+        mg.add(IINC,new MethodGen.Pair(0,-1));
+
+        mg.add(ILOAD_0);
+        mg.add(IFGT,top);
+        
+        mg.add(RETURN);
+        
+        cg.dump("Test1.class");
+
+ +Will create this bytecode: +
+public class Test1 extends java.lang.Object{
+private static java.lang.String message;
+
+public static void main(java.lang.String[]);
+   throws 
+  Code:
+   0:   bipush  10
+   2:   istore_0
+   3:   getstatic       #10; //Field java/lang/System.out:Ljava/io/PrintStream;
+   6:   new     #12; //class StringBuffer
+   9:   dup
+   10:  invokespecial   #16; //Method java/lang/StringBuffer."":()V
+   13:  getstatic       #20; //Field message:Ljava/lang/String;
+   16:  invokevirtual   #24; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
+   19:  ldc     #26; //String  - 
+   21:  invokevirtual   #24; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
+   24:  iload_0
+   25:  invokevirtual   #29; //Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;
+   28:  iload_0
+   29:  iconst_1
+   30:  iand
+   31:  ifne    39
+   34:  bipush  33
+   36:  invokevirtual   #32; //Method java/lang/StringBuffer.append:(C)Ljava/lang/StringBuffer;
+   39:  invokevirtual   #36; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
+   42:  invokevirtual   #42; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
+   45:  iinc    0, -1
+   48:  iload_0
+   49:  ifgt    3
+   52:  return
+
+}
+
+