using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ModTool.Shared.Verification
{
///
/// Base class for restrictions. A restriction either requires or prohibits something.
///
[Serializable]
public abstract class Restriction
{
///
/// The message to use when this restriction causes the verification to fail.
///
public string message;
///
/// The base type to which the Restriction will be applied.
///
public string targetType;
///
/// Does the Restriction require or prohibit the use of something?
///
public RestrictionMode restrictionMode;
///
/// Initialize a new Restriction.
///
/// The base Type this Restriction will apply to.
/// The Message that will be shown upon failing the Restriction.
/// Should it be required or prohibited?
protected Restriction(string targetType, string message, RestrictionMode restrictionMode)
{
this.targetType = targetType;
this.message = message;
this.restrictionMode = restrictionMode;
}
///
/// Verify a member with this Restriction.
///
/// A member.
/// A list of messages of failed Restrictions.
public void Verify(MemberReference member, List messages)
{
if (!Applicable(member))
return;
bool present;
if (member is MethodReference)
present = PresentInMethodRecursive(member as MethodReference);
else
present = Present(member);
if (present && restrictionMode == RestrictionMode.Prohibited)
messages.Add(GetMessage(member));
if (!present &&restrictionMode == RestrictionMode.Required)
messages.Add(GetMessage(member));
}
///
/// Is the Restriction present in the member?
///
/// A member.
/// True if the Restriction is present in the member.
protected virtual bool Present(MemberReference member)
{
return false;
}
///
/// Is the restriction present in a local variable?
///
/// A local variable
/// True if the restriction is present in the local variable.
protected virtual bool PresentMethodVariable(VariableReference variable)
{
return false;
}
private bool PresentInMethodRecursive(MethodReference method)
{
HashSet visited = new HashSet();
return PresentInMethodRecursive(method, visited);
}
private bool PresentInMethodRecursive(MethodReference method, HashSet visited)
{
MethodDefinition resolvedMethod = null;
try
{
resolvedMethod = method.Resolve();
}
catch (AssemblyResolutionException e)
{
LogUtility.LogWarning(e.Message);
}
if (resolvedMethod != null)
{
if (!resolvedMethod.HasBody)
return false;
if (visited.Contains(resolvedMethod.FullName))
return false;
visited.Add(resolvedMethod.FullName);
foreach (VariableDefinition variable in resolvedMethod.Body.Variables)
{
if (PresentMethodVariable(variable))
return true;
}
foreach (Instruction instruction in resolvedMethod.Body.Instructions)
{
if (instruction.Operand == null)
continue;
if (instruction.Operand is MemberReference)
{
MemberReference member = instruction.Operand as MemberReference;
if (member.Module.Assembly.Name.Name == "ModTool.Interface")
continue;
if (Present(member))
return true;
if (member.DeclaringType == null)
continue;
if (member.DeclaringType.Namespace.StartsWith("System"))
continue;
if (member.DeclaringType.Namespace.StartsWith("Unity"))
continue;
if (AssemblyUtility.IsShared(member.Module.Assembly.Name.Name))
continue;
if (member is MethodReference)
{
if (PresentInMethodRecursive(member as MethodReference, visited))
return true;
}
}
}
}
return false;
}
///
/// Get a Restriction message for a MemberReference.
///
///
///
protected virtual string GetMessage(MemberReference member)
{
return string.Format("{0}: {1} - {2}", restrictionMode, member.FullName, message);
}
///
/// Is this Restriction applicable to the member?
///
/// A member.
/// True if the Restriction is applicable.
protected bool Applicable(MemberReference member)
{
if(member is TypeReference)
{
return Applicable(member as TypeReference);
}
if (member.DeclaringType == null)
return false;
return Applicable(member.DeclaringType);
}
///
/// Is this Restriction applicable to the Type?
///
/// A Type.
/// True if the Restriction is applicable.
protected bool Applicable(TypeReference type)
{
if (targetType == null)
return true;
if (string.IsNullOrEmpty(targetType))
return true;
try
{
return type.Resolve().IsSubClassOf(targetType);
}
catch (AssemblyResolutionException e)
{
LogUtility.LogWarning(e.Message);
}
return false;
}
}
///
/// A restriction that either requires or prohibits the use of a namespace.
///
[Serializable]
public class NamespaceRestriction : Restriction
{
///
/// The namespace that will be checked for this restriction.
///
public string nameSpace;
///
/// Should nested namespaces be restricted as well?
///
public bool includeNested;
///
/// Initialize a new NamespaceRestriction
///
/// The namespace that will be checked for this restriction.
/// Should nested namespaces be restricted as well?
/// The base Type this Restriction will apply to.
/// The Message that will be shown upon failing the Restriction.
/// Should it be required or prohibited?
public NamespaceRestriction(string nameSpace, bool includeNested, string targetType, string message, RestrictionMode restrictionMode) : base(targetType, message, restrictionMode)
{
this.nameSpace = nameSpace;
this.includeNested = includeNested;
}
protected override bool Present(MemberReference member)
{
if (member is TypeReference)
return CheckNamespace((member as TypeReference).Namespace);
if (member is FieldReference)
return CheckNamespace((member as FieldReference).FieldType.Namespace);
if (member is PropertyReference)
return CheckNamespace((member as PropertyReference).PropertyType.Namespace);
if (member.DeclaringType == null)
return false;
return CheckNamespace(member.DeclaringType.Namespace);
}
protected override bool PresentMethodVariable(VariableReference variable)
{
return CheckNamespace(variable.VariableType.Namespace);
}
private bool CheckNamespace(string nameSpace)
{
if (includeNested)
return nameSpace.StartsWith(this.nameSpace);
else
return nameSpace == this.nameSpace;
}
}
///
/// A restriction that either requires or prohibits the use of a Type
///
[Serializable]
public class TypeRestriction : Restriction
{
///
/// The Type that will be checked for this Restriction.
///
public string type;
///
/// Initialize a new TypeRestriction.
///
/// The Type that will be checked for this Restriction.
/// The base Type this Restriction will apply to.
/// The Message that will be shown upon failing the Restriction.
/// Should it be required or prohibited?
public TypeRestriction(string type, string targetType, string message, RestrictionMode restrictionMode)
: base(targetType, message, restrictionMode)
{
this.type = type;
}
protected override bool Present(MemberReference member)
{
if (member is FieldReference)
{
FieldReference field = member as FieldReference;
return field.FieldType.GetFullName() == type;
}
if (member is PropertyReference)
{
PropertyReference property = member as PropertyReference;
return property.PropertyType.GetFullName() == type;
}
if (member.DeclaringType == null)
return false;
return member.DeclaringType.GetFullName() == type;
}
protected override bool PresentMethodVariable(VariableReference variable)
{
return (variable.VariableType.GetFullName() == type);
}
}
///
/// A restriction that either requires or prohibits inheritance from a class
///
[Serializable]
public class InheritanceRestriction : Restriction
{
///
/// The Type that will be checked for this Restriction.
///
public string type;
///
/// Initialize a new InheritanceRestriction.
///
/// The Type that will be checked for this Restriction
/// The base Type this Restriction will apply to.
/// The Message that will be shown upon failing the Restriction.
/// Should it be required or prohibited?
public InheritanceRestriction(string type, string targetType, string message, RestrictionMode restrictionMode)
: base(targetType, message, restrictionMode)
{
this.type = type;
}
protected override bool Present(MemberReference member)
{
if (member is TypeReference)
{
TypeDefinition typeDefinition = (member as TypeReference).Resolve();
return typeDefinition.IsSubClassOf(type);
}
return false;
}
}
///
/// Type for applying Restrictions.
///
public enum RestrictionMode { Prohibited, Required }
///
/// A restriction that either requires or prohibits the use of a given Type's member
///
[Serializable]
public class MemberRestriction : Restriction
{
///
/// The member that will be checked for this Restriction.
///
public string member;
///
/// Initialize a new MemberRestriction.
///
/// The member that will be checked for this Restriction.
/// The base Type this Restriction will apply to.
/// The Message that will be shown upon failing the Restriction.
/// Should it be required or prohibited?
public MemberRestriction(string member, string targetType, string message, RestrictionMode restrictionMode)
: base(targetType, message, restrictionMode)
{
this.member = member;
}
protected override bool Present(MemberReference member)
{
return member.GetFullName() == this.member;
}
}
}