private boolean isAllowedOdex(@Nonnull Opcode opcode) {
    baksmaliOptions options = methodDef.classDef.options;
    if (options.allowOdex) {
      return true;
    }

    if (methodDef.classDef.options.apiLevel >= 14) {
      return false;
    }

    return opcode.isOdexedInstanceVolatile()
        || opcode.isOdexedStaticVolatile()
        || opcode == Opcode.THROW_VERIFICATION_ERROR;
  }
  @Override
  public boolean writeTo(IndentingWriter writer) throws IOException {
    Opcode opcode = instruction.getOpcode();
    String verificationErrorName = null;
    String referenceString = null;

    boolean commentOutInstruction = false;

    if (instruction instanceof Instruction20bc) {
      int verificationError = ((Instruction20bc) instruction).getVerificationError();
      verificationErrorName = VerificationError.getVerificationErrorName(verificationError);
      if (verificationErrorName == null) {
        writer.write("#was invalid verification error type: ");
        writer.printSignedIntAsDec(verificationError);
        writer.write("\n");
        verificationErrorName = "generic-error";
      }
    }

    if (instruction instanceof ReferenceInstruction) {
      ReferenceInstruction referenceInstruction = (ReferenceInstruction) instruction;
      try {
        Reference reference = referenceInstruction.getReference();

        String classContext = null;
        if (methodDef.classDef.options.useImplicitReferences) {
          classContext = methodDef.method.getDefiningClass();
        }

        referenceString = ReferenceUtil.getReferenceString(reference, classContext);
        assert referenceString != null;
      } catch (InvalidItemIndex ex) {
        writer.write("#");
        writer.write(ex.getMessage());
        writer.write("\n");
        commentOutInstruction = true;

        referenceString =
            String.format(
                "%s@%d",
                ReferenceType.toString(referenceInstruction.getReferenceType()),
                ex.getInvalidIndex());
      } catch (ReferenceType.InvalidReferenceTypeException ex) {
        writer.write("#invalid reference type: ");
        writer.printSignedIntAsDec(ex.getReferenceType());
        commentOutInstruction = true;

        referenceString = "invalid_reference";
      }
    }

    if (instruction instanceof Instruction31t) {
      boolean validPayload = true;

      switch (instruction.getOpcode()) {
        case PACKED_SWITCH:
          int baseAddress =
              methodDef.getPackedSwitchBaseAddress(
                  this.codeAddress + ((Instruction31t) instruction).getCodeOffset());
          if (baseAddress == -1) {
            validPayload = false;
          }
          break;
        case SPARSE_SWITCH:
          baseAddress =
              methodDef.getSparseSwitchBaseAddress(
                  this.codeAddress + ((Instruction31t) instruction).getCodeOffset());
          if (baseAddress == -1) {
            validPayload = false;
          }
          break;
        case FILL_ARRAY_DATA:
          try {
            methodDef.findPayloadOffset(
                this.codeAddress + ((Instruction31t) instruction).getCodeOffset(),
                Opcode.ARRAY_PAYLOAD);
          } catch (InvalidSwitchPayload ex) {
            validPayload = false;
          }
          break;
        default:
          throw new ExceptionWithContext("Invalid 31t opcode: %s", instruction.getOpcode());
      }

      if (!validPayload) {
        writer.write("#invalid payload reference\n");
        commentOutInstruction = true;
      }
    }

    if (opcode.odexOnly()) {
      if (!isAllowedOdex(opcode)) {
        writer.write("#disallowed odex opcode\n");
        commentOutInstruction = true;
      }
    }

    if (commentOutInstruction) {
      writer.write("#");
    }

    switch (instruction.getOpcode().format) {
      case Format10t:
        writeOpcode(writer);
        writer.write(' ');
        writeTargetLabel(writer);
        break;
      case Format10x:
        if (instruction instanceof UnknownInstruction) {
          writer.write("#unknown opcode: 0x");
          writer.printUnsignedLongAsHex(((UnknownInstruction) instruction).getOriginalOpcode());
          writer.write('\n');
        }
        writeOpcode(writer);
        break;
      case Format11n:
        writeOpcode(writer);
        writer.write(' ');
        writeFirstRegister(writer);
        writer.write(", ");
        writeLiteral(writer);
        break;
      case Format11x:
        writeOpcode(writer);
        writer.write(' ');
        writeFirstRegister(writer);
        break;
      case Format12x:
        writeOpcode(writer);
        writer.write(' ');
        writeFirstRegister(writer);
        writer.write(", ");
        writeSecondRegister(writer);
        break;
      case Format20bc:
        writeOpcode(writer);
        writer.write(' ');
        writer.write(verificationErrorName);
        writer.write(", ");
        writer.write(referenceString);
        break;
      case Format20t:
      case Format30t:
        writeOpcode(writer);
        writer.write(' ');
        writeTargetLabel(writer);
        break;
      case Format21c:
      case Format31c:
        writeOpcode(writer);
        writer.write(' ');
        writeFirstRegister(writer);
        writer.write(", ");
        writer.write(referenceString);
        break;
      case Format21ih:
      case Format21lh:
      case Format21s:
      case Format31i:
      case Format51l:
        writeOpcode(writer);
        writer.write(' ');
        writeFirstRegister(writer);
        writer.write(", ");
        writeLiteral(writer);
        if (instruction.getOpcode().setsWideRegister()) {
          writeCommentIfLikelyDouble(writer);
        } else {
          boolean isResourceId = writeCommentIfResourceId(writer);
          if (!isResourceId) writeCommentIfLikelyFloat(writer);
        }
        break;
      case Format21t:
      case Format31t:
        writeOpcode(writer);
        writer.write(' ');
        writeFirstRegister(writer);
        writer.write(", ");
        writeTargetLabel(writer);
        break;
      case Format22b:
      case Format22s:
        writeOpcode(writer);
        writer.write(' ');
        writeFirstRegister(writer);
        writer.write(", ");
        writeSecondRegister(writer);
        writer.write(", ");
        writeLiteral(writer);
        break;
      case Format22c:
        writeOpcode(writer);
        writer.write(' ');
        writeFirstRegister(writer);
        writer.write(", ");
        writeSecondRegister(writer);
        writer.write(", ");
        writer.write(referenceString);
        break;
      case Format22cs:
        writeOpcode(writer);
        writer.write(' ');
        writeFirstRegister(writer);
        writer.write(", ");
        writeSecondRegister(writer);
        writer.write(", ");
        writeFieldOffset(writer);
        break;
      case Format22t:
        writeOpcode(writer);
        writer.write(' ');
        writeFirstRegister(writer);
        writer.write(", ");
        writeSecondRegister(writer);
        writer.write(", ");
        writeTargetLabel(writer);
        break;
      case Format22x:
      case Format32x:
        writeOpcode(writer);
        writer.write(' ');
        writeFirstRegister(writer);
        writer.write(", ");
        writeSecondRegister(writer);
        break;
      case Format23x:
        writeOpcode(writer);
        writer.write(' ');
        writeFirstRegister(writer);
        writer.write(", ");
        writeSecondRegister(writer);
        writer.write(", ");
        writeThirdRegister(writer);
        break;
      case Format25x:
        writeOpcode(writer);
        writer.write(' ');
        writeInvoke25xRegisters(writer); // vC, {vD, ...}
        break;
      case Format35c:
        writeOpcode(writer);
        writer.write(' ');
        writeInvokeRegisters(writer);
        writer.write(", ");
        writer.write(referenceString);
        break;
      case Format35mi:
        writeOpcode(writer);
        writer.write(' ');
        writeInvokeRegisters(writer);
        writer.write(", ");
        writeInlineIndex(writer);
        break;
      case Format35ms:
        writeOpcode(writer);
        writer.write(' ');
        writeInvokeRegisters(writer);
        writer.write(", ");
        writeVtableIndex(writer);
        break;
      case Format3rc:
        writeOpcode(writer);
        writer.write(' ');
        writeInvokeRangeRegisters(writer);
        writer.write(", ");
        writer.write(referenceString);
        break;
      case Format3rmi:
        writeOpcode(writer);
        writer.write(' ');
        writeInvokeRangeRegisters(writer);
        writer.write(", ");
        writeInlineIndex(writer);
        break;
      case Format3rms:
        writeOpcode(writer);
        writer.write(' ');
        writeInvokeRangeRegisters(writer);
        writer.write(", ");
        writeVtableIndex(writer);
        break;
      default:
        assert false;
        return false;
    }

    if (commentOutInstruction) {
      writer.write("\nnop");
    }

    return true;
  }