Java tutorial
/* * Copyright (c) 2016, Venkatesh-Prasad Ranganath * * BSD 3-clause License * * Author: Venkatesh-Prasad Ranganath (rvprasad) */ package dyco4j.instrumentation.internals; import org.objectweb.asm.*; import java.util.HashMap; import java.util.Map; import java.util.Stack; /** * This class is an adaptation of <code></code>org.objectweb.asm.commons.AdviceAdapter</code> class in * <a href="http://asm.ow2.org/">ASM</a>. */ final class InitTracingMethodVisitor extends MethodVisitor { private static final Object THIS = new Object(); private static final Object OTHER = new Object(); private final Map<Label, Stack<Object>> branchTarget2frame = new HashMap<>(); private boolean thisIsInitialized; private Stack<Object> stackFrame = new Stack<>(); InitTracingMethodVisitor(final int access, final String name, final TracingMethodVisitor mv) { super(CLI.ASM_VERSION, mv); assert name.equals("<init>"); this.thisIsInitialized = false; } @Override public void visitLabel(Label label) { super.visitLabel(label); final Stack<Object> _frame = this.branchTarget2frame.get(label); if (_frame != null) { stackFrame = _frame; this.branchTarget2frame.remove(label); } } @Override public void visitInsn(final int opcode) { super.visitInsn(opcode); switch (opcode) { case Opcodes.IRETURN: // 1 before n/a after case Opcodes.FRETURN: // 1 before n/a after case Opcodes.ARETURN: // 1 before n/a after case Opcodes.ATHROW: // 1 before n/a after this.stackFrame.pop(); break; case Opcodes.LRETURN: // 2 before n/a after case Opcodes.DRETURN: // 2 before n/a after this.stackFrame.pop(); this.stackFrame.pop(); break; case Opcodes.NOP: case Opcodes.LALOAD: // remove 2 add 2 case Opcodes.DALOAD: // remove 2 add 2 case Opcodes.LNEG: case Opcodes.DNEG: case Opcodes.FNEG: case Opcodes.INEG: case Opcodes.L2D: case Opcodes.D2L: case Opcodes.F2I: case Opcodes.I2B: case Opcodes.I2C: case Opcodes.I2S: case Opcodes.I2F: case Opcodes.ARRAYLENGTH: break; case Opcodes.ACONST_NULL: case Opcodes.ICONST_M1: case Opcodes.ICONST_0: case Opcodes.ICONST_1: case Opcodes.ICONST_2: case Opcodes.ICONST_3: case Opcodes.ICONST_4: case Opcodes.ICONST_5: case Opcodes.FCONST_0: case Opcodes.FCONST_1: case Opcodes.FCONST_2: case Opcodes.F2L: // 1 before 2 after case Opcodes.F2D: case Opcodes.I2L: case Opcodes.I2D: this.stackFrame.push(OTHER); break; case Opcodes.LCONST_0: case Opcodes.LCONST_1: case Opcodes.DCONST_0: case Opcodes.DCONST_1: this.stackFrame.push(OTHER); this.stackFrame.push(OTHER); break; case Opcodes.IALOAD: // remove 2 add 1 case Opcodes.FALOAD: // remove 2 add 1 case Opcodes.AALOAD: // remove 2 add 1 case Opcodes.BALOAD: // remove 2 add 1 case Opcodes.CALOAD: // remove 2 add 1 case Opcodes.SALOAD: // remove 2 add 1 case Opcodes.POP: case Opcodes.IADD: case Opcodes.FADD: case Opcodes.ISUB: case Opcodes.LSHL: // 3 before 2 after case Opcodes.LSHR: // 3 before 2 after case Opcodes.LUSHR: // 3 before 2 after case Opcodes.L2I: // 2 before 1 after case Opcodes.L2F: // 2 before 1 after case Opcodes.D2I: // 2 before 1 after case Opcodes.D2F: // 2 before 1 after case Opcodes.FSUB: case Opcodes.FMUL: case Opcodes.FDIV: case Opcodes.FREM: case Opcodes.FCMPL: // 2 before 1 after case Opcodes.FCMPG: // 2 before 1 after case Opcodes.IMUL: case Opcodes.IDIV: case Opcodes.IREM: case Opcodes.ISHL: case Opcodes.ISHR: case Opcodes.IUSHR: case Opcodes.IAND: case Opcodes.IOR: case Opcodes.IXOR: case Opcodes.MONITORENTER: case Opcodes.MONITOREXIT: this.stackFrame.pop(); break; case Opcodes.POP2: case Opcodes.LSUB: case Opcodes.LMUL: case Opcodes.LDIV: case Opcodes.LREM: case Opcodes.LADD: case Opcodes.LAND: case Opcodes.LOR: case Opcodes.LXOR: case Opcodes.DADD: case Opcodes.DMUL: case Opcodes.DSUB: case Opcodes.DDIV: case Opcodes.DREM: this.stackFrame.pop(); this.stackFrame.pop(); break; case Opcodes.IASTORE: case Opcodes.FASTORE: case Opcodes.AASTORE: case Opcodes.BASTORE: case Opcodes.CASTORE: case Opcodes.SASTORE: case Opcodes.LCMP: // 4 before 1 after case Opcodes.DCMPL: case Opcodes.DCMPG: this.stackFrame.pop(); this.stackFrame.pop(); this.stackFrame.pop(); break; case Opcodes.LASTORE: case Opcodes.DASTORE: this.stackFrame.pop(); this.stackFrame.pop(); this.stackFrame.pop(); this.stackFrame.pop(); break; case Opcodes.DUP: this.stackFrame.push(this.stackFrame.peek()); break; case Opcodes.DUP_X1: { final int _s = stackFrame.size(); stackFrame.add(_s - 2, stackFrame.get(_s - 1)); break; } case Opcodes.DUP_X2: { final int _s = stackFrame.size(); stackFrame.add(_s - 3, stackFrame.get(_s - 1)); break; } case Opcodes.DUP2: { final int _s = stackFrame.size(); stackFrame.add(_s - 2, stackFrame.get(_s - 1)); stackFrame.add(_s - 2, stackFrame.get(_s - 1)); break; } case Opcodes.DUP2_X1: { final int _s = stackFrame.size(); stackFrame.add(_s - 3, stackFrame.get(_s - 1)); stackFrame.add(_s - 3, stackFrame.get(_s - 1)); break; } case Opcodes.DUP2_X2: { final int _s = stackFrame.size(); stackFrame.add(_s - 4, stackFrame.get(_s - 1)); stackFrame.add(_s - 4, stackFrame.get(_s - 1)); break; } case Opcodes.SWAP: { final int _s = stackFrame.size(); stackFrame.add(_s - 2, stackFrame.get(_s - 1)); stackFrame.remove(_s); break; } } } @Override public void visitVarInsn(final int opcode, final int var) { super.visitVarInsn(opcode, var); switch (opcode) { case Opcodes.ILOAD: case Opcodes.FLOAD: this.stackFrame.push(OTHER); break; case Opcodes.LLOAD: case Opcodes.DLOAD: this.stackFrame.push(OTHER); this.stackFrame.push(OTHER); break; case Opcodes.ALOAD: this.stackFrame.push(var == 0 ? THIS : OTHER); break; case Opcodes.ASTORE: case Opcodes.ISTORE: case Opcodes.FSTORE: this.stackFrame.pop(); break; case Opcodes.LSTORE: case Opcodes.DSTORE: this.stackFrame.pop(); this.stackFrame.pop(); break; } } @Override public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { this.mv.visitFieldInsn(opcode, owner, name, desc); final char _c = desc.charAt(0); final boolean _longOrDouble = _c == 'J' || _c == 'D'; switch (opcode) { case Opcodes.GETSTATIC: // add 1 or 2 this.stackFrame.push(OTHER); if (_longOrDouble) this.stackFrame.push(OTHER); break; case Opcodes.PUTSTATIC: // remove 1 or 2 this.stackFrame.pop(); if (_longOrDouble) this.stackFrame.pop(); break; case Opcodes.PUTFIELD: // remove 2 or 3 this.stackFrame.pop(); this.stackFrame.pop(); if (_longOrDouble) this.stackFrame.pop(); break; case Opcodes.GETFIELD: // remove 1 add 1 or 2 if (_longOrDouble) this.stackFrame.push(OTHER); } } @Override public void visitIntInsn(final int opcode, final int operand) { this.mv.visitIntInsn(opcode, operand); if (opcode != Opcodes.NEWARRAY) this.stackFrame.push(OTHER); } @Override public void visitLdcInsn(final Object cst) { this.mv.visitLdcInsn(cst); this.stackFrame.push(OTHER); if (cst instanceof Double || cst instanceof Long) this.stackFrame.push(OTHER); } @Override public void visitMultiANewArrayInsn(final String desc, final int dims) { this.mv.visitMultiANewArrayInsn(desc, dims); for (int _i = 0; _i < dims; _i++) this.stackFrame.pop(); this.stackFrame.push(OTHER); } @Override public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) { super.visitTableSwitchInsn(min, max, dflt, labels); this.stackFrame.pop(); this.addBranch(dflt); for (final Label l : labels) this.addBranch(l); } @Override public void visitTypeInsn(final int opcode, final String type) { this.mv.visitTypeInsn(opcode, type); // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack if (opcode == Opcodes.NEW) { this.stackFrame.push(OTHER); } } @Override public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc, final boolean itf) { for (final Type _type : Type.getArgumentTypes(desc)) { this.stackFrame.pop(); if (_type.getSize() == 2) this.stackFrame.pop(); } boolean _flag = false; switch (opcode) { case Opcodes.INVOKESTATIC: break; case Opcodes.INVOKEINTERFACE: case Opcodes.INVOKEVIRTUAL: this.stackFrame.pop(); // objectref break; case Opcodes.INVOKESPECIAL: _flag = this.stackFrame.pop() == THIS && !this.thisIsInitialized; // objectref break; } /* * FIXME-1 * * After bug #8172282 (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8172282) in JDK is fixed, * delete lines with DELETE-ME-1 markers to ensure the call to super constructor is covered by the exception * handler. */ if (_flag) { this.thisIsInitialized = true; ((TracingMethodVisitor) this.mv).setThisInitialized(); ((TracingMethodVisitor) this.mv).endOutermostExceptionHandler(); // DELETE-ME-1 } super.visitMethodInsn(opcode, owner, name, desc, itf); if (_flag) { // DELETE-ME-1 ((TracingMethodVisitor) this.mv).beginOutermostExceptionHandler(); // DELETE-ME-1 } // DELETE-ME-1 final Type _returnType = Type.getReturnType(desc); if (_returnType != Type.VOID_TYPE) { this.stackFrame.push(OTHER); if (_returnType.getSize() == 2) this.stackFrame.push(OTHER); } } @Override public void visitJumpInsn(final int opcode, final Label label) { mv.visitJumpInsn(opcode, label); switch (opcode) { case Opcodes.IFEQ: case Opcodes.IFNE: case Opcodes.IFLT: case Opcodes.IFGE: case Opcodes.IFGT: case Opcodes.IFLE: case Opcodes.IFNULL: case Opcodes.IFNONNULL: this.stackFrame.pop(); break; case Opcodes.IF_ICMPEQ: case Opcodes.IF_ICMPNE: case Opcodes.IF_ICMPLT: case Opcodes.IF_ICMPGE: case Opcodes.IF_ICMPGT: case Opcodes.IF_ICMPLE: case Opcodes.IF_ACMPEQ: case Opcodes.IF_ACMPNE: this.stackFrame.pop(); this.stackFrame.pop(); break; case Opcodes.JSR: this.stackFrame.push(OTHER); break; } addBranch(label); } @Override public void visitInvokeDynamicInsn(final String name, final String desc, final Handle bsm, final Object... bsmArgs) { final Type[] _types = Type.getArgumentTypes(desc); for (final Type _type : _types) { this.stackFrame.pop(); if (_type.getSize() == 2) this.stackFrame.pop(); } super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); final Type _returnType = Type.getReturnType(desc); if (_returnType != Type.VOID_TYPE) { this.stackFrame.push(OTHER); if (_returnType.getSize() == 2) this.stackFrame.push(OTHER); } } @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { super.visitTryCatchBlock(start, end, handler, type); if (!this.branchTarget2frame.containsKey(handler)) { final Stack<Object> _frame = new Stack<>(); _frame.push(OTHER); this.branchTarget2frame.put(handler, _frame); } } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { super.visitLookupSwitchInsn(dflt, keys, labels); this.stackFrame.pop(); this.addBranch(dflt); for (final Label l : labels) this.addBranch(l); } private void addBranch(final Label label) { if (!this.branchTarget2frame.containsKey(label)) { final Stack<Object> _frame = new Stack<>(); _frame.addAll(this.stackFrame); this.branchTarget2frame.put(label, _frame); } } }