do not generate ops for load insns
[org.ibex.classgen.git] / src / org / ibex / classgen / JSSA.java
1 package org.ibex.classgen;
2 import java.io.*;
3 import java.util.*;
4
5 /**
6  *  a highly streamlined SSA-form intermediate representation of a
7  *  sequence of JVM instructions; all stack manipulation is factored
8  *  out.
9  */
10 public class JSSA extends MethodGen implements CGConst {
11
12     // Constructor //////////////////////////////////////////////////////////////////////////////
13     
14     public JSSA(Type.Class c, DataInput in, ConstantPool cp) throws IOException {
15         super(c, in, cp);
16         local = new Expr[maxLocals];
17         stack = new Expr[maxStack];
18         int n=0;
19         if (!isStatic())
20             local[n++] = new Argument("this",method.getDeclaringClass());
21         for(int i=0;i<this.method.getNumArgs(); i++)
22             local[n++] = new Argument("arg"+i, this.method.getArgType(i));
23         for(int i=0; i<size(); i++) {
24             int    op  = get(i);
25             Object arg = getArg(i);
26             try {
27                 Object o = addOp(op, arg);
28                 if (o != null) {
29                     ops[numOps] = o;
30                     ofs[numOps++] = i;
31                 }
32             } catch(RuntimeException e) {
33                 System.err.println("Had a problem at PC: " + i + " of " + method);
34                 e.printStackTrace();
35                 throw new IOException("invalid class file");
36             }
37         }
38     }
39
40     public void debugBodyToString(StringBuffer sb) {
41         StringBuffer sb0 = new StringBuffer();
42         super.debugBodyToString(sb0);
43         StringTokenizer st = new StringTokenizer(sb0.toString(), "\n");
44         String[] lines = new String[st.countTokens()];
45         for(int i=0; i<lines.length; i++) lines[i] = st.nextToken();
46         for(int j=0; j<ofs[0]; j++) {
47             String s = "    /* " + lines[j].trim();
48             while(s.length() < 50) s += " ";
49             s += " */";
50             sb.append(s);
51             sb.append("\n");
52         }
53         for(int i=0; i<numOps; i++) {
54             String s = "    /* " + lines[ofs[i]].trim();
55             while(s.length() < 50) s += " ";
56             s += " */  ";
57             s += ops[i].toString();
58             sb.append(s);
59             sb.append(";\n");
60             for(int j=ofs[i]+1; j<(i==numOps-1?size():ofs[i+1]); j++) {
61                 s = "    /* " + lines[j].trim();
62                 while(s.length() < 50) s += " ";
63                 s += " */";
64                 sb.append(s);
65                 sb.append("\n");
66             }
67         }
68     }
69     
70     private Object[] ops = new Object[65535];
71     private int[] ofs = new int[65535];
72     private int numOps = 0;
73
74     // Instance Data; used ONLY during constructor; then thrown away /////////////////////////////////////////////////
75
76     /** this models the JVM locals; it is only used for unwinding stack-ops into an SSA-tree, then thrown away */
77     private final Expr[] local;
78     
79     /** this models the JVM stack; it is only used for unwinding stack-ops into an SSA-tree, then thrown away */
80     private final Expr[] stack;
81
82     /** JVM stack pointer */
83     private int sp = 0;
84     
85     private Expr push(Expr e) {
86         if(sp == stack.length) {
87             for(int i=0;i<stack.length;i++) System.err.println("Stack " + i + ": " + stack[i]);
88             throw new IllegalStateException("stack overflow (" + stack.length + ")");
89         }
90         if(e.getType() == Type.VOID) throw new IllegalArgumentException("can't push a void");
91         return stack[sp++] = e;
92     }
93     private Expr pop() {
94         if(sp == 0) throw new IllegalStateException("stack underflow");
95         return stack[--sp];
96     }
97     
98     private Op seqPush(Expr e) {
99         push(e);
100         return new Seq(e);
101     }
102
103
104     // SSA-node classes /////////////////////////////////////////////////////////////////////////////////////////
105
106     public final Expr VOID_EXPR = new Expr() {
107         public Type getType() { return Type.VOID; }
108     };
109     
110     /** an purely imperative operation which does not generate data */
111     public abstract class Op {
112         //public abstract Op[] predecessors();  // not implemented yet
113         //public abstract Op[] successors();    // not implemented yet
114         public String toString() { return name(); }
115         String name() {
116             String name = this.getClass().getName();
117             if (name.indexOf('$') != -1) name = name.substring(name.lastIndexOf('$')+1);
118             if (name.indexOf('.') != -1) name = name.substring(name.lastIndexOf('.')+1);
119             return name;
120         }
121     }
122     
123     /** A sequence point. expr is evaluated for side effects at this point, this does not generate data 
124         Expressions that haven't been evaluated with Seq are evaluated when they are first encountered
125       */
126     public class Seq extends Op {
127         private final Expr expr;
128         public String toString() { return expr.toString(); }
129         public Seq(Expr expr) { this.expr = expr; }
130     }
131     
132     /** an operation which generates data */
133     public abstract class Expr extends Op {
134         //public abstract Expr[] contributors();  // not implemented yet
135         //public abstract Expr[] dependents();    // not implemented yet
136
137         /** every JSSA.Expr either remembers its type _OR_ knows how to figure it out (the latter is preferred to eliminate
138          *  redundant information that could possibly "disagree" with itself -- this happened a LOT in Soot) */
139         public abstract Type getType();
140     }
141
142     /**
143      *  A "nondeterministic merge" -- for example when the first instruction in a loop reads from a local which could have been
144      *  written to either by some instruction at the end of the previous iteration of the loop or by some instruction before
145      *  the loop (on the first iteration).
146      */
147     public class Phi extends Expr {
148         private final Expr[] inputs;
149         public Phi(Expr[] inputs) {
150             this.inputs = new Expr[inputs.length];
151             System.arraycopy(inputs, 0, this.inputs, 0, inputs.length);
152         }
153         public Type getType() {
154             // sanity check
155             Type t = inputs[0].getType();
156
157             // FIXME: actually this should check type-unifiability... fe, the "type of null" unifies with any Type.Ref
158             for(int i=1; i<inputs.length; i++)
159                 if (inputs[i].getType() != t)
160                     throw new Error("Phi node with disagreeing types!  Crisis!");
161             return t;
162         }
163     }
164
165     public class Argument extends Expr {
166         public final String name;
167         public final Type t;
168         public Argument(String name, Type t) { this.name = name; this.t = t; }
169         public String toString() { return name; }
170         public Type getType() { return t; }
171     }
172     
173     // Unary Operations
174     public class Not extends Expr {
175         public final Expr e;
176         public Not(Expr e) {
177             if(e.getType() != Type.BOOLEAN) throw new IllegalArgumentException("not needs a boolean expression");
178             this.e = e;
179         }
180         public Type getType() { return Type.BOOLEAN; }
181         public String toString() { return "!(" + e + ")"; }
182     }
183     
184     public class Neg extends Expr {
185         public final Expr e;
186         public Neg(Expr e) {
187             if(!e.getType().isPrimitive()) throw new IllegalArgumentException("can only negate a primitive");
188             this.e = e;
189         }
190         public Type getType() { return e.getType(); }
191         public String toString() { return "- (" + e + ")"; }
192     }
193     
194     // Binary Operations //////////////////////////////////////////////////////////////////////////////
195
196     public abstract class BinExpr extends Expr {
197         public final Expr e1;
198         public final Expr e2;
199         private final String show;
200         public BinExpr(Expr e1, Expr e2, String show) { this.e1 = e1; this.e2 = e2; this.show = show; }
201         public String toString() {
202             // FEATURE: should we be doing some precedence stuff here? probably no worth it for debugging output
203             return "(" + e1 + show + e2 + ")";
204         }
205     }
206
207     public class Comparison extends BinExpr {
208         public Comparison(Expr e1, Expr e2, String show) { super(e1, e2, show); }
209         public Type getType() { return Type.BOOLEAN; }
210     }
211
212     public class Eq extends Comparison {
213         public Eq(Expr e1, Expr e2) {
214             super(e1, e2, "=="); 
215             if(e1.getType().isPrimitive() != e2.getType().isPrimitive())
216                 throw new IllegalArgumentException("type mismatch");
217             if(e1.getType().isPrimitive() && e1.getType() != e2.getType())
218                 throw new IllegalArgumentException("type mismatch");            
219             // FEATURE: Check if we can compare these classes
220         }
221     }
222     
223
224     public class PrimitiveComparison extends Comparison {
225         public PrimitiveComparison(Expr e1, Expr e2, String show) {
226             super(e1, e2, show);
227             if(!e1.getType().isPrimitive() || e1.getType() != e2.getType()) throw new IllegalArgumentException("type mismatch");
228         }
229     }
230     
231     public class Gt extends PrimitiveComparison { public Gt(Expr e1, Expr e2) { super(e1, e2, ">"); } }
232     public class Lt extends PrimitiveComparison { public Lt(Expr e1, Expr e2) { super(e1, e2, "<"); } }
233     public class Ge extends PrimitiveComparison { public Ge(Expr e1, Expr e2) { super(e1, e2, ">="); } }
234     public class Le extends PrimitiveComparison { public Le(Expr e1, Expr e2) { super(e1, e2, "<="); } }
235     
236     // Math Operations //////////////////////////////////////////////////////////////////////////////
237
238     public class BinMath extends BinExpr {
239         public BinMath(Expr e1, Expr e2, String show) {
240             super(e2, e1, show); 
241             if(e1.getType() != e2.getType()) throw new IllegalArgumentException("types disagree");
242         }
243         public Type getType() { return e1.getType(); }
244     }
245     
246     public class Add  extends BinMath { public  Add(Expr e, Expr e2) { super(e, e2, "+"); } }
247     public class Sub  extends BinMath { public  Sub(Expr e, Expr e2) { super(e, e2, "-"); } }
248     public class Mul  extends BinMath { public  Mul(Expr e, Expr e2) { super(e, e2, "*"); } }
249     public class Rem  extends BinMath { public  Rem(Expr e, Expr e2) { super(e, e2, "%"); } }
250     public class Div  extends BinMath { public  Div(Expr e, Expr e2) { super(e, e2, "/"); } }
251     public class And  extends BinMath { public  And(Expr e, Expr e2) { super(e, e2, "&"); } }
252     public class Or   extends BinMath { public   Or(Expr e, Expr e2) { super(e, e2, "|"); } }
253     public class Xor  extends BinMath { public  Xor(Expr e, Expr e2) { super(e, e2, "^"); } }
254     
255     public class BitShiftExpr extends BinExpr {
256         public BitShiftExpr(Expr e1, Expr e2, String show) {
257             super(e1,e2,show);
258             Type t = e1.getType();
259             if(t != Type.INT && t != Type.LONG) throw new IllegalArgumentException("type mismatch");
260             if(e2.getType() != Type.INT) throw new IllegalArgumentException("type mismatch");
261         }
262         public Type getType() { return e1.getType(); }
263     }
264     public class Shl  extends BitShiftExpr { public  Shl(Expr e, Expr e2) { super(e, e2, "<<"); } }
265     public class Shr  extends BitShiftExpr { public  Shr(Expr e, Expr e2) { super(e, e2, ">>"); } }
266     public class Ushr extends BitShiftExpr { public Ushr(Expr e, Expr e2) { super(e, e2, ">>>"); } }
267
268     // Other operations //////////////////////////////////////////////////////////////////////////////
269
270     public class Cast extends Expr {
271         final Expr e;
272         final Type t;
273         public Cast(Expr e, Type t) {
274             if(e.getType().isRef() != t.isRef()) throw new IllegalArgumentException("invalid cast");
275             // FEATURE: Check that one is a subclass of the other if it is a ref
276             this.e = e;
277             this.t = t; 
278         }
279         public Type getType() { return t; }
280     }
281
282     public class InstanceOf extends Expr {
283         final Expr e;
284         final Type.Ref t;
285         public InstanceOf(Expr e, Type.Ref t) {
286             if(!e.getType().isRef()) throw new IllegalArgumentException("can't do an instanceof check on a non-ref");
287             this.e = e; 
288             this.t = t; 
289         }
290         public Type getType() { return Type.BOOLEAN; }
291     }
292
293     public class Throw extends Op {
294         public final Expr e;
295         public Throw(Expr e) {
296             if(!e.getType().isRef()) throw new IllegalArgumentException("can't throw a non ref");
297             // FEATURE: CHeck that it is a subclass of Throwable
298             this.e = e; 
299         }
300     }
301
302     public class Branch extends Op {
303         public Branch(Expr condition, Object destination) { }
304         public Branch(Label destination) { }
305         public Branch(MethodGen.Switch s) { }
306         public Branch() { }
307     }
308     public class Goto extends Branch { }
309     public class RET extends Branch { }
310     public class JSR extends Branch { public JSR(Label l) { super(l); } }
311     public class If extends Branch { }
312
313     /** represents a "returnaddr" pushed onto the stack */
314     public class Label extends Expr {
315         public final Op op;
316         public Type getType() { throw new Error("attempted to call getType() on a Label"); }
317         public Label(Op op) { this.op = op; }
318         public Label(int i) { this.op = null; /* FIXME */ }
319     }
320
321     public class New extends Expr {
322         public final Type.Class t;
323         public Type getType() { return t; }
324         public New(Type.Class t) { this.t = t; }
325         public String toString() { return "new " + t + "(...)"; }
326     }
327     
328     public class NewArray extends Expr {
329         public final Type.Array t;
330         public final Expr[] dims;
331         public NewArray(Type.Array t, Expr[] dims) { this.t = t; this.dims = dims; }
332         public NewArray(Type.Array t, Expr dim) { this(t,new Expr[]{dim}); }
333         public Type getType() { return t; }
334     }
335     
336     public class Return extends Op {
337         final Expr e;
338         public Return() { this(VOID_EXPR); }
339         public Return(Expr e) { this.e = e; }
340         public String toString() { return e.getType() == Type.VOID ? "return" : ("return "+e.toString()); }
341     }
342
343     /** GETFIELD and GETSTATIC */
344     public class Get extends Expr {
345         final Type.Class.Field f;
346         final Expr e;
347         public Type getType() { return f.getType(); }
348         public Get(Type.Class.Field f) { this(f, null); }
349         public Get(Type.Class.Field f, Expr e) { this.f = f; this.e = e; }
350         public String toString() {
351             return
352                 (e!=null
353                  ? e+"."+f.name
354                  : f.getDeclaringClass() == JSSA.this.method.getDeclaringClass()
355                  ? f.name
356                  : f.toString());
357         }
358     }
359
360     /** PUTFIELD and PUTSTATIC */
361     public class Put extends Op {
362         final Type.Class.Field f;
363         final Expr v;
364         final Expr e;
365         public Put(Type.Class.Field f, Expr v) { this(f, v, null); }
366         public Put(Type.Class.Field f, Expr v, Expr e) { this.f = f; this.v = v; this.e = e; }
367         public String toString() {
368             return
369                 (e!=null
370                  ? e+"."+f.name
371                  : f.getDeclaringClass() == JSSA.this.method.getDeclaringClass()
372                  ? f.name
373                  : f.toString()) + " = " + v;
374         }
375     }
376
377     public class ArrayPut extends Op {
378         final Expr e, i, v;
379         public ArrayPut(Expr e, Expr i, Expr v) { this.e = e; this.i = i; this.v = v; }
380     }
381
382     public class ArrayGet extends Expr {
383         final Expr e, i;
384         public ArrayGet(Expr e, Expr i) { this.e = e; this.i = i; }
385         public Type getType() { return e.getType().asArray().getElementType(); }
386     }
387
388     public class ArrayLength extends Expr {
389         final Expr e;
390         public ArrayLength(Expr e) { this.e = e; }
391         public Type getType() { return Type.INT; }
392     }
393
394     public abstract class Invoke extends Expr {
395         public final Expr[] arguments;
396         public final Type.Class.Method method;
397         protected Invoke(Type.Class.Method m, Expr[] a) { this.arguments = a; this.method = m; } 
398
399         public Type getType() { return method.getReturnType(); }
400         protected void args(StringBuffer sb) {
401             sb.append("(");
402             for(int i=0; i<arguments.length; i++) {
403                 if (i>0) sb.append(", ");
404                 sb.append(arguments[i]+"");
405             }
406             sb.append(")");
407         }
408
409         public String toString() {
410             StringBuffer sb = new StringBuffer();
411             sb.append(method.getDeclaringClass() == JSSA.this.method.getDeclaringClass()
412                       ? method.name
413                       : (method.getDeclaringClass() + "." + method.name));
414             args(sb);
415             return sb.toString();
416         }
417     }
418     public class InvokeStatic    extends Invoke  { public InvokeStatic(Type.Class.Method m, Expr[] a) { super(m,a); } }
419     public class InvokeSpecial   extends InvokeVirtual {
420         public InvokeSpecial(Type.Class.Method m, Expr[] a, Expr e) { super(m,a,e); }
421         public String toString() { return toString(method.name.equals("<init>") ? method.getDeclaringClass().getName() : method.name); }
422     }
423     public class InvokeInterface extends InvokeVirtual{public InvokeInterface(Type.Class.Method m, Expr[] a, Expr e){super(m,a,e);}}
424     public class InvokeVirtual   extends Invoke  {
425         public final Expr instance;
426         public InvokeVirtual(Type.Class.Method m, Expr[] a, Expr e) { super(m, a); instance = e; }
427         public String toString() { return toString(method.name); }
428         protected String toString(String name) {
429             StringBuffer sb = new StringBuffer();
430             sb.append(instance+".");
431             sb.append(name);
432             args(sb);
433             return sb.toString();
434         }
435     }
436
437     public class Constant extends Expr {
438         private final Object o;
439         public Constant(int i) { this(new Integer(i)); }
440         public Constant(Object o) { this.o = o; }
441         public String toString() { return o instanceof String ? "\"" + o + "\"" : o.toString(); }
442         public Type getType() {
443             if (o instanceof Byte) return Type.BYTE;
444             if (o instanceof Short) return Type.SHORT;
445             if (o instanceof Character) return Type.CHAR;
446             if (o instanceof Boolean) return Type.BOOLEAN;
447             if (o instanceof Long) return Type.LONG;
448             if (o instanceof Double) return Type.DOUBLE;
449             if (o instanceof Float) return Type.FLOAT;
450             if (o instanceof Integer) return Type.INT;
451             if (o instanceof String) return Type.STRING;
452             throw new IllegalStateException("unknown constant type");
453         }
454     }
455
456
457     // Implementation //////////////////////////////////////////////////////////////////////////////
458
459     private Object addOp(int op, Object arg) {
460         int i1 = 0;
461         int i2 = 0;
462         if (op==WIDE) {
463             MethodGen.Wide w = (MethodGen.Wide)arg;
464             op = w.op;
465             arg = null;
466             i1 = w.varNum;
467             i2 = w.n;
468         }
469         if (op==IINC) {
470             MethodGen.Pair p = (MethodGen.Pair)arg;
471             arg = null;
472             i1 = p.i1;
473             i2 = p.i2;
474         }
475         switch(op) {
476
477             case NOP: return null;
478
479                 // Stack manipulations //////////////////////////////////////////////////////////////////////////////
480
481             case ACONST_NULL:                                                      return stack[sp++] = new Constant(null);
482             case ICONST_M1:                                                        return stack[sp++] = new Constant(-1);
483             case ICONST_0: case LCONST_0: case FCONST_0: case DCONST_0:            push(new Constant(0)); return null;
484             case ICONST_1: case LCONST_1: case FCONST_1: case DCONST_1:            push(new Constant(1)); return null;
485             case ICONST_2: case FCONST_2:                                          push(new Constant(2)); return null;
486             case ICONST_3:                                                         push(new Constant(3)); return null;
487             case ICONST_4:                                                         push(new Constant(4)); return null;
488             case ICONST_5:                                                         push(new Constant(5)); return null;
489             case ILOAD:    case LLOAD:    case FLOAD:    case DLOAD:    case ALOAD:    push(local[i1]); return null;
490             case ILOAD_0:  case LLOAD_0:  case FLOAD_0:  case DLOAD_0:  case ALOAD_0:  push(local[0]); return null;
491             case ILOAD_1:  case LLOAD_1:  case FLOAD_1:  case DLOAD_1:  case ALOAD_1:  push(local[1]); return null;
492             case ALOAD_2:  case DLOAD_2:  case FLOAD_2:  case LLOAD_2:  case ILOAD_2:  push(local[2]); return null;
493             case ILOAD_3:  case LLOAD_3:  case FLOAD_3:  case DLOAD_3:  case ALOAD_3:  push(local[3]); return null;
494             case ISTORE:   case LSTORE:   case FSTORE:   case DSTORE:   case ASTORE:   local[i1] = pop(); return null;
495             case ISTORE_0: case LSTORE_0: case FSTORE_0: case DSTORE_0: case ASTORE_0: local[0]  = pop(); return null;
496             case ISTORE_1: case LSTORE_1: case FSTORE_1: case DSTORE_1: case ASTORE_1: local[1]  = pop(); return null;
497             case ASTORE_2: case DSTORE_2: case FSTORE_2: case LSTORE_2: case ISTORE_2: local[2]  = pop(); return null;
498             case ISTORE_3: case LSTORE_3: case FSTORE_3: case DSTORE_3: case ASTORE_3: local[3]  = pop(); return null;
499             case POP:      pop(); return null;
500             case POP2:     pop(); pop(); return null;
501             case DUP:      push(stack[sp-1]); return null;
502             case DUP2:     push(stack[sp-2]); push(stack[sp-2]); return null;
503
504                 // Conversions //////////////////////////////////////////////////////////////////////////////
505
506                 // coercions are added as-needed when converting from JSSA back to bytecode, so we can
507                 // simply discard them here (assuming the bytecode we're reading in was valid in the first place)
508
509             case I2L: case F2L: case D2L:               push(new Cast(pop(), Type.LONG)); return null;
510             case I2F: case L2F: case D2F:               push(new Cast(pop(), Type.FLOAT)); return null;
511             case I2D: case L2D: case F2D:               push(new Cast(pop(), Type.DOUBLE)); return null;
512             case L2I: case F2I: case D2I:               push(new Cast(pop(), Type.INT)); return null;
513             case I2B:                                   push(new Cast(pop(), Type.BYTE)); return null;
514             case I2C:                                   push(new Cast(pop(), Type.CHAR)); return null;
515             case I2S:                                   push(new Cast(pop(), Type.SHORT)); return null;
516             case SWAP:                                  { Expr e1 = pop(), e2 = pop(); push(e2);  push(e1); return null; }
517
518                 // Math //////////////////////////////////////////////////////////////////////////////
519                    
520             case IADD: case LADD: case FADD: case DADD: push(new Add(pop(), pop())); return null;
521             case ISUB: case LSUB: case FSUB: case DSUB: push(new Sub(pop(), pop())); return null;
522             case IMUL: case LMUL: case FMUL: case DMUL: push(new Mul(pop(), pop())); return null;
523             case IREM: case LREM: case FREM: case DREM: push(new Rem(pop(), pop())); return null;
524                 //case INEG: case LNEG: case FNEG: case DNEG: push(new Neg(pop())); return null;
525             case IDIV: case LDIV: case FDIV: case DDIV: push(new Div(pop(), pop())); return null;
526             case ISHL: case LSHL:                       push(new Shl(pop(), pop())); return null;
527             case ISHR: case LSHR:                       push(new Shr(pop(), pop())); return null;
528             case IUSHR: case LUSHR:                     push(new Ushr(pop(), pop())); return null;
529             case IAND: case LAND:                       push(new And(pop(), pop())); return null;
530             case IOR:  case LOR:                        push(new Or(pop(), pop())); return null;
531             case IXOR: case LXOR:                       push(new Xor(pop(), pop())); return null;
532             case IINC:                                  return local[i1] = new Add(local[i1], new Constant(i2));
533
534                 // Control and branching //////////////////////////////////////////////////////////////////////////////
535
536             case IFNULL:                                return new Branch(new Eq(pop(), new Constant(null)), new Label(i1));
537             case IFNONNULL:                             return new Branch(new Not(new Eq(pop(),new Constant(null))),new Label(i1));
538             case IFEQ:                                  return new Branch(    new Eq(new Constant(0), pop()),  arg);
539             case IFNE:                                  return new Branch(new Not(new Eq(new Constant(0), pop())), arg);
540             case IFLT:                                  return new Branch(    new Lt(new Constant(0), pop()),  arg);
541             case IFGE:                                  return new Branch(new Not(new Lt(new Constant(0), pop())), arg);
542             case IFGT:                                  return new Branch(    new Gt(new Constant(0), pop()),  arg);
543             case IFLE:                                  return new Branch(new Not(new Gt(new Constant(0), pop())), arg);
544             case IF_ICMPEQ:                             return new Branch(    new Eq(pop(), pop()),  arg);
545             case IF_ICMPNE:                             return new Branch(new Not(new Eq(pop(), pop())), arg);
546             case IF_ICMPLT:                             return new Branch(    new Lt(pop(), pop()),  arg);
547             case IF_ICMPGE:                             return new Branch(new Not(new Lt(pop(), pop())), arg);
548             case IF_ICMPGT:                             return new Branch(    new Gt(pop(), pop()),  arg);
549             case IF_ICMPLE:                             return new Branch(new Not(new Gt(pop(), pop())), arg);
550             case IF_ACMPEQ:                             return new Branch(    new Eq(pop(), pop()),  arg);
551             case IF_ACMPNE:                             return new Branch(new Not(new Eq(pop(), pop())), arg);
552             case ATHROW:                                return new Throw(pop());
553             case GOTO:                                  return new Branch(new Label(i1));
554             case JSR:                                   return new JSR(new Label(i1));
555             case RET:                                   return new RET();
556             case RETURN:                                return new Return();
557             case IRETURN: case LRETURN: case FRETURN: case DRETURN: case ARETURN:
558                 return new Return(pop());
559
560                 // Array manipulations //////////////////////////////////////////////////////////////////////////////
561
562             case IALOAD:  case LALOAD:  case FALOAD:  case DALOAD:  case AALOAD:
563             case BALOAD:  case CALOAD:  case SALOAD:
564                 return seqPush(new ArrayGet(pop(), pop()));
565             case IASTORE: case LASTORE: case FASTORE: case DASTORE: case AASTORE:
566             case BASTORE: case CASTORE: case SASTORE:
567                 return new ArrayPut(pop(), pop(), pop());
568
569                 // Invocation //////////////////////////////////////////////////////////////////////////////
570
571             case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: case INVOKEINTERFACE: {
572                 Type.Class.Method method = (Type.Class.Method)arg;
573                 Expr args[] = new Expr[method.getNumArgs()];
574                 for(int i=0; i<args.length; i++) args[args.length-i-1] = pop();
575                 Expr ret;
576                 switch(op) {
577                     case INVOKEVIRTUAL:   ret = new InvokeVirtual(method, args, pop()); break;
578                     case INVOKEINTERFACE: ret = new InvokeInterface(method, args, pop()); break;
579                     case INVOKESPECIAL:   ret = new InvokeSpecial(method, args, pop()); break;
580                     case INVOKESTATIC:    ret = new InvokeStatic(method, args); break;
581                     default: throw new Error("should never happen");
582                 }
583                 if(ret.getType() != Type.VOID) push(ret);
584                 return new Seq(ret);
585             }
586
587                 // Field Access //////////////////////////////////////////////////////////////////////////////
588
589             case GETSTATIC:         return seqPush(new Get((Type.Class.Field)arg, null));
590             case PUTSTATIC:         return new Put((Type.Class.Field)arg, pop(), null);
591             case GETFIELD:          return seqPush(new Get((Type.Class.Field)arg, pop()));
592             case PUTFIELD:          return new Put((Type.Class.Field)arg, pop(), pop());
593
594                 // Allocation //////////////////////////////////////////////////////////////////////////////
595
596             case NEW:               push(new New((Type.Class)arg)); return null;
597             case NEWARRAY: {
598                 Type base;
599                 switch(((Integer)arg).intValue()) {
600                     case 4: base = Type.BOOLEAN; break;
601                     case 5: base = Type.CHAR; break;
602                     case 6: base = Type.FLOAT; break;
603                     case 7: base = Type.DOUBLE; break;
604                     case 8: base = Type.BYTE; break;
605                     case 9: base = Type.SHORT; break;
606                     case 10: base = Type.INT; break;
607                     case 11: base = Type.LONG; break;
608                     default: throw new IllegalStateException("invalid array type");
609                 }
610                 return seqPush(new NewArray(base.makeArray(),pop()));
611             }
612             case ANEWARRAY:         push(new NewArray(((Type.Ref)arg).makeArray(), pop())); return null;
613             case MULTIANEWARRAY: {
614                 MethodGen.MultiANewArray mana = (MethodGen.MultiANewArray) arg;
615                 Expr[] dims = new Expr[mana.dims];
616                 for(int i=0;i<dims.length;i++) dims[i] = pop();
617                 return seqPush(new NewArray(mana.type, dims));
618             }
619             case ARRAYLENGTH:       return seqPush(new ArrayLength(pop()));
620
621                 // Runtime Type information //////////////////////////////////////////////////////////////////////////////
622
623             case CHECKCAST:         return seqPush(new Cast(pop(), (Type.Ref)arg));
624             case INSTANCEOF:        push(new InstanceOf(pop(), (Type.Ref)arg)); return null;
625
626             case LDC: case LDC_W: case LDC2_W: push(new Constant(arg)); return null;
627
628             case BIPUSH:    push(new Constant((Integer)arg)); return null;
629             case SIPUSH:    push(new Constant((Integer)arg)); return null;
630
631             case TABLESWITCH:    return new Branch((MethodGen.Switch)arg);
632             case LOOKUPSWITCH:   return new Branch((MethodGen.Switch)arg);
633
634                 /*
635             case MONITORENTER:   Op.monitorEnter(pop());
636             case MONITOREXIT:    Op.monitorExit(pop());
637                 */
638
639             case DUP_X1:         throw new Error("unimplemented");
640             case DUP_X2:         throw new Error("unimplemented");
641             case DUP2_X1:         throw new Error("unimplemented");
642             case DUP2_X2:         throw new Error("unimplemented");
643             case LCMP:         throw new Error("unimplemented");
644             case FCMPL:         throw new Error("unimplemented");
645             case FCMPG:         throw new Error("unimplemented");
646             case DCMPL:         throw new Error("unimplemented");
647             case DCMPG:         throw new Error("unimplemented");
648             case GOTO_W:         throw new Error("unimplemented");
649             case JSR_W:         throw new Error("unimplemented");
650             default:          throw new Error("unhandled");
651         }
652     }
653
654     public static void main(String[] args) throws Exception {
655         InputStream is = Class.forName(args[0]).getClassLoader().getResourceAsStream(args[0].replace('.', '/')+".class");
656         System.out.println(new ClassFile(new DataInputStream(is), true).toString());
657     }
658 }