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 {
16 local = new Expr[maxLocals];
17 stack = new Expr[maxStack];
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++) {
25 Object arg = getArg(i);
27 Object o = addOp(op, arg);
32 } catch(RuntimeException e) {
33 System.err.println("Had a problem at PC: " + i + " of " + method);
35 throw new IOException("invalid class file");
40 public void debugBodyToString(StringBuffer sb) {
41 StringBuffer sb0 = new StringBuffer();
42 super.debugBodyToString(sb0);
43 StringTokenizer st = new StringTokenizer(sb0.toString(), "\n");
44 String[] lines = new String[st.countTokens()];
45 for(int i=0; i<lines.length; i++) lines[i] = st.nextToken();
46 for(int j=0; j<ofs[0]; j++) {
47 String s = " /* " + lines[j].trim();
48 while(s.length() < 50) s += " ";
53 for(int i=0; i<numOps; i++) {
54 String s = " /* " + lines[ofs[i]].trim();
55 while(s.length() < 50) s += " ";
57 s += ops[i].toString();
60 for(int j=ofs[i]+1; j<(i==numOps-1?size():ofs[i+1]); j++) {
61 s = " /* " + lines[j].trim();
62 while(s.length() < 50) s += " ";
70 private Object[] ops = new Object[65535];
71 private int[] ofs = new int[65535];
72 private int numOps = 0;
74 // Instance Data; used ONLY during constructor; then thrown away /////////////////////////////////////////////////
76 /** this models the JVM locals; it is only used for unwinding stack-ops into an SSA-tree, then thrown away */
77 private final Expr[] local;
79 /** this models the JVM stack; it is only used for unwinding stack-ops into an SSA-tree, then thrown away */
80 private final Expr[] stack;
82 /** JVM stack pointer */
85 private Expr push(Expr e) {
86 if(sp == stack.length) {
87 for(int i=0;i<stack.length;i++) System.err.println("Stack " + i + ": " + stack[i]);
88 throw new IllegalStateException("stack overflow (" + stack.length + ")");
90 if(e.getType() == Type.VOID) throw new IllegalArgumentException("can't push a void");
91 return stack[sp++] = e;
94 if(sp == 0) throw new IllegalStateException("stack underflow");
98 private Op seqPush(Expr e) {
104 // SSA-node classes /////////////////////////////////////////////////////////////////////////////////////////
106 public final Expr VOID_EXPR = new Expr() {
107 public Type getType() { return Type.VOID; }
110 /** an purely imperative operation which does not generate data */
111 public abstract class Op {
112 //public abstract Op[] predecessors(); // not implemented yet
113 //public abstract Op[] successors(); // not implemented yet
114 public String toString() { return name(); }
116 String name = this.getClass().getName();
117 if (name.indexOf('$') != -1) name = name.substring(name.lastIndexOf('$')+1);
118 if (name.indexOf('.') != -1) name = name.substring(name.lastIndexOf('.')+1);
123 /** A sequence point. expr is evaluated for side effects at this point, this does not generate data
124 Expressions that haven't been evaluated with Seq are evaluated when they are first encountered
126 public class Seq extends Op {
127 private final Expr expr;
128 public String toString() { return expr.toString(); }
129 public Seq(Expr expr) { this.expr = expr; }
132 /** an operation which generates data */
133 public abstract class Expr extends Op {
134 //public abstract Expr[] contributors(); // not implemented yet
135 //public abstract Expr[] dependents(); // not implemented yet
137 /** every JSSA.Expr either remembers its type _OR_ knows how to figure it out (the latter is preferred to eliminate
138 * redundant information that could possibly "disagree" with itself -- this happened a LOT in Soot) */
139 public abstract Type getType();
143 * A "nondeterministic merge" -- for example when the first instruction in a loop reads from a local which could have been
144 * written to either by some instruction at the end of the previous iteration of the loop or by some instruction before
145 * the loop (on the first iteration).
147 public class Phi extends Expr {
148 private final Expr[] inputs;
149 public Phi(Expr[] inputs) {
150 this.inputs = new Expr[inputs.length];
151 System.arraycopy(inputs, 0, this.inputs, 0, inputs.length);
153 public Type getType() {
155 Type t = inputs[0].getType();
157 // FIXME: actually this should check type-unifiability... fe, the "type of null" unifies with any Type.Ref
158 for(int i=1; i<inputs.length; i++)
159 if (inputs[i].getType() != t)
160 throw new Error("Phi node with disagreeing types! Crisis!");
165 public class Argument extends Expr {
166 public final String name;
168 public Argument(String name, Type t) { this.name = name; this.t = t; }
169 public String toString() { return name; }
170 public Type getType() { return t; }
174 public class Not extends Expr {
177 if(e.getType() != Type.BOOLEAN) throw new IllegalArgumentException("not needs a boolean expression");
180 public Type getType() { return Type.BOOLEAN; }
181 public String toString() { return "!(" + e + ")"; }
184 public class Neg extends Expr {
187 if(!e.getType().isPrimitive()) throw new IllegalArgumentException("can only negate a primitive");
190 public Type getType() { return e.getType(); }
191 public String toString() { return "- (" + e + ")"; }
194 // Binary Operations //////////////////////////////////////////////////////////////////////////////
196 public abstract class BinExpr extends Expr {
197 public final Expr e1;
198 public final Expr e2;
199 private final String show;
200 public BinExpr(Expr e1, Expr e2, String show) { this.e1 = e1; this.e2 = e2; this.show = show; }
201 public String toString() {
202 // FEATURE: should we be doing some precedence stuff here? probably no worth it for debugging output
203 return "(" + e1 + show + e2 + ")";
207 public class Comparison extends BinExpr {
208 public Comparison(Expr e1, Expr e2, String show) { super(e1, e2, show); }
209 public Type getType() { return Type.BOOLEAN; }
212 public class Eq extends Comparison {
213 public Eq(Expr e1, Expr e2) {
215 if(e1.getType().isPrimitive() != e2.getType().isPrimitive())
216 throw new IllegalArgumentException("type mismatch");
217 if(e1.getType().isPrimitive() && e1.getType() != e2.getType())
218 throw new IllegalArgumentException("type mismatch");
219 // FEATURE: Check if we can compare these classes
224 public class PrimitiveComparison extends Comparison {
225 public PrimitiveComparison(Expr e1, Expr e2, String show) {
227 if(!e1.getType().isPrimitive() || e1.getType() != e2.getType()) throw new IllegalArgumentException("type mismatch");
231 public class Gt extends PrimitiveComparison { public Gt(Expr e1, Expr e2) { super(e1, e2, ">"); } }
232 public class Lt extends PrimitiveComparison { public Lt(Expr e1, Expr e2) { super(e1, e2, "<"); } }
233 public class Ge extends PrimitiveComparison { public Ge(Expr e1, Expr e2) { super(e1, e2, ">="); } }
234 public class Le extends PrimitiveComparison { public Le(Expr e1, Expr e2) { super(e1, e2, "<="); } }
236 // Math Operations //////////////////////////////////////////////////////////////////////////////
238 public class BinMath extends BinExpr {
239 public BinMath(Expr e1, Expr e2, String show) {
241 if(e1.getType() != e2.getType()) throw new IllegalArgumentException("types disagree");
243 public Type getType() { return e1.getType(); }
246 public class Add extends BinMath { public Add(Expr e, Expr e2) { super(e, e2, "+"); } }
247 public class Sub extends BinMath { public Sub(Expr e, Expr e2) { super(e, e2, "-"); } }
248 public class Mul extends BinMath { public Mul(Expr e, Expr e2) { super(e, e2, "*"); } }
249 public class Rem extends BinMath { public Rem(Expr e, Expr e2) { super(e, e2, "%"); } }
250 public class Div extends BinMath { public Div(Expr e, Expr e2) { super(e, e2, "/"); } }
251 public class And extends BinMath { public And(Expr e, Expr e2) { super(e, e2, "&"); } }
252 public class Or extends BinMath { public Or(Expr e, Expr e2) { super(e, e2, "|"); } }
253 public class Xor extends BinMath { public Xor(Expr e, Expr e2) { super(e, e2, "^"); } }
255 public class BitShiftExpr extends BinExpr {
256 public BitShiftExpr(Expr e1, Expr e2, String show) {
258 Type t = e1.getType();
259 if(t != Type.INT && t != Type.LONG) throw new IllegalArgumentException("type mismatch");
260 if(e2.getType() != Type.INT) throw new IllegalArgumentException("type mismatch");
262 public Type getType() { return e1.getType(); }
264 public class Shl extends BitShiftExpr { public Shl(Expr e, Expr e2) { super(e, e2, "<<"); } }
265 public class Shr extends BitShiftExpr { public Shr(Expr e, Expr e2) { super(e, e2, ">>"); } }
266 public class Ushr extends BitShiftExpr { public Ushr(Expr e, Expr e2) { super(e, e2, ">>>"); } }
268 // Other operations //////////////////////////////////////////////////////////////////////////////
270 public class Cast extends Expr {
273 public Cast(Expr e, Type t) {
274 if(e.getType().isRef() != t.isRef()) throw new IllegalArgumentException("invalid cast");
275 // FEATURE: Check that one is a subclass of the other if it is a ref
279 public Type getType() { return t; }
282 public class InstanceOf extends Expr {
285 public InstanceOf(Expr e, Type.Ref t) {
286 if(!e.getType().isRef()) throw new IllegalArgumentException("can't do an instanceof check on a non-ref");
290 public Type getType() { return Type.BOOLEAN; }
293 public class Throw extends Op {
295 public Throw(Expr e) {
296 if(!e.getType().isRef()) throw new IllegalArgumentException("can't throw a non ref");
297 // FEATURE: CHeck that it is a subclass of Throwable
302 public class Branch extends Op {
303 public Branch(Expr condition, Object destination) { }
304 public Branch(Label destination) { }
305 public Branch(MethodGen.Switch s) { }
308 public class Goto extends Branch { }
309 public class RET extends Branch { }
310 public class JSR extends Branch { public JSR(Label l) { super(l); } }
311 public class If extends Branch { }
313 /** represents a "returnaddr" pushed onto the stack */
314 public class Label extends Expr {
316 public Type getType() { throw new Error("attempted to call getType() on a Label"); }
317 public Label(Op op) { this.op = op; }
318 public Label(int i) { this.op = null; /* FIXME */ }
321 public class New extends Expr {
322 public final Type.Class t;
323 public Type getType() { return t; }
324 public New(Type.Class t) { this.t = t; }
325 public String toString() { return "new " + t + "(...)"; }
328 public class NewArray extends Expr {
329 public final Type.Array t;
330 public final Expr[] dims;
331 public NewArray(Type.Array t, Expr[] dims) { this.t = t; this.dims = dims; }
332 public NewArray(Type.Array t, Expr dim) { this(t,new Expr[]{dim}); }
333 public Type getType() { return t; }
336 public class Return extends Op {
338 public Return() { this(VOID_EXPR); }
339 public Return(Expr e) { this.e = e; }
340 public String toString() { return e.getType() == Type.VOID ? "return" : ("return "+e.toString()); }
343 /** GETFIELD and GETSTATIC */
344 public class Get extends Expr {
345 final Type.Class.Field f;
347 public Type getType() { return f.getType(); }
348 public Get(Type.Class.Field f) { this(f, null); }
349 public Get(Type.Class.Field f, Expr e) { this.f = f; this.e = e; }
350 public String toString() {
354 : f.getDeclaringClass() == JSSA.this.method.getDeclaringClass()
360 /** PUTFIELD and PUTSTATIC */
361 public class Put extends Op {
362 final Type.Class.Field f;
365 public Put(Type.Class.Field f, Expr v) { this(f, v, null); }
366 public Put(Type.Class.Field f, Expr v, Expr e) { this.f = f; this.v = v; this.e = e; }
367 public String toString() {
371 : f.getDeclaringClass() == JSSA.this.method.getDeclaringClass()
373 : f.toString()) + " = " + v;
377 public class ArrayPut extends Op {
379 public ArrayPut(Expr e, Expr i, Expr v) { this.e = e; this.i = i; this.v = v; }
382 public class ArrayGet extends Expr {
384 public ArrayGet(Expr e, Expr i) { this.e = e; this.i = i; }
385 public Type getType() { return e.getType().asArray().getElementType(); }
388 public class ArrayLength extends Expr {
390 public ArrayLength(Expr e) { this.e = e; }
391 public Type getType() { return Type.INT; }
394 public abstract class Invoke extends Expr {
395 public final Expr[] arguments;
396 public final Type.Class.Method method;
397 protected Invoke(Type.Class.Method m, Expr[] a) { this.arguments = a; this.method = m; }
399 public Type getType() { return method.getReturnType(); }
400 protected void args(StringBuffer sb) {
402 for(int i=0; i<arguments.length; i++) {
403 if (i>0) sb.append(", ");
404 sb.append(arguments[i]+"");
409 public String toString() {
410 StringBuffer sb = new StringBuffer();
411 sb.append(method.getDeclaringClass() == JSSA.this.method.getDeclaringClass()
413 : (method.getDeclaringClass() + "." + method.name));
415 return sb.toString();
418 public class InvokeStatic extends Invoke { public InvokeStatic(Type.Class.Method m, Expr[] a) { super(m,a); } }
419 public class InvokeSpecial extends InvokeVirtual {
420 public InvokeSpecial(Type.Class.Method m, Expr[] a, Expr e) { super(m,a,e); }
421 public String toString() { return toString(method.name.equals("<init>") ? method.getDeclaringClass().getName() : method.name); }
423 public class InvokeInterface extends InvokeVirtual{public InvokeInterface(Type.Class.Method m, Expr[] a, Expr e){super(m,a,e);}}
424 public class InvokeVirtual extends Invoke {
425 public final Expr instance;
426 public InvokeVirtual(Type.Class.Method m, Expr[] a, Expr e) { super(m, a); instance = e; }
427 public String toString() { return toString(method.name); }
428 protected String toString(String name) {
429 StringBuffer sb = new StringBuffer();
430 sb.append(instance+".");
433 return sb.toString();
437 public class Constant extends Expr {
438 private final Object o;
439 public Constant(int i) { this(new Integer(i)); }
440 public Constant(Object o) { this.o = o; }
441 public String toString() { return o instanceof String ? "\"" + o + "\"" : o.toString(); }
442 public Type getType() {
443 if (o instanceof Byte) return Type.BYTE;
444 if (o instanceof Short) return Type.SHORT;
445 if (o instanceof Character) return Type.CHAR;
446 if (o instanceof Boolean) return Type.BOOLEAN;
447 if (o instanceof Long) return Type.LONG;
448 if (o instanceof Double) return Type.DOUBLE;
449 if (o instanceof Float) return Type.FLOAT;
450 if (o instanceof Integer) return Type.INT;
451 if (o instanceof String) return Type.STRING;
452 throw new IllegalStateException("unknown constant type");
457 // Implementation //////////////////////////////////////////////////////////////////////////////
459 private Object addOp(int op, Object arg) {
463 MethodGen.Wide w = (MethodGen.Wide)arg;
470 MethodGen.Pair p = (MethodGen.Pair)arg;
477 case NOP: return null;
479 // Stack manipulations //////////////////////////////////////////////////////////////////////////////
481 case ACONST_NULL: return stack[sp++] = new Constant(null);
482 case ICONST_M1: return stack[sp++] = new Constant(-1);
483 case ICONST_0: case LCONST_0: case FCONST_0: case DCONST_0: push(new Constant(0)); return null;
484 case ICONST_1: case LCONST_1: case FCONST_1: case DCONST_1: push(new Constant(1)); return null;
485 case ICONST_2: case FCONST_2: push(new Constant(2)); return null;
486 case ICONST_3: push(new Constant(3)); return null;
487 case ICONST_4: push(new Constant(4)); return null;
488 case ICONST_5: push(new Constant(5)); return null;
489 case ILOAD: case LLOAD: case FLOAD: case DLOAD: case ALOAD: return push(local[i1]);
490 case ILOAD_0: case LLOAD_0: case FLOAD_0: case DLOAD_0: case ALOAD_0: return push(local[0]);
491 case ILOAD_1: case LLOAD_1: case FLOAD_1: case DLOAD_1: case ALOAD_1: return push(local[1]);
492 case ALOAD_2: case DLOAD_2: case FLOAD_2: case LLOAD_2: case ILOAD_2: return push(local[2]);
493 case ILOAD_3: case LLOAD_3: case FLOAD_3: case DLOAD_3: case ALOAD_3: return push(local[3]);
494 case ISTORE: case LSTORE: case FSTORE: case DSTORE: case ASTORE: local[i1] = pop(); return null;
495 case ISTORE_0: case LSTORE_0: case FSTORE_0: case DSTORE_0: case ASTORE_0: local[0] = pop(); return null;
496 case ISTORE_1: case LSTORE_1: case FSTORE_1: case DSTORE_1: case ASTORE_1: local[1] = pop(); return null;
497 case ASTORE_2: case DSTORE_2: case FSTORE_2: case LSTORE_2: case ISTORE_2: local[2] = pop(); return null;
498 case ISTORE_3: case LSTORE_3: case FSTORE_3: case DSTORE_3: case ASTORE_3: local[3] = pop(); return null;
499 case POP: pop(); return null;
500 case POP2: pop(); pop(); return null;
501 case DUP: push(stack[sp-1]); return null;
502 case DUP2: push(stack[sp-2]); push(stack[sp-2]); return null;
504 // Conversions //////////////////////////////////////////////////////////////////////////////
506 // coercions are added as-needed when converting from JSSA back to bytecode, so we can
507 // simply discard them here (assuming the bytecode we're reading in was valid in the first place)
509 case I2L: case F2L: case D2L: push(new Cast(pop(), Type.LONG)); return null;
510 case I2F: case L2F: case D2F: push(new Cast(pop(), Type.FLOAT)); return null;
511 case I2D: case L2D: case F2D: push(new Cast(pop(), Type.DOUBLE)); return null;
512 case L2I: case F2I: case D2I: push(new Cast(pop(), Type.INT)); return null;
513 case I2B: push(new Cast(pop(), Type.BYTE)); return null;
514 case I2C: push(new Cast(pop(), Type.CHAR)); return null;
515 case I2S: push(new Cast(pop(), Type.SHORT)); return null;
516 case SWAP: { Expr e1 = pop(), e2 = pop(); push(e2); push(e1); return null; }
518 // Math //////////////////////////////////////////////////////////////////////////////
520 case IADD: case LADD: case FADD: case DADD: push(new Add(pop(), pop())); return null;
521 case ISUB: case LSUB: case FSUB: case DSUB: push(new Sub(pop(), pop())); return null;
522 case IMUL: case LMUL: case FMUL: case DMUL: push(new Mul(pop(), pop())); return null;
523 case IREM: case LREM: case FREM: case DREM: push(new Rem(pop(), pop())); return null;
524 //case INEG: case LNEG: case FNEG: case DNEG: push(new Neg(pop())); return null;
525 case IDIV: case LDIV: case FDIV: case DDIV: push(new Div(pop(), pop())); return null;
526 case ISHL: case LSHL: push(new Shl(pop(), pop())); return null;
527 case ISHR: case LSHR: push(new Shr(pop(), pop())); return null;
528 case IUSHR: case LUSHR: push(new Ushr(pop(), pop())); return null;
529 case IAND: case LAND: push(new And(pop(), pop())); return null;
530 case IOR: case LOR: push(new Or(pop(), pop())); return null;
531 case IXOR: case LXOR: push(new Xor(pop(), pop())); return null;
532 case IINC: return local[i1] = new Add(local[i1], new Constant(i2));
534 // Control and branching //////////////////////////////////////////////////////////////////////////////
536 case IFNULL: return new Branch(new Eq(pop(), new Constant(null)), new Label(i1));
537 case IFNONNULL: return new Branch(new Not(new Eq(pop(),new Constant(null))),new Label(i1));
538 case IFEQ: return new Branch( new Eq(new Constant(0), pop()), arg);
539 case IFNE: return new Branch(new Not(new Eq(new Constant(0), pop())), arg);
540 case IFLT: return new Branch( new Lt(new Constant(0), pop()), arg);
541 case IFGE: return new Branch(new Not(new Lt(new Constant(0), pop())), arg);
542 case IFGT: return new Branch( new Gt(new Constant(0), pop()), arg);
543 case IFLE: return new Branch(new Not(new Gt(new Constant(0), pop())), arg);
544 case IF_ICMPEQ: return new Branch( new Eq(pop(), pop()), arg);
545 case IF_ICMPNE: return new Branch(new Not(new Eq(pop(), pop())), arg);
546 case IF_ICMPLT: return new Branch( new Lt(pop(), pop()), arg);
547 case IF_ICMPGE: return new Branch(new Not(new Lt(pop(), pop())), arg);
548 case IF_ICMPGT: return new Branch( new Gt(pop(), pop()), arg);
549 case IF_ICMPLE: return new Branch(new Not(new Gt(pop(), pop())), arg);
550 case IF_ACMPEQ: return new Branch( new Eq(pop(), pop()), arg);
551 case IF_ACMPNE: return new Branch(new Not(new Eq(pop(), pop())), arg);
552 case ATHROW: return new Throw(pop());
553 case GOTO: return new Branch(new Label(i1));
554 case JSR: return new JSR(new Label(i1));
555 case RET: return new RET();
556 case RETURN: return new Return();
557 case IRETURN: case LRETURN: case FRETURN: case DRETURN: case ARETURN:
558 return new Return(pop());
560 // Array manipulations //////////////////////////////////////////////////////////////////////////////
562 case IALOAD: case LALOAD: case FALOAD: case DALOAD: case AALOAD:
563 case BALOAD: case CALOAD: case SALOAD:
564 return seqPush(new ArrayGet(pop(), pop()));
565 case IASTORE: case LASTORE: case FASTORE: case DASTORE: case AASTORE:
566 case BASTORE: case CASTORE: case SASTORE:
567 return new ArrayPut(pop(), pop(), pop());
569 // Invocation //////////////////////////////////////////////////////////////////////////////
571 case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: case INVOKEINTERFACE: {
572 Type.Class.Method method = (Type.Class.Method)arg;
573 Expr args[] = new Expr[method.getNumArgs()];
574 for(int i=0; i<args.length; i++) args[args.length-i-1] = pop();
577 case INVOKEVIRTUAL: ret = new InvokeVirtual(method, args, pop()); break;
578 case INVOKEINTERFACE: ret = new InvokeInterface(method, args, pop()); break;
579 case INVOKESPECIAL: ret = new InvokeSpecial(method, args, pop()); break;
580 case INVOKESTATIC: ret = new InvokeStatic(method, args); break;
581 default: throw new Error("should never happen");
583 if(ret.getType() != Type.VOID) push(ret);
587 // Field Access //////////////////////////////////////////////////////////////////////////////
589 case GETSTATIC: return seqPush(new Get((Type.Class.Field)arg, null));
590 case PUTSTATIC: return new Put((Type.Class.Field)arg, pop(), null);
591 case GETFIELD: return seqPush(new Get((Type.Class.Field)arg, pop()));
592 case PUTFIELD: return new Put((Type.Class.Field)arg, pop(), pop());
594 // Allocation //////////////////////////////////////////////////////////////////////////////
596 case NEW: push(new New((Type.Class)arg)); return null;
599 switch(((Integer)arg).intValue()) {
600 case 4: base = Type.BOOLEAN; break;
601 case 5: base = Type.CHAR; break;
602 case 6: base = Type.FLOAT; break;
603 case 7: base = Type.DOUBLE; break;
604 case 8: base = Type.BYTE; break;
605 case 9: base = Type.SHORT; break;
606 case 10: base = Type.INT; break;
607 case 11: base = Type.LONG; break;
608 default: throw new IllegalStateException("invalid array type");
610 return seqPush(new NewArray(base.makeArray(),pop()));
612 case ANEWARRAY: push(new NewArray(((Type.Ref)arg).makeArray(), pop())); return null;
613 case MULTIANEWARRAY: {
614 MethodGen.MultiANewArray mana = (MethodGen.MultiANewArray) arg;
615 Expr[] dims = new Expr[mana.dims];
616 for(int i=0;i<dims.length;i++) dims[i] = pop();
617 return seqPush(new NewArray(mana.type, dims));
619 case ARRAYLENGTH: return seqPush(new ArrayLength(pop()));
621 // Runtime Type information //////////////////////////////////////////////////////////////////////////////
623 case CHECKCAST: return seqPush(new Cast(pop(), (Type.Ref)arg));
624 case INSTANCEOF: push(new InstanceOf(pop(), (Type.Ref)arg)); return null;
626 case LDC: case LDC_W: case LDC2_W: push(new Constant(arg)); return null;
628 case BIPUSH: push(new Constant(i1)); // FIXME return null;
629 case SIPUSH: push(new Constant(i1)); // FIXME return null;
631 case TABLESWITCH: new Branch((MethodGen.Switch)arg);
632 case LOOKUPSWITCH: new Branch((MethodGen.Switch)arg);
635 case MONITORENTER: Op.monitorEnter(pop());
636 case MONITOREXIT: Op.monitorExit(pop());
639 case DUP_X1: throw new Error("unimplemented");
640 case DUP_X2: throw new Error("unimplemented");
641 case DUP2_X1: throw new Error("unimplemented");
642 case DUP2_X2: throw new Error("unimplemented");
643 case LCMP: throw new Error("unimplemented");
644 case FCMPL: throw new Error("unimplemented");
645 case FCMPG: throw new Error("unimplemented");
646 case DCMPL: throw new Error("unimplemented");
647 case DCMPG: throw new Error("unimplemented");
648 case GOTO_W: throw new Error("unimplemented");
649 case JSR_W: throw new Error("unimplemented");
650 default: throw new Error("unhandled");
654 public static void main(String[] args) throws Exception {
655 InputStream is = Class.forName(args[0]).getClassLoader().getResourceAsStream(args[0].replace('.', '/')+".class");
656 System.out.println(new ClassFile(new DataInputStream(is), true).toString());