【问题标题】:Code Contracts Static Analysis: Prover Limitations?代码合约静态分析:证明者限制?
【发布时间】:2010-06-18 20:50:26
【问题描述】:

我一直在玩代码合同,我真的很喜欢我目前所看到的。他们鼓励我评估并明确声明我的假设,这已经帮助我确定了一些我在添加合同的代码中没有考虑过的极端情况。现在我正在尝试强制执行更复杂的不变量。我有一个案例目前无法证明,我很好奇除了简单地添加 Contract.Assume 调用之外是否还有其他方法可以解决这个问题。以下是有问题的课程,为了便于阅读而精简:

public abstract class MemoryEncoder
{
    private const int CapacityDelta = 16;

    private int _currentByte;

    /// <summary>
    ///   The current byte index in the encoding stream.
    ///   This should not need to be modified, under typical usage,
    ///   but can be used to randomly access the encoding region.
    /// </summary>
    public int CurrentByte
    {
        get
        {
            Contract.Ensures(Contract.Result<int>() >= 0);
            Contract.Ensures(Contract.Result<int>() <= Length);
            return _currentByte;
        }
        set
        {
            Contract.Requires(value >= 0);
            Contract.Requires(value <= Length);
            _currentByte = value;
        }
    }

    /// <summary>
    ///   Current number of bytes encoded in the buffer.
    ///   This may be less than the size of the buffer (capacity).
    /// </summary>
    public int Length { get; private set; }

    /// <summary>
    /// The raw buffer encapsulated by the encoder.
    /// </summary>
    protected internal Byte[] Buffer { get; private set; }

    /// <summary>
    /// Reserve space in the encoder buffer for the specified number of new bytes
    /// </summary>
    /// <param name="bytesRequired">The number of bytes required</param>
    protected void ReserveSpace(int bytesRequired)
    {
        Contract.Requires(bytesRequired > 0);
        Contract.Ensures((Length - CurrentByte) >= bytesRequired);

        //Check if these bytes would overflow the current buffer););
        if ((CurrentByte + bytesRequired) > Buffer.Length)
        {
            //Create a new buffer with at least enough space for the additional bytes required
            var newBuffer = new Byte[Buffer.Length + Math.Max(bytesRequired, CapacityDelta)];

            //Copy the contents of the previous buffer and replace the original buffer reference
            Buffer.CopyTo(newBuffer, 0);
            Buffer = newBuffer;
        }

        //Check if the total length of written bytes has increased
        if ((CurrentByte + bytesRequired) > Length)
        {
            Length = CurrentByte + bytesRequired;
        }
    }

    [ContractInvariantMethod]
    private void GlobalRules()
    {
        Contract.Invariant(Buffer != null);
        Contract.Invariant(Length <= Buffer.Length);
        Contract.Invariant(CurrentByte >= 0);
        Contract.Invariant(CurrentByte <= Length);
    }
}

我对如何在 ReserveSpace 中构造 Contract 调用感兴趣,以便类不变量是可证明的。特别是,它抱怨 (Length

【问题讨论】:

    标签: c# .net static-analysis code-contracts


    【解决方案1】:

    在与这个问题斗争了一段时间后,我想出了这个可证明的解决方案(构造函数是一个允许隔离测试的假人):

    public abstract class MemoryEncoder
    {
        private const int CapacityDelta = 16;
    
        private byte[] _buffer;
        private int _currentByte;
        private int _length;
    
        protected MemoryEncoder()
        {
            Buffer = new byte[500];
            Length = 0;
            CurrentByte = 0;
        }
    
        /// <summary>
        ///   The current byte index in the encoding stream.
        ///   This should not need to be modified, under typical usage,
        ///   but can be used to randomly access the encoding region.
        /// </summary>
        public int CurrentByte
        {
            get
            {
                return _currentByte;
            }
            set
            {
                Contract.Requires(value >= 0);
                Contract.Requires(value <= Length);
                _currentByte = value;
            }
        }
    
    
        /// <summary>
        ///   Current number of bytes encoded in the buffer.
        ///   This may be less than the size of the buffer (capacity).
        /// </summary>
        public int Length
        {
            get { return _length; }
            private set
            {
                Contract.Requires(value >= 0);
                Contract.Requires(value <= _buffer.Length);
                Contract.Requires(value >= CurrentByte);
                Contract.Ensures(_length <= _buffer.Length);
                _length = value;
            }
        }
    
        /// <summary>
        /// The raw buffer encapsulated by the encoder.
        /// </summary>
        protected internal Byte[] Buffer
        {
            get { return _buffer; }
            private set
            {
                Contract.Requires(value != null);
                Contract.Requires(value.Length >= _length);
                _buffer = value;
            }
        }
    
        /// <summary>
        /// Reserve space in the encoder buffer for the specified number of new bytes
        /// </summary>
        /// <param name="bytesRequired">The number of bytes required</param>
        protected void ReserveSpace(int bytesRequired)
        {
            Contract.Requires(bytesRequired > 0);
            Contract.Ensures((Length - CurrentByte) >= bytesRequired);
    
            //Check if these bytes would overflow the current buffer););
            if ((CurrentByte + bytesRequired) > Buffer.Length)
            {
                //Create a new buffer with at least enough space for the additional bytes required
                var newBuffer = new Byte[Buffer.Length + Math.Max(bytesRequired, CapacityDelta)];
    
                //Copy the contents of the previous buffer and replace the original buffer reference
                Buffer.CopyTo(newBuffer, 0);
                Buffer = newBuffer;
            }
    
            //Check if the total length of written bytes has increased
            if ((CurrentByte + bytesRequired) > Length)
            {
                Contract.Assume(CurrentByte + bytesRequired <= _buffer.Length);
                Length = CurrentByte + bytesRequired;
            }
        }
    
        [ContractInvariantMethod]
        private void GlobalRules()
        {
            Contract.Invariant(_buffer != null);
            Contract.Invariant(_length <= _buffer.Length);
            Contract.Invariant(_currentByte >= 0);
            Contract.Invariant(_currentByte <= _length);
        }
    }
    

    我注意到的主要事情是在属性上放置不变量会变得很麻烦,但在字段上放置不变量似乎更容易解决。对财产访问者规定适当的合同义务也很重要。我将不得不继续试验,看看哪些有效,哪些无效。这是一个有趣的系统,但如果有人有关于证明者如何工作的优秀“备忘单”,我肯定想知道更多。

    【讨论】:

    • +1 表示支持。我非常努力地尝试在我们的新产品中使用代码契约,但最终不得不放弃它们,因为它们变得非常麻烦,更不用说编译速度慢了。不过,它们作为脑力锻炼真的很巧妙,我很喜欢它们呈现的脑力谜题。
    • 我的实验目标是采用一个相对较小的内部实用程序库,通过合约对其进行全面规范,然后使用 Pex 生成测试。我的探索已经发现了一些我们现有测试没有发现的极端情况,所以我肯定有兴趣看看它是如何进展的。到目前为止,我还没有注意到任何主要的构建时间问题,但它可能只是代码库太小了。不过,后台静态分析器确实需要相当长的时间来证明。
    • 只是一个注释;您可以将自动道具与 CC 结合使用...只需添加一个类不变量,它将作为前置/后置条件添加。
    • @Porges,在使用自动道具并将约束置于类不变量中时,让证明者正常工作时遇到了很多麻烦。我得再做一些实验。
    • @Porges,感谢您对这一切的跟进; Code Contracts 似乎仍有一些问题需要解决。我会坚持下去,因为我认为它是一个非常有价值的工具,即使只是作为一种概念帮助来指导我明确指定我的所有假设。
    猜你喜欢
    • 2012-10-27
    • 1970-01-01
    • 1970-01-01
    • 2012-10-04
    • 2023-03-25
    • 2018-04-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-03
    相关资源
    最近更新 更多