create binding for all expressions already evaluated
[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
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     // Math Operations //////////////////////////////////////////////////////////////////////////////
210
211     public class BinMath extends BinExpr {
212         public BinMath(Expr e1, Expr e2, String show) {
213             super(e2, e1, show); 
214             if(e1.getType() != e2.getType()) throw new IllegalArgumentException("types disagree");
215         }
216         public Type getType() { return e1.getType(); }
217     }
218     
219     public class Add  extends BinMath { public  Add(Expr e, Expr e2) { super(e, e2, "+"); } }
220     public class Sub  extends BinMath { public  Sub(Expr e, Expr e2) { super(e, e2, "-"); } }
221     public class Mul  extends BinMath { public  Mul(Expr e, Expr e2) { super(e, e2, "*"); } }
222     public class Rem  extends BinMath { public  Rem(Expr e, Expr e2) { super(e, e2, "%"); } }
223     public class Div  extends BinMath { public  Div(Expr e, Expr e2) { super(e, e2, "/"); } }
224     public class And  extends BinMath { public  And(Expr e, Expr e2) { super(e, e2, "&"); } }
225     public class Or   extends BinMath { public   Or(Expr e, Expr e2) { super(e, e2, "|"); } }
226     public class Xor  extends BinMath { public  Xor(Expr e, Expr e2) { super(e, e2, "^"); } }
227     
228     public class BitShiftExpr extends BinExpr {
229         public BitShiftExpr(Expr e1, Expr e2, String show) {
230             super(e1,e2,show);
231             Type t = e1.getType();
232             if(t != Type.INT && t != Type.LONG) throw new IllegalArgumentException("type mismatch");
233             if(e2.getType() != Type.INT) throw new IllegalArgumentException("type mismatch");
234         }
235         public Type getType() { return e1.getType(); }
236     }
237     public class Shl  extends BitShiftExpr { public  Shl(Expr e, Expr e2) { super(e, e2, "<<"); } }
238     public class Shr  extends BitShiftExpr { public  Shr(Expr e, Expr e2) { super(e, e2, ">>"); } }
239     public class Ushr extends BitShiftExpr { public Ushr(Expr e, Expr e2) { super(e, e2, ">>>"); } }
240
241     // Other operations //////////////////////////////////////////////////////////////////////////////
242
243     public class Cast extends Expr {
244         final Expr e;
245         final Type t;
246         public Cast(Expr e, Type t) {
247             if(e.getType().isRef() != t.isRef()) throw new IllegalArgumentException("invalid cast");
248             // FEATURE: Check that one is a subclass of the other if it is a ref
249             this.e = e;
250             this.t = t; 
251         }
252         public Type getType() { return t; }
253     }
254
255     public class InstanceOf extends Expr {
256         final Expr e;
257         final Type.Ref t;
258         public InstanceOf(Expr e, Type.Ref t) {
259             if(!e.getType().isRef()) throw new IllegalArgumentException("can't do an instanceof check on a non-ref");
260             this.e = e; 
261             this.t = t; 
262         }
263         public Type getType() { return Type.BOOLEAN; }
264     }
265
266     public class Throw extends Op {
267         public final Expr e;
268         public Throw(Expr e) {
269             if(!e.getType().isRef()) throw new IllegalArgumentException("can't throw a non ref");
270             // FEATURE: CHeck that it is a subclass of Throwable
271             this.e = e; 
272         }
273     }
274
275     public class Branch extends Op {
276         public Branch(Expr condition, Object destination) { }
277         public Branch(Label destination) { }
278         public Branch(MethodGen.Switch s) { }
279         public Branch() { }
280     }
281     public class Goto extends Branch { }
282     public class RET extends Branch { }
283     public class JSR extends Branch { public JSR(Label l) { super(l); } }
284     public class If extends Branch { }
285
286     /** represents a "returnaddr" pushed onto the stack */
287     public class Label extends Expr {
288         public final Op op;
289         public Type getType() { throw new Error("attempted to call getType() on a Label"); }
290         public Label(Op op) { this.op = op; }
291         public Label(int i) { this.op = null; /* FIXME */ }
292     }
293
294     public class New extends Expr {
295         public final Type.Class t;
296         public Type getType() { return t; }
297         public New(Type.Class t) { this.t = t; }
298         public String _toString() { return "new " + t + "()"; }
299     }
300     
301     public class NewArray extends Expr {
302         public final Type.Array t;
303         public final Expr[] dims;
304         public NewArray(Type.Array t, Expr[] dims) { this.t = t; this.dims = dims; }
305         public NewArray(Type.Array t, Expr dim) { this(t,new Expr[]{dim}); }
306         public Type getType() { return t; }
307     }
308     
309     public class Return extends Op {
310         final Expr e;
311         public Return() { this(VOID_EXPR); }
312         public Return(Expr e) { this.e = e; }
313         public String toString() { return e.getType() == Type.VOID ? "return" : ("return "+e.toString()); }
314     }
315
316     /** GETFIELD and GETSTATIC */
317     public class Get extends Expr {
318         final Type.Class.Field f;
319         final Expr e;
320         public Type getType() { return f.getType(); }
321         public Get(Type.Class.Field f) { this(f, null); }
322         public Get(Type.Class.Field f, Expr e) { this.f = f; this.e = e; }
323         public String _toString() {
324             return
325                 (e!=null
326                  ? e+"."+f.name
327                  : f.getDeclaringClass() == JSSA.this.method.getDeclaringClass()
328                  ? f.name
329                  : f.toString());
330         }
331     }
332
333     /** PUTFIELD and PUTSTATIC */
334     public class Put extends Op {
335         final Type.Class.Field f;
336         final Expr v;
337         final Expr e;
338         public Put(Type.Class.Field f, Expr v) { this(f, v, null); }
339         public Put(Type.Class.Field f, Expr v, Expr e) { this.f = f; this.v = v; this.e = e; }
340         public String toString() {
341             return
342                 (e!=null
343                  ? e+"."+f.name
344                  : f.getDeclaringClass() == JSSA.this.method.getDeclaringClass()
345                  ? f.name
346                  : f.toString()) + " = " + v;
347         }
348     }
349
350     public class ArrayPut extends Op {
351         final Expr e, i, v;
352         public ArrayPut(Expr e, Expr i, Expr v) { this.e = e; this.i = i; this.v = v; }
353     }
354
355     public class ArrayGet extends Expr {
356         final Expr e, i;
357         public ArrayGet(Expr e, Expr i) { this.e = e; this.i = i; }
358         public Type getType() { return e.getType().asArray().getElementType(); }
359     }
360
361     public class ArrayLength extends Expr {
362         final Expr e;
363         public ArrayLength(Expr e) { this.e = e; }
364         public Type getType() { return Type.INT; }
365     }
366
367     public abstract class Invoke extends Expr {
368         public final Expr[] arguments;
369         public final Type.Class.Method method;
370         protected Invoke(Type.Class.Method m, Expr[] a) { this.arguments = a; this.method = m; } 
371
372         public Type getType() { return method.getReturnType(); }
373         protected void args(StringBuffer sb) {
374             sb.append("(");
375             for(int i=0; i<arguments.length; i++) {
376                 if (i>0) sb.append(", ");
377                 sb.append(arguments[i]+"");
378             }
379             sb.append(")");
380         }
381
382         public String _toString() {
383             StringBuffer sb = new StringBuffer();
384             sb.append(method.getDeclaringClass() == JSSA.this.method.getDeclaringClass()
385                       ? method.name
386                       : (method.getDeclaringClass() + "." + method.name));
387             args(sb);
388             return sb.toString();
389         }
390     }
391     public class InvokeStatic    extends Invoke  { public InvokeStatic(Type.Class.Method m, Expr[] a) { super(m,a); } }
392     public class InvokeSpecial   extends InvokeVirtual {
393         public InvokeSpecial(Type.Class.Method m, Expr[] a, Expr e) { super(m,a,e); }
394         public String _toString() { return _toString(method.name.equals("<init>") ? method.getDeclaringClass().getName() : method.name); }
395     }
396     public class InvokeInterface extends InvokeVirtual{public InvokeInterface(Type.Class.Method m, Expr[] a, Expr e){super(m,a,e);}}
397     public class InvokeVirtual   extends Invoke  {
398         public final Expr instance;
399         public InvokeVirtual(Type.Class.Method m, Expr[] a, Expr e) { super(m, a); instance = e; }
400         public String _toString() { return _toString(method.name); }
401         protected String _toString(String name) {
402             StringBuffer sb = new StringBuffer();
403             sb.append(instance+".");
404             sb.append(name);
405             args(sb);
406             return sb.toString();
407         }
408     }
409
410     public class Constant extends Expr {
411         private final Object o;
412         public Constant(int i) { this(new Integer(i)); }
413         public Constant(Object o) { this.o = o; }
414         public String _toString() { return o instanceof String ? "\"" + o + "\"" : o.toString(); }
415         public Type getType() {
416             if (o instanceof Byte) return Type.BYTE;
417             if (o instanceof Short) return Type.SHORT;
418             if (o instanceof Character) return Type.CHAR;
419             if (o instanceof Boolean) return Type.BOOLEAN;
420             if (o instanceof Long) return Type.LONG;
421             if (o instanceof Double) return Type.DOUBLE;
422             if (o instanceof Float) return Type.FLOAT;
423             if (o instanceof Integer) return Type.INT;
424             if (o instanceof String) return Type.STRING;
425             throw new IllegalStateException("unknown constant type");
426         }
427     }
428
429
430     // Implementation //////////////////////////////////////////////////////////////////////////////
431
432     private Object addOp(int op, Object arg) {
433         int i1 = 0;
434         int i2 = 0;
435         if (op==WIDE) {
436             MethodGen.Wide w = (MethodGen.Wide)arg;
437             op = w.op;
438             arg = null;
439             i1 = w.varNum;
440             i2 = w.n;
441         }
442         if (op==IINC) {
443             MethodGen.Pair p = (MethodGen.Pair)arg;
444             arg = null;
445             i1 = p.i1;
446             i2 = p.i2;
447         }
448         switch(op) {
449
450             case NOP: return null;
451
452                 // Stack manipulations //////////////////////////////////////////////////////////////////////////////
453
454             case ACONST_NULL:                                                      return stack[sp++] = new Constant(null);
455             case ICONST_M1:                                                        return stack[sp++] = new Constant(-1);
456             case ICONST_0: case LCONST_0: case FCONST_0: case DCONST_0:            push(new Constant(0)); return null;
457             case ICONST_1: case LCONST_1: case FCONST_1: case DCONST_1:            push(new Constant(1)); return null;
458             case ICONST_2: case FCONST_2:                                          push(new Constant(2)); return null;
459             case ICONST_3:                                                         push(new Constant(3)); return null;
460             case ICONST_4:                                                         push(new Constant(4)); return null;
461             case ICONST_5:                                                         push(new Constant(5)); return null;
462             case ILOAD:    case LLOAD:    case FLOAD:    case DLOAD:    case ALOAD:    push(local[i1]); return null;
463             case ILOAD_0:  case LLOAD_0:  case FLOAD_0:  case DLOAD_0:  case ALOAD_0:  push(local[0]); return null;
464             case ILOAD_1:  case LLOAD_1:  case FLOAD_1:  case DLOAD_1:  case ALOAD_1:  push(local[1]); return null;
465             case ALOAD_2:  case DLOAD_2:  case FLOAD_2:  case LLOAD_2:  case ILOAD_2:  push(local[2]); return null;
466             case ILOAD_3:  case LLOAD_3:  case FLOAD_3:  case DLOAD_3:  case ALOAD_3:  push(local[3]); return null;
467             case ISTORE:   case LSTORE:   case FSTORE:   case DSTORE:   case ASTORE:   local[i1] = pop(); return null;
468             case ISTORE_0: case LSTORE_0: case FSTORE_0: case DSTORE_0: case ASTORE_0: local[0]  = pop(); return null;
469             case ISTORE_1: case LSTORE_1: case FSTORE_1: case DSTORE_1: case ASTORE_1: local[1]  = pop(); return null;
470             case ASTORE_2: case DSTORE_2: case FSTORE_2: case LSTORE_2: case ISTORE_2: local[2]  = pop(); return null;
471             case ISTORE_3: case LSTORE_3: case FSTORE_3: case DSTORE_3: case ASTORE_3: local[3]  = pop(); return null;
472             case POP:      pop(); return null;
473             case POP2:     pop(); pop(); return null;
474             case DUP:      push(stack[sp-1]); return null;
475             case DUP2:     push(stack[sp-2]); push(stack[sp-2]); return null;
476
477                 // Conversions //////////////////////////////////////////////////////////////////////////////
478
479                 // coercions are added as-needed when converting from JSSA back to bytecode, so we can
480                 // simply discard them here (assuming the bytecode we're reading in was valid in the first place)
481
482             case I2L: case F2L: case D2L:               push(new Cast(pop(), Type.LONG)); return null;
483             case I2F: case L2F: case D2F:               push(new Cast(pop(), Type.FLOAT)); return null;
484             case I2D: case L2D: case F2D:               push(new Cast(pop(), Type.DOUBLE)); return null;
485             case L2I: case F2I: case D2I:               push(new Cast(pop(), Type.INT)); return null;
486             case I2B:                                   push(new Cast(pop(), Type.BYTE)); return null;
487             case I2C:                                   push(new Cast(pop(), Type.CHAR)); return null;
488             case I2S:                                   push(new Cast(pop(), Type.SHORT)); return null;
489             case SWAP:                                  { Expr e1 = pop(), e2 = pop(); push(e2);  push(e1); return null; }
490
491                 // Math //////////////////////////////////////////////////////////////////////////////
492                    
493             case IADD: case LADD: case FADD: case DADD: push(new Add(pop(), pop())); return null;
494             case ISUB: case LSUB: case FSUB: case DSUB: push(new Sub(pop(), pop())); return null;
495             case IMUL: case LMUL: case FMUL: case DMUL: push(new Mul(pop(), pop())); return null;
496             case IREM: case LREM: case FREM: case DREM: push(new Rem(pop(), pop())); return null;
497                 //case INEG: case LNEG: case FNEG: case DNEG: push(new Neg(pop())); return null;
498             case IDIV: case LDIV: case FDIV: case DDIV: push(new Div(pop(), pop())); return null;
499             case ISHL: case LSHL:                       push(new Shl(pop(), pop())); return null;
500             case ISHR: case LSHR:                       push(new Shr(pop(), pop())); return null;
501             case IUSHR: case LUSHR:                     push(new Ushr(pop(), pop())); return null;
502             case IAND: case LAND:                       push(new And(pop(), pop())); return null;
503             case IOR:  case LOR:                        push(new Or(pop(), pop())); return null;
504             case IXOR: case LXOR:                       push(new Xor(pop(), pop())); return null;
505             case IINC:                                  return local[i1] = new Add(local[i1], new Constant(i2));
506
507                 // Control and branching //////////////////////////////////////////////////////////////////////////////
508
509             case IFNULL:                                return new Branch(new Eq(pop(), new Constant(null)), new Label(i1));
510             case IFNONNULL:                             return new Branch(new Not(new Eq(pop(),new Constant(null))),new Label(i1));
511             case IFEQ:                                  return new Branch(    new Eq(new Constant(0), pop()),  arg);
512             case IFNE:                                  return new Branch(new Not(new Eq(new Constant(0), pop())), arg);
513             case IFLT:                                  return new Branch(    new Lt(new Constant(0), pop()),  arg);
514             case IFGE:                                  return new Branch(new Not(new Lt(new Constant(0), pop())), arg);
515             case IFGT:                                  return new Branch(    new Gt(new Constant(0), pop()),  arg);
516             case IFLE:                                  return new Branch(new Not(new Gt(new Constant(0), pop())), arg);
517             case IF_ICMPEQ:                             return new Branch(    new Eq(pop(), pop()),  arg);
518             case IF_ICMPNE:                             return new Branch(new Not(new Eq(pop(), pop())), arg);
519             case IF_ICMPLT:                             return new Branch(    new Lt(pop(), pop()),  arg);
520             case IF_ICMPGE:                             return new Branch(new Not(new Lt(pop(), pop())), arg);
521             case IF_ICMPGT:                             return new Branch(    new Gt(pop(), pop()),  arg);
522             case IF_ICMPLE:                             return new Branch(new Not(new Gt(pop(), pop())), arg);
523             case IF_ACMPEQ:                             return new Branch(    new Eq(pop(), pop()),  arg);
524             case IF_ACMPNE:                             return new Branch(new Not(new Eq(pop(), pop())), arg);
525             case ATHROW:                                return new Throw(pop());
526             case GOTO:                                  return new Branch(new Label(i1));
527             case JSR:                                   return new JSR(new Label(i1));
528             case RET:                                   return new RET();
529             case RETURN:                                return new Return();
530             case IRETURN: case LRETURN: case FRETURN: case DRETURN: case ARETURN:
531                 return new Return(pop());
532
533                 // Array manipulations //////////////////////////////////////////////////////////////////////////////
534
535             case IALOAD:  case LALOAD:  case FALOAD:  case DALOAD:  case AALOAD:
536             case BALOAD:  case CALOAD:  case SALOAD:
537                 return seqPush(new ArrayGet(pop(), pop()));
538             case IASTORE: case LASTORE: case FASTORE: case DASTORE: case AASTORE:
539             case BASTORE: case CASTORE: case SASTORE:
540                 return new ArrayPut(pop(), pop(), pop());
541
542                 // Invocation //////////////////////////////////////////////////////////////////////////////
543
544             case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: case INVOKEINTERFACE: {
545                 Type.Class.Method method = (Type.Class.Method)arg;
546                 Expr args[] = new Expr[method.getNumArgs()];
547                 for(int i=0; i<args.length; i++) args[args.length-i-1] = pop();
548                 Expr ret;
549                 switch(op) {
550                     case INVOKEVIRTUAL:   ret = new InvokeVirtual(method, args, pop()); break;
551                     case INVOKEINTERFACE: ret = new InvokeInterface(method, args, pop()); break;
552                     case INVOKESPECIAL:   ret = new InvokeSpecial(method, args, pop()); break;
553                     case INVOKESTATIC:    ret = new InvokeStatic(method, args); break;
554                     default: throw new Error("should never happen");
555                 }
556                 if(ret.getType() != Type.VOID) push(ret);
557                 return new Seq(ret);
558             }
559
560                 // Field Access //////////////////////////////////////////////////////////////////////////////
561
562             case GETSTATIC:         return seqPush(new Get((Type.Class.Field)arg, null));
563             case PUTSTATIC:         return new Put((Type.Class.Field)arg, pop(), null);
564             case GETFIELD:          return seqPush(new Get((Type.Class.Field)arg, pop()));
565             case PUTFIELD:          return new Put((Type.Class.Field)arg, pop(), pop());
566
567                 // Allocation //////////////////////////////////////////////////////////////////////////////
568
569             case NEW:               push(new New((Type.Class)arg)); return null;
570             case NEWARRAY: {
571                 Type base;
572                 switch(((Integer)arg).intValue()) {
573                     case 4: base = Type.BOOLEAN; break;
574                     case 5: base = Type.CHAR; break;
575                     case 6: base = Type.FLOAT; break;
576                     case 7: base = Type.DOUBLE; break;
577                     case 8: base = Type.BYTE; break;
578                     case 9: base = Type.SHORT; break;
579                     case 10: base = Type.INT; break;
580                     case 11: base = Type.LONG; break;
581                     default: throw new IllegalStateException("invalid array type");
582                 }
583                 return seqPush(new NewArray(base.makeArray(),pop()));
584             }
585             case ANEWARRAY:         push(new NewArray(((Type.Ref)arg).makeArray(), pop())); return null;
586             case MULTIANEWARRAY: {
587                 MethodGen.MultiANewArray mana = (MethodGen.MultiANewArray) arg;
588                 Expr[] dims = new Expr[mana.dims];
589                 for(int i=0;i<dims.length;i++) dims[i] = pop();
590                 return seqPush(new NewArray(mana.type, dims));
591             }
592             case ARRAYLENGTH:       return seqPush(new ArrayLength(pop()));
593
594                 // Runtime Type information //////////////////////////////////////////////////////////////////////////////
595
596             case CHECKCAST:         return seqPush(new Cast(pop(), (Type.Ref)arg));
597             case INSTANCEOF:        push(new InstanceOf(pop(), (Type.Ref)arg)); return null;
598
599             case LDC: case LDC_W: case LDC2_W: push(new Constant(arg)); return null;
600
601             case BIPUSH:    push(new Constant((Integer)arg)); return null;
602             case SIPUSH:    push(new Constant((Integer)arg)); return null;
603
604             case TABLESWITCH:    return new Branch((MethodGen.Switch)arg);
605             case LOOKUPSWITCH:   return new Branch((MethodGen.Switch)arg);
606
607                 /*
608             case MONITORENTER:   Op.monitorEnter(pop());
609             case MONITOREXIT:    Op.monitorExit(pop());
610                 */
611
612             case DUP_X1:         throw new Error("unimplemented");
613             case DUP_X2:         throw new Error("unimplemented");
614             case DUP2_X1:         throw new Error("unimplemented");
615             case DUP2_X2:         throw new Error("unimplemented");
616             case LCMP:         throw new Error("unimplemented");
617             case FCMPL:         throw new Error("unimplemented");
618             case FCMPG:         throw new Error("unimplemented");
619             case DCMPL:         throw new Error("unimplemented");
620             case DCMPG:         throw new Error("unimplemented");
621             case GOTO_W:         throw new Error("unimplemented");
622             case JSR_W:         throw new Error("unimplemented");
623             default:          throw new Error("unhandled");
624         }
625     }
626
627     
628     public void debugBodyToString(StringBuffer sb) {
629         StringBuffer sb0 = new StringBuffer();
630         super.debugBodyToString(sb0);
631         StringTokenizer st = new StringTokenizer(sb0.toString(), "\n");
632         String[] lines = new String[st.countTokens()];
633         for(int i=0; i<lines.length; i++) lines[i] = st.nextToken();
634         for(int j=0; j<ofs[0]; j++) {
635             String s = "    /* " + lines[j].trim();
636              while(s.length() < 50) s += " ";
637              s += " */";
638              sb.append(s);
639              sb.append("\n");
640         }
641         
642         bindingMap = new IdentityHashMap();
643         nextVar = 0;
644         
645         for(int i=0; i<numOps; i++) {
646             String s = "    /* " + lines[ofs[i]].trim();
647              while(s.length() < 50) s += " ";
648              s += " */  ";
649              s += ops[i].toString();
650              sb.append(s);
651              sb.append(";\n");
652              for(int j=ofs[i]+1; j<(i==numOps-1?size():ofs[i+1]); j++) {
653                  s = "    /* " + lines[j].trim();
654                   while(s.length() < 50) s += " ";
655                   s += " */";
656                   sb.append(s);
657                   sb.append("\n");
658              }
659         }
660     }
661     
662     private Map bindingMap;
663     private int nextVar;
664     String exprToString(Expr e) {
665         if(e instanceof Constant) return e._toString();
666         String s = (String)bindingMap.get(e);
667         if(s != null) return s;
668         String prefix;
669         if(e.getType() == Type.VOID) return e._toString();
670         else if(e.getType() == Type.DOUBLE || e.getType() == Type.FLOAT) prefix = "f";
671         else if(e.getType().isPrimitive()) prefix = "i";
672         else if(e.getType().isArray()) prefix = "a";
673         else prefix = "o";
674         s = prefix + (nextVar++);
675         bindingMap.put(e,s);
676         return "(" + s + ":= " + e._toString() + ")";
677     }
678     
679     public static void main(String[] args) throws Exception {
680         InputStream is = Class.forName(args[0]).getClassLoader().getResourceAsStream(args[0].replace('.', '/')+".class");
681         System.out.println(new ClassFile(new DataInputStream(is), true).toString());
682     }
683 }