@Override
  public DataValue naryOperation(AbstractInsnNode insn, List<? extends DataValue> values) {
    int insnIndex = methodNode.instructions.indexOf(insn);
    int opCode = insn.getOpcode();
    switch (opCode) {
      case Opcodes.MULTIANEWARRAY -> {
        return DataValue.LocalDataValue;
      }
      case Opcodes.INVOKEDYNAMIC -> {
        // Lambda creation (w/o invocation) and StringConcatFactory have no side-effect
        InvokeDynamicInsnNode indy = (InvokeDynamicInsnNode)insn;
        if (LambdaIndy.from(indy) == null && !ClassDataIndexer.STRING_CONCAT_FACTORY.equals(indy.bsm.getOwner())) {
          effects[insnIndex] = EffectQuantum.TopEffectQuantum;
        }
        return (ASMUtils.getReturnSizeFast((indy).desc) == 1) ? DataValue.UnknownDataValue1 : DataValue.UnknownDataValue2;
      }
      case Opcodes.INVOKEVIRTUAL, Opcodes.INVOKESPECIAL, Opcodes.INVOKESTATIC, Opcodes.INVOKEINTERFACE -> {
        boolean stable = opCode == Opcodes.INVOKESPECIAL || opCode == Opcodes.INVOKESTATIC;
        MethodInsnNode mNode = ((MethodInsnNode)insn);
        DataValue[] data = values.toArray(DataValue.EMPTY);
        Member method = new Member(mNode.owner, mNode.name, mNode.desc);
        EKey key = new EKey(method, Direction.Pure, stable);
        EffectQuantum quantum = new EffectQuantum.CallQuantum(key, data, opCode == Opcodes.INVOKESTATIC);
        DataValue result;
        if (ASMUtils.getReturnSizeFast(mNode.desc) == 1) {
          if (ASMUtils.isReferenceReturnType(mNode.desc)) {
            result = new DataValue.ReturnDataValue(key);
          }
          else {
            result = DataValue.UnknownDataValue1;
          }
        }
        else {
          result = DataValue.UnknownDataValue2;
        }
        if (HardCodedPurity.getInstance().isPureMethod(method)) {
          quantum = null;
          result = HardCodedPurity.getInstance().getReturnValueForPureMethod(method);
        }
        else if (HardCodedPurity.getInstance().isThisChangingMethod(method)) {
          DataValue receiver = ArrayUtil.getFirstElement(data);
          if (receiver == DataValue.ThisDataValue) {
            quantum = EffectQuantum.ThisChangeQuantum;
          }
          else if (receiver == DataValue.LocalDataValue || receiver == DataValue.OwnedDataValue) {
            quantum = null;
          }
          else if (receiver instanceof DataValue.ParameterDataValue) {
            quantum = new EffectQuantum.ParamChangeQuantum(((DataValue.ParameterDataValue)receiver).n);
          }
          if (HardCodedPurity.getInstance().isBuilderChainCall(method)) {
            // mostly to support string concatenation
            result = receiver;
          }
        }
        effects[insnIndex] = quantum;
        return result;
      }
    }
    return null;
  }
@Override
  public DataValue naryOperation(AbstractInsnNode insn, List<? extends DataValue> values)