@Override
 public void visitInsn(int opcode) {
   if ((newInvokeReplacer != null) && (opcode == DUP)) {
     return; // skip dup after new
   }
   super.visitInsn(opcode);
 }
 @Override
 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
   // [*]
   // Fix all Foo.<field> to FooClone.<field>
   if (owner.equals(wd.superReference)) {
     owner = wd.thisReference;
   }
   super.visitFieldInsn(opcode, owner, name, desc);
 }
  @Override
  public void visitTypeInsn(int opcode, String type) {
    if (opcode == NEW) {
      InvokeInfo invokeInfo = new InvokeInfo(type, INIT, StringPool.EMPTY);
      for (InvokeAspect aspect : aspects) {
        InvokeReplacer ir = aspect.pointcut(invokeInfo);
        if (ir != null) {
          newInvokeReplacer = ir;

          // new pointcut found, skip the new instruction and the following dup.
          // and then go to the invokespecial
          return;
        }
      }
    }
    super.visitTypeInsn(opcode, type);
  }
  /** Invoked on INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE or INVOKEDYNAMIC. */
  @Override
  public void visitMethodInsn(int opcode, String owner, String name, String desc) {

    // replace NEW.<init>
    if ((newInvokeReplacer != null) && (opcode == INVOKESPECIAL)) {
      String exOwner = owner;
      owner = newInvokeReplacer.getOwner();
      name = newInvokeReplacer.getMethodName();
      desc = changeReturnType(desc, 'L' + exOwner + ';');
      super.visitMethodInsn(INVOKESTATIC, owner, name, desc);
      newInvokeReplacer = null;
      return;
    }

    InvokeInfo invokeInfo = new InvokeInfo(owner, name, desc);

    // [*]
    // creating FooClone.<init>; inside the FOO constructor
    // replace the very first invokespecial <init> call (SUB.<init>)
    // to targets subclass with target (FOO.<init>).
    if (methodInfo.getMethodName().equals(INIT)) {
      if ((firstSuperCtorInitCalled == false)
          && (opcode == INVOKESPECIAL)
          && name.equals(INIT)
          && owner.equals(wd.nextSupername)) {
        firstSuperCtorInitCalled = true;
        owner = wd.superReference;
        super.visitMethodInsn(opcode, owner, name, desc);
        return;
      }
    }

    // detection of super calls
    if ((opcode == INVOKESPECIAL)
        && (owner.equals(wd.nextSupername) && (name.equals(INIT) == false))) {
      throw new ProxettaException(
          "Super call detected in class "
              + methodInfo.getClassname()
              + " method: "
              + methodInfo.getSignature()
              + "\nProxetta can't handle super calls due to VM limitations.");
    }

    InvokeReplacer ir = null;

    // find first matching aspect
    for (InvokeAspect aspect : aspects) {
      ir = aspect.pointcut(invokeInfo);
      if (ir != null) {
        break;
      }
    }

    if (ir == null) {
      super.visitMethodInsn(opcode, owner, name, desc);
      return;
    }

    wd.proxyApplied = true;

    String exOwner = owner;
    owner = ir.getOwner();
    name = ir.getMethodName();

    switch (opcode) {
      case INVOKEINTERFACE:
        desc = prependArgument(desc, AsmUtil.L_SIGNATURE_JAVA_LANG_OBJECT);
        break;
      case INVOKEVIRTUAL:
        desc = prependArgument(desc, AsmUtil.L_SIGNATURE_JAVA_LANG_OBJECT);
        break;
      case INVOKESTATIC:
        break;
      default:
        throw new ProxettaException("Unsupported opcode: " + opcode);
    }

    // additional arguments
    if (ir.isPassOwnerName()) {
      desc = appendArgument(desc, AsmUtil.L_SIGNATURE_JAVA_LANG_STRING);
      super.visitLdcInsn(exOwner);
    }
    if (ir.isPassMethodName()) {
      desc = appendArgument(desc, AsmUtil.L_SIGNATURE_JAVA_LANG_STRING);
      super.visitLdcInsn(methodInfo.getMethodName());
    }
    if (ir.isPassMethodSignature()) {
      desc = appendArgument(desc, AsmUtil.L_SIGNATURE_JAVA_LANG_STRING);
      super.visitLdcInsn(methodInfo.getSignature());
    }
    if (ir.isPassTargetClass()) {
      desc = appendArgument(desc, AsmUtil.L_SIGNATURE_JAVA_LANG_CLASS);
      super.mv.visitLdcInsn(Type.getType('L' + wd.superReference + ';'));
    }
    if (ir.isPassThis()) {
      desc = appendArgument(desc, AsmUtil.L_SIGNATURE_JAVA_LANG_OBJECT);
      super.mv.visitVarInsn(ALOAD, 0);
    }

    super.visitMethodInsn(INVOKESTATIC, owner, name, desc);
  }