public static boolean methodDescsCloseEnough(String mightHaveVMState, String desc) {
    Type[] d1 = Type.getArgumentTypes(mightHaveVMState);
    Type[] d2 = Type.getArgumentTypes(desc);
    if (Arrays.deepEquals(d1, d2)) return true;
    if (d1[d1.length - 1].getInternalName().equals(Type.getInternalName(VMState.class))) {

      if (d1.length - 1 != d2.length) return false;
      for (int i = 0; i < d1.length - 1; i++) if (!d1[i].equals(d2[i])) return false;
      return true;
    }
    return false;
  }
  //	private static Logger log = Logger.getLogger(InvivoAdapter.class);
  public InvivoAdapter(
      int api,
      MethodVisitor mv,
      int access,
      String name,
      String desc,
      String className,
      boolean generateReverseStub) {
    //		super(api, mv, access, name, desc);
    super(mv);
    this.access = access;
    this.name = name;
    this.desc = desc;
    this.className = className;

    Type[] args = Type.getArgumentTypes(desc);
    firstLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0;
    for (int i = 0; i < args.length; i++) {
      firstLocal += args[i].getSize();
    }
    sandboxVar = isMain() || isClinit() ? getFirstLocal() : getFirstLocal() - 1;

    if (generateReverseStub) {
      GeneratorAdapter gv = new GeneratorAdapter(mv, access, name, desc);
      gv.visitCode();
      gv.loadThis();
      for (int i = 0; i < args.length - 1; i++) {
        gv.loadArg(i);
      }
      Type[] args2 = new Type[args.length - 1];
      System.arraycopy(args, 0, args2, 0, args.length - 1);
      gv.visitMethodInsn(
          INVOKEVIRTUAL,
          className,
          name.substring(1),
          Type.getMethodDescriptor(Type.getReturnType(desc), args2),
          false);
      //			switch(Type.getReturnType(desc).getSort())
      //			{
      //			case Type.ARRAY:
      //			case Type.OBJECT:
      //				gv.visitLdcInsn(null);
      //			break;
      //			case Type.VOID:
      //				break;
      //			default:
      //				gv.push(0);
      //			}
      gv.returnValue();
      gv.visitMaxs(0, 0);
      //			mv.visitEnd();
    }
  }
 @Override
 public SourceValue newValue(final Type type) {
   if (type == Type.VOID_TYPE) {
     return null;
   }
   return new SourceValue(type == null ? 1 : type.getSize());
 }
 @Override
 public SourceValue naryOperation(
     final AbstractInsnNode insn, final List<? extends SourceValue> values) {
   int size;
   int opcode = insn.getOpcode();
   if (opcode == MULTIANEWARRAY) {
     size = 1;
   } else {
     String desc =
         (opcode == INVOKEDYNAMIC)
             ? ((InvokeDynamicInsnNode) insn).desc
             : ((MethodInsnNode) insn).desc;
     size = Type.getReturnType(desc).getSize();
   }
   return new SourceValue(size, insn);
 }
 @Override
 public SourceValue unaryOperation(final AbstractInsnNode insn, final SourceValue value) {
   int size;
   switch (insn.getOpcode()) {
     case LNEG:
     case DNEG:
     case I2L:
     case I2D:
     case L2D:
     case F2L:
     case F2D:
     case D2L:
       size = 2;
       break;
     case GETFIELD:
       size = Type.getType(((FieldInsnNode) insn).desc).getSize();
       break;
     default:
       size = 1;
   }
   return new SourceValue(size, insn);
 }
 @Override
 public SourceValue newOperation(final AbstractInsnNode insn) {
   int size;
   switch (insn.getOpcode()) {
     case LCONST_0:
     case LCONST_1:
     case DCONST_0:
     case DCONST_1:
       size = 2;
       break;
     case LDC:
       Object cst = ((LdcInsnNode) insn).cst;
       size = cst instanceof Long || cst instanceof Double ? 2 : 1;
       break;
     case GETSTATIC:
       size = Type.getType(((FieldInsnNode) insn).desc).getSize();
       break;
     default:
       size = 1;
   }
   return new SourceValue(size, insn);
 }
 private void visitVMVMLocalVariable() {
   if (sandboxVar >= 0 && !sandboxVarVisited && isRegularClass()) {
     try {
       sandboxVarVisited = true;
       super.visitLocalVariable(
           "secretVMVMD4t4z", Type.getDescriptor(VMState.class), null, start, end, sandboxVar);
     } catch (NullPointerException ex) {
       System.err.println(
           "Unable to visit local variable vmvmSandboxIndx (num "
               + sandboxVar
               + ") [labels="
               + start
               + "; "
               + end
               + "]"
               + "[method = "
               + this.className
               + "."
               + this.name
               + this.desc);
       ex.printStackTrace();
     }
   }
 }
  public void cloneLocals(int whereToStop) {
    oldLVtoSandboxLV = new HashMap<>();
    List<?> oldLocals = getLocals();
    for (int i = 0; i < whereToStop; i++) {
      Type t = lvs.getLocalTypes().get(i);
      if (i == 0 && t == null && (access & ACC_STATIC) == 0)
        t = Type.getType("L" + className + ";");
      if (i < firstLocal && t == null) {
        if ((Opcodes.ACC_STATIC & access) == 0)
          t = Type.getMethodType(this.desc).getArgumentTypes()[i - 1];
        else t = Type.getMethodType(this.desc).getArgumentTypes()[i];
      }
      if (t.getSort() == Type.OBJECT && t.getInternalName().equals("java/lang/Object")) {
        // Default to the type of what we cloened from
        for (Object o : oldLocals) {
          LocalVariableNode lv = (LocalVariableNode) o;
          if (lv.index + 1 == i) {
            t = Type.getType(lv.desc);
            break;
          }
        }
      }

      int idx = lvs.newLocal(t);
      oldLVtoSandboxLV.put(i, idx);
      newLVs.add(new LocalVariableNode("clone_" + i, t.getDescriptor(), null, null, null, idx));

      load(i, t);
      if (t.getSort() == Type.ARRAY || t.getSort() == Type.OBJECT)
        cloneValAtTopOfStack(t.getDescriptor());
      store(idx, t);
    }
  }
 public void getSandboxFlag() {
   getSandboxFlagState();
   super.visitMethodInsn(
       INVOKEVIRTUAL, Type.getInternalName(VMState.class), "getState", "()I", false);
 }