@Override
  public BasicValue binaryOperation(AbstractInsnNode insn, BasicValue value1, BasicValue value2)
      throws AnalyzerException {
    switch (insn.getOpcode()) {
      case PUTFIELD: // put value2 into value1
        // skip untagged values
        if (!isTagged(value1)) {
          return null;
        }

        final TaggedValue taggedValue = (TaggedValue) value1;
        final FieldInsnNode field = (FieldInsnNode) insn;
        final boolean value2HasInputDependency = hasImportantDependencies(value2);

        // if value1 is not an input, make value1 a container and add value2 to it
        // PUTFIELD on inputs is not allowed
        if (!taggedValue.isInput() && value2HasInputDependency) {
          if (!taggedValue.canContainFields()) {
            taggedValue.setTag(Tag.CONTAINER);
          }
          taggedValue.addContainerMapping(field.name, tagged(value2), currentFrame);
        }
        // if value1 is filled with non-input, make it container and mark the field
        // PUTFIELD on inputs is not allowed
        else if (!taggedValue.isInput() && !value2HasInputDependency) {
          if (!taggedValue.canContainFields()) {
            taggedValue.setTag(Tag.CONTAINER);
          }
          taggedValue.addContainerMapping(field.name, null, currentFrame);
        }
        // PUTFIELD on input leads to input modification
        // make input regular
        else if (taggedValue.isInput()) {
          taggedValue.makeRegular();
        }
        return null;
      default:
        return super.binaryOperation(insn, value1, value2);
    }
  }
  @Override
  public BasicValue unaryOperation(AbstractInsnNode insn, BasicValue value)
      throws AnalyzerException {
    switch (insn.getOpcode()) {
        // modify jump instructions if we can assume that the hasNext operation will always
        // be true at the first call
      case IFEQ:
        if (isTagged(value) && tagged(value).isIteratorTrueAssumption()) {
          modifiedAsmAnalyzer.requestIFEQLoopModification();
        }
        return super.unaryOperation(insn, value);
      case IFNE:
        if (isTagged(value) && tagged(value).isIteratorTrueAssumption()) {
          modifiedAsmAnalyzer.requestIFNELoopModification();
        }
        return super.unaryOperation(insn, value);

      case CHECKCAST:
        return value;
      case PUTSTATIC:
        analyzer.handlePutStatic();
        return super.unaryOperation(insn, value);
      case GETFIELD:
        final FieldInsnNode field = (FieldInsnNode) insn;
        // skip untagged values
        if (!isTagged(value)) {
          return super.unaryOperation(insn, value);
        }
        final TaggedValue taggedValue = (TaggedValue) value;

        // inputs are atomic, a GETFIELD results in undefined state
        if (taggedValue.isInput()) {
          return super.unaryOperation(insn, value);
        }
        // access of input container field
        // or access of a KNOWN UDF instance variable
        else if (taggedValue.canContainFields() && taggedValue.containerContains(field.name)) {
          final TaggedValue tv = taggedValue.getContainerMapping().get(field.name);
          if (tv != null) {
            return tv;
          }
        }
        // access of a yet UNKNOWN UDF instance variable
        else if (taggedValue.isThis() && !taggedValue.containerContains(field.name)) {
          final TaggedValue tv = new TaggedValue(Type.getType(field.desc));
          taggedValue.addContainerMapping(field.name, tv, currentFrame);
          return tv;
        }
        // access of a yet unknown container, mark it as a container
        else if (taggedValue.isRegular()) {
          taggedValue.setTag(Tag.CONTAINER);
          final TaggedValue tv = new TaggedValue(Type.getType(field.desc));
          taggedValue.addContainerMapping(field.name, tv, currentFrame);
          return tv;
        }
        return super.unaryOperation(insn, value);
      case IINC:
        // modification of a local variable or input
        if (isTagged(value) && (tagged(value).isIntConstant() || tagged(value).isInput())) {
          tagged(value).makeRegular();
        }
        return super.unaryOperation(insn, value);
      default:
        return super.unaryOperation(insn, value);
    }
  }