2010-04-08 Jb Evain <jbevain@novell.com>
[mcs.git] / tools / misc / verifier.cs
blob25268147b2653443f84777632fa10fc7785c764b
1 //
2 // verifier.cs: compares two assemblies and reports differences.
3 //
4 // Author:
5 // Sergey Chaban (serge@wildwestsoftware.com)
6 //
7 // (C) Sergey Chaban (serge@wildwestsoftware.com)
8 //
10 using System;
11 using System.IO;
12 using System.Collections;
13 using System.Reflection;
15 namespace Mono.Verifier {
19 ////////////////////////////////
20 // Collections
21 ////////////////////////////////
23 public abstract class MemberCollection : IEnumerable {
25 public delegate MemberInfo [] InfoQuery (Type type, BindingFlags bindings);
26 public delegate bool MemberComparer (MemberInfo mi1, MemberInfo mi2);
28 protected SortedList list;
29 protected MemberComparer comparer;
31 protected BindingFlags bindings;
33 protected MemberCollection (Type type, InfoQuery query, MemberComparer comparer, BindingFlags bindings)
35 if (query == null)
36 throw new NullReferenceException ("Invalid query delegate.");
38 if (comparer == null)
39 throw new NullReferenceException ("Invalid comparer.");
41 this.comparer = comparer;
42 this.bindings = bindings;
44 this.list = new SortedList ();
46 MemberInfo [] data = query (type, bindings);
47 foreach (MemberInfo info in data) {
48 this.list [info.Name] = info;
54 public MemberInfo this [string name] {
55 get {
56 return list [name] as MemberInfo;
61 public override int GetHashCode ()
63 return list.GetHashCode ();
67 public override bool Equals (object o)
69 bool res = (o is MemberCollection);
70 if (res) {
71 MemberCollection another = o as MemberCollection;
72 IEnumerator it = GetEnumerator ();
73 while (it.MoveNext () && res) {
74 MemberInfo inf1 = it.Current as MemberInfo;
75 MemberInfo inf2 = another [inf1.Name];
76 res &= comparer (inf1, inf2);
79 return res;
84 public static bool operator == (MemberCollection c1, MemberCollection c2)
86 return c1.Equals (c2);
89 public static bool operator != (MemberCollection c1, MemberCollection c2)
91 return !(c1 == c2);
96 public IEnumerator GetEnumerator()
98 return new Iterator (this);
102 internal class Iterator : IEnumerator {
103 private MemberCollection host;
104 private int pos;
106 internal Iterator (MemberCollection host)
108 this.host=host;
109 this.Reset ();
112 /// <summary></summary>
113 public object Current
115 get {
116 if (host != null && pos >=0 && pos < host.list.Count) {
117 return host.list.GetByIndex (pos);
118 } else {
119 return null;
124 /// <summary></summary>
125 public bool MoveNext ()
127 if (host!=null) {
128 return (++pos) < host.list.Count;
129 } else {
130 return false;
134 /// <summary></summary>
135 public void Reset ()
137 this.pos = -1;
146 //--- Method collections
148 /// <summary>
149 /// Abstract collection of class' methods.
150 /// </summary>
151 public abstract class MethodCollectionBase : MemberCollection {
154 protected MethodCollectionBase (Type type, BindingFlags bindings)
155 : base (type, new InfoQuery (Query), new MemberComparer (Comparer), bindings)
160 private static MemberInfo [] Query (Type type, BindingFlags bindings)
162 // returns MethodInfo []
163 return type.GetMethods (bindings);
166 private static bool Comparer (MemberInfo mi1, MemberInfo mi2)
168 bool res = false;
169 if (mi1 is MethodInfo && (mi2 == null || mi2 is MethodInfo)) {
170 MethodInfo inf1 = mi1 as MethodInfo;
171 MethodInfo inf2 = mi2 as MethodInfo;
172 res = Compare.Methods (inf1, inf2);
173 } else {
174 Verifier.log.Write ("internal-error", "Wrong comparer arguments.", ImportanceLevel.HIGH);
176 return res;
182 /// <summary>
183 /// Collection of public instance methods of a class.
184 /// </summary>
185 public class PublicMethods : MethodCollectionBase {
187 public PublicMethods (Type type)
188 : base (type, BindingFlags.Public | BindingFlags.Instance)
193 /// <summary>
194 /// Collection of public static methods of a class.
195 /// </summary>
196 public class PublicStaticMethods : MethodCollectionBase {
198 public PublicStaticMethods (Type type)
199 : base (type, BindingFlags.Public | BindingFlags.Static)
204 /// <summary>
205 /// Collection of non-public instance methods of a class.
206 /// </summary>
207 public class NonPublicMethods : MethodCollectionBase {
209 public NonPublicMethods (Type type)
210 : base (type, BindingFlags.NonPublic | BindingFlags.Instance)
215 /// <summary>
216 /// Collection of non-public static methods of a class.
217 /// </summary>
218 public class NonPublicStaticMethods : MethodCollectionBase {
220 public NonPublicStaticMethods (Type type)
221 : base (type, BindingFlags.NonPublic | BindingFlags.Static)
230 //--- Field collections
232 public abstract class FieldCollectionBase : MemberCollection {
235 protected FieldCollectionBase (Type type, BindingFlags bindings)
236 : base (type, new InfoQuery (Query), new MemberComparer (Comparer), bindings)
241 private static MemberInfo [] Query (Type type, BindingFlags bindings)
243 // returns FieldInfo []
244 return type.GetFields (bindings);
247 private static bool Comparer (MemberInfo mi1, MemberInfo mi2)
249 bool res = false;
250 if (mi1 is FieldInfo && (mi2 == null || mi2 is FieldInfo)) {
251 FieldInfo inf1 = mi1 as FieldInfo;
252 FieldInfo inf2 = mi2 as FieldInfo;
253 res = Compare.Fields (inf1, inf2);
254 } else {
255 Verifier.log.Write ("internal-error", "Wrong comparer arguments.", ImportanceLevel.HIGH);
257 return res;
262 public class PublicFields : FieldCollectionBase {
264 public PublicFields (Type type)
265 : base (type, BindingFlags.Public | BindingFlags.Instance)
270 public class PublicStaticFields : FieldCollectionBase {
272 public PublicStaticFields (Type type)
273 : base (type, BindingFlags.Public | BindingFlags.Static)
278 public class NonPublicFields : FieldCollectionBase {
280 public NonPublicFields (Type type)
281 : base (type, BindingFlags.NonPublic | BindingFlags.Instance)
286 public class NonPublicStaticFields : FieldCollectionBase {
288 public NonPublicStaticFields (Type type)
289 : base (type, BindingFlags.NonPublic | BindingFlags.Static)
298 public abstract class AbstractTypeStuff {
299 public readonly Type type;
301 public AbstractTypeStuff (Type type)
303 if (type == null)
304 throw new NullReferenceException ("Invalid type.");
306 this.type = type;
309 public override int GetHashCode ()
311 return type.GetHashCode ();
314 public static bool operator == (AbstractTypeStuff t1, AbstractTypeStuff t2)
316 if ((t1 as object) == null) {
317 if ((t2 as object) == null) return true;
318 return false;
320 return t1.Equals (t2);
323 public static bool operator != (AbstractTypeStuff t1, AbstractTypeStuff t2)
325 return !(t1 == t2);
328 public override bool Equals (object o)
330 return (o is AbstractTypeStuff && CompareTypes (o as AbstractTypeStuff));
333 protected virtual bool CompareTypes (AbstractTypeStuff that)
335 Verifier.Log.Write ("info", "Comparing types.", ImportanceLevel.LOW);
336 bool res;
338 res = Compare.Types (this.type, that.type);
340 return res;
348 /// <summary>
349 /// Represents a class.
350 /// </summary>
351 public class ClassStuff : AbstractTypeStuff {
353 public PublicMethods publicMethods;
354 public PublicStaticMethods publicStaticMethods;
355 public NonPublicMethods nonpublicMethods;
356 public NonPublicStaticMethods nonpublicStaticMethods;
358 public PublicFields publicFields;
359 public PublicStaticFields publicStaticFields;
360 public NonPublicFields nonpublicFields;
361 public NonPublicStaticFields nonpublicStaticFields;
363 public ClassStuff (Type type) : base (type)
365 publicMethods = new PublicMethods (type);
366 publicStaticMethods = new PublicStaticMethods (type);
367 nonpublicMethods = new NonPublicMethods (type);
368 nonpublicStaticMethods = new NonPublicStaticMethods (type);
370 publicFields = new PublicFields (type);
371 publicStaticFields = new PublicStaticFields (type);
372 nonpublicFields = new NonPublicFields (type);
373 nonpublicStaticFields = new NonPublicStaticFields (type);
377 public override int GetHashCode ()
379 return base.GetHashCode ();
382 private bool CompareMethods (ClassStuff that)
384 bool res = true;
385 bool ok;
387 Verifier.Log.Write ("info", "Comparing public instance methods.", ImportanceLevel.LOW);
388 ok = (this.publicMethods == that.publicMethods);
389 res &= ok;
390 if (!ok && Verifier.stopOnError) return res;
392 Verifier.Log.Write ("info", "Comparing public static methods.", ImportanceLevel.LOW);
393 ok = (this.publicStaticMethods == that.publicStaticMethods);
394 res &= ok;
395 if (!ok && Verifier.stopOnError) return res;
397 Verifier.Log.Write ("info", "Comparing non-public instance methods.", ImportanceLevel.LOW);
398 ok = (this.nonpublicMethods == that.nonpublicMethods);
399 res &= ok;
400 if (!ok && Verifier.stopOnError) return res;
402 Verifier.Log.Write ("info", "Comparing non-public static methods.", ImportanceLevel.LOW);
403 ok = (this.nonpublicStaticMethods == that.nonpublicStaticMethods);
404 res &= ok;
405 if (!ok && Verifier.stopOnError) return res;
407 return res;
411 private bool CompareFields (ClassStuff that)
413 bool res = true;
414 bool ok;
416 Verifier.Log.Write ("info", "Comparing public instance fields.", ImportanceLevel.LOW);
417 ok = (this.publicFields == that.publicFields);
418 res &= ok;
419 if (!ok && Verifier.stopOnError) return res;
421 Verifier.Log.Write ("info", "Comparing public static fields.", ImportanceLevel.LOW);
422 ok = (this.publicStaticFields == that.publicStaticFields);
423 res &= ok;
424 if (!ok && Verifier.stopOnError) return res;
426 Verifier.Log.Write ("info", "Comparing non-public instance fields.", ImportanceLevel.LOW);
427 ok = (this.nonpublicFields == that.nonpublicFields);
428 res &= ok;
429 if (!ok && Verifier.stopOnError) return res;
431 Verifier.Log.Write ("info", "Comparing non-public static fields.", ImportanceLevel.LOW);
432 ok = (this.nonpublicStaticFields == that.nonpublicStaticFields);
433 res &= ok;
434 if (!ok && Verifier.stopOnError) return res;
436 return res;
440 public override bool Equals (object o)
442 bool res = (o is ClassStuff);
443 if (res) {
444 ClassStuff that = o as ClassStuff;
446 res &= this.CompareTypes (that);
447 if (!res && Verifier.stopOnError) return res;
449 res &= this.CompareMethods (that);
450 if (!res && Verifier.stopOnError) return res;
452 res &= this.CompareFields (that);
453 if (!res && Verifier.stopOnError) return res;
456 return res;
463 /// <summary>
464 /// Represents an interface.
465 /// </summary>
466 public class InterfaceStuff : AbstractTypeStuff {
468 public PublicMethods publicMethods;
470 public InterfaceStuff (Type type) : base (type)
472 publicMethods = new PublicMethods (type);
475 public override int GetHashCode ()
477 return base.GetHashCode ();
480 public override bool Equals (object o)
482 bool res = (o is InterfaceStuff);
483 if (res) {
484 bool ok;
485 InterfaceStuff that = o as InterfaceStuff;
487 res = this.CompareTypes (that);
488 if (!res && Verifier.stopOnError) return res;
490 Verifier.Log.Write ("info", "Comparing interface methods.", ImportanceLevel.LOW);
491 ok = (this.publicMethods == that.publicMethods);
492 res &= ok;
493 if (!ok && Verifier.stopOnError) return res;
495 return res;
502 /// <summary>
503 /// Represents an enumeration.
504 /// </summary>
505 public class EnumStuff : AbstractTypeStuff {
507 //public FieldInfo [] members;
509 public string baseType;
510 public Hashtable enumTable;
511 public bool isFlags;
513 public EnumStuff (Type type) : base (type)
515 //members = type.GetFields (BindingFlags.Public | BindingFlags.Static);
517 Array values = Enum.GetValues (type);
518 Array names = Enum.GetNames (type);
520 baseType = Enum.GetUnderlyingType (type).Name;
522 enumTable = new Hashtable ();
524 object [] attrs = type.GetCustomAttributes (false);
525 isFlags = (attrs != null && attrs.Length > 0);
526 if (isFlags) {
527 foreach (object attr in attrs) {
528 isFlags |= (attr is FlagsAttribute);
532 int indx = 0;
533 foreach (string id in names) {
534 enumTable [id] = Convert.ToInt64(values.GetValue(indx) as Enum);
535 ++indx;
539 public override int GetHashCode ()
541 return base.GetHashCode ();
544 public override bool Equals (object o)
546 bool res = (o is EnumStuff);
547 bool ok;
549 if (res) {
550 EnumStuff that = o as EnumStuff;
551 ok = this.CompareTypes (that);
552 res &= ok;
553 if (!ok && Verifier.stopOnError) return res;
555 ok = (this.baseType == that.baseType);
556 res &= ok;
557 if (!ok) {
558 Verifier.log.Write ("error",
559 String.Format ("Underlying types mismatch [{0}, {1}].", this.baseType, that.baseType),
560 ImportanceLevel.MEDIUM);
561 if (Verifier.stopOnError) return res;
564 Verifier.Log.Write ("info", "Comparing [Flags] attribute.");
565 ok = !(this.isFlags ^ that.isFlags);
566 res &= ok;
567 if (!ok) {
568 Verifier.log.Write ("error",
569 String.Format ("[Flags] attribute mismatch ({0} : {1}).", this.isFlags ? "Yes" : "No", that.isFlags ? "Yes" : "No"),
570 ImportanceLevel.MEDIUM);
571 if (Verifier.stopOnError) return res;
574 Verifier.Log.Write ("info", "Comparing enum values.");
576 ICollection names = enumTable.Keys;
577 foreach (string id in names) {
578 ok = that.enumTable.ContainsKey (id);
579 res &= ok;
580 if (!ok) {
581 Verifier.log.Write ("error", String.Format("{0} absent in enumeration.", id),
582 ImportanceLevel.MEDIUM);
583 if (Verifier.stopOnError) return res;
586 if (ok) {
587 long val1 = (long) this.enumTable [id];
588 long val2 = (long) that.enumTable [id];
589 ok = (val1 == val2);
590 res &= ok;
591 if (!ok) {
592 Verifier.log.Write ("error",
593 String.Format ("Enum values mismatch [{0}: {1} != {2}].", id, val1, val2),
594 ImportanceLevel.MEDIUM);
595 if (Verifier.stopOnError) return res;
600 return res;
606 public sealed class TypeArray {
607 public static readonly TypeArray empty = new TypeArray (Type.EmptyTypes);
609 public Type [] types;
611 public TypeArray (Type [] types)
613 this.types = new Type [types.Length];
614 for (int i = 0; i < types.Length; i++) {
615 this.types.SetValue (types.GetValue (i), i);
622 public class AssemblyLoader {
623 public delegate void Hook (TypeArray assemblyTypes);
625 private static Hashtable cache;
627 private Hook hook;
629 static AssemblyLoader ()
631 cache = new Hashtable (11);
634 public AssemblyLoader (Hook hook)
636 if (hook == null)
637 throw new NullReferenceException ("Invalid loader hook.");
639 this.hook = hook;
643 public bool LoadFrom (string assemblyName)
645 bool res = false;
646 try {
647 TypeArray types = TypeArray.empty;
649 lock (cache) {
650 if (cache.Contains (assemblyName)) {
651 types = (cache [assemblyName] as TypeArray);
652 if (types == null) types = TypeArray.empty;
653 } else {
654 Assembly asm = Assembly.LoadFrom (assemblyName);
655 Type [] allTypes = asm.GetTypes ();
656 if (allTypes == null) allTypes = Type.EmptyTypes;
657 types = new TypeArray (allTypes);
658 cache [assemblyName] = types;
661 hook (types);
662 res = true;
663 } catch (ReflectionTypeLoadException rtle) {
664 // FIXME: Should we try to recover? Use loaded portion of types.
665 Type [] loaded = rtle.Types;
666 for (int i = 0, xCnt = 0; i < loaded.Length; i++) {
667 if (loaded [i] == null) {
668 Verifier.log.Write ("fatal error",
669 String.Format ("Unable to load {0}, reason - {1}", loaded [i], rtle.LoaderExceptions [xCnt++]),
670 ImportanceLevel.LOW);
673 } catch (FileNotFoundException fnfe) {
674 Verifier.log.Write ("fatal error", fnfe.ToString (), ImportanceLevel.LOW);
675 } catch (Exception x) {
676 Verifier.log.Write ("fatal error", x.ToString (), ImportanceLevel.LOW);
679 return res;
687 public abstract class AbstractTypeCollection : SortedList {
689 private AssemblyLoader loader;
691 public AbstractTypeCollection ()
693 loader = new AssemblyLoader (new AssemblyLoader.Hook (LoaderHook));
696 public AbstractTypeCollection (string assemblyName) : this ()
698 LoadFrom (assemblyName);
701 public abstract void LoaderHook (TypeArray types);
704 public bool LoadFrom (string assemblyName)
706 return loader.LoadFrom (assemblyName);
713 public class ClassCollection : AbstractTypeCollection {
715 public ClassCollection () : base ()
719 public ClassCollection (string assemblyName)
720 : base (assemblyName)
725 public override void LoaderHook (TypeArray types)
727 foreach (Type type in types.types) {
728 if (type.IsClass) {
729 this [type.FullName] = new ClassStuff (type);
737 public class InterfaceCollection : AbstractTypeCollection {
739 public InterfaceCollection () : base ()
743 public InterfaceCollection (string assemblyName)
744 : base (assemblyName)
749 public override void LoaderHook (TypeArray types)
751 foreach (Type type in types.types) {
752 if (type.IsInterface) {
753 this [type.FullName] = new InterfaceStuff (type);
762 public class EnumCollection : AbstractTypeCollection {
764 public EnumCollection () : base ()
768 public EnumCollection (string assemblyName)
769 : base (assemblyName)
773 public override void LoaderHook (TypeArray types)
775 foreach (Type type in types.types) {
776 if (type.IsEnum) {
777 this [type.FullName] = new EnumStuff (type);
785 public class AssemblyStuff {
787 public string name;
788 public bool valid;
790 public ClassCollection classes;
791 public InterfaceCollection interfaces;
792 public EnumCollection enums;
795 protected delegate bool Comparer (AssemblyStuff asm1, AssemblyStuff asm2);
796 private static ArrayList comparers;
798 static AssemblyStuff ()
800 comparers = new ArrayList ();
801 comparers.Add (new Comparer (CompareNumClasses));
802 comparers.Add (new Comparer (CompareNumInterfaces));
803 comparers.Add (new Comparer (CompareClasses));
804 comparers.Add (new Comparer (CompareInterfaces));
805 comparers.Add (new Comparer (CompareEnums));
808 protected static bool CompareNumClasses (AssemblyStuff asm1, AssemblyStuff asm2)
810 bool res = (asm1.classes.Count == asm2.classes.Count);
811 if (!res) Verifier.Log.Write ("error", "Number of classes mismatch.", ImportanceLevel.MEDIUM);
812 return res;
815 protected static bool CompareNumInterfaces (AssemblyStuff asm1, AssemblyStuff asm2)
817 bool res = (asm1.interfaces.Count == asm2.interfaces.Count);
818 if (!res) Verifier.Log.Write ("error", "Number of interfaces mismatch.", ImportanceLevel.MEDIUM);
819 return res;
823 protected static bool CompareClasses (AssemblyStuff asm1, AssemblyStuff asm2)
825 bool res = true;
826 Verifier.Log.Write ("info", "Comparing classes.");
828 foreach (DictionaryEntry c in asm1.classes) {
829 string className = c.Key as string;
831 if (Verifier.Excluded.Contains (className)) {
832 Verifier.Log.Write ("info", String.Format ("Ignoring class {0}.", className), ImportanceLevel.MEDIUM);
833 continue;
836 Verifier.Log.Write ("class", className);
838 ClassStuff class1 = c.Value as ClassStuff;
839 ClassStuff class2 = asm2.classes [className] as ClassStuff;
841 if (class2 == null) {
842 Verifier.Log.Write ("error", String.Format ("There is no such class in {0}", asm2.name));
843 res = false;
844 if (Verifier.stopOnError || !Verifier.ignoreMissingTypes) return res;
845 continue;
848 res &= (class1 == class2);
849 if (!res && Verifier.stopOnError) return res;
852 return res;
856 protected static bool CompareInterfaces (AssemblyStuff asm1, AssemblyStuff asm2)
858 bool res = true;
859 Verifier.Log.Write ("info", "Comparing interfaces.");
861 foreach (DictionaryEntry ifc in asm1.interfaces) {
862 string ifcName = ifc.Key as string;
863 Verifier.Log.Write ("interface", ifcName);
865 InterfaceStuff ifc1 = ifc.Value as InterfaceStuff;
866 InterfaceStuff ifc2 = asm2.interfaces [ifcName] as InterfaceStuff;
868 if (ifc2 == null) {
869 Verifier.Log.Write ("error", String.Format ("There is no such interface in {0}", asm2.name));
870 res = false;
871 if (Verifier.stopOnError || !Verifier.ignoreMissingTypes) return res;
872 continue;
875 res &= (ifc1 == ifc2);
876 if (!res && Verifier.stopOnError) return res;
880 return res;
884 protected static bool CompareEnums (AssemblyStuff asm1, AssemblyStuff asm2)
886 bool res = true;
887 Verifier.Log.Write ("info", "Comparing enums.");
889 foreach (DictionaryEntry e in asm1.enums) {
890 string enumName = e.Key as string;
891 Verifier.Log.Write ("enum", enumName);
893 EnumStuff e1 = e.Value as EnumStuff;
894 EnumStuff e2 = asm2.enums [enumName] as EnumStuff;
896 if (e2 == null) {
897 Verifier.Log.Write ("error", String.Format ("There is no such enum in {0}", asm2.name));
898 res = false;
899 if (Verifier.stopOnError || !Verifier.ignoreMissingTypes) return res;
900 continue;
902 res &= (e1 == e2);
903 if (!res && Verifier.stopOnError) return res;
906 return res;
911 public AssemblyStuff (string assemblyName)
913 this.name = assemblyName;
914 valid = false;
917 public bool Load ()
919 bool res = true;
920 bool ok;
922 classes = new ClassCollection ();
923 ok = classes.LoadFrom (name);
924 res &= ok;
925 if (!ok) Verifier.log.Write ("error", String.Format ("Unable to load classes from {0}.", name), ImportanceLevel.HIGH);
927 interfaces = new InterfaceCollection ();
928 ok = interfaces.LoadFrom (name);
929 res &= ok;
930 if (!ok) Verifier.log.Write ("error", String.Format ("Unable to load interfaces from {0}.", name), ImportanceLevel.HIGH);
932 enums = new EnumCollection ();
933 ok = enums.LoadFrom (name);
934 res &= ok;
935 if (!ok) Verifier.log.Write ("error", String.Format ("Unable to load enums from {0}.", name), ImportanceLevel.HIGH);
937 valid = res;
938 return res;
942 public override bool Equals (object o)
944 bool res = (o is AssemblyStuff);
945 if (res) {
946 AssemblyStuff that = o as AssemblyStuff;
947 IEnumerator it = comparers.GetEnumerator ();
948 while ((res || !Verifier.stopOnError) && it.MoveNext ()) {
949 Comparer compare = it.Current as Comparer;
950 res &= compare (this, that);
953 return res;
957 public static bool operator == (AssemblyStuff asm1, AssemblyStuff asm2)
959 return asm1.Equals (asm2);
962 public static bool operator != (AssemblyStuff asm1, AssemblyStuff asm2)
964 return !(asm1 == asm2);
967 public override int GetHashCode ()
969 return classes.GetHashCode () ^ interfaces.GetHashCode ();
973 public override string ToString ()
975 string res;
976 if (valid) {
977 res = String.Format ("Asssembly {0}, valid, {1} classes, {2} interfaces, {3} enums.",
978 name, classes.Count, interfaces.Count, enums.Count);
979 } else {
980 res = String.Format ("Asssembly {0}, invalid.", name);
982 return res;
990 ////////////////////////////////
991 // Compare
992 ////////////////////////////////
994 public sealed class Compare {
996 private Compare ()
1001 public static bool Parameters (ParameterInfo[] params1, ParameterInfo[] params2)
1003 bool res = true;
1004 if (params1.Length != params2.Length) {
1005 Verifier.Log.Write ("Parameter count mismatch.");
1006 return false;
1009 int count = params1.Length;
1011 for (int i = 0; i < count && res; i++) {
1012 if (params1 [i].Name != params2 [i].Name) {
1013 Verifier.Log.Write ("error", String.Format ("Parameters names mismatch {0}, {1}.", params1 [i].Name, params2 [i].Name));
1014 res = false;
1015 if (Verifier.stopOnError) break;
1018 Verifier.Log.Write ("parameter", params1 [i].Name);
1020 if (!Compare.Types (params1 [i].ParameterType, params2 [i].ParameterType)) {
1021 Verifier.Log.Write ("error", String.Format ("Parameters types mismatch {0}, {1}.", params1 [i].ParameterType, params2 [i].ParameterType));
1022 res = false;
1023 if (Verifier.stopOnError) break;
1027 if (Verifier.checkOptionalFlags) {
1028 if (params1 [i].IsIn != params2 [i].IsIn) {
1029 Verifier.Log.Write ("error", "[in] mismatch.");
1030 res = false;
1031 if (Verifier.stopOnError) break;
1034 if (params1 [i].IsOut != params2 [i].IsOut) {
1035 Verifier.Log.Write ("error", "[out] mismatch.");
1036 res = false;
1037 if (Verifier.stopOnError) break;
1040 if (params1 [i].IsRetval != params2 [i].IsRetval) {
1041 Verifier.Log.Write ("error", "[ref] mismatch.");
1042 res = false;
1043 if (Verifier.stopOnError) break;
1046 if (params1 [i].IsOptional != params2 [i].IsOptional) {
1047 Verifier.Log.Write ("error", "Optional flag mismatch.");
1048 res = false;
1049 if (Verifier.stopOnError) break;
1052 } // checkOptionalFlags
1057 return res;
1062 public static bool Methods (MethodInfo mi1, MethodInfo mi2)
1065 if (mi2 == null) {
1066 Verifier.Log.Write ("error", String.Format ("There is no such method {0}.", mi1.Name), ImportanceLevel.MEDIUM);
1067 return false;
1071 Verifier.Log.Flush ();
1072 Verifier.Log.Write ("method", String.Format ("{0}.", mi1.Name));
1073 bool res = true;
1074 bool ok;
1075 string expected;
1077 ok = Compare.Types (mi1.ReturnType, mi2.ReturnType);
1078 res &= ok;
1079 if (!ok) {
1080 Verifier.Log.Write ("error", "Return types mismatch.", ImportanceLevel.MEDIUM);
1081 if (Verifier.stopOnError) return res;
1087 ok = (mi1.IsAbstract == mi2.IsAbstract);
1088 res &= ok;
1089 if (!ok) {
1090 expected = (mi1.IsAbstract) ? "abstract" : "non-abstract";
1091 Verifier.Log.Write ("error", String.Format ("Expected to be {0}.", expected), ImportanceLevel.MEDIUM);
1092 if (Verifier.stopOnError) return res;
1095 ok = (mi1.IsVirtual == mi2.IsVirtual);
1096 res &= ok;
1097 if (!ok) {
1098 expected = (mi1.IsVirtual) ? "virtual" : "non-virtual";
1099 Verifier.Log.Write ("error", String.Format ("Expected to be {0}.", expected), ImportanceLevel.MEDIUM);
1100 if (Verifier.stopOnError) return res;
1103 ok = (mi1.IsFinal == mi2.IsFinal);
1104 res &= ok;
1105 if (!ok) {
1106 expected = (mi1.IsFinal) ? "final" : "overridable";
1107 Verifier.Log.Write ("error", String.Format ("Expected to be {0}.", expected), ImportanceLevel.MEDIUM);
1108 if (Verifier.stopOnError) return res;
1113 // compare access modifiers
1115 ok = (mi1.IsPrivate == mi2.IsPrivate);
1116 res &= ok;
1117 if (!ok) {
1118 expected = (mi1.IsPublic) ? "public" : "private";
1119 Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
1120 if (Verifier.stopOnError) return res;
1124 ok = (mi1.IsFamily == mi2.IsFamily);
1125 res &= ok;
1126 if (!ok) {
1127 expected = (mi1.IsFamily) ? "protected" : "!protected";
1128 Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
1129 if (Verifier.stopOnError) return res;
1132 ok = (mi1.IsAssembly == mi2.IsAssembly);
1133 res &= ok;
1134 if (!ok) {
1135 expected = (mi1.IsAssembly) ? "internal" : "!internal";
1136 Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
1137 if (Verifier.stopOnError) return res;
1141 ok = (mi1.IsStatic == mi2.IsStatic);
1142 res &= ok;
1143 if (!ok) {
1144 expected = (mi1.IsStatic) ? "static" : "instance";
1145 Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
1146 if (Verifier.stopOnError) return res;
1151 // parameters
1153 ok = Compare.Parameters (mi1.GetParameters (), mi2.GetParameters ());
1154 res &= ok;
1155 if (!ok && Verifier.stopOnError) return res;
1158 ok = (mi1.CallingConvention == mi2.CallingConvention);
1159 res &= ok;
1160 if (!ok) {
1161 Verifier.Log.Write ("error", "Calling conventions mismatch.", ImportanceLevel.MEDIUM);
1162 if (Verifier.stopOnError) return res;
1168 return res;
1172 public static bool Fields (FieldInfo fi1, FieldInfo fi2)
1174 if (fi2 == null) {
1175 Verifier.Log.Write ("error", String.Format ("There is no such field {0}.", fi1.Name), ImportanceLevel.MEDIUM);
1176 return false;
1179 bool res = true;
1180 bool ok;
1181 string expected;
1183 Verifier.Log.Write ("field", String.Format ("{0}.", fi1.Name));
1185 ok = (fi1.IsPrivate == fi2.IsPrivate);
1186 res &= ok;
1187 if (!ok) {
1188 expected = (fi1.IsPublic) ? "public" : "private";
1189 Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
1190 if (Verifier.stopOnError) return res;
1193 ok = (fi1.IsFamily == fi2.IsFamily);
1194 res &= ok;
1195 if (!ok) {
1196 expected = (fi1.IsFamily) ? "protected" : "!protected";
1197 Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
1198 if (Verifier.stopOnError) return res;
1201 ok = (fi1.IsAssembly == fi2.IsAssembly);
1202 res &= ok;
1203 if (!ok) {
1204 expected = (fi1.IsAssembly) ? "internal" : "!internal";
1205 Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
1206 if (Verifier.stopOnError) return res;
1209 ok = (fi1.IsInitOnly == fi2.IsInitOnly);
1210 res &= ok;
1211 if (!ok) {
1212 expected = (fi1.IsInitOnly) ? "readonly" : "!readonly";
1213 Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
1214 if (Verifier.stopOnError) return res;
1217 ok = (fi1.IsStatic == fi2.IsStatic);
1218 res &= ok;
1219 if (!ok) {
1220 expected = (fi1.IsStatic) ? "static" : "instance";
1221 Verifier.Log.Write ("error", String.Format ("Accessibility levels mismatch (expected [{0}]).", expected), ImportanceLevel.MEDIUM);
1222 if (Verifier.stopOnError) return res;
1225 return res;
1230 public static bool Types (Type type1, Type type2)
1232 // NOTE:
1233 // simply calling type1.Equals (type2) won't work,
1234 // types are in different assemblies hence they have
1235 // different (fully-qualified) names.
1236 int eqFlags = 0;
1237 eqFlags |= (type1.IsAbstract == type2.IsAbstract) ? 0 : 0x001;
1238 eqFlags |= (type1.IsClass == type2.IsClass) ? 0 : 0x002;
1239 eqFlags |= (type1.IsValueType == type2.IsValueType) ? 0 : 0x004;
1240 eqFlags |= (type1.IsPublic == type2.IsPublic) ? 0 : 0x008;
1241 eqFlags |= (type1.IsSealed == type2.IsSealed) ? 0 : 0x010;
1242 eqFlags |= (type1.IsEnum == type2.IsEnum) ? 0 : 0x020;
1243 eqFlags |= (type1.IsPointer == type2.IsPointer) ? 0 : 0x040;
1244 eqFlags |= (type1.IsPrimitive == type2.IsPrimitive) ? 0 : 0x080;
1245 bool res = (eqFlags == 0);
1247 if (!res) {
1248 // TODO: convert flags into descriptive message.
1249 Verifier.Log.Write ("error", "Types mismatch (0x" + eqFlags.ToString("X") + ").", ImportanceLevel.HIGH);
1253 bool ok;
1255 ok = (type1.Attributes & TypeAttributes.BeforeFieldInit) ==
1256 (type2.Attributes & TypeAttributes.BeforeFieldInit);
1257 if (!ok) {
1258 Verifier.Log.Write ("error", "Types attributes mismatch: BeforeFieldInit.", ImportanceLevel.HIGH);
1260 res &= ok;
1262 ok = (type1.Attributes & TypeAttributes.ExplicitLayout) ==
1263 (type2.Attributes & TypeAttributes.ExplicitLayout);
1264 if (!ok) {
1265 Verifier.Log.Write ("error", "Types attributes mismatch: ExplicitLayout.", ImportanceLevel.HIGH);
1267 res &= ok;
1269 ok = (type1.Attributes & TypeAttributes.SequentialLayout) ==
1270 (type2.Attributes & TypeAttributes.SequentialLayout);
1271 if (!ok) {
1272 Verifier.Log.Write ("error", "Types attributes mismatch: SequentialLayout.", ImportanceLevel.HIGH);
1274 res &= ok;
1276 ok = (type1.Attributes & TypeAttributes.Serializable) ==
1277 (type2.Attributes & TypeAttributes.Serializable);
1278 if (!ok) {
1279 Verifier.Log.Write ("error", "Types attributes mismatch: Serializable.", ImportanceLevel.HIGH);
1281 res &= ok;
1283 return res;
1291 ////////////////////////////////
1292 // Log
1293 ////////////////////////////////
1295 public enum ImportanceLevel : int {
1296 LOW = 0, MEDIUM, HIGH
1300 public interface ILogger {
1302 void Write (string tag, string msg, ImportanceLevel importance);
1303 void Write (string msg, ImportanceLevel level);
1304 void Write (string tag, string msg);
1305 void Write (string msg);
1306 ImportanceLevel DefaultImportance {get; set;}
1307 void Flush ();
1308 void Close ();
1312 public abstract class AbstractLogger : ILogger {
1313 private ImportanceLevel defImportance = ImportanceLevel.MEDIUM;
1315 public abstract void Write (string tag, string msg, ImportanceLevel importance);
1316 public abstract void Write (string msg, ImportanceLevel level);
1318 public virtual void Write (string tag, string msg)
1320 Write (tag, msg, DefaultImportance);
1323 public virtual void Write (string msg)
1325 Write (msg, DefaultImportance);
1328 public virtual ImportanceLevel DefaultImportance {
1329 get {
1330 return defImportance;
1332 set {
1333 defImportance = value < ImportanceLevel.LOW
1334 ? ImportanceLevel.LOW
1335 : value > ImportanceLevel.HIGH
1336 ? ImportanceLevel.HIGH
1337 : value;
1341 public abstract void Flush ();
1342 public abstract void Close ();
1348 public class TextLogger : AbstractLogger {
1350 private TextWriter writer;
1352 public TextLogger (TextWriter writer)
1354 if (writer == null)
1355 throw new NullReferenceException ();
1357 this.writer = writer;
1360 private void DoWrite (string tag, string msg)
1362 if (tag != null && tag.Length > 0) {
1363 writer.WriteLine ("[{0}]\t{1}", tag, msg);
1364 } else {
1365 writer.WriteLine ("\t\t" + msg);
1369 public override void Write (string tag, string msg, ImportanceLevel importance)
1371 int v = Log.VerboseLevel;
1372 switch (v) {
1373 case 0 :
1374 break;
1375 case 1 :
1376 if (importance >= ImportanceLevel.HIGH) {
1377 DoWrite (tag, msg);
1379 break;
1380 case 2 :
1381 if (importance >= ImportanceLevel.MEDIUM) {
1382 DoWrite (tag, msg);
1384 break;
1385 case 3 :
1386 DoWrite (tag, msg);
1387 break;
1388 default:
1389 break;
1393 public override void Write (string msg, ImportanceLevel importance)
1395 Write (null, msg, importance);
1398 public override void Flush ()
1400 Console.Out.Flush ();
1403 public override void Close ()
1405 if (writer != Console.Out && writer != Console.Error) {
1406 writer.Close ();
1413 public sealed class Log {
1415 private static int verbose = 3;
1417 private ArrayList consumers;
1419 public Log (bool useDefault)
1421 consumers = new ArrayList ();
1422 if (useDefault) AddConsumer (new TextLogger (Console.Out));
1425 public Log () : this (true)
1430 public static int VerboseLevel {
1431 get {
1432 return verbose;
1434 set {
1435 verbose = (value < 0)
1437 : (value > 3)
1438 ? 3 : value;
1442 public void AddConsumer (ILogger consumer)
1444 consumers.Add (consumer);
1448 public void Write (string tag, string msg, ImportanceLevel importance)
1450 foreach (ILogger logger in consumers) {
1451 if (tag == null || tag == "") {
1452 logger.Write (msg, importance);
1453 } else {
1454 logger.Write (tag, msg, importance);
1459 public void Write (string msg, ImportanceLevel importance)
1461 Write (null, msg, importance);
1465 public void Write (string tag, string msg)
1467 foreach (ILogger logger in consumers) {
1468 if (tag == null || tag == "") {
1469 logger.Write (msg);
1470 } else {
1471 logger.Write (tag, msg);
1476 public void Write (string msg)
1478 Write (null, msg);
1482 public void Flush ()
1484 foreach (ILogger logger in consumers) {
1485 logger.Flush ();
1490 public void Close ()
1492 foreach (ILogger logger in consumers) {
1493 logger.Flush ();
1494 logger.Close ();
1505 ////////////////////////////////
1506 // Main
1507 ////////////////////////////////
1509 public class Verifier {
1511 public static readonly Log log = new Log ();
1512 public static bool stopOnError = false;
1513 public static bool ignoreMissingTypes = true;
1514 public static bool checkOptionalFlags = true;
1516 private static readonly IList excluded;
1518 static Verifier ()
1520 excluded = new ArrayList ();
1521 excluded.Add ("<PrivateImplementationDetails>");
1525 private Verifier ()
1529 public static Log Log {
1530 get {
1531 return log;
1535 public static IList Excluded {
1536 get {
1537 return excluded;
1543 public static void Main (String [] args)
1545 if (args.Length < 2) {
1546 Console.WriteLine ("Usage: verifier assembly1 assembly2");
1547 } else {
1548 string name1 = args [0];
1549 string name2 = args [1];
1551 bool ok = false;
1553 AssemblyStuff asm1 = new AssemblyStuff (name1);
1554 AssemblyStuff asm2 = new AssemblyStuff (name2);
1555 ok = asm1.Load ();
1556 if (!ok) {
1557 Console.WriteLine ("Unable to load assembly {0}.", name1);
1558 Environment.Exit (-1);
1561 ok = asm2.Load ();
1562 if (!ok) {
1563 Console.WriteLine ("Unable to load assembly {0}.", name2);
1564 Environment.Exit (-1);
1568 try {
1569 ok = (asm1 == asm2);
1570 } catch {
1571 ok = false;
1572 } finally {
1573 Log.Close ();
1576 if (!ok) {
1577 Console.WriteLine ("--- not equal");
1578 Environment.Exit (-1);