@Override
  public void visitMethodInsn(
      int opcode, String owner, String name, String signature, boolean itf) {
    if (opcode == Opcodes.INVOKESTATIC
        // Array does its own native allocation.  Grr.
        && owner.equals("java/lang/reflect/Array")
        && name.equals("newInstance")) {
      if (signature.equals("(Ljava/lang/Class;I)Ljava/lang/Object;")) {

        Label beginScopeLabel = new Label();
        Label endScopeLabel = new Label();
        super.visitLabel(beginScopeLabel);

        // stack: ... class count
        int countIndex = newLocal("I", beginScopeLabel, endScopeLabel);
        super.visitVarInsn(Opcodes.ISTORE, countIndex);
        // -> stack: ... class
        pushClassNameOnStack();
        // -> stack: ... class className
        int typeNameIndex = newLocal("Ljava/lang/String;", beginScopeLabel, endScopeLabel);
        super.visitVarInsn(Opcodes.ASTORE, typeNameIndex);
        // -> stack: ... class
        super.visitVarInsn(Opcodes.ILOAD, countIndex);
        // -> stack: ... class count
        super.visitMethodInsn(opcode, owner, name, signature, itf);
        // -> stack: ... newobj
        super.visitInsn(Opcodes.DUP);
        // -> stack: ... newobj newobj
        super.visitVarInsn(Opcodes.ILOAD, countIndex);
        // -> stack: ... newobj newobj count
        super.visitInsn(Opcodes.SWAP);
        // -> stack: ... newobj count newobj
        super.visitVarInsn(Opcodes.ALOAD, typeNameIndex);
        super.visitLabel(endScopeLabel);
        // -> stack: ... newobj count newobj className
        super.visitInsn(Opcodes.SWAP);
        // -> stack: ... newobj count className newobj
        super.visitMethodInsn(
            Opcodes.INVOKESTATIC, recorderClass, recorderMethod, RECORDER_SIGNATURE, false);
        // -> stack: ... newobj
        return;
      } else if (signature.equals("(Ljava/lang/Class;[I)Ljava/lang/Object;")) {
        Label beginScopeLabel = new Label();
        Label endScopeLabel = new Label();
        super.visitLabel(beginScopeLabel);

        int dimsArrayIndex = newLocal("[I", beginScopeLabel, endScopeLabel);
        // stack: ... class dimsArray
        pushProductOfIntArrayOnStack();
        // -> stack: ... class dimsArray product
        int productIndex = newLocal("I", beginScopeLabel, endScopeLabel);
        super.visitVarInsn(Opcodes.ISTORE, productIndex);
        // -> stack: ... class dimsArray

        super.visitVarInsn(Opcodes.ASTORE, dimsArrayIndex);
        // -> stack: ... class
        pushClassNameOnStack();
        // -> stack: ... class className
        int typeNameIndex = newLocal("Ljava/lang/String;", beginScopeLabel, endScopeLabel);
        super.visitVarInsn(Opcodes.ASTORE, typeNameIndex);
        // -> stack: ... class
        super.visitVarInsn(Opcodes.ALOAD, dimsArrayIndex);
        // -> stack: ... class dimsArray
        super.visitMethodInsn(opcode, owner, name, signature, itf);
        // -> stack: ... newobj

        super.visitInsn(Opcodes.DUP);
        // -> stack: ... newobj newobj
        super.visitVarInsn(Opcodes.ILOAD, productIndex);
        // -> stack: ... newobj newobj product
        super.visitInsn(Opcodes.SWAP);
        // -> stack: ... newobj product newobj
        super.visitVarInsn(Opcodes.ALOAD, typeNameIndex);
        super.visitLabel(endScopeLabel);
        // -> stack: ... newobj product newobj className
        super.visitInsn(Opcodes.SWAP);
        // -> stack: ... newobj product className newobj
        super.visitMethodInsn(
            Opcodes.INVOKESTATIC, recorderClass, recorderMethod, RECORDER_SIGNATURE, false);
        // -> stack: ... newobj
        return;
      }
    }

    if (opcode == Opcodes.INVOKEVIRTUAL) {
      if ("clone".equals(name) && owner.startsWith("[")) {
        super.visitMethodInsn(opcode, owner, name, signature, itf);

        int i = 0;
        while (i < owner.length()) {
          if (owner.charAt(i) != '[') {
            break;
          }
          i++;
        }
        if (i > 1) {
          // -> stack: ... newobj
          super.visitTypeInsn(Opcodes.CHECKCAST, owner);
          // -> stack: ... arrayref
          calculateArrayLengthAndDispatch(owner.substring(i), i);
        } else {
          // -> stack: ... newobj
          super.visitInsn(Opcodes.DUP);
          // -> stack: ... newobj newobj
          super.visitTypeInsn(Opcodes.CHECKCAST, owner);
          // -> stack: ... newobj arrayref
          super.visitInsn(Opcodes.ARRAYLENGTH);
          // -> stack: ... newobj length
          super.visitInsn(Opcodes.SWAP);
          // -> stack: ... length newobj
          invokeRecordAllocation(owner.substring(i));
        }
        return;
      } else if ("newInstance".equals(name)) {
        if ("java/lang/Class".equals(owner) && "()Ljava/lang/Object;".equals(signature)) {
          super.visitInsn(Opcodes.DUP);
          // -> stack: ... Class Class
          super.visitMethodInsn(opcode, owner, name, signature, itf);
          // -> stack: ... Class newobj
          super.visitInsn(Opcodes.DUP_X1);
          // -> stack: ... newobj Class newobj
          super.visitMethodInsn(
              Opcodes.INVOKESTATIC, recorderClass, recorderMethod, CLASS_RECORDER_SIG, false);
          // -> stack: ... newobj
          return;
        } else if ("java/lang/reflect/Constructor".equals(owner)
            && "([Ljava/lang/Object;)Ljava/lang/Object;".equals(signature)) {
          buildRecorderFromObject(opcode, owner, name, signature, itf);
          return;
        }
      }
    }

    if (opcode == Opcodes.INVOKESPECIAL) {
      if ("clone".equals(name) && "java/lang/Object".equals(owner)) {
        buildRecorderFromObject(opcode, owner, name, signature, itf);
        return;
      } else if ("<init>".equals(name) && outstandingAllocs > 0) {
        // Tricky because superclass initializers mean there can be more calls
        // to <init> than calls to NEW; hence outstandingAllocs.
        --outstandingAllocs;

        // Most of the time (i.e. in bytecode generated by javac) it is the case
        // that following an <init> call the top of the stack has a reference ot
        // the newly-initialized object.  But nothing in the JVM Spec requires
        // this, so we need to play games with the stack to make an explicit
        // extra copy (and then discard it).

        dupStackElementBeforeSignatureArgs(signature);
        super.visitMethodInsn(opcode, owner, name, signature, itf);
        super.visitLdcInsn(-1);
        super.visitInsn(Opcodes.SWAP);
        invokeRecordAllocation(owner);
        super.visitInsn(Opcodes.POP);
        return;
      }
    }

    super.visitMethodInsn(opcode, owner, name, signature, itf);
  }
Reflection-based allocation (@see java.lang.reflect.Array#newInstance) is triggered with a
static method call (INVOKESTATIC), so we hook it here. Class initialization is triggered with a
constructor call (INVOKESPECIAL) so we hook that here too as a proxy for the new bytecode which
leaves an uninitialized object on the stack that we're not allowed to touch. {@link
java.lang.Object#clone} is also a call to INVOKESPECIAL, and is hooked here. {@link
java.lang.Class#newInstance} and {@link java.lang.reflect.Constructor#newInstance} are both
INVOKEVIRTUAL calls, so they are hooked here, as well.
src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java