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