/* * Copyright 2009-2010 MBTE Sweden AB. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.mbte.groovypp.compiler.bytecode; import org.objectweb.asm.Label; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.util.IdentityHashMap; import java.util.Stack; public class StackAwareMethodAdapter extends MethodAdapter implements Opcodes, LocalVarTypeInferenceState { // TODO: Unimplemented bytecodes // int IINC = 132; // int JSR = 168; // int RET = 169; // int TABLESWITCH = 170; // int LOOKUPSWITCH = 171; private BytecodeStack stack = new BytecodeStack(); private IdentityHashMap<Label, LocalVarInferenceTypes> labelMap = new IdentityHashMap<Label, LocalVarInferenceTypes>(); private LocalVarInferenceTypes curInference = new LocalVarInferenceTypes(); private static final LocalVarInferenceTypes AFTER_GOTO = new LocalVarInferenceTypes(); public Stack<LocalVarInferenceTypes> loopInferences = new Stack<LocalVarInferenceTypes>(); { loopInferences.push(AFTER_GOTO); } public void startLoopVisitLabel(Label label) { loopInferences.push(getLabelInfo(label)); visitLabel(label); } private void jumpToLabel(int opcode, Label label) { final LocalVarInferenceTypes li = getLabelInfo(label); li.initFromStack(stack); li.jumpFrom(curInference); if (opcode == GOTO) { stack.clear(); curInference = AFTER_GOTO; if (li.equals(loopInferences.peek())) loopInferences.pop(); } } private void comeToLabel(Label label) { final LocalVarInferenceTypes li = getLabelInfo(label); li.initFromStack(stack); if (curInference != AFTER_GOTO) li.comeFrom(curInference); // System.out.println("// " + li.defVars); li.parentScopeInference = loopInferences.peek(); curInference = li; } private LocalVarInferenceTypes getLabelInfo(Label label) { LocalVarInferenceTypes li = labelMap.get(label); if (li == null) { li = new LocalVarInferenceTypes(); labelMap.put(label, li); } return li; } public LocalVarInferenceTypes getLocalVarInferenceTypes() { return curInference; } public StackAwareMethodAdapter(MethodVisitor methodVisitor) { super(methodVisitor); } @Override public void visitInsn(int i) { switch (i) { case LCMP: stack.pop(BytecodeStack.KIND_LONG); stack.pop(BytecodeStack.KIND_LONG); stack.push(BytecodeStack.KIND_INT); break; case FCMPL: case FCMPG: stack.pop(BytecodeStack.KIND_FLOAT); stack.pop(BytecodeStack.KIND_FLOAT); stack.push(BytecodeStack.KIND_INT); break; case DCMPL: case DCMPG: stack.pop(BytecodeStack.KIND_DOUBLE); stack.pop(BytecodeStack.KIND_DOUBLE); stack.push(BytecodeStack.KIND_INT); break; case NOP: break; case ACONST_NULL: stack.push(BytecodeStack.KIND_OBJ); break; case ICONST_M1: case ICONST_0: case ICONST_1: case ICONST_2: case ICONST_3: case ICONST_4: case ICONST_5: stack.push(BytecodeStack.KIND_INT); break; case LCONST_0: case LCONST_1: stack.push(BytecodeStack.KIND_LONG); break; case FCONST_0: case FCONST_1: case FCONST_2: stack.push(BytecodeStack.KIND_FLOAT); break; case DCONST_0: case DCONST_1: stack.push(BytecodeStack.KIND_DOUBLE); break; case POP: stack.pop(); break; case POP2: stack.pop2(); break; case DUP: stack.dup(); break; case DUP_X1: stack.dup_x1(); break; case DUP_X2: stack.dup_x2(); break; case DUP2: stack.dup2(); break; case DUP2_X1: stack.dup2_x1(); break; case DUP2_X2: stack.dup2_x2(); break; case SWAP: stack.swap(); break; case I2L: stack.pop(BytecodeStack.KIND_INT); stack.push(BytecodeStack.KIND_LONG); break; case I2F: stack.pop(BytecodeStack.KIND_INT); stack.push(BytecodeStack.KIND_FLOAT); break; case I2D: stack.pop(BytecodeStack.KIND_INT); stack.push(BytecodeStack.KIND_DOUBLE); break; case L2I: stack.pop(BytecodeStack.KIND_LONG); stack.push(BytecodeStack.KIND_INT); break; case L2F: stack.pop(BytecodeStack.KIND_LONG); stack.push(BytecodeStack.KIND_FLOAT); break; case L2D: stack.pop(BytecodeStack.KIND_LONG); stack.push(BytecodeStack.KIND_DOUBLE); break; case F2I: stack.pop(BytecodeStack.KIND_FLOAT); stack.push(BytecodeStack.KIND_INT); break; case F2L: stack.pop(BytecodeStack.KIND_FLOAT); stack.push(BytecodeStack.KIND_LONG); break; case F2D: stack.pop(BytecodeStack.KIND_FLOAT); stack.push(BytecodeStack.KIND_DOUBLE); break; case D2I: stack.pop(BytecodeStack.KIND_DOUBLE); stack.push(BytecodeStack.KIND_INT); break; case D2L: stack.pop(BytecodeStack.KIND_DOUBLE); stack.push(BytecodeStack.KIND_LONG); break; case D2F: stack.pop(BytecodeStack.KIND_DOUBLE); stack.push(BytecodeStack.KIND_FLOAT); break; case I2B: stack.pop(BytecodeStack.KIND_INT); stack.push(BytecodeStack.KIND_INT); break; case I2C: stack.pop(BytecodeStack.KIND_INT); stack.push(BytecodeStack.KIND_INT); break; case I2S: stack.pop(BytecodeStack.KIND_INT); stack.push(BytecodeStack.KIND_INT); break; case IADD: case ISUB: case IDIV: case IMUL: case IREM: case ISHL: case IAND: case IOR: case IXOR: case IUSHR: case ISHR: stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_INT); stack.push(BytecodeStack.KIND_INT); break; case LADD: case LSUB: case LMUL: case LDIV: case LSHL: case LUSHR: case LAND: case LOR: case LXOR: case LREM: case LSHR: stack.pop(BytecodeStack.KIND_LONG); stack.pop(BytecodeStack.KIND_LONG); stack.push(BytecodeStack.KIND_LONG); break; case FADD: case FSUB: case FMUL: case FREM: case FDIV: stack.pop(BytecodeStack.KIND_FLOAT); stack.pop(BytecodeStack.KIND_FLOAT); stack.push(BytecodeStack.KIND_FLOAT); break; case DADD: case DSUB: case DMUL: case DDIV: case DREM: stack.pop(BytecodeStack.KIND_DOUBLE); stack.pop(BytecodeStack.KIND_DOUBLE); stack.push(BytecodeStack.KIND_DOUBLE); break; case INEG: stack.pop(BytecodeStack.KIND_INT); stack.push(BytecodeStack.KIND_INT); break; case LNEG: stack.pop(BytecodeStack.KIND_LONG); stack.push(BytecodeStack.KIND_LONG); break; case FNEG: stack.pop(BytecodeStack.KIND_FLOAT); stack.push(BytecodeStack.KIND_FLOAT); break; case DNEG: stack.pop(BytecodeStack.KIND_DOUBLE); stack.push(BytecodeStack.KIND_DOUBLE); break; case IRETURN: stack.pop(BytecodeStack.KIND_INT); stack.clear(); break; case LRETURN: stack.pop(BytecodeStack.KIND_LONG); stack.clear(); break; case FRETURN: stack.pop(BytecodeStack.KIND_FLOAT); stack.clear(); break; case DRETURN: stack.pop(BytecodeStack.KIND_DOUBLE); stack.clear(); break; case ARETURN: case ATHROW: stack.pop(BytecodeStack.KIND_OBJ); stack.clear(); break; case RETURN: stack.clear(); break; case MONITORENTER: case MONITOREXIT: stack.pop(BytecodeStack.KIND_OBJ); break; case IALOAD: case BALOAD: case CALOAD: case SALOAD: stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_OBJ); stack.push(BytecodeStack.KIND_INT); break; case LALOAD: stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_OBJ); stack.push(BytecodeStack.KIND_LONG); break; case FALOAD: stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_OBJ); stack.push(BytecodeStack.KIND_FLOAT); break; case DALOAD: stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_OBJ); stack.push(BytecodeStack.KIND_DOUBLE); break; case AALOAD: stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_OBJ); stack.push(BytecodeStack.KIND_OBJ); break; case IASTORE: case BASTORE: case CASTORE: case SASTORE: stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_OBJ); break; case LASTORE: stack.pop(BytecodeStack.KIND_LONG); stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_OBJ); break; case FASTORE: stack.pop(BytecodeStack.KIND_FLOAT); stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_OBJ); break; case DASTORE: stack.pop(BytecodeStack.KIND_DOUBLE); stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_OBJ); break; case AASTORE: stack.pop(BytecodeStack.KIND_OBJ); stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_OBJ); break; case ARRAYLENGTH: stack.pop(BytecodeStack.KIND_OBJ); stack.push(BytecodeStack.KIND_INT); break; default: throw new RuntimeException("Unrecognized operation"); } super.visitInsn(i); } @Override public void visitIntInsn(int i, int i1) { switch (i) { case BIPUSH: case SIPUSH: stack.push(BytecodeStack.KIND_INT); break; case NEWARRAY: stack.pop(BytecodeStack.KIND_INT); stack.push(BytecodeStack.KIND_OBJ); break; default: throw new RuntimeException("Unrecognized operation"); } super.visitIntInsn(i, i1); } @Override public void visitVarInsn(int i, int i1) { switch (i) { case ILOAD: stack.push(BytecodeStack.KIND_INT); break; case LLOAD: stack.push(BytecodeStack.KIND_LONG); break; case FLOAD: stack.push(BytecodeStack.KIND_FLOAT); break; case DLOAD: stack.push(BytecodeStack.KIND_DOUBLE); break; case ALOAD: stack.push(BytecodeStack.KIND_OBJ); break; case ISTORE: stack.pop(BytecodeStack.KIND_INT); break; case LSTORE: stack.pop(BytecodeStack.KIND_LONG); break; case FSTORE: stack.pop(BytecodeStack.KIND_FLOAT); break; case DSTORE: stack.pop(BytecodeStack.KIND_DOUBLE); break; case ASTORE: stack.pop(BytecodeStack.KIND_OBJ); break; default: throw new RuntimeException("Unrecognized operation"); } super.visitVarInsn(i, i1); } @Override public void visitTypeInsn(int i, String s) { switch (i) { case NEW: stack.push(BytecodeStack.KIND_OBJ); break; case ANEWARRAY: stack.pop(BytecodeStack.KIND_INT); stack.push(BytecodeStack.KIND_OBJ); break; case CHECKCAST: stack.pop(BytecodeStack.KIND_OBJ); stack.push(BytecodeStack.KIND_OBJ); break; case INSTANCEOF: stack.pop(BytecodeStack.KIND_OBJ); stack.push(BytecodeStack.KIND_INT); break; default: throw new RuntimeException("Unrecognized operation"); } super.visitTypeInsn(i, s); } @Override public void visitFieldInsn(int i, String s, String s1, String s2) { switch (i) { case GETSTATIC: stack.push(fieldKind(s2)); break; case PUTSTATIC: stack.pop(fieldKind(s2)); break; case GETFIELD: stack.pop(BytecodeStack.KIND_OBJ); stack.push(fieldKind(s2)); break; case PUTFIELD: stack.pop(fieldKind(s2)); stack.pop(BytecodeStack.KIND_OBJ); break; default: throw new RuntimeException("Unrecognized operation"); } super.visitFieldInsn(i, s, s1, s2); } private byte fieldKind(String s2) { switch (s2.charAt(0)) { case 'I': case 'B': case 'S': case 'Z': case 'C': return BytecodeStack.KIND_INT; case 'F': return BytecodeStack.KIND_FLOAT; case 'D': return BytecodeStack.KIND_DOUBLE; case 'J': return BytecodeStack.KIND_LONG; default: return BytecodeStack.KIND_OBJ; } } @Override public void visitMethodInsn(int i, String s, String s1, String s2) { popArgs(s2); switch (i) { case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKEINTERFACE: stack.pop(BytecodeStack.KIND_OBJ); break; case INVOKESTATIC: break; default: throw new RuntimeException("Unrecognized operation"); } pushResult(s2); super.visitMethodInsn(i, s, s1, s2); } private void pushResult(String s2) { s2 = s2.substring(s2.indexOf(')') + 1); if (s2.charAt(0) != 'V') stack.push(fieldKind(s2)); } private void popArgs(String s2) { s2 = s2.substring(1, s2.lastIndexOf(')')); byte[] args = new byte[256]; int count = 0; while (s2.length() > 0) { switch (s2.charAt(0)) { case 'I': case 'B': case 'S': case 'Z': case 'C': args[count++] = BytecodeStack.KIND_INT; s2 = s2.substring(1); break; case 'F': args[count++] = BytecodeStack.KIND_FLOAT; s2 = s2.substring(1); break; case 'D': args[count++] = BytecodeStack.KIND_DOUBLE; s2 = s2.substring(1); break; case 'J': args[count++] = BytecodeStack.KIND_LONG; s2 = s2.substring(1); break; case '[': args[count++] = BytecodeStack.KIND_OBJ; int k = 1; while (s2.charAt(k) == '[') k++; if (s2.charAt(k) == 'L') s2 = s2.substring(s2.indexOf(';') + 1); else s2 = s2.substring(k + 1); break; default: args[count++] = BytecodeStack.KIND_OBJ; s2 = s2.substring(s2.indexOf(';') + 1); } } while (count > 0) { stack.pop(args[--count]); } } @Override public void visitJumpInsn(int i, Label label) { switch (i) { case IFEQ: case IFNE: case IFLT: case IFGE: case IFGT: case IFLE: stack.pop(BytecodeStack.KIND_INT); break; case IF_ICMPEQ: case IF_ICMPNE: case IF_ICMPLT: case IF_ICMPGE: case IF_ICMPGT: case IF_ICMPLE: stack.pop(BytecodeStack.KIND_INT); stack.pop(BytecodeStack.KIND_INT); break; case IF_ACMPEQ: case IF_ACMPNE: stack.pop(BytecodeStack.KIND_OBJ); stack.pop(BytecodeStack.KIND_OBJ); break; case IFNULL: case IFNONNULL: stack.pop(BytecodeStack.KIND_OBJ); break; case GOTO: break; } jumpToLabel(i, label); super.visitJumpInsn(i, label); } @Override public void visitLabel(Label label) { comeToLabel(label); super.visitLabel(label); } @Override public void visitLdcInsn(Object o) { if (o instanceof Integer || o instanceof Boolean) stack.push(BytecodeStack.KIND_INT); else if (o instanceof Float) stack.push(BytecodeStack.KIND_FLOAT); else if (o instanceof Double) stack.push(BytecodeStack.KIND_DOUBLE); else if (o instanceof Long) stack.push(BytecodeStack.KIND_LONG); else if (o instanceof String) stack.push(BytecodeStack.KIND_OBJ); else throw new RuntimeException("Unrecognized operation"); super.visitLdcInsn(o); } @Override public void visitIincInsn(int i, int i1) { super.visitIincInsn(i, i1); } @Override public void visitTableSwitchInsn(int i, int i1, Label label, Label[] labels) { super.visitTableSwitchInsn(i, i1, label, labels); } @Override public void visitLookupSwitchInsn(Label label, int[] ints, Label[] labels) { super.visitLookupSwitchInsn(label, ints, labels); } @Override public void visitMultiANewArrayInsn(String s, int i) { for (int ii = i; ii > 0; --ii) stack.pop(BytecodeStack.KIND_INT); stack.push(BytecodeStack.KIND_OBJ); super.visitMultiANewArrayInsn(s, i); } public void startExceptionBlock() { stack.push(BytecodeStack.KIND_OBJ); } }