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; } } }