1 package org.ibex.classgen;
6 * a highly streamlined SSA-form intermediate representation of a
7 * sequence of JVM instructions; all stack manipulation is factored
10 public class JSSA extends MethodGen implements CGConst {
12 // Constructor //////////////////////////////////////////////////////////////////////////////
14 public JSSA(Type.Class c, DataInput in, ConstantPool cp) throws IOException {
17 stacks = new Phi[size()][];
18 locals = new Phi[size()][];
19 for(int i=0; i<size(); i++) {
21 locals[i] = new Phi[maxLocals];
22 for(int j=0; j<locals[i].length; j++) locals[i][j] = new Phi();
24 if (!isStatic()) locals[i][n++].merge(new Argument("this",method.getDeclaringClass()));
25 for(int j=0;j<this.method.getNumArgs(); j++) locals[i][n++].merge(new Argument("arg"+j,this.method.getArgType(j)));
29 stack = new Phi[maxStack];
31 for(pc=0; pc<size(); pc++) {
33 Object arg = getArg(pc);
35 Object o = addOp(op, arg);
40 if (o!=null && o instanceof Branch) ((Branch)o).branchTo();
41 if (o==null || (!(o instanceof Branch))) branchTo(pc+1);
42 } catch(RuntimeException e) {
43 System.err.println("Had a problem at PC: " + pc + " of " + method);
45 throw new IOException("invalid class file");
50 public void branchTo(int newPC) {
51 System.out.println("!!!branchTo("+newPC+")!!!");
52 if (stacks[newPC] == null) {
53 stacks[newPC] = new Phi[sp];
54 for(int i=0; i<sp; i++) stacks[newPC][i] = new Phi();
56 if (stacks[newPC].length != sp)
57 throw new IllegalArgumentException("stack depth disagreement: " + sp + " " + stacks[newPC].length);
58 for(int i=0; i<stacks[newPC].length; i++) stacks[newPC][i].merge(stack[i]);
59 for(int i=0; i<maxLocals; i++) locals[newPC][i].merge(locals[pc][i]);
62 private Object[] ops = new Object[65535];
63 private int[] ofs = new int[65535];
64 private int numOps = 0;
66 // Instance Data; used ONLY during constructor; then thrown away /////////////////////////////////////////////////
68 /** this models the JVM locals; it is only used for unwinding stack-ops into an SSA-tree, then thrown away */
69 private final Phi[][] locals;
71 /** this models the JVM stack; it is only used for unwinding stack-ops into an SSA-tree, then thrown away */
72 private final Phi[][] stacks;
73 private final Phi[] stack;
75 /** JVM stack pointer */
79 private void push(Expr e) {
80 if (e.getType() == Type.VOID) throw new IllegalArgumentException("can't push a void");
81 if (stack[sp] == null) stack[sp] = new Phi();
85 Expr ret = stack[sp-1];
91 private Op seqPush(Expr e) {
97 // SSA-node classes /////////////////////////////////////////////////////////////////////////////////////////
99 public final Expr VOID_EXPR = new Expr() {
100 public Type getType() { return Type.VOID; }
103 /** an purely imperative operation which does not generate data */
104 public abstract class Op {
105 //public abstract Op[] predecessors(); // not implemented yet
106 //public abstract Op[] successors(); // not implemented yet
107 public String toString() { return name(); }
109 String name = this.getClass().getName();
110 if (name.indexOf('$') != -1) name = name.substring(name.lastIndexOf('$')+1);
111 if (name.indexOf('.') != -1) name = name.substring(name.lastIndexOf('.')+1);
116 /** A sequence point. expr is evaluated for side effects at this point, this does not generate data
117 Expressions that haven't been evaluated with Seq are evaluated when they are first encountered
119 public class Seq extends Op {
120 private final Expr expr;
121 public String toString() { return expr.toString(); }
122 public Seq(Expr expr) { this.expr = expr; }
125 /** an operation which generates data */
126 public abstract class Expr extends Op {
127 //public abstract Expr[] contributors(); // not implemented yet
128 //public abstract Expr[] dependents(); // not implemented yet
130 /** every JSSA.Expr either remembers its type _OR_ knows how to figure it out (the latter is preferred to eliminate
131 * redundant information that could possibly "disagree" with itself -- this happened a LOT in Soot) */
132 public abstract Type getType();
133 public String _toString() { return super.toString(); }
134 public String toString() { return _toString(); } /*
135 String s = (String)bindingMap.get(this);
136 if (s != null) return s;
138 if (getType() == Type.VOID) return _toString();
139 else if (getType() == Type.DOUBLE || getType() == Type.FLOAT) prefix = "f";
140 else if (getType().isPrimitive()) prefix = "i";
141 else if (getType().isArray()) prefix = "a";
143 s = prefix + (nextVar++);
144 bindingMap.put(this,s);
145 return "(" + s + " = " + _toString() + ")";
150 * A "nondeterministic merge" -- for example when the first
151 * instruction in a loop reads from a local which could have been
152 * written to either by some instruction at the end of the
153 * previous iteration of the loop or by some instruction before
154 * the loop (on the first iteration).
156 * Note that Phi's are *mutable*. This means that when one Phi
157 * holds a reference to another, updates to the referenced Phi
158 * are "seen" by the other Phi. This prevents us from having to
159 * repeat loops to propagate the Phis.
161 public class Phi extends Expr {
163 public Phi(Expr[] inputs) { this.inputs = inputs; }
164 public Phi() { this.inputs = new Expr[0]; }
165 public Phi(Expr e1) {
166 this.inputs = new Expr[1];
169 public Phi(Expr e1, Expr e2) {
170 this.inputs = new Expr[2];
174 public void merge(Expr e) {
176 for(int i=0; i<inputs.length; i++) if (inputs[i]==e) return;
177 Expr[] newinputs = new Expr[inputs.length + 1];
178 System.arraycopy(inputs, 0, newinputs, 0, inputs.length);
179 newinputs[newinputs.length-1] = e;
182 public String toString() {
183 if (inputs.length == 1) return inputs[0].toString();
184 StringBuffer ret = new StringBuffer();
186 for(int i=0; i<inputs.length; i++) {
187 String s = inputs[i].toString().trim();
188 if (s.length() == 0) continue;
193 if (count == 0) return "";
194 if (count == 1) return ret.toString().trim();
195 return "{{ " + ret.toString() + "}}";
196 //return "{"+ret+"}";
198 public Type getType() {
199 if (inputs.length == 0) return null;
201 Type t = inputs[0].getType();
203 // FIXME: actually this should check type-unifiability... fe, the "type of null" unifies with any Type.Ref
204 for(int i=1; i<inputs.length; i++) {
205 if (t==null) { t = inputs[i].getType(); continue; }
206 if (inputs[i].getType() == null) continue;
207 if (inputs[i].getType() != t)
208 throw new Error("Phi node with disagreeing types: " + t + " " + inputs[i].getType() +"\n Crisis!");
214 public Phi phi(Expr e) { return e instanceof Phi ? ((Phi)e) : new Phi(e); }
216 public class Argument extends Expr {
217 public final String name;
219 public Argument(String name, Type t) { this.name = name; this.t = t; }
220 public String _toString() { return name; }
221 public Type getType() { return t; }
225 // Unary Operations //////////////////////////////////////////////////////////////////////////////
227 public class Not extends Expr {
230 if (e.getType() != Type.BOOLEAN) throw new IllegalArgumentException("not needs a boolean expression");
233 public Type getType() { return Type.BOOLEAN; }
234 public String _toString() { return "!(" + e + ")"; }
237 public class Neg extends Expr {
240 if (!e.getType().isPrimitive()) throw new IllegalArgumentException("can only negate a primitive");
243 public Type getType() { return e.getType(); }
244 public String _toString() { return "- (" + e + ")"; }
248 // Binary Operations //////////////////////////////////////////////////////////////////////////////
250 public abstract class BinExpr extends Expr {
251 public final Expr e1;
252 public final Expr e2;
253 private final String show;
254 public BinExpr(Expr e1, Expr e2, String show) { this.e1 = e1; this.e2 = e2; this.show = show; }
255 public String _toString() {
256 // FEATURE: should we be doing some precedence stuff here? probably not worth it for debugging output
257 return "(" + e1 + show + e2 + ")";
261 public class Comparison extends BinExpr {
262 public Comparison(Expr e1, Expr e2, String show) { super(e1, e2, show); }
263 public Type getType() { return Type.BOOLEAN; }
266 public class Eq extends Comparison {
267 public Eq(Expr e1, Expr e2) {
269 if (e1.getType().isPrimitive() != e2.getType().isPrimitive())
270 throw new IllegalArgumentException("type mismatch: " + e1.getType() + " and " + e2.getType());
271 if (e1.getType().isPrimitive() && e1.getType() != e2.getType())
272 throw new IllegalArgumentException("type mismatch: " + e1.getType() + " and " + e2.getType());
273 // FEATURE: Check if we can compare these classes
278 public class PrimitiveComparison extends Comparison {
279 public PrimitiveComparison(Expr e1, Expr e2, String show) {
281 if (!e1.getType().isPrimitive() || e1.getType() != e2.getType()) throw new IllegalArgumentException("type mismatch");
285 public class Gt extends PrimitiveComparison { public Gt(Expr e1, Expr e2) { super(e1, e2, ">"); } }
286 public class Lt extends PrimitiveComparison { public Lt(Expr e1, Expr e2) { super(e1, e2, "<"); } }
287 public class Ge extends PrimitiveComparison { public Ge(Expr e1, Expr e2) { super(e1, e2, ">="); } }
288 public class Le extends PrimitiveComparison { public Le(Expr e1, Expr e2) { super(e1, e2, "<="); } }
292 // Math Operations //////////////////////////////////////////////////////////////////////////////
294 public class BinMath extends BinExpr {
295 public BinMath(Expr e1, Expr e2, String show) {
297 if (e1.getType() != null && e2.getType() != null && e1.getType() != e2.getType())
298 throw new IllegalArgumentException("types disagree");
300 public Type getType() { return e1.getType(); }
303 public class Add extends BinMath { public Add(Expr e, Expr e2) { super(e, e2, "+"); } }
304 public class Sub extends BinMath { public Sub(Expr e, Expr e2) { super(e, e2, "-"); } }
305 public class Mul extends BinMath { public Mul(Expr e, Expr e2) { super(e, e2, "*"); } }
306 public class Rem extends BinMath { public Rem(Expr e, Expr e2) { super(e, e2, "%"); } }
307 public class Div extends BinMath { public Div(Expr e, Expr e2) { super(e, e2, "/"); } }
308 public class And extends BinMath { public And(Expr e, Expr e2) { super(e, e2, "&"); } }
309 public class Or extends BinMath { public Or(Expr e, Expr e2) { super(e, e2, "|"); } }
310 public class Xor extends BinMath { public Xor(Expr e, Expr e2) { super(e, e2, "^"); } }
312 public class BitShiftExpr extends BinExpr {
313 public BitShiftExpr(Expr e1, Expr e2, String show) {
315 Type t = e1.getType();
316 if (t != Type.INT && t != Type.LONG) throw new IllegalArgumentException("type mismatch");
317 if (e2.getType() != Type.INT) throw new IllegalArgumentException("type mismatch");
319 public Type getType() { return e1.getType(); }
321 public class Shl extends BitShiftExpr { public Shl(Expr e, Expr e2) { super(e, e2, "<<"); } }
322 public class Shr extends BitShiftExpr { public Shr(Expr e, Expr e2) { super(e, e2, ">>"); } }
323 public class Ushr extends BitShiftExpr { public Ushr(Expr e, Expr e2) { super(e, e2, ">>>"); } }
326 // Other operations //////////////////////////////////////////////////////////////////////////////
328 public class Cast extends Expr {
331 public Cast(Expr e, Type t) {
332 if (e.getType().isRef() != t.isRef()) throw new IllegalArgumentException("invalid cast");
333 // FEATURE: Check that one is a subclass of the other if it is a ref
337 public Type getType() { return t; }
340 public class InstanceOf extends Expr {
343 public InstanceOf(Expr e, Type.Ref t) {
344 if (!e.getType().isRef()) throw new IllegalArgumentException("can't do an instanceof check on a non-ref");
348 public Type getType() { return Type.BOOLEAN; }
351 public class Branch extends Op {
352 Expr destination = null;
353 public Branch(Expr destination) { this.destination = destination; }
354 public Branch(MethodGen.Switch s) { /* FIXME */ }
356 public void branchTo() { if (destination != null) branchTo(destination); }
357 private void branchTo(Expr e) {
358 if (e instanceof Phi) {
360 for(int i=0; i<phi.inputs.length; i++) branchTo(phi.inputs[i]);
361 } else if (e instanceof Label) {
362 JSSA.this.branchTo(((Label)e).pc);
364 throw new IllegalArgumentException("can't branch to a " + e.getClass());
368 public class Throw extends Branch {
370 public Throw(Expr e) {
371 if (!e.getType().isRef()) throw new IllegalArgumentException("can't throw a non ref");
372 // FEATURE: CHeck that it is a subclass of Throwable
376 public class Return extends Branch {
378 public Return() { this(VOID_EXPR); }
379 public Return(Expr e) {
381 if (Type.unify(method.getReturnType(),e.getType()) != method.getReturnType())
382 throw new IllegalArgumentException("type mismatch");
384 public String toString() { return e.getType() == Type.VOID ? "return" : ("return "+e.toString()); }
386 public class Goto extends Branch {
387 public Goto(Expr destination) { super(destination); }
388 public String toString() { return "goto " + destination; }
390 public class RET extends Branch {
391 public RET(Expr destination) { super(destination); }
392 public String toString() { return "retsub [" + destination + "]"; }
394 public class JSR extends Branch {
395 public JSR(Expr destination) { super(destination); }
396 public String toString() { return "callsub " + destination; }
398 public class If extends Branch {
399 Expr condition = null;
400 public If(Expr condition, Expr destination) { super(destination); this.condition = condition; }
401 public String toString() { return "if (" + condition + ") goto " + destination; }
402 public void branchTo() {
403 if (condition != null) JSSA.this.branchTo(pc+1);
408 /** represents a "returnaddr" pushed onto the stack */
409 public class Label extends Expr {
411 public Label(int i) { this.pc = i; }
412 public Type getType() { throw new Error("attempted to call getType() on a Label"); }
413 public String toString() { return "<<label " + pc + ">>"; }
416 public class New extends Expr {
417 public final Type.Class t;
418 public Type getType() { return t; }
419 public New(Type.Class t) { this.t = t; }
420 public String _toString() { return "new " + t + "()"; }
423 public class NewArray extends Expr {
424 public final Type.Array t;
425 public final Expr[] dims;
426 public NewArray(Type.Array t, Expr[] dims) { this.t = t; this.dims = dims; }
427 public NewArray(Type.Array t, Expr dim) { this(t,new Expr[]{dim}); }
428 public Type getType() { return t; }
429 public String _toString() {
432 while(base.isArray()) {
434 base = base.asArray().getElementType();
436 StringBuffer sb = new StringBuffer("new " + base);
437 for(int i=0;i<totalDims;i++)
438 sb.append("[" + (i < dims.length ? dims[i].toString() : "") + "]");
439 return sb.toString();
443 /** GETFIELD and GETSTATIC */
444 public class Get extends Expr {
445 final Type.Class.Field f;
447 public Type getType() { return f.getType(); }
448 public Get(Type.Class.Field f) { this(f, null); }
449 public Get(Type.Class.Field f, Expr e) { this.f = f; this.e = e; }
450 public String _toString() {
454 : f.getDeclaringClass() == JSSA.this.method.getDeclaringClass()
460 /** PUTFIELD and PUTSTATIC */
461 public class Put extends Op {
462 final Type.Class.Field f;
465 public Put(Type.Class.Field f, Expr v) { this(f, v, null); }
466 public Put(Type.Class.Field f, Expr v, Expr e) { this.f = f; this.v = v; this.e = e; }
467 public String toString() {
471 : f.getDeclaringClass() == JSSA.this.method.getDeclaringClass()
473 : f.toString()) + " = " + v;
477 public class ArrayPut extends Op {
479 public ArrayPut(Expr v, Expr i, Expr e) { this.e = e; this.i = i; this.v = v; }
480 public String toString() { return e + "[" + i + "] := " + v; }
483 public class ArrayGet extends Expr {
485 public ArrayGet(Expr i, Expr e) { this.e = e; this.i = i; }
486 public Type getType() { return e.getType().asArray().getElementType(); }
487 public String _toString() { return e + "[" + i + "]"; }
490 public class ArrayLength extends Expr {
492 public ArrayLength(Expr e) { this.e = e; }
493 public Type getType() { return Type.INT; }
496 public abstract class Invoke extends Expr {
497 public final Expr[] arguments;
498 public final Type.Class.Method method;
499 protected Invoke(Type.Class.Method m, Expr[] a) { this.arguments = a; this.method = m; }
501 public Type getType() { return method.getReturnType(); }
502 protected void args(StringBuffer sb) {
504 for(int i=0; i<arguments.length; i++) {
505 if (i>0) sb.append(", ");
506 sb.append(arguments[i]+"");
511 public String _toString() {
512 StringBuffer sb = new StringBuffer();
513 sb.append(method.getDeclaringClass() == JSSA.this.method.getDeclaringClass()
515 : (method.getDeclaringClass() + "." + method.name));
517 return sb.toString();
520 public class InvokeStatic extends Invoke { public InvokeStatic(Type.Class.Method m, Expr[] a) { super(m,a); } }
521 public class InvokeSpecial extends InvokeVirtual {
522 public InvokeSpecial(Type.Class.Method m, Expr[] a, Expr e) { super(m,a,e); }
523 public String _toString() { return _toString(method.name.equals("<init>")
524 ? method.getDeclaringClass().getName()
527 public class InvokeInterface extends InvokeVirtual{
528 public InvokeInterface(Type.Class.Method m, Expr[] a, Expr e) { super(m,a,e); } }
529 public class InvokeVirtual extends Invoke {
530 public final Expr instance;
531 public InvokeVirtual(Type.Class.Method m, Expr[] a, Expr e) { super(m, a); instance = e; }
532 public String _toString() { return _toString(method.name); }
533 protected String _toString(String name) {
534 StringBuffer sb = new StringBuffer();
535 sb.append(instance+".");
538 return sb.toString();
542 public class Constant extends Expr {
543 private final Object o;
544 public Constant(int i) { this(new Integer(i)); }
545 public Constant(Object o) { this.o = o; }
546 public String toString() { return o == null ? "null" : o instanceof String ? "\"" + o + "\"" : o.toString(); }
547 public Type getType() {
548 if (o == null) return Type.NULL;
549 if (o instanceof Byte) return Type.BYTE;
550 if (o instanceof Short) return Type.SHORT;
551 if (o instanceof Character) return Type.CHAR;
552 if (o instanceof Boolean) return Type.BOOLEAN;
553 if (o instanceof Long) return Type.LONG;
554 if (o instanceof Double) return Type.DOUBLE;
555 if (o instanceof Float) return Type.FLOAT;
556 if (o instanceof Integer) return Type.INT;
557 if (o instanceof String) return Type.STRING;
558 throw new IllegalStateException("unknown constant type");
563 // Implementation //////////////////////////////////////////////////////////////////////////////
565 private Object addOp(int op, Object arg) {
569 MethodGen.Wide w = (MethodGen.Wide)arg;
576 MethodGen.Pair p = (MethodGen.Pair)arg;
581 if (arg instanceof Number) i1 = ((Integer)arg).intValue();
582 Label label = (arg instanceof Label) ? (Label)arg : null;
585 case NOP: return null;
587 // Stack manipulations //////////////////////////////////////////////////////////////////////////////
589 case ACONST_NULL: push(new Constant(null)); return null;
590 case ICONST_M1: push(new Constant(-1)); return null;
591 case ICONST_0: case LCONST_0: case FCONST_0: case DCONST_0: push(new Constant(0)); return null;
592 case ICONST_1: case LCONST_1: case FCONST_1: case DCONST_1: push(new Constant(1)); return null;
593 case ICONST_2: case FCONST_2: push(new Constant(2)); return null;
594 case ICONST_3: push(new Constant(3)); return null;
595 case ICONST_4: push(new Constant(4)); return null;
596 case ICONST_5: push(new Constant(5)); return null;
597 case ILOAD: case LLOAD: case FLOAD: case DLOAD: case ALOAD: push(locals[pc][i1]); return null;
598 case ILOAD_0: case LLOAD_0: case FLOAD_0: case DLOAD_0: case ALOAD_0: push(locals[pc][0]); return null;
599 case ILOAD_1: case LLOAD_1: case FLOAD_1: case DLOAD_1: case ALOAD_1: push(locals[pc][1]); return null;
600 case ALOAD_2: case DLOAD_2: case FLOAD_2: case LLOAD_2: case ILOAD_2: push(locals[pc][2]); return null;
601 case ILOAD_3: case LLOAD_3: case FLOAD_3: case DLOAD_3: case ALOAD_3: push(locals[pc][3]); return null;
602 case ISTORE: case LSTORE: case FSTORE: case DSTORE: case ASTORE:
603 locals[pc+1][i1].merge(pop()); return null;
604 case ISTORE_0: case LSTORE_0: case FSTORE_0: case DSTORE_0: case ASTORE_0:
605 locals[pc+1][0].merge(pop()); return null;
606 case ISTORE_1: case LSTORE_1: case FSTORE_1: case DSTORE_1: case ASTORE_1:
607 locals[pc+1][1].merge(pop()); return null;
608 case ASTORE_2: case DSTORE_2: case FSTORE_2: case LSTORE_2: case ISTORE_2:
609 locals[pc+1][2].merge(pop()); return null;
610 case ISTORE_3: case LSTORE_3: case FSTORE_3: case DSTORE_3: case ASTORE_3:
611 locals[pc+1][3].merge(pop()); return null;
612 case POP: pop(); return null;
613 case POP2: pop(); pop(); return null;
614 case DUP: push(stack[sp-1]); return null;
615 case DUP2: push(stack[sp-2]); push(stack[sp-1]); return null;
617 // Conversions //////////////////////////////////////////////////////////////////////////////
619 // coercions are added as-needed when converting from JSSA back to bytecode, so we can
620 // simply discard them here (assuming the bytecode we're reading in was valid in the first place)
622 case I2L: case F2L: case D2L: push(new Cast(pop(), Type.LONG)); return null;
623 case I2F: case L2F: case D2F: push(new Cast(pop(), Type.FLOAT)); return null;
624 case I2D: case L2D: case F2D: push(new Cast(pop(), Type.DOUBLE)); return null;
625 case L2I: case F2I: case D2I: push(new Cast(pop(), Type.INT)); return null;
626 case I2B: push(new Cast(pop(), Type.BYTE)); return null;
627 case I2C: push(new Cast(pop(), Type.CHAR)); return null;
628 case I2S: push(new Cast(pop(), Type.SHORT)); return null;
629 case SWAP: { Expr e1 = pop(), e2 = pop(); push(e2); push(e1); return null; }
631 // Math //////////////////////////////////////////////////////////////////////////////
633 case IADD: case LADD: case FADD: case DADD: push(new Add(pop(), pop())); return null;
634 case ISUB: case LSUB: case FSUB: case DSUB: push(new Sub(pop(), pop())); return null;
635 case IMUL: case LMUL: case FMUL: case DMUL: push(new Mul(pop(), pop())); return null;
636 case IREM: case LREM: case FREM: case DREM: push(new Rem(pop(), pop())); return null;
637 case INEG: case LNEG: case FNEG: case DNEG: push(new Neg(pop())); return null;
638 case IDIV: case LDIV: case FDIV: case DDIV: push(new Div(pop(), pop())); return null;
639 case ISHL: case LSHL: push(new Shl(pop(), pop())); return null;
640 case ISHR: case LSHR: push(new Shr(pop(), pop())); return null;
641 case IUSHR: case LUSHR: push(new Ushr(pop(), pop())); return null;
642 case IAND: case LAND: push(new And(pop(), pop())); return null;
643 case IOR: case LOR: push(new Or(pop(), pop())); return null;
644 case IXOR: case LXOR: push(new Xor(pop(), pop())); return null;
645 case IINC: return locals[pc+1][i1] = phi(new Add(locals[pc][i1], new Constant(i2)));
647 // Control and branching //////////////////////////////////////////////////////////////////////////////
649 case IFNULL: return new If(new Eq(pop(), new Constant(null)), new Label(i1));
650 case IFNONNULL: return new If(new Not(new Eq(pop(),new Constant(null))),new Label(i1));
651 case IFEQ: return new If( new Eq(new Constant(0), pop()), new Label(i1));
652 case IFNE: return new If(new Not(new Eq(new Constant(0), pop())), new Label(i1));
653 case IFLT: return new If( new Lt(new Constant(0), pop()), new Label(i1));
654 case IFGE: return new If(new Not(new Lt(new Constant(0), pop())), new Label(i1));
655 case IFGT: return new If( new Gt(new Constant(0), pop()), new Label(i1));
656 case IFLE: return new If(new Not(new Gt(new Constant(0), pop())), new Label(i1));
657 case IF_ICMPEQ: return new If( new Eq(pop(), pop()), new Label(i1));
658 case IF_ICMPNE: return new If(new Not(new Eq(pop(), pop())), new Label(i1));
659 case IF_ICMPLT: return new If( new Lt(pop(), pop()), new Label(i1));
660 case IF_ICMPGE: return new If(new Not(new Lt(pop(), pop())), new Label(i1));
661 case IF_ICMPGT: return new If( new Gt(pop(), pop()), new Label(i1));
662 case IF_ICMPLE: return new If(new Not(new Gt(pop(), pop())), new Label(i1));
663 case IF_ACMPEQ: return new If( new Eq(pop(), pop()), new Label(i1));
664 case IF_ACMPNE: return new If(new Not(new Eq(pop(), pop())), new Label(i1));
665 case ATHROW: return new Throw(pop());
666 case GOTO: return new Goto(locals[pc][i1]);
667 case JSR: push(new Label(pc)); return new JSR(new Label(i1));
668 case RET: return new RET(pop());
669 case RETURN: return new Return();
670 case IRETURN: case LRETURN: case FRETURN: case DRETURN: case ARETURN:
671 return new Return(pop());
673 // Array manipulations //////////////////////////////////////////////////////////////////////////////
675 case IALOAD: case LALOAD: case FALOAD: case DALOAD: case AALOAD:
676 case BALOAD: case CALOAD: case SALOAD:
677 return seqPush(new ArrayGet(pop(), pop()));
678 case IASTORE: case LASTORE: case FASTORE: case DASTORE: case AASTORE:
679 case BASTORE: case CASTORE: case SASTORE:
680 return new ArrayPut(pop(), pop(), pop());
682 // Invocation //////////////////////////////////////////////////////////////////////////////
684 case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: case INVOKEINTERFACE: {
685 Type.Class.Method method = (Type.Class.Method)arg;
686 Expr args[] = new Expr[method.getNumArgs()];
687 for(int i=0; i<args.length; i++) args[args.length-i-1] = pop();
690 case INVOKEVIRTUAL: ret = new InvokeVirtual(method, args, pop()); break;
691 case INVOKEINTERFACE: ret = new InvokeInterface(method, args, pop()); break;
692 case INVOKESPECIAL: ret = new InvokeSpecial(method, args, pop()); break;
693 case INVOKESTATIC: ret = new InvokeStatic(method, args); break;
694 default: throw new Error("should never happen");
696 if (ret.getType() != Type.VOID) push(ret);
700 // Field Access //////////////////////////////////////////////////////////////////////////////
702 case GETSTATIC: return seqPush(new Get((Type.Class.Field)arg, null));
703 case PUTSTATIC: return new Put((Type.Class.Field)arg, pop(), null);
704 case GETFIELD: return seqPush(new Get((Type.Class.Field)arg, pop()));
705 case PUTFIELD: return new Put((Type.Class.Field)arg, pop(), pop());
707 // Allocation //////////////////////////////////////////////////////////////////////////////
709 case NEW: push(new New((Type.Class)arg)); return null;
710 case NEWARRAY: return seqPush(new NewArray(Type.fromArraySpec(((Integer)arg).intValue()).makeArray(), pop()));
711 case ANEWARRAY: push(new NewArray(((Type.Ref)arg).makeArray(), pop())); return null;
712 case MULTIANEWARRAY: {
713 MethodGen.MultiANewArray mana = (MethodGen.MultiANewArray) arg;
714 Expr[] dims = new Expr[mana.dims];
715 for(int i=0;i<dims.length;i++) dims[i] = pop();
716 return seqPush(new NewArray(mana.type, dims));
718 case ARRAYLENGTH: return seqPush(new ArrayLength(pop()));
720 // Runtime Type information //////////////////////////////////////////////////////////////////////////////
722 case CHECKCAST: return seqPush(new Cast(pop(), (Type.Ref)arg));
723 case INSTANCEOF: push(new InstanceOf(pop(), (Type.Ref)arg)); return null;
725 case LDC: case LDC_W: case LDC2_W: push(new Constant(arg)); return null;
727 case BIPUSH: push(new Constant((Integer)arg)); return null;
728 case SIPUSH: push(new Constant((Integer)arg)); return null;
730 case TABLESWITCH: return new Branch((MethodGen.Switch)arg);
731 case LOOKUPSWITCH: return new Branch((MethodGen.Switch)arg);
734 case MONITORENTER: Op.monitorEnter(pop());
735 case MONITOREXIT: Op.monitorExit(pop());
738 case DUP_X1: throw new Error("unimplemented");
739 case DUP_X2: throw new Error("unimplemented");
740 case DUP2_X1: throw new Error("unimplemented");
741 case DUP2_X2: throw new Error("unimplemented");
742 case LCMP: throw new Error("unimplemented");
743 case FCMPL: throw new Error("unimplemented");
744 case FCMPG: throw new Error("unimplemented");
745 case DCMPL: throw new Error("unimplemented");
746 case DCMPG: throw new Error("unimplemented");
747 case GOTO_W: throw new Error("unimplemented");
748 case JSR_W: throw new Error("unimplemented");
749 default: throw new Error("unhandled");
754 public void debugBodyToString(StringBuffer sb) {
755 StringBuffer sb0 = new StringBuffer();
756 super.debugBodyToString(sb0);
757 StringTokenizer st = new StringTokenizer(sb0.toString(), "\n");
758 String[] lines = new String[st.countTokens()];
759 for(int i=0; i<lines.length; i++) lines[i] = st.nextToken();
760 for(int j=0; j<ofs[0]; j++) {
761 String s = " /* " + lines[j].trim();
762 while(s.length() < 50) s += " ";
768 bindingMap = new IdentityHashMap();
771 for(int i=0; i<numOps; i++) {
772 String s = " /* " + lines[ofs[i]].trim();
773 while(s.length() < 50) s += " ";
775 s += ops[i].toString();
778 for(int j=ofs[i]+1; j<(i==numOps-1?size():ofs[i+1]); j++) {
779 s = " /* " + lines[j].trim();
780 while(s.length() < 50) s += " ";
788 private Map bindingMap;
791 public static void main(String[] args) throws Exception {
792 InputStream is = Class.forName(args[0]).getClassLoader().getResourceAsStream(args[0].replace('.', '/')+".class");
793 System.out.println(new ClassFile(new DataInputStream(is), true).toString());