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