Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

222 lines
7.9 KiB
C#

//
// ContractNodes.cs
//
// Authors:
// Alexander Chebaturkin (chebaturkin@gmail.com)
//
// Copyright (C) 2011 Alexander Chebaturkin
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Mono.CodeContracts.Static.AST;
namespace Mono.CodeContracts.Static.ContractExtraction {
class ContractNodes {
public static readonly string ContractNamespace = "System.Diagnostics.Contracts";
public static readonly string ContractClassName = "Contract";
public static readonly string RequiresName = "Requires";
public static readonly string EnsuresName = "Ensures";
public static readonly string AssertName = "Assert";
public static readonly string AssumeName = "Assume";
public static readonly string EndContractBlockName = "EndContractBlock";
[RepresentationFor ("Contract.Assert(bool)")]
public readonly Method AssertMethod;
[RepresentationFor ("Contract.Assert(bool, string)")]
public readonly Method AssertWithMessageMethod;
[RepresentationFor ("Contract.Assume(bool)")]
public readonly Method AssumeMethod;
[RepresentationFor ("Contract.Assume(bool, string)")]
public readonly Method AssumeWithMessageMethod;
[RepresentationFor ("System.Diagnostics.Contracts.Contract")]
public readonly Class ContractClass;
[RepresentationFor ("Contract.EndContractBlock()")]
public readonly Method EndContractBlock;
[RepresentationFor ("Contract.Ensures(bool)")]
public readonly Method EnsuresMethod;
[RepresentationFor ("Contract.Ensures(bool, string)")]
public readonly Method EnsuresWithMessageMethod;
[RepresentationFor ("Contract.Requires(bool)")]
public readonly Method RequiresMethod;
[RepresentationFor ("Contract.Requires(bool, string)")]
public readonly Method RequiresWithMessageMethod;
private ContractNodes (AssemblyNode assembly, Action<string> errorHandler)
{
CoreSystemTypes.ModuleDefinition = assembly.Modules.First ().Definition;
if (errorHandler != null)
ErrorFound += errorHandler;
this.ContractClass = assembly.GetType (ContractNamespace, ContractClassName) as Class;
if (this.ContractClass == null)
return;
IEnumerable<Method> methods = this.ContractClass.GetMethods (RequiresName, CoreSystemTypes.Instance.TypeBoolean);
foreach (Method method in methods) {
if (method.GenericParameters == null || method.GenericParameters.Count == 0)
this.RequiresMethod = method;
}
if (this.RequiresMethod == null) {
this.ContractClass = null;
return;
}
methods = this.ContractClass.GetMethods (RequiresName, CoreSystemTypes.Instance.TypeBoolean, CoreSystemTypes.Instance.TypeString);
foreach (Method method in methods) {
if (method.GenericParameters == null || method.GenericParameters.Count == 0)
this.RequiresWithMessageMethod = method;
}
this.EnsuresMethod = this.ContractClass.GetMethod (EnsuresName, CoreSystemTypes.Instance.TypeBoolean);
this.EnsuresWithMessageMethod = this.ContractClass.GetMethod (EnsuresName,
CoreSystemTypes.Instance.TypeBoolean, CoreSystemTypes.Instance.TypeString);
this.AssertMethod = this.ContractClass.GetMethod (AssertName, CoreSystemTypes.Instance.TypeBoolean);
this.AssertWithMessageMethod = this.ContractClass.GetMethod (AssertName,
CoreSystemTypes.Instance.TypeBoolean, CoreSystemTypes.Instance.TypeString);
this.AssumeMethod = this.ContractClass.GetMethod (AssumeName, CoreSystemTypes.Instance.TypeBoolean);
this.AssumeWithMessageMethod = this.ContractClass.GetMethod (AssumeName,
CoreSystemTypes.Instance.TypeBoolean, CoreSystemTypes.Instance.TypeString);
this.EndContractBlock = this.ContractClass.GetMethod (EndContractBlockName);
foreach (FieldInfo fieldInfo in typeof (ContractNodes).GetFields ()) {
if (fieldInfo.GetValue (this) != null)
continue;
string runtimeName = null;
bool isRequired = false;
object[] attributes = fieldInfo.GetCustomAttributes (typeof (RepresentationForAttribute), false);
foreach (object attribute in attributes) {
var representationForAttribute = attribute as RepresentationForAttribute;
if (representationForAttribute != null) {
runtimeName = representationForAttribute.RuntimeName;
isRequired = representationForAttribute.IsRequired;
break;
}
}
if (isRequired) {
string message = string.Format ("Could not find contract node for '{0}'", fieldInfo.Name);
if (runtimeName != null)
message = string.Format ("Could not find the method/type '{0}'", runtimeName);
FireErrorFound (message);
ClearFields ();
}
}
}
public static ContractNodes GetContractNodes (AssemblyNode assembly, Action<string> errorHandler)
{
var contractNodes = new ContractNodes (assembly, errorHandler);
if (contractNodes.ContractClass != null)
return contractNodes;
return null;
}
private void ClearFields ()
{
foreach (FieldInfo fieldInfo in typeof (ContractNodes).GetFields ()) {
object[] customAttributes = fieldInfo.GetCustomAttributes (typeof (RepresentationForAttribute), false);
if (customAttributes.Length == 1)
fieldInfo.SetValue (this, null);
}
}
public event Action<string> ErrorFound;
private void FireErrorFound (string message)
{
if (ErrorFound == null)
throw new InvalidOperationException (message);
ErrorFound (message);
}
public Method IsContractCall (Statement s)
{
Method m = HelperMethods.IsMethodCall (s);
if (IsContractMethod (m))
return m;
return null;
}
public bool IsContractMethod (Method method)
{
if (method == null)
return false;
if (IsPlainPrecondition (method) || IsPostCondition (method) || IsEndContractBlock (method))
return true;
return false;
}
public bool IsPostCondition (Method method)
{
TypeNode genericArgument;
return IsContractMethod (EnsuresName, method, out genericArgument) && genericArgument == null;
}
public bool IsPlainPrecondition (Method method)
{
TypeNode genericArgument;
return IsContractMethod (RequiresName, method, out genericArgument);
}
private bool IsContractMethod (string methodName, Method m, out TypeNode genericArgument)
{
genericArgument = null;
if (m == null)
return false;
if (m.HasGenericParameters) {
if (m.GenericParameters == null || m.GenericParameters.Count != 1)
return false;
genericArgument = m.GenericParameters [0];
}
return m.Name != null && m.Name == methodName &&
(m.DeclaringType.Equals (this.ContractClass)
|| (m.Parameters != null && m.Parameters.Count == 3 && m.DeclaringType != null && m.DeclaringType.Name != ContractClassName));
}
public bool IsEndContractBlock (Method method)
{
TypeNode dummy;
return IsContractMethod (EndContractBlockName, method, out dummy);
}
}
}