2002-11-29 Daniel Morgan <danmorg@sc.rr.com>
[mono.git] / mcs / tools / verifier.cs
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 //
9
10 using System;
11 using System.IO;
12 using System.Collections;
13 using System.Reflection;
14
15 namespace Mono.Verifier {
16
17
18
19         ////////////////////////////////
20         // Collections
21         ////////////////////////////////
22
23         public abstract class MemberCollection : IEnumerable {
24
25                 public delegate MemberInfo [] InfoQuery (Type type, BindingFlags bindings);
26                 public delegate bool MemberComparer (MemberInfo mi1, MemberInfo mi2);
27
28                 protected SortedList list;
29                 protected MemberComparer comparer;
30
31                 protected BindingFlags bindings;
32
33                 protected MemberCollection (Type type, InfoQuery query, MemberComparer comparer, BindingFlags bindings)
34                 {
35                         if (query == null)
36                                 throw new NullReferenceException ("Invalid query delegate.");
37
38                         if (comparer == null)
39                                 throw new NullReferenceException ("Invalid comparer.");
40
41                         this.comparer = comparer;
42                         this.bindings = bindings;
43
44                         this.list = new SortedList ();
45
46                         MemberInfo [] data = query (type, bindings);
47                         foreach (MemberInfo info in data) {
48                                 this.list [info.Name] = info;
49                         }
50                 }
51
52
53
54                 public MemberInfo this [string name] {
55                         get {
56                                 return list [name] as MemberInfo;
57                         }
58                 }
59
60
61                 public override int GetHashCode ()
62                 {
63                         return list.GetHashCode ();
64                 }
65
66
67                 public override bool Equals (object o)
68                 {
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);
77                                 }
78                         }
79                         return res;
80                 }
81
82
83
84                 public static bool operator == (MemberCollection c1, MemberCollection c2)
85                 {
86                         return c1.Equals (c2);
87                 }
88
89                 public static bool operator != (MemberCollection c1, MemberCollection c2)
90                 {
91                         return !(c1 == c2);
92                 }
93
94
95
96                 public IEnumerator GetEnumerator()
97                 {
98                         return new Iterator (this);
99                 }
100
101
102                 internal class Iterator : IEnumerator  {
103                         private MemberCollection host;
104                         private int pos;
105
106                         internal Iterator (MemberCollection host)
107                         {
108                                 this.host=host;
109                                 this.Reset ();
110                         }
111
112                         /// <summary></summary>
113                         public object Current
114                         {
115                                 get {
116                                         if (host != null && pos >=0 && pos < host.list.Count) {
117                                                 return host.list.GetByIndex (pos);
118                                         } else {
119                                                 return null;
120                                         }
121                                 }
122                         }
123
124                         /// <summary></summary>
125                         public bool MoveNext ()
126                         {
127                                 if (host!=null) {
128                                         return (++pos) < host.list.Count;
129                                 } else {
130                                         return false;
131                                 }
132                         }
133
134                         /// <summary></summary>
135                         public void Reset ()
136                         {
137                                 this.pos = -1;
138                         }
139                 }
140
141         }
142
143
144
145
146         //--- Method collections
147
148         /// <summary>
149         /// Abstract collection of class' methods.
150         /// </summary>
151         public abstract class MethodCollectionBase : MemberCollection {
152
153
154                 protected MethodCollectionBase (Type type, BindingFlags bindings)
155                        : base (type, new InfoQuery (Query), new MemberComparer (Comparer), bindings)
156                 {
157                 }
158
159
160                 private static MemberInfo [] Query (Type type, BindingFlags bindings)
161                 {
162                         // returns MethodInfo []
163                         return type.GetMethods (bindings);
164                 }
165
166                 private static bool Comparer (MemberInfo mi1, MemberInfo mi2)
167                 {
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);
175                         }
176                         return res;
177                 }
178         }
179
180
181
182         /// <summary>
183         /// Collection of public instance methods of a class.
184         /// </summary>
185         public class PublicMethods : MethodCollectionBase {
186
187                 public PublicMethods (Type type)
188                        : base (type, BindingFlags.Public | BindingFlags.Instance)
189                 {
190                 }
191         }
192
193         /// <summary>
194         /// Collection of public static methods of a class.
195         /// </summary>
196         public class PublicStaticMethods : MethodCollectionBase {
197
198                 public PublicStaticMethods (Type type)
199                        : base (type, BindingFlags.Public | BindingFlags.Static)
200                 {
201                 }
202         }
203
204         /// <summary>
205         /// Collection of non-public instance methods of a class.
206         /// </summary>
207         public class NonPublicMethods : MethodCollectionBase {
208
209                 public NonPublicMethods (Type type)
210                        : base (type, BindingFlags.NonPublic | BindingFlags.Instance)
211                 {
212                 }
213         }
214
215         /// <summary>
216         /// Collection of non-public static methods of a class.
217         /// </summary>
218         public class NonPublicStaticMethods : MethodCollectionBase {
219
220                 public NonPublicStaticMethods (Type type)
221                        : base (type, BindingFlags.NonPublic | BindingFlags.Static)
222                 {
223                 }
224         }
225
226
227
228
229
230         //--- Field collections
231
232         public abstract class FieldCollectionBase : MemberCollection {
233
234
235                 protected FieldCollectionBase (Type type, BindingFlags bindings)
236                        : base (type, new InfoQuery (Query), new MemberComparer (Comparer), bindings)
237                 {
238                 }
239
240
241                 private static MemberInfo [] Query (Type type, BindingFlags bindings)
242                 {
243                         // returns FieldInfo []
244                         return type.GetFields (bindings);
245                 }
246
247                 private static bool Comparer (MemberInfo mi1, MemberInfo mi2)
248                 {
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);
256                         }
257                         return res;
258                 }
259         }
260
261
262         public class PublicFields : FieldCollectionBase {
263
264                 public PublicFields (Type type)
265                        : base (type, BindingFlags.Public | BindingFlags.Instance)
266                 {
267                 }
268         }
269
270         public class PublicStaticFields : FieldCollectionBase {
271
272                 public PublicStaticFields (Type type)
273                        : base (type, BindingFlags.Public | BindingFlags.Static)
274                 {
275                 }
276         }
277
278         public class NonPublicFields : FieldCollectionBase {
279
280                 public NonPublicFields (Type type)
281                        : base (type, BindingFlags.NonPublic | BindingFlags.Instance)
282                 {
283                 }
284         }
285
286         public class NonPublicStaticFields : FieldCollectionBase {
287
288                 public NonPublicStaticFields (Type type)
289                        : base (type, BindingFlags.NonPublic | BindingFlags.Static)
290                 {
291                 }
292         }
293
294
295
296
297
298         public abstract class AbstractTypeStuff {
299                 public readonly Type type;
300
301                 public AbstractTypeStuff (Type type)
302                 {
303                         if (type == null)
304                                 throw new NullReferenceException ("Invalid type.");
305
306                         this.type = type;
307                 }
308
309                 public override int GetHashCode ()
310                 {
311                         return type.GetHashCode ();
312                 }
313
314                 public static bool operator == (AbstractTypeStuff t1, AbstractTypeStuff t2)
315                 {
316                         if ((t1 as object) == null) {
317                                 if ((t2 as object) == null) return true;
318                                 return false;
319                         }
320                         return t1.Equals (t2);
321                 }
322
323                 public static bool operator != (AbstractTypeStuff t1, AbstractTypeStuff t2)
324                 {
325                         return !(t1 == t2);
326                 }
327
328                 public override bool Equals (object o)
329                 {
330                         return (o is AbstractTypeStuff && CompareTypes (o as AbstractTypeStuff));
331                 }
332
333                 protected virtual bool CompareTypes (AbstractTypeStuff that)
334                 {
335                         Verifier.Log.Write ("info", "Comparing types.", ImportanceLevel.LOW);
336                         bool res;
337
338                         res = Compare.Types (this.type, that.type);
339
340                         return res;
341                 }
342
343         }
344
345
346
347
348         /// <summary>
349         ///  Represents a class.
350         /// </summary>
351         public class ClassStuff : AbstractTypeStuff {
352
353                 public PublicMethods publicMethods;
354                 public PublicStaticMethods publicStaticMethods;
355                 public NonPublicMethods nonpublicMethods;
356                 public NonPublicStaticMethods nonpublicStaticMethods;
357
358                 public PublicFields publicFields;
359                 public PublicStaticFields publicStaticFields;
360                 public NonPublicFields nonpublicFields;
361                 public NonPublicStaticFields nonpublicStaticFields;
362
363                 public ClassStuff (Type type) : base (type)
364                 {
365                         publicMethods = new PublicMethods (type);
366                         publicStaticMethods = new PublicStaticMethods (type);
367                         nonpublicMethods = new NonPublicMethods (type);
368                         nonpublicStaticMethods = new NonPublicStaticMethods (type);
369
370                         publicFields = new PublicFields (type);
371                         publicStaticFields = new PublicStaticFields (type);
372                         nonpublicFields = new NonPublicFields (type);
373                         nonpublicStaticFields = new NonPublicStaticFields (type);
374                 }
375
376
377                 public override int GetHashCode ()
378                 {
379                         return base.GetHashCode ();
380                 }
381
382                 private bool CompareMethods (ClassStuff that)
383                 {
384                         bool res = true;
385                         bool ok;
386
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;
391
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;
396
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;
401
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;
406
407                         return res;
408                 }
409
410
411                 private bool CompareFields (ClassStuff that)
412                 {
413                         bool res = true;
414                         bool ok;
415
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;
420
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;
425
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;
430
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;
435
436                         return res;
437                 }
438
439
440                 public override bool Equals (object o)
441                 {
442                         bool res = (o is ClassStuff);
443                         if (res) {
444                                 ClassStuff that = o as ClassStuff;
445
446                                 res &= this.CompareTypes (that);
447                                 if (!res && Verifier.stopOnError) return res;
448
449                                 res &= this.CompareMethods (that);
450                                 if (!res && Verifier.stopOnError) return res;
451
452                                 res &= this.CompareFields (that);
453                                 if (!res && Verifier.stopOnError) return res;
454
455                         }
456                         return res;
457                 }
458
459         }
460
461
462
463         /// <summary>
464         ///  Represents an interface.
465         /// </summary>
466         public class InterfaceStuff : AbstractTypeStuff {
467
468                 public PublicMethods publicMethods;
469
470                 public InterfaceStuff (Type type) : base (type)
471                 {
472                         publicMethods = new PublicMethods (type);
473                 }
474
475                 public override int GetHashCode ()
476                 {
477                         return base.GetHashCode ();
478                 }
479
480                 public override bool Equals (object o)
481                 {
482                         bool res = (o is InterfaceStuff);
483                         if (res) {
484                                 bool ok;
485                                 InterfaceStuff that = o as InterfaceStuff;
486
487                                 res = this.CompareTypes (that);
488                                 if (!res && Verifier.stopOnError) return res;
489
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;
494                         }
495                         return res;
496                 }
497
498         }
499
500
501
502         /// <summary>
503         ///  Represents an enumeration.
504         /// </summary>
505         public class EnumStuff : AbstractTypeStuff {
506
507                 //public FieldInfo [] members;
508
509                 public string baseType;
510                 public Hashtable enumTable;
511                 public bool isFlags;
512
513                 public EnumStuff (Type type) : base (type)
514                 {
515                         //members = type.GetFields (BindingFlags.Public | BindingFlags.Static);
516
517                         Array values = Enum.GetValues (type);
518                         Array names = Enum.GetNames (type);
519
520                         baseType = Enum.GetUnderlyingType (type).Name;
521
522                         enumTable = new Hashtable ();
523
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);
529                                 }
530                         }
531
532                         int indx = 0;
533                         foreach (string id in names) {
534                                 enumTable [id] = Convert.ToInt64(values.GetValue(indx) as Enum);
535                                 ++indx;
536                         }
537                 }
538
539                 public override int GetHashCode ()
540                 {
541                         return base.GetHashCode ();
542                 }
543
544                 public override bool Equals (object o)
545                 {
546                         bool res = (o is EnumStuff);
547                         bool ok;
548
549                         if (res) {
550                                 EnumStuff that = o as EnumStuff;
551                                 ok = this.CompareTypes (that);
552                                 res &= ok;
553                                 if (!ok && Verifier.stopOnError) return res;
554
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;
562                                 }
563
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;
572                                 }
573
574                                 Verifier.Log.Write ("info", "Comparing enum values.");
575
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;
584                                         }
585
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;
596                                                 }
597                                         }
598                                 }
599                         }
600                         return res;
601                 }
602         }
603
604
605
606         public sealed class TypeArray {
607                 public static readonly TypeArray empty = new TypeArray (Type.EmptyTypes);
608
609                 public Type [] types;
610
611                 public TypeArray (Type [] types)
612                 {
613                         this.types = new Type [types.Length];
614                         for (int i = 0; i < types.Length; i++) {
615                                 this.types.SetValue (types.GetValue (i), i);
616                         }
617                 }
618         }
619
620
621
622         public class AssemblyLoader {
623                 public delegate void Hook (TypeArray assemblyTypes);
624
625                 private static Hashtable cache;
626
627                 private Hook hook;
628
629                 static AssemblyLoader ()
630                 {
631                         cache = new Hashtable (11);
632                 }
633
634                 public AssemblyLoader (Hook hook)
635                 {
636                         if (hook == null)
637                                 throw new NullReferenceException ("Invalid loader hook.");
638
639                         this.hook = hook;
640                 }
641
642
643                 public bool LoadFrom (string assemblyName)
644                 {
645                         bool res = false;
646                         try {
647                                 TypeArray types = TypeArray.empty;
648
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;
659                                         }
660                                 }
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);
671                                         }
672                                 }
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);
677                         }
678
679                         return res;
680                 }
681
682         }
683
684
685
686
687         public abstract class AbstractTypeCollection : SortedList {
688
689                 private AssemblyLoader loader;
690
691                 public AbstractTypeCollection ()
692                 {
693                         loader = new AssemblyLoader (new AssemblyLoader.Hook (LoaderHook));
694                 }
695
696                 public AbstractTypeCollection (string assemblyName) : this ()
697                 {
698                         LoadFrom (assemblyName);
699                 }
700
701                 public abstract void LoaderHook (TypeArray types);
702
703
704                 public bool LoadFrom (string assemblyName)
705                 {
706                         return loader.LoadFrom (assemblyName);
707                 }
708
709         }
710
711
712
713         public class ClassCollection : AbstractTypeCollection {
714
715                 public ClassCollection () : base ()
716                 {
717                 }
718
719                 public ClassCollection (string assemblyName)
720                 : base (assemblyName)
721                 {
722                 }
723
724
725                 public override void LoaderHook (TypeArray types)
726                 {
727                         foreach (Type type in types.types) {
728                                 if (type.IsClass) {
729                                         this [type.FullName] = new ClassStuff (type);
730                                 }
731                         }
732                 }
733
734         }
735
736
737         public class InterfaceCollection : AbstractTypeCollection {
738
739                 public InterfaceCollection () : base ()
740                 {
741                 }
742
743                 public InterfaceCollection (string assemblyName)
744                 : base (assemblyName)
745                 {
746                 }
747
748
749                 public override void LoaderHook (TypeArray types)
750                 {
751                         foreach (Type type in types.types) {
752                                 if (type.IsInterface) {
753                                         this [type.FullName] = new InterfaceStuff (type);
754                                 }
755                         }
756                 }
757
758         }
759
760
761
762         public class EnumCollection : AbstractTypeCollection {
763
764                 public EnumCollection () : base ()
765                 {
766                 }
767
768                 public EnumCollection (string assemblyName)
769                 : base (assemblyName)
770                 {
771                 }
772
773                 public override void LoaderHook (TypeArray types)
774                 {
775                         foreach (Type type in types.types) {
776                                 if (type.IsEnum) {
777                                         this [type.FullName] = new EnumStuff (type);
778                                 }
779                         }
780                 }
781         }
782
783
784
785         public class AssemblyStuff {
786
787                 public string name;
788                 public bool valid;
789
790                 public ClassCollection classes;
791                 public InterfaceCollection interfaces;
792                 public EnumCollection enums;
793
794
795                 protected delegate bool Comparer (AssemblyStuff asm1, AssemblyStuff asm2);
796                 private static ArrayList comparers;
797
798                 static AssemblyStuff ()
799                 {
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));
806                 }
807
808                 protected static bool CompareNumClasses (AssemblyStuff asm1, AssemblyStuff asm2)
809                 {
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;
813                 }
814
815                 protected static bool CompareNumInterfaces (AssemblyStuff asm1, AssemblyStuff asm2)
816                 {
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;
820                 }
821
822
823                 protected static bool CompareClasses (AssemblyStuff asm1, AssemblyStuff asm2)
824                 {
825                         bool res = true;
826                         Verifier.Log.Write ("info", "Comparing classes.");
827
828                         foreach (DictionaryEntry c in asm1.classes) {
829                                 string className = c.Key as string;
830
831                                 if (Verifier.Excluded.Contains (className)) {
832                                         Verifier.Log.Write ("info", String.Format ("Ignoring class {0}.", className), ImportanceLevel.MEDIUM);
833                                         continue;
834                                 }
835
836                                 Verifier.Log.Write ("class", className);
837
838                                 ClassStuff class1 = c.Value as ClassStuff;
839                                 ClassStuff class2 = asm2.classes [className] as ClassStuff;
840
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;
846                                 }
847
848                                 res &= (class1 == class2);
849                                 if (!res && Verifier.stopOnError) return res;
850                         }
851
852                         return res;
853                 }
854
855
856                 protected static bool CompareInterfaces (AssemblyStuff asm1, AssemblyStuff asm2)
857                 {
858                         bool res = true;
859                         Verifier.Log.Write ("info", "Comparing interfaces.");
860
861                         foreach (DictionaryEntry ifc in asm1.interfaces) {
862                                 string ifcName = ifc.Key as string;
863                                 Verifier.Log.Write ("interface", ifcName);
864
865                                 InterfaceStuff ifc1 = ifc.Value as InterfaceStuff;
866                                 InterfaceStuff ifc2 = asm2.interfaces [ifcName] as InterfaceStuff;
867
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;
873                                 }
874
875                                 res &= (ifc1 == ifc2);
876                                 if (!res && Verifier.stopOnError) return res;
877
878                         }
879
880                         return res;
881                 }
882
883
884                 protected static bool CompareEnums (AssemblyStuff asm1, AssemblyStuff asm2)
885                 {
886                         bool res = true;
887                         Verifier.Log.Write ("info", "Comparing enums.");
888
889                         foreach (DictionaryEntry e in asm1.enums) {
890                                 string enumName = e.Key as string;
891                                 Verifier.Log.Write ("enum", enumName);
892
893                                 EnumStuff e1 = e.Value as EnumStuff;
894                                 EnumStuff e2 = asm2.enums [enumName] as EnumStuff;
895
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;
901                                 }
902                                 res &= (e1 == e2);
903                                 if (!res && Verifier.stopOnError) return res;
904                         }
905
906                         return res;
907                 }
908
909
910
911                 public AssemblyStuff (string assemblyName)
912                 {
913                         this.name = assemblyName;
914                         valid = false;
915                 }
916
917                 public bool Load ()
918                 {
919                         bool res = true;
920                         bool ok;
921
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);
926
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);
931
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);
936
937                         valid = res;
938                         return res;
939                 }
940
941
942                 public override bool Equals (object o)
943                 {
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);
951                                 }
952                         }
953                         return res;
954                 }
955
956
957                 public static bool operator == (AssemblyStuff asm1, AssemblyStuff asm2)
958                 {
959                         return asm1.Equals (asm2);
960                 }
961
962                 public static bool operator != (AssemblyStuff asm1, AssemblyStuff asm2)
963                 {
964                         return !(asm1 == asm2);
965                 }
966
967                 public override int GetHashCode ()
968                 {
969                         return classes.GetHashCode () ^ interfaces.GetHashCode ();
970                 }
971
972
973                 public override string ToString ()
974                 {
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);
981                         }
982                         return res;
983                 }
984
985         }
986
987
988
989
990         ////////////////////////////////
991         // Compare
992         ////////////////////////////////
993
994         public sealed class Compare {
995
996                 private Compare ()
997                 {
998                 }
999
1000
1001                 public static bool Parameters (ParameterInfo[] params1, ParameterInfo[] params2)
1002                 {
1003                         bool res = true;
1004                         if (params1.Length != params2.Length) {
1005                                 Verifier.Log.Write ("Parameter count mismatch.");
1006                                 return false;
1007                         }
1008
1009                         int count = params1.Length;
1010
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;
1016                                 }
1017
1018                                 Verifier.Log.Write ("parameter", params1 [i].Name);
1019
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;
1024                                 }
1025
1026
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;
1032                                         }
1033
1034                                         if (params1 [i].IsOut != params2 [i].IsOut) {
1035                                                 Verifier.Log.Write ("error", "[out] mismatch.");
1036                                                 res = false;
1037                                                 if (Verifier.stopOnError) break;
1038                                         }
1039
1040                                         if (params1 [i].IsRetval != params2 [i].IsRetval) {
1041                                                 Verifier.Log.Write ("error", "[ref] mismatch.");
1042                                                 res = false;
1043                                                 if (Verifier.stopOnError) break;
1044                                         }
1045
1046                                         if (params1 [i].IsOptional != params2 [i].IsOptional) {
1047                                                 Verifier.Log.Write ("error", "Optional flag mismatch.");
1048                                                 res = false;
1049                                                 if (Verifier.stopOnError) break;
1050                                         }
1051
1052                                 } // checkOptionalFlags
1053
1054
1055                         }
1056
1057                         return res;
1058                 }
1059
1060
1061
1062                 public static bool Methods (MethodInfo mi1, MethodInfo mi2)
1063                 {
1064                         
1065                         if (mi2 == null) {
1066                                 Verifier.Log.Write ("error", String.Format ("There is no such method {0}.", mi1.Name), ImportanceLevel.MEDIUM);
1067                                 return false;
1068                         }
1069
1070
1071                         Verifier.Log.Flush ();
1072                         Verifier.Log.Write ("method", String.Format ("{0}.", mi1.Name));
1073                         bool res = true;
1074                         bool ok;
1075                         string expected;
1076
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;
1082                         }
1083
1084
1085
1086
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;
1093                         }
1094
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;
1101                         }
1102
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;
1109                         }
1110
1111
1112
1113                         // compare access modifiers
1114
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;
1121                         }
1122
1123
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;
1130                         }
1131
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;
1138                         }
1139
1140
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;
1147                         }
1148
1149
1150
1151                         // parameters
1152
1153                         ok = Compare.Parameters (mi1.GetParameters (), mi2.GetParameters ());
1154                         res &= ok;
1155                         if (!ok && Verifier.stopOnError) return res;
1156
1157
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;
1163                         }
1164
1165
1166
1167
1168                         return res;
1169                 }
1170
1171
1172                 public static bool Fields (FieldInfo fi1, FieldInfo fi2)
1173                 {
1174                         if (fi2 == null) {
1175                                 Verifier.Log.Write ("error", String.Format ("There is no such field {0}.", fi1.Name), ImportanceLevel.MEDIUM);
1176                                 return false;
1177                         }
1178
1179                         bool res = true;
1180                         bool ok;
1181                         string expected;
1182
1183                         Verifier.Log.Write ("field", String.Format ("{0}.", fi1.Name));
1184
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;
1191                         }
1192
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;
1199                         }
1200
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;
1207                         }
1208
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;
1215                         }
1216
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;
1223                         }
1224
1225                         return res;
1226                 }
1227
1228
1229
1230                 public static bool Types (Type type1, Type type2)
1231                 {
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);
1246
1247                         if (!res) {
1248                                 // TODO: convert flags into descriptive message.
1249                                 Verifier.Log.Write ("error", "Types mismatch (0x" + eqFlags.ToString("X") + ").", ImportanceLevel.HIGH);
1250                         }
1251
1252
1253                         bool ok;
1254
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);
1259                         }
1260                         res &= ok;
1261
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);
1266                         }
1267                         res &= ok;
1268
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);
1273                         }
1274                         res &= ok;
1275
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);
1280                         }
1281                         res &= ok;
1282
1283                         return res;
1284                 }
1285
1286         }
1287
1288
1289
1290
1291         ////////////////////////////////
1292         // Log
1293         ////////////////////////////////
1294
1295         public enum ImportanceLevel : int {
1296                 LOW = 0, MEDIUM, HIGH
1297         }
1298
1299
1300         public interface ILogger {
1301
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 ();
1309         }
1310
1311
1312         public abstract class AbstractLogger : ILogger {
1313                 private ImportanceLevel defImportance = ImportanceLevel.MEDIUM;
1314
1315                 public abstract void Write (string tag, string msg, ImportanceLevel importance);
1316                 public abstract void Write (string msg, ImportanceLevel level);
1317
1318                 public virtual void Write (string tag, string msg)
1319                 {
1320                         Write (tag, msg, DefaultImportance);
1321                 }
1322
1323                 public virtual void Write (string msg)
1324                 {
1325                         Write (msg, DefaultImportance);
1326                 }
1327
1328                 public virtual ImportanceLevel DefaultImportance {
1329                         get {
1330                                 return defImportance;
1331                         }
1332                         set {
1333                                 defImportance = value < ImportanceLevel.LOW
1334                                                  ? ImportanceLevel.LOW
1335                                                  : value > ImportanceLevel.HIGH
1336                                                    ? ImportanceLevel.HIGH
1337                                                    : value;
1338                         }
1339                 }
1340
1341                 public abstract void Flush ();
1342                 public abstract void Close ();
1343
1344         }
1345
1346
1347
1348         public class TextLogger : AbstractLogger {
1349
1350                 private TextWriter writer;
1351
1352                 public TextLogger (TextWriter writer)
1353                 {
1354                         if (writer == null)
1355                                 throw new NullReferenceException ();
1356
1357                         this.writer = writer;
1358                 }
1359
1360                 private void DoWrite (string tag, string msg)
1361                 {
1362                         if (tag != null && tag.Length > 0) {
1363                                 writer.WriteLine ("[{0}]\t{1}", tag, msg);
1364                         } else {
1365                                 writer.WriteLine ("\t\t" + msg);
1366                         }
1367                 }
1368
1369                 public override void Write (string tag, string msg, ImportanceLevel importance)
1370                 {
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);
1378                                 }
1379                                 break;
1380                         case 2 :
1381                                 if (importance >= ImportanceLevel.MEDIUM) {
1382                                         DoWrite (tag, msg);
1383                                 }
1384                                 break;
1385                         case 3 :
1386                                 DoWrite (tag, msg);
1387                                 break;
1388                         default:
1389                                 break;
1390                         }
1391                 }
1392
1393                 public override void Write (string msg, ImportanceLevel importance)
1394                 {
1395                         Write (null, msg, importance);
1396                 }
1397
1398                 public override void Flush ()
1399                 {
1400                         Console.Out.Flush ();
1401                 }
1402
1403                 public override void Close ()
1404                 {
1405                         if (writer != Console.Out && writer != Console.Error) {
1406                                 writer.Close ();
1407                         }
1408                 }
1409         }
1410
1411
1412
1413         public sealed class Log {
1414
1415                 private static int verbose = 3;
1416
1417                 private ArrayList consumers;
1418
1419                 public Log (bool useDefault)
1420                 {
1421                         consumers = new ArrayList ();
1422                         if (useDefault) AddConsumer (new TextLogger (Console.Out));
1423                 }
1424
1425                 public Log () : this (true)
1426                 {
1427                 }
1428
1429
1430                 public static int VerboseLevel {
1431                         get {
1432                                 return verbose;
1433                         }
1434                         set {
1435                                 verbose = (value < 0)
1436                                            ? 0
1437                                            : (value > 3)
1438                                              ? 3 : value;
1439                         }
1440                 }
1441
1442                 public void AddConsumer (ILogger consumer)
1443                 {
1444                         consumers.Add (consumer);
1445                 }
1446
1447
1448                 public void Write (string tag, string msg, ImportanceLevel importance)
1449                 {
1450                         foreach (ILogger logger in consumers) {
1451                                 if (tag == null || tag == "") {
1452                                         logger.Write (msg, importance);
1453                                 } else {
1454                                         logger.Write (tag, msg, importance);
1455                                 }
1456                         }
1457                 }
1458
1459                 public void Write (string msg, ImportanceLevel importance)
1460                 {
1461                         Write (null, msg, importance);
1462                 }
1463
1464
1465                 public void Write (string tag, string msg)
1466                 {
1467                         foreach (ILogger logger in consumers) {
1468                                 if (tag == null || tag == "") {
1469                                         logger.Write (msg);
1470                                 } else {
1471                                         logger.Write (tag, msg);
1472                                 }
1473                         }
1474                 }
1475
1476                 public void Write (string msg)
1477                 {
1478                         Write (null, msg);
1479                 }
1480
1481
1482                 public void Flush ()
1483                 {
1484                         foreach (ILogger logger in consumers) {
1485                                 logger.Flush ();
1486                         }
1487                 }
1488
1489
1490                 public void Close ()
1491                 {
1492                         foreach (ILogger logger in consumers) {
1493                                 logger.Flush ();
1494                                 logger.Close ();
1495                         }
1496                 }
1497
1498         }
1499
1500
1501
1502
1503
1504
1505         ////////////////////////////////
1506         // Main
1507         ////////////////////////////////
1508
1509         public class Verifier {
1510
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;
1515
1516                 private static readonly IList excluded;
1517
1518                 static Verifier ()
1519                 {
1520                         excluded = new ArrayList ();
1521                         excluded.Add ("<PrivateImplementationDetails>");
1522                 }
1523
1524
1525                 private Verifier ()
1526                 {
1527                 }
1528
1529                 public static Log Log {
1530                         get {
1531                                 return log;
1532                         }
1533                 }
1534
1535                 public static IList Excluded {
1536                         get {
1537                                 return excluded;
1538                         }
1539                 }
1540
1541
1542
1543                 public static void Main (String [] args)
1544                 {
1545                         if (args.Length < 2) {
1546                                 Console.WriteLine ("Usage: verifier assembly1 assembly2");
1547                         } else {
1548                                 string name1 = args [0];
1549                                 string name2 = args [1];
1550
1551                                 bool ok = false;
1552
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);
1559                                 }
1560
1561                                 ok = asm2.Load ();
1562                                 if (!ok) {
1563                                         Console.WriteLine ("Unable to load assembly {0}.", name2);
1564                                         Environment.Exit (-1);
1565                                 }
1566
1567
1568                                 try {
1569                                         ok = (asm1 == asm2);
1570                                 } catch {
1571                                         ok = false;
1572                                 } finally {
1573                                         Log.Close ();
1574                                 }
1575
1576                                 if (!ok) {
1577                                         Console.WriteLine ("--- not equal");
1578                                         Environment.Exit (-1);
1579                                 }
1580                         }
1581                 }
1582
1583         }
1584
1585
1586 }
1587