-//\r
-// verifier.cs: compares two assemblies and reports differences.\r
-//\r
-// Author:\r
-// Sergey (serge@wildwestsoftware.com)\r
-//\r
-// (C) Sergey (serge@wildwestsoftware.com)\r
-//\r
-using System;\r
-using System.IO;\r
-using System.Collections;\r
-using System.Reflection;\r
-\r
-namespace Mono.Verifier {\r
-\r
- public class MethodCollection : IEnumerable {\r
-\r
- private SortedList methods;\r
-\r
- public MethodCollection (Type type)\r
- {\r
- this.methods = new SortedList ();\r
-\r
- MethodInfo [] methods = type.GetMethods ();\r
- foreach (MethodInfo mi in methods) {\r
- this.methods [mi.Name] = mi;\r
- }\r
- }\r
-\r
-\r
- public IEnumerator GetEnumerator() {\r
- return new Iterator (this);\r
- }\r
-\r
-\r
- public MethodInfo this [string name] {\r
- get {\r
- return methods [name] as MethodInfo;\r
- }\r
- }\r
-\r
- internal class Iterator : IEnumerator {\r
- private MethodCollection host;\r
- private int pos;\r
-\r
- internal Iterator (MethodCollection host)\r
- {\r
- this.host=host;\r
- this.Reset ();\r
- }\r
-\r
- /// <summary></summary>\r
- public Object Current\r
- {\r
- get {\r
- if (host != null && pos >=0 && pos < host.methods.Count) {\r
- return host.methods.GetByIndex (pos);\r
- } else {\r
- return null;\r
- }\r
- }\r
- }\r
-\r
- /// <summary></summary>\r
- public bool MoveNext ()\r
- {\r
- if (host!=null) {\r
- return (++pos) < host.methods.Count;\r
- } else {\r
- return false;\r
- }\r
- }\r
-\r
- /// <summary></summary>\r
- public void Reset ()\r
- {\r
- this.pos = -1;\r
- }\r
- }\r
-\r
- }\r
-\r
- public class ClassCollection : SortedList {\r
-\r
- public ClassCollection ()\r
- {\r
- }\r
-\r
- public ClassCollection (string assemblyName) : this ()\r
- {\r
- LoadFrom (assemblyName);\r
- }\r
-\r
- public void LoadFrom (string assemblyName)\r
- {\r
- try {\r
- Assembly asm = Assembly.LoadFrom (assemblyName);\r
- Type [] publics = asm.GetExportedTypes ();\r
- foreach (Type type in publics) {\r
- if (type.IsClass) {\r
- this [type.FullName] = new MethodCollection (type);\r
- }\r
- }\r
- } catch (ReflectionTypeLoadException rtle) {\r
- Type [] loaded = rtle.Types;\r
- for (int i = 0; i < loaded.Length; i++) {\r
- Console.Error.WriteLine ("Unable to load {0}, reason - {1}", loaded [i], rtle.LoaderExceptions [i]);\r
- }\r
- } catch (FileNotFoundException fnfe) {\r
- Console.Error.WriteLine (fnfe);\r
- } catch (Exception x) {\r
- Console.Error.WriteLine (x);\r
- }\r
- }\r
-\r
-\r
-\r
- private static bool CompareParameters (ParameterInfo[] params1, ParameterInfo[] params2)\r
- {\r
- bool res = true;\r
- if (params1.Length != params2.Length) {\r
- Console.WriteLine ("Parameter count mismatch.");\r
- return false;\r
- }\r
-\r
- int count = params1.Length;\r
-\r
- for (int i = 0; i < count && res; i++) {\r
- if (params1 [i].Name != params2 [i].Name) {\r
- Console.WriteLine ("Parameters names mismatch {0}, {1}.", params1 [i].Name, params2 [i].Name);\r
- res = false;\r
- break;\r
- }\r
-\r
- if (params1 [i].ParameterType != params2 [i].ParameterType) {\r
- Console.WriteLine ("Parameters types mismatch {0}, {1}.", params1 [i].ParameterType, params2 [i].ParameterType);\r
- res = false;\r
- break;\r
- }\r
- }\r
-\r
- return res;\r
- }\r
-\r
-\r
-\r
- private static void Verify (string assembly1, string assembly2)\r
- {\r
- ClassCollection classes1 = new ClassCollection (assembly1);\r
- ClassCollection classes2 = new ClassCollection (assembly2);\r
-\r
- foreach (DictionaryEntry c in classes1) {\r
- string className = c.Key as string;\r
- Console.WriteLine ("class: " + className);\r
- if (classes2 [className] == null) {\r
- Console.WriteLine ("There is no such class in {0}", assembly2);\r
- Environment.Exit (-1);\r
- }\r
-\r
- MethodCollection methods2 = classes2 [className] as MethodCollection;\r
-\r
- foreach (MethodInfo method in (c.Value as MethodCollection)) {\r
- Console.WriteLine ("\t method: " + method);\r
- MethodInfo m2 = methods2 [method.Name];\r
- if (m2 == null) {\r
- Console.WriteLine ("There is no such method in {0} ({1})", className, assembly2);\r
- Environment.Exit (-1);\r
- }\r
- bool paramsIndentical = CompareParameters (method.GetParameters (), m2.GetParameters ());\r
- if (!paramsIndentical) {\r
- Environment.Exit (-1);\r
- }\r
- }\r
- }\r
- }\r
-\r
-\r
-\r
- public static void Main (String [] args) {\r
- if (args.Length < 2) {\r
- Console.WriteLine ("Usage: verifier asm1 asm2");\r
- } else {\r
- string asm1 = args [0];\r
- string asm2 = args [1];\r
- Verify (asm1, asm2);\r
- }\r
- }\r
-\r
- }\r
-\r
-}\r
-\r
+//
+// verifier.cs: compares two assemblies and reports differences.
+//
+// Author:
+// Sergey Chaban (serge@wildwestsoftware.com)
+//
+// (C) Sergey Chaban (serge@wildwestsoftware.com)
+//
+
+using System;
+using System.IO;
+using System.Collections;
+using System.Reflection;
+
+namespace Mono.Verifier {
+
+
+
+ ////////////////////////////////
+ // Collections
+ ////////////////////////////////
+
+ public abstract class MemberCollection : IEnumerable {
+
+ public delegate MemberInfo [] InfoQuery (Type type, BindingFlags bindings);
+ public delegate bool MemberComparer (MemberInfo mi1, MemberInfo mi2);
+
+ protected SortedList list;
+ protected MemberComparer comparer;
+
+ protected BindingFlags bindings;
+
+ protected MemberCollection (Type type, InfoQuery query, MemberComparer comparer, BindingFlags bindings)
+ {
+ if (query == null)
+ throw new NullReferenceException ("Invalid query delegate.");
+
+ if (comparer == null)
+ throw new NullReferenceException ("Invalid comparer.");
+
+ this.comparer = comparer;
+ this.bindings = bindings;
+
+ this.list = new SortedList ();
+
+ MemberInfo [] data = query (type, bindings);
+ foreach (MemberInfo info in data) {
+ this.list [info.Name] = info;
+ }
+ }
+
+
+
+ public MemberInfo this [string name] {
+ get {
+ return list [name] as MemberInfo;
+ }
+ }
+
+
+ public override int GetHashCode ()
+ {
+ return list.GetHashCode ();
+ }
+
+
+ public override bool Equals (object o)
+ {
+ bool res = (o is MemberCollection);
+ if (res) {
+ MemberCollection another = o as MemberCollection;
+ IEnumerator it = GetEnumerator ();
+ while (it.MoveNext () && res) {
+ MemberInfo inf1 = it.Current as MemberInfo;
+ MemberInfo inf2 = another [inf1.Name];
+ res &= comparer (inf1, inf2);
+ }
+ }
+ return res;
+ }
+
+
+
+ public static bool operator == (MemberCollection c1, MemberCollection c2)
+ {
+ return c1.Equals (c2);
+ }
+
+ public static bool operator != (MemberCollection c1, MemberCollection c2)
+ {
+ return !(c1 == c2);
+ }
+
+
+
+ public IEnumerator GetEnumerator()
+ {
+ return new Iterator (this);
+ }
+
+
+ internal class Iterator : IEnumerator {
+ private MemberCollection host;
+ private int pos;
+
+ internal Iterator (MemberCollection host)
+ {
+ this.host=host;
+ this.Reset ();
+ }
+
+ /// <summary></summary>
+ public object Current
+ {
+ get {
+ if (host != null && pos >=0 && pos < host.list.Count) {
+ return host.list.GetByIndex (pos);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ /// <summary></summary>
+ public bool MoveNext ()
+ {
+ if (host!=null) {
+ return (++pos) < host.list.Count;
+ } else {
+ return false;
+ }
+ }
+
+ /// <summary></summary>
+ public void Reset ()
+ {
+ this.pos = -1;
+ }
+ }
+
+ }
+
+
+
+
+ //--- Method collections
+
+ /// <summary>
+ /// Abstract collection of class' methods.
+ /// </summary>
+ public abstract class MethodCollectionBase : MemberCollection {
+
+
+ protected MethodCollectionBase (Type type, BindingFlags bindings)
+ : base (type, new InfoQuery (Query), new MemberComparer (Comparer), bindings)
+ {
+ }
+
+
+ private static MemberInfo [] Query (Type type, BindingFlags bindings)
+ {
+ // returns MethodInfo []
+ return type.GetMethods (bindings);
+ }
+
+ private static bool Comparer (MemberInfo mi1, MemberInfo mi2)
+ {
+ bool res = false;
+ if (mi1 is MethodInfo && (mi2 == null || mi2 is MethodInfo)) {
+ MethodInfo inf1 = mi1 as MethodInfo;
+ MethodInfo inf2 = mi2 as MethodInfo;
+ res = Compare.Methods (inf1, inf2);
+ } else {
+ Verifier.log.Write ("internal-error", "Wrong comparer arguments.", ImportanceLevel.HIGH);
+ }
+ return res;
+ }
+ }
+
+
+
+ /// <summary>
+ /// Collection of public instance methods of a class.
+ /// </summary>
+ public class PublicMethods : MethodCollectionBase {
+
+ public PublicMethods (Type type)
+ : base (type, BindingFlags.Public | BindingFlags.Instance)
+ {
+ }
+ }
+
+ /// <summary>
+ /// Collection of public static methods of a class.
+ /// </summary>
+ public class PublicStaticMethods : MethodCollectionBase {
+
+ public PublicStaticMethods (Type type)
+ : base (type, BindingFlags.Public | BindingFlags.Static)
+ {
+ }
+ }
+
+ /// <summary>
+ /// Collection of non-public instance methods of a class.
+ /// </summary>
+ public class NonPublicMethods : MethodCollectionBase {
+
+ public NonPublicMethods (Type type)
+ : base (type, BindingFlags.NonPublic | BindingFlags.Instance)
+ {
+ }
+ }
+
+ /// <summary>
+ /// Collection of non-public static methods of a class.
+ /// </summary>
+ public class NonPublicStaticMethods : MethodCollectionBase {
+
+ public NonPublicStaticMethods (Type type)
+ : base (type, BindingFlags.NonPublic | BindingFlags.Static)
+ {
+ }
+ }
+
+
+
+
+
+ //--- Field collections
+
+ public abstract class FieldCollectionBase : MemberCollection {
+
+
+ protected FieldCollectionBase (Type type, BindingFlags bindings)
+ : base (type, new InfoQuery (Query), new MemberComparer (Comparer), bindings)
+ {
+ }
+
+
+ private static MemberInfo [] Query (Type type, BindingFlags bindings)
+ {
+ // returns FieldInfo []
+ return type.GetFields (bindings);
+ }
+
+ private static bool Comparer (MemberInfo mi1, MemberInfo mi2)
+ {
+ bool res = false;
+ if (mi1 is FieldInfo && (mi2 == null || mi2 is FieldInfo)) {
+ FieldInfo inf1 = mi1 as FieldInfo;
+ FieldInfo inf2 = mi2 as FieldInfo;
+ res = Compare.Fields (inf1, inf2);
+ } else {
+ Verifier.log.Write ("internal-error", "Wrong comparer arguments.", ImportanceLevel.HIGH);
+ }
+ return res;
+ }
+ }
+
+
+ public class PublicFields : FieldCollectionBase {
+
+ public PublicFields (Type type)
+ : base (type, BindingFlags.Public | BindingFlags.Instance)
+ {
+ }
+ }
+
+ public class PublicStaticFields : FieldCollectionBase {
+
+ public PublicStaticFields (Type type)
+ : base (type, BindingFlags.Public | BindingFlags.Static)
+ {
+ }
+ }
+
+ public class NonPublicFields : FieldCollectionBase {
+
+ public NonPublicFields (Type type)
+ : base (type, BindingFlags.NonPublic | BindingFlags.Instance)
+ {
+ }
+ }
+
+ public class NonPublicStaticFields : FieldCollectionBase {
+
+ public NonPublicStaticFields (Type type)
+ : base (type, BindingFlags.NonPublic | BindingFlags.Static)
+ {
+ }
+ }
+
+
+
+
+
+ public abstract class AbstractTypeStuff {
+ public readonly Type type;
+
+ public AbstractTypeStuff (Type type)
+ {
+ if (type == null)
+ throw new NullReferenceException ("Invalid type.");
+
+ this.type = type;
+ }
+
+ public override int GetHashCode ()
+ {
+ return type.GetHashCode ();
+ }
+
+ public static bool operator == (AbstractTypeStuff t1, AbstractTypeStuff t2)
+ {
+ if ((t1 as object) == null) {
+ if ((t2 as object) == null) return true;
+ return false;
+ }
+ return t1.Equals (t2);
+ }
+
+ public static bool operator != (AbstractTypeStuff t1, AbstractTypeStuff t2)
+ {
+ return !(t1 == t2);
+ }
+
+ public override bool Equals (object o)
+ {
+ return (o is AbstractTypeStuff && CompareTypes (o as AbstractTypeStuff));
+ }
+
+ protected virtual bool CompareTypes (AbstractTypeStuff that)
+ {
+ Verifier.Log.Write ("info", "Comparing types.", ImportanceLevel.LOW);
+ bool res;
+
+ res = Compare.Types (this.type, that.type);
+
+ return res;
+ }
+
+ }
+
+
+
+
+ /// <summary>
+ /// Represents a class.
+ /// </summary>
+ public class ClassStuff : AbstractTypeStuff {
+
+ public PublicMethods publicMethods;
+ public PublicStaticMethods publicStaticMethods;
+ public NonPublicMethods nonpublicMethods;
+ public NonPublicStaticMethods nonpublicStaticMethods;
+
+ public PublicFields publicFields;
+ public PublicStaticFields publicStaticFields;
+ public NonPublicFields nonpublicFields;
+ public NonPublicStaticFields nonpublicStaticFields;
+
+ public ClassStuff (Type type) : base (type)
+ {
+ publicMethods = new PublicMethods (type);
+ publicStaticMethods = new PublicStaticMethods (type);
+ nonpublicMethods = new NonPublicMethods (type);
+ nonpublicStaticMethods = new NonPublicStaticMethods (type);
+
+ publicFields = new PublicFields (type);
+ publicStaticFields = new PublicStaticFields (type);
+ nonpublicFields = new NonPublicFields (type);
+ nonpublicStaticFields = new NonPublicStaticFields (type);
+ }
+
+
+ public override int GetHashCode ()
+ {
+ return base.GetHashCode ();
+ }
+
+ private bool CompareMethods (ClassStuff that)
+ {
+ bool res = true;
+ bool ok;
+
+ Verifier.Log.Write ("info", "Comparing public instance methods.", ImportanceLevel.LOW);
+ ok = (this.publicMethods == that.publicMethods);
+ res &= ok;
+ if (!ok && Verifier.stopOnError) return res;
+
+ Verifier.Log.Write ("info", "Comparing public static methods.", ImportanceLevel.LOW);
+ ok = (this.publicStaticMethods == that.publicStaticMethods);
+ res &= ok;
+ if (!ok && Verifier.stopOnError) return res;
+
+ Verifier.Log.Write ("info", "Comparing non-public instance methods.", ImportanceLevel.LOW);
+ ok = (this.nonpublicMethods == that.nonpublicMethods);
+ res &= ok;
+ if (!ok && Verifier.stopOnError) return res;
+
+ Verifier.Log.Write ("info", "Comparing non-public static methods.", ImportanceLevel.LOW);
+ ok = (this.nonpublicStaticMethods == that.nonpublicStaticMethods);
+ res &= ok;
+ if (!ok && Verifier.stopOnError) return res;
+
+ return res;
+ }
+
+
+ private bool CompareFields (ClassStuff that)
+ {
+ bool res = true;
+ bool ok;
+
+ Verifier.Log.Write ("info", "Comparing public instance fields.", ImportanceLevel.LOW);
+ ok = (this.publicFields == that.publicFields);
+ res &= ok;
+ if (!ok && Verifier.stopOnError) return res;
+
+ Verifier.Log.Write ("info", "Comparing public static fields.", ImportanceLevel.LOW);
+ ok = (this.publicStaticFields == that.publicStaticFields);
+ res &= ok;
+ if (!ok && Verifier.stopOnError) return res;
+
+ Verifier.Log.Write ("info", "Comparing non-public instance fields.", ImportanceLevel.LOW);
+ ok = (this.nonpublicFields == that.nonpublicFields);
+ res &= ok;
+ if (!ok && Verifier.stopOnError) return res;
+
+ Verifier.Log.Write ("info", "Comparing non-public static fields.", ImportanceLevel.LOW);
+ ok = (this.nonpublicStaticFields == that.nonpublicStaticFields);
+ res &= ok;
+ if (!ok && Verifier.stopOnError) return res;
+
+ return res;
+ }
+
+
+ public override bool Equals (object o)
+ {
+ bool res = (o is ClassStuff);
+ if (res) {
+ ClassStuff that = o as ClassStuff;
+
+ res &= this.CompareTypes (that);
+ if (!res && Verifier.stopOnError) return res;
+
+ res &= this.CompareMethods (that);
+ if (!res && Verifier.stopOnError) return res;
+
+ res &= this.CompareFields (that);
+ if (!res && Verifier.stopOnError) return res;
+
+ }
+ return res;
+ }
+
+ }
+
+
+
+ /// <summary>
+ /// Represents an interface.
+ /// </summary>
+ public class InterfaceStuff : AbstractTypeStuff {
+
+ public PublicMethods publicMethods;
+
+ public InterfaceStuff (Type type) : base (type)
+ {
+ publicMethods = new PublicMethods (type);
+ }
+
+ public override int GetHashCode ()
+ {
+ return base.GetHashCode ();
+ }
+
+ public override bool Equals (object o)
+ {
+ bool res = (o is InterfaceStuff);
+ if (res) {
+ bool ok;
+ InterfaceStuff that = o as InterfaceStuff;
+
+ res = this.CompareTypes (that);
+ if (!res && Verifier.stopOnError) return res;
+
+ Verifier.Log.Write ("info", "Comparing interface methods.", ImportanceLevel.LOW);
+ ok = (this.publicMethods == that.publicMethods);
+ res &= ok;
+ if (!ok && Verifier.stopOnError) return res;
+ }
+ return res;
+ }
+
+ }
+
+
+
+ /// <summary>
+ /// Represents an enumeration.
+ /// </summary>
+ public class EnumStuff : AbstractTypeStuff {
+
+ //public FieldInfo [] members;
+
+ public string baseType;
+ public Hashtable enumTable;
+ public bool isFlags;
+
+ public EnumStuff (Type type) : base (type)
+ {
+ //members = type.GetFields (BindingFlags.Public | BindingFlags.Static);
+
+ Array values = Enum.GetValues (type);
+ Array names = Enum.GetNames (type);
+
+ baseType = Enum.GetUnderlyingType (type).Name;
+
+ enumTable = new Hashtable ();
+
+ object [] attrs = type.GetCustomAttributes (false);
+ isFlags = (attrs != null && attrs.Length > 0);
+ if (isFlags) {
+ foreach (object attr in attrs) {
+ isFlags |= (attr is FlagsAttribute);
+ }
+ }
+
+ int indx = 0;
+ foreach (string id in names) {
+ enumTable [id] = Convert.ToInt64(values.GetValue(indx) as Enum);
+ ++indx;
+ }
+ }
+
+ public override int GetHashCode ()
+ {
+ return base.GetHashCode ();
+ }
+
+ public override bool Equals (object o)
+ {
+ bool res = (o is EnumStuff);
+ bool ok;
+
+ if (res) {
+ EnumStuff that = o as EnumStuff;
+ ok = this.CompareTypes (that);
+ res &= ok;
+ if (!ok && Verifier.stopOnError) return res;
+
+ ok = (this.baseType == that.baseType);
+ res &= ok;
+ if (!ok) {
+ Verifier.log.Write ("error",
+ String.Format ("Underlying types mismatch [{0}, {1}].", this.baseType, that.baseType),
+ ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+ Verifier.Log.Write ("info", "Comparing [Flags] attribute.");
+ ok = !(this.isFlags ^ that.isFlags);
+ res &= ok;
+ if (!ok) {
+ Verifier.log.Write ("error",
+ String.Format ("[Flags] attribute mismatch ({0} : {1}).", this.isFlags ? "Yes" : "No", that.isFlags ? "Yes" : "No"),
+ ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+ Verifier.Log.Write ("info", "Comparing enum values.");
+
+ ICollection names = enumTable.Keys;
+ foreach (string id in names) {
+ ok = that.enumTable.ContainsKey (id);
+ res &= ok;
+ if (!ok) {
+ Verifier.log.Write ("error", String.Format("{0} absent in enumeration.", id),
+ ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+ if (ok) {
+ long val1 = (long) this.enumTable [id];
+ long val2 = (long) that.enumTable [id];
+ ok = (val1 == val2);
+ res &= ok;
+ if (!ok) {
+ Verifier.log.Write ("error",
+ String.Format ("Enum values mismatch [{0}: {1} != {2}].", id, val1, val2),
+ ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+ }
+ }
+ }
+ return res;
+ }
+ }
+
+
+
+ public sealed class TypeArray {
+ public static readonly TypeArray empty = new TypeArray (Type.EmptyTypes);
+
+ public Type [] types;
+
+ public TypeArray (Type [] types)
+ {
+ this.types = new Type [types.Length];
+ for (int i = 0; i < types.Length; i++) {
+ this.types.SetValue (types.GetValue (i), i);
+ }
+ }
+ }
+
+
+
+ public class AssemblyLoader {
+ public delegate void Hook (TypeArray assemblyTypes);
+
+ private static Hashtable cache;
+
+ private Hook hook;
+
+ static AssemblyLoader ()
+ {
+ cache = new Hashtable (11);
+ }
+
+ public AssemblyLoader (Hook hook)
+ {
+ if (hook == null)
+ throw new NullReferenceException ("Invalid loader hook.");
+
+ this.hook = hook;
+ }
+
+
+ public bool LoadFrom (string assemblyName)
+ {
+ bool res = false;
+ try {
+ TypeArray types = TypeArray.empty;
+
+ lock (cache) {
+ if (cache.Contains (assemblyName)) {
+ types = (cache [assemblyName] as TypeArray);
+ if (types == null) types = TypeArray.empty;
+ } else {
+ Assembly asm = Assembly.LoadFrom (assemblyName);
+ Type [] allTypes = asm.GetTypes ();
+ if (allTypes == null) allTypes = Type.EmptyTypes;
+ types = new TypeArray (allTypes);
+ cache [assemblyName] = types;
+ }
+ }
+ hook (types);
+ res = true;
+ } catch (ReflectionTypeLoadException rtle) {
+ // FIXME: Should we try to recover? Use loaded portion of types.
+ Type [] loaded = rtle.Types;
+ for (int i = 0, xCnt = 0; i < loaded.Length; i++) {
+ if (loaded [i] == null) {
+ Verifier.log.Write ("fatal error",
+ String.Format ("Unable to load {0}, reason - {1}", loaded [i], rtle.LoaderExceptions [xCnt++]),
+ ImportanceLevel.LOW);
+ }
+ }
+ } catch (FileNotFoundException fnfe) {
+ Verifier.log.Write ("fatal error", fnfe.ToString (), ImportanceLevel.LOW);
+ } catch (Exception x) {
+ Verifier.log.Write ("fatal error", x.ToString (), ImportanceLevel.LOW);
+ }
+
+ return res;
+ }
+
+ }
+
+
+
+
+ public abstract class AbstractTypeCollection : SortedList {
+
+ private AssemblyLoader loader;
+
+ public AbstractTypeCollection ()
+ {
+ loader = new AssemblyLoader (new AssemblyLoader.Hook (LoaderHook));
+ }
+
+ public AbstractTypeCollection (string assemblyName) : this ()
+ {
+ LoadFrom (assemblyName);
+ }
+
+ public abstract void LoaderHook (TypeArray types);
+
+
+ public bool LoadFrom (string assemblyName)
+ {
+ return loader.LoadFrom (assemblyName);
+ }
+
+ }
+
+
+
+ public class ClassCollection : AbstractTypeCollection {
+
+ public ClassCollection () : base ()
+ {
+ }
+
+ public ClassCollection (string assemblyName)
+ : base (assemblyName)
+ {
+ }
+
+
+ public override void LoaderHook (TypeArray types)
+ {
+ foreach (Type type in types.types) {
+ if (type.IsClass) {
+ this [type.FullName] = new ClassStuff (type);
+ }
+ }
+ }
+
+ }
+
+
+ public class InterfaceCollection : AbstractTypeCollection {
+
+ public InterfaceCollection () : base ()
+ {
+ }
+
+ public InterfaceCollection (string assemblyName)
+ : base (assemblyName)
+ {
+ }
+
+
+ public override void LoaderHook (TypeArray types)
+ {
+ foreach (Type type in types.types) {
+ if (type.IsInterface) {
+ this [type.FullName] = new InterfaceStuff (type);
+ }
+ }
+ }
+
+ }
+
+
+
+ public class EnumCollection : AbstractTypeCollection {
+
+ public EnumCollection () : base ()
+ {
+ }
+
+ public EnumCollection (string assemblyName)
+ : base (assemblyName)
+ {
+ }
+
+ public override void LoaderHook (TypeArray types)
+ {
+ foreach (Type type in types.types) {
+ if (type.IsEnum) {
+ this [type.FullName] = new EnumStuff (type);
+ }
+ }
+ }
+ }
+
+
+
+ public class AssemblyStuff {
+
+ public string name;
+ public bool valid;
+
+ public ClassCollection classes;
+ public InterfaceCollection interfaces;
+ public EnumCollection enums;
+
+
+ protected delegate bool Comparer (AssemblyStuff asm1, AssemblyStuff asm2);
+ private static ArrayList comparers;
+
+ static AssemblyStuff ()
+ {
+ comparers = new ArrayList ();
+ comparers.Add (new Comparer (CompareNumClasses));
+ comparers.Add (new Comparer (CompareNumInterfaces));
+ comparers.Add (new Comparer (CompareClasses));
+ comparers.Add (new Comparer (CompareInterfaces));
+ comparers.Add (new Comparer (CompareEnums));
+ }
+
+ protected static bool CompareNumClasses (AssemblyStuff asm1, AssemblyStuff asm2)
+ {
+ bool res = (asm1.classes.Count == asm2.classes.Count);
+ if (!res) Verifier.Log.Write ("error", "Number of classes mismatch.", ImportanceLevel.MEDIUM);
+ return res;
+ }
+
+ protected static bool CompareNumInterfaces (AssemblyStuff asm1, AssemblyStuff asm2)
+ {
+ bool res = (asm1.interfaces.Count == asm2.interfaces.Count);
+ if (!res) Verifier.Log.Write ("error", "Number of interfaces mismatch.", ImportanceLevel.MEDIUM);
+ return res;
+ }
+
+
+ protected static bool CompareClasses (AssemblyStuff asm1, AssemblyStuff asm2)
+ {
+ bool res = true;
+ Verifier.Log.Write ("info", "Comparing classes.");
+
+ foreach (DictionaryEntry c in asm1.classes) {
+ string className = c.Key as string;
+
+ if (Verifier.Excluded.Contains (className)) {
+ Verifier.Log.Write ("info", String.Format ("Ignoring class {0}.", className), ImportanceLevel.MEDIUM);
+ continue;
+ }
+
+ Verifier.Log.Write ("class", className);
+
+ ClassStuff class1 = c.Value as ClassStuff;
+ ClassStuff class2 = asm2.classes [className] as ClassStuff;
+
+ if (class2 == null) {
+ Verifier.Log.Write ("error", String.Format ("There is no such class in {0}", asm2.name));
+ res = false;
+ if (Verifier.stopOnError || !Verifier.ignoreMissingTypes) return res;
+ continue;
+ }
+
+ res &= (class1 == class2);
+ if (!res && Verifier.stopOnError) return res;
+ }
+
+ return res;
+ }
+
+
+ protected static bool CompareInterfaces (AssemblyStuff asm1, AssemblyStuff asm2)
+ {
+ bool res = true;
+ Verifier.Log.Write ("info", "Comparing interfaces.");
+
+ foreach (DictionaryEntry ifc in asm1.interfaces) {
+ string ifcName = ifc.Key as string;
+ Verifier.Log.Write ("interface", ifcName);
+
+ InterfaceStuff ifc1 = ifc.Value as InterfaceStuff;
+ InterfaceStuff ifc2 = asm2.interfaces [ifcName] as InterfaceStuff;
+
+ if (ifc2 == null) {
+ Verifier.Log.Write ("error", String.Format ("There is no such interface in {0}", asm2.name));
+ res = false;
+ if (Verifier.stopOnError || !Verifier.ignoreMissingTypes) return res;
+ continue;
+ }
+
+ res &= (ifc1 == ifc2);
+ if (!res && Verifier.stopOnError) return res;
+
+ }
+
+ return res;
+ }
+
+
+ protected static bool CompareEnums (AssemblyStuff asm1, AssemblyStuff asm2)
+ {
+ bool res = true;
+ Verifier.Log.Write ("info", "Comparing enums.");
+
+ foreach (DictionaryEntry e in asm1.enums) {
+ string enumName = e.Key as string;
+ Verifier.Log.Write ("enum", enumName);
+
+ EnumStuff e1 = e.Value as EnumStuff;
+ EnumStuff e2 = asm2.enums [enumName] as EnumStuff;
+
+ if (e2 == null) {
+ Verifier.Log.Write ("error", String.Format ("There is no such enum in {0}", asm2.name));
+ res = false;
+ if (Verifier.stopOnError || !Verifier.ignoreMissingTypes) return res;
+ continue;
+ }
+ res &= (e1 == e2);
+ if (!res && Verifier.stopOnError) return res;
+ }
+
+ return res;
+ }
+
+
+
+ public AssemblyStuff (string assemblyName)
+ {
+ this.name = assemblyName;
+ valid = false;
+ }
+
+ public bool Load ()
+ {
+ bool res = true;
+ bool ok;
+
+ classes = new ClassCollection ();
+ ok = classes.LoadFrom (name);
+ res &= ok;
+ if (!ok) Verifier.log.Write ("error", String.Format ("Unable to load classes from {0}.", name), ImportanceLevel.HIGH);
+
+ interfaces = new InterfaceCollection ();
+ ok = interfaces.LoadFrom (name);
+ res &= ok;
+ if (!ok) Verifier.log.Write ("error", String.Format ("Unable to load interfaces from {0}.", name), ImportanceLevel.HIGH);
+
+ enums = new EnumCollection ();
+ ok = enums.LoadFrom (name);
+ res &= ok;
+ if (!ok) Verifier.log.Write ("error", String.Format ("Unable to load enums from {0}.", name), ImportanceLevel.HIGH);
+
+ valid = res;
+ return res;
+ }
+
+
+ public override bool Equals (object o)
+ {
+ bool res = (o is AssemblyStuff);
+ if (res) {
+ AssemblyStuff that = o as AssemblyStuff;
+ IEnumerator it = comparers.GetEnumerator ();
+ while ((res || !Verifier.stopOnError) && it.MoveNext ()) {
+ Comparer compare = it.Current as Comparer;
+ res &= compare (this, that);
+ }
+ }
+ return res;
+ }
+
+
+ public static bool operator == (AssemblyStuff asm1, AssemblyStuff asm2)
+ {
+ return asm1.Equals (asm2);
+ }
+
+ public static bool operator != (AssemblyStuff asm1, AssemblyStuff asm2)
+ {
+ return !(asm1 == asm2);
+ }
+
+ public override int GetHashCode ()
+ {
+ return classes.GetHashCode () ^ interfaces.GetHashCode ();
+ }
+
+
+ public override string ToString ()
+ {
+ string res;
+ if (valid) {
+ res = String.Format ("Asssembly {0}, valid, {1} classes, {2} interfaces, {3} enums.",
+ name, classes.Count, interfaces.Count, enums.Count);
+ } else {
+ res = String.Format ("Asssembly {0}, invalid.", name);
+ }
+ return res;
+ }
+
+ }
+
+
+
+
+ ////////////////////////////////
+ // Compare
+ ////////////////////////////////
+
+ public sealed class Compare {
+
+ private Compare ()
+ {
+ }
+
+
+ public static bool Parameters (ParameterInfo[] params1, ParameterInfo[] params2)
+ {
+ bool res = true;
+ if (params1.Length != params2.Length) {
+ Verifier.Log.Write ("Parameter count mismatch.");
+ return false;
+ }
+
+ int count = params1.Length;
+
+ for (int i = 0; i < count && res; i++) {
+ if (params1 [i].Name != params2 [i].Name) {
+ Verifier.Log.Write ("error", String.Format ("Parameters names mismatch {0}, {1}.", params1 [i].Name, params2 [i].Name));
+ res = false;
+ if (Verifier.stopOnError) break;
+ }
+
+ Verifier.Log.Write ("parameter", params1 [i].Name);
+
+ if (!Compare.Types (params1 [i].ParameterType, params2 [i].ParameterType)) {
+ Verifier.Log.Write ("error", String.Format ("Parameters types mismatch {0}, {1}.", params1 [i].ParameterType, params2 [i].ParameterType));
+ res = false;
+ if (Verifier.stopOnError) break;
+ }
+
+
+ if (Verifier.checkOptionalFlags) {
+ if (params1 [i].IsIn != params2 [i].IsIn) {
+ Verifier.Log.Write ("error", "[in] mismatch.");
+ res = false;
+ if (Verifier.stopOnError) break;
+ }
+
+ if (params1 [i].IsOut != params2 [i].IsOut) {
+ Verifier.Log.Write ("error", "[out] mismatch.");
+ res = false;
+ if (Verifier.stopOnError) break;
+ }
+
+ if (params1 [i].IsRetval != params2 [i].IsRetval) {
+ Verifier.Log.Write ("error", "[ref] mismatch.");
+ res = false;
+ if (Verifier.stopOnError) break;
+ }
+
+ if (params1 [i].IsOptional != params2 [i].IsOptional) {
+ Verifier.Log.Write ("error", "Optional flag mismatch.");
+ res = false;
+ if (Verifier.stopOnError) break;
+ }
+
+ } // checkOptionalFlags
+
+
+ }
+
+ return res;
+ }
+
+
+
+ public static bool Methods (MethodInfo mi1, MethodInfo mi2)
+ {
+
+ if (mi2 == null) {
+ Verifier.Log.Write ("error", String.Format ("There is no such method {0}.", mi1.Name), ImportanceLevel.MEDIUM);
+ return false;
+ }
+
+
+ Verifier.Log.Flush ();
+ Verifier.Log.Write ("method", String.Format ("{0}.", mi1.Name));
+ bool res = true;
+ bool ok;
+ string expected;
+
+ ok = Compare.Types (mi1.ReturnType, mi2.ReturnType);
+ res &= ok;
+ if (!ok) {
+ Verifier.Log.Write ("error", "Return types mismatch.", ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+
+
+
+ ok = (mi1.IsAbstract == mi2.IsAbstract);
+ res &= ok;
+ if (!ok) {
+ expected = (mi1.IsAbstract) ? "abstract" : "non-abstract";
+ Verifier.Log.Write ("error", String.Format ("Expected to be {0}.", expected), ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+ ok = (mi1.IsVirtual == mi2.IsVirtual);
+ res &= ok;
+ if (!ok) {
+ expected = (mi1.IsVirtual) ? "virtual" : "non-virtual";
+ Verifier.Log.Write ("error", String.Format ("Expected to be {0}.", expected), ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+ ok = (mi1.IsFinal == mi2.IsFinal);
+ res &= ok;
+ if (!ok) {
+ expected = (mi1.IsFinal) ? "final" : "overridable";
+ Verifier.Log.Write ("error", String.Format ("Expected to be {0}.", expected), ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+
+
+ // compare access modifiers
+
+ ok = (mi1.IsPrivate == mi2.IsPrivate);
+ res &= ok;
+ if (!ok) {
+ expected = (mi1.IsPublic) ? "public" : "private";
+ Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+
+ ok = (mi1.IsFamily == mi2.IsFamily);
+ res &= ok;
+ if (!ok) {
+ expected = (mi1.IsFamily) ? "protected" : "!protected";
+ Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+ ok = (mi1.IsAssembly == mi2.IsAssembly);
+ res &= ok;
+ if (!ok) {
+ expected = (mi1.IsAssembly) ? "internal" : "!internal";
+ Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+
+ ok = (mi1.IsStatic == mi2.IsStatic);
+ res &= ok;
+ if (!ok) {
+ expected = (mi1.IsStatic) ? "static" : "instance";
+ Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+
+
+ // parameters
+
+ ok = Compare.Parameters (mi1.GetParameters (), mi2.GetParameters ());
+ res &= ok;
+ if (!ok && Verifier.stopOnError) return res;
+
+
+ ok = (mi1.CallingConvention == mi2.CallingConvention);
+ res &= ok;
+ if (!ok) {
+ Verifier.Log.Write ("error", "Calling conventions mismatch.", ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+
+
+
+ return res;
+ }
+
+
+ public static bool Fields (FieldInfo fi1, FieldInfo fi2)
+ {
+ if (fi2 == null) {
+ Verifier.Log.Write ("error", String.Format ("There is no such field {0}.", fi1.Name), ImportanceLevel.MEDIUM);
+ return false;
+ }
+
+ bool res = true;
+ bool ok;
+ string expected;
+
+ Verifier.Log.Write ("field", String.Format ("{0}.", fi1.Name));
+
+ ok = (fi1.IsPrivate == fi2.IsPrivate);
+ res &= ok;
+ if (!ok) {
+ expected = (fi1.IsPublic) ? "public" : "private";
+ Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+ ok = (fi1.IsFamily == fi2.IsFamily);
+ res &= ok;
+ if (!ok) {
+ expected = (fi1.IsFamily) ? "protected" : "!protected";
+ Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+ ok = (fi1.IsAssembly == fi2.IsAssembly);
+ res &= ok;
+ if (!ok) {
+ expected = (fi1.IsAssembly) ? "internal" : "!internal";
+ Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+ ok = (fi1.IsInitOnly == fi2.IsInitOnly);
+ res &= ok;
+ if (!ok) {
+ expected = (fi1.IsInitOnly) ? "readonly" : "!readonly";
+ Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+ ok = (fi1.IsStatic == fi2.IsStatic);
+ res &= ok;
+ if (!ok) {
+ expected = (fi1.IsStatic) ? "static" : "instance";
+ Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
+ if (Verifier.stopOnError) return res;
+ }
+
+ return res;
+ }
+
+
+
+ public static bool Types (Type type1, Type type2)
+ {
+ // NOTE:
+ // simply calling type1.Equals (type2) won't work,
+ // types are in different assemblies hence they have
+ // different (fully-qualified) names.
+ int eqFlags = 0;
+ eqFlags |= (type1.IsAbstract == type2.IsAbstract) ? 0 : 0x001;
+ eqFlags |= (type1.IsClass == type2.IsClass) ? 0 : 0x002;
+ eqFlags |= (type1.IsValueType == type2.IsValueType) ? 0 : 0x004;
+ eqFlags |= (type1.IsPublic == type2.IsPublic) ? 0 : 0x008;
+ eqFlags |= (type1.IsSealed == type2.IsSealed) ? 0 : 0x010;
+ eqFlags |= (type1.IsEnum == type2.IsEnum) ? 0 : 0x020;
+ eqFlags |= (type1.IsPointer == type2.IsPointer) ? 0 : 0x040;
+ eqFlags |= (type1.IsPrimitive == type2.IsPrimitive) ? 0 : 0x080;
+ bool res = (eqFlags == 0);
+
+ if (!res) {
+ // TODO: convert flags into descriptive message.
+ Verifier.Log.Write ("error", "Types mismatch (0x" + eqFlags.ToString("X") + ").", ImportanceLevel.HIGH);
+ }
+
+
+ bool ok;
+
+ ok = (type1.Attributes & TypeAttributes.BeforeFieldInit) ==
+ (type2.Attributes & TypeAttributes.BeforeFieldInit);
+ if (!ok) {
+ Verifier.Log.Write ("error", "Types attributes mismatch: BeforeFieldInit.", ImportanceLevel.HIGH);
+ }
+ res &= ok;
+
+ ok = (type1.Attributes & TypeAttributes.ExplicitLayout) ==
+ (type2.Attributes & TypeAttributes.ExplicitLayout);
+ if (!ok) {
+ Verifier.Log.Write ("error", "Types attributes mismatch: ExplicitLayout.", ImportanceLevel.HIGH);
+ }
+ res &= ok;
+
+ ok = (type1.Attributes & TypeAttributes.SequentialLayout) ==
+ (type2.Attributes & TypeAttributes.SequentialLayout);
+ if (!ok) {
+ Verifier.Log.Write ("error", "Types attributes mismatch: SequentialLayout.", ImportanceLevel.HIGH);
+ }
+ res &= ok;
+
+ ok = (type1.Attributes & TypeAttributes.Serializable) ==
+ (type2.Attributes & TypeAttributes.Serializable);
+ if (!ok) {
+ Verifier.Log.Write ("error", "Types attributes mismatch: Serializable.", ImportanceLevel.HIGH);
+ }
+ res &= ok;
+
+ return res;
+ }
+
+ }
+
+
+
+
+ ////////////////////////////////
+ // Log
+ ////////////////////////////////
+
+ public enum ImportanceLevel : int {
+ LOW = 0, MEDIUM, HIGH
+ }
+
+
+ public interface ILogger {
+
+ void Write (string tag, string msg, ImportanceLevel importance);
+ void Write (string msg, ImportanceLevel level);
+ void Write (string tag, string msg);
+ void Write (string msg);
+ ImportanceLevel DefaultImportance {get; set;}
+ void Flush ();
+ void Close ();
+ }
+
+
+ public abstract class AbstractLogger : ILogger {
+ private ImportanceLevel defImportance = ImportanceLevel.MEDIUM;
+
+ public abstract void Write (string tag, string msg, ImportanceLevel importance);
+ public abstract void Write (string msg, ImportanceLevel level);
+
+ public virtual void Write (string tag, string msg)
+ {
+ Write (tag, msg, DefaultImportance);
+ }
+
+ public virtual void Write (string msg)
+ {
+ Write (msg, DefaultImportance);
+ }
+
+ public virtual ImportanceLevel DefaultImportance {
+ get {
+ return defImportance;
+ }
+ set {
+ defImportance = value < ImportanceLevel.LOW
+ ? ImportanceLevel.LOW
+ : value > ImportanceLevel.HIGH
+ ? ImportanceLevel.HIGH
+ : value;
+ }
+ }
+
+ public abstract void Flush ();
+ public abstract void Close ();
+
+ }
+
+
+
+ public class TextLogger : AbstractLogger {
+
+ private TextWriter writer;
+
+ public TextLogger (TextWriter writer)
+ {
+ if (writer == null)
+ throw new NullReferenceException ();
+
+ this.writer = writer;
+ }
+
+ private void DoWrite (string tag, string msg)
+ {
+ if (tag != null && tag.Length > 0) {
+ writer.WriteLine ("[{0}]\t{1}", tag, msg);
+ } else {
+ writer.WriteLine ("\t\t" + msg);
+ }
+ }
+
+ public override void Write (string tag, string msg, ImportanceLevel importance)
+ {
+ int v = Log.VerboseLevel;
+ switch (v) {
+ case 0 :
+ break;
+ case 1 :
+ if (importance >= ImportanceLevel.HIGH) {
+ DoWrite (tag, msg);
+ }
+ break;
+ case 2 :
+ if (importance >= ImportanceLevel.MEDIUM) {
+ DoWrite (tag, msg);
+ }
+ break;
+ case 3 :
+ DoWrite (tag, msg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ public override void Write (string msg, ImportanceLevel importance)
+ {
+ Write (null, msg, importance);
+ }
+
+ public override void Flush ()
+ {
+ Console.Out.Flush ();
+ }
+
+ public override void Close ()
+ {
+ if (writer != Console.Out && writer != Console.Error) {
+ writer.Close ();
+ }
+ }
+ }
+
+
+
+ public sealed class Log {
+
+ private static int verbose = 3;
+
+ private ArrayList consumers;
+
+ public Log (bool useDefault)
+ {
+ consumers = new ArrayList ();
+ if (useDefault) AddConsumer (new TextLogger (Console.Out));
+ }
+
+ public Log () : this (true)
+ {
+ }
+
+
+ public static int VerboseLevel {
+ get {
+ return verbose;
+ }
+ set {
+ verbose = (value < 0)
+ ? 0
+ : (value > 3)
+ ? 3 : value;
+ }
+ }
+
+ public void AddConsumer (ILogger consumer)
+ {
+ consumers.Add (consumer);
+ }
+
+
+ public void Write (string tag, string msg, ImportanceLevel importance)
+ {
+ foreach (ILogger logger in consumers) {
+ if (tag == null || tag == "") {
+ logger.Write (msg, importance);
+ } else {
+ logger.Write (tag, msg, importance);
+ }
+ }
+ }
+
+ public void Write (string msg, ImportanceLevel importance)
+ {
+ Write (null, msg, importance);
+ }
+
+
+ public void Write (string tag, string msg)
+ {
+ foreach (ILogger logger in consumers) {
+ if (tag == null || tag == "") {
+ logger.Write (msg);
+ } else {
+ logger.Write (tag, msg);
+ }
+ }
+ }
+
+ public void Write (string msg)
+ {
+ Write (null, msg);
+ }
+
+
+ public void Flush ()
+ {
+ foreach (ILogger logger in consumers) {
+ logger.Flush ();
+ }
+ }
+
+
+ public void Close ()
+ {
+ foreach (ILogger logger in consumers) {
+ logger.Flush ();
+ logger.Close ();
+ }
+ }
+
+ }
+
+
+
+
+
+
+ ////////////////////////////////
+ // Main
+ ////////////////////////////////
+
+ public class Verifier {
+
+ public static readonly Log log = new Log ();
+ public static bool stopOnError = false;
+ public static bool ignoreMissingTypes = true;
+ public static bool checkOptionalFlags = true;
+
+ private static readonly IList excluded;
+
+ static Verifier ()
+ {
+ excluded = new ArrayList ();
+ excluded.Add ("<PrivateImplementationDetails>");
+ }
+
+
+ private Verifier ()
+ {
+ }
+
+ public static Log Log {
+ get {
+ return log;
+ }
+ }
+
+ public static IList Excluded {
+ get {
+ return excluded;
+ }
+ }
+
+
+
+ public static void Main (String [] args)
+ {
+ if (args.Length < 2) {
+ Console.WriteLine ("Usage: verifier assembly1 assembly2");
+ } else {
+ string name1 = args [0];
+ string name2 = args [1];
+
+ bool ok = false;
+
+ AssemblyStuff asm1 = new AssemblyStuff (name1);
+ AssemblyStuff asm2 = new AssemblyStuff (name2);
+ ok = asm1.Load ();
+ if (!ok) {
+ Console.WriteLine ("Unable to load assembly {0}.", name1);
+ Environment.Exit (-1);
+ }
+
+ ok = asm2.Load ();
+ if (!ok) {
+ Console.WriteLine ("Unable to load assembly {0}.", name2);
+ Environment.Exit (-1);
+ }
+
+
+ try {
+ ok = (asm1 == asm2);
+ } catch {
+ ok = false;
+ } finally {
+ Log.Close ();
+ }
+
+ if (!ok) {
+ Console.WriteLine ("--- not equal");
+ Environment.Exit (-1);
+ }
+ }
+ }
+
+ }
+
+
+}
+