1 //
2 // mono-api-info.cs - Dumps public assembly information to an xml file.
3 //
4 // Authors:
5 //	Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // Copyright (C) 2003-2008 Novell, Inc (http://www.novell.com)
8 //
9 
10 using System;
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.Globalization;
14 using System.Linq;
15 using System.Runtime.CompilerServices;
16 using System.Runtime.InteropServices;
17 using System.Security.Permissions;
18 using System.Text;
19 using System.Xml;
20 
21 using Mono.Cecil;
22 using Mono.Cecil.Cil;
23 using System.IO;
24 
25 namespace CorCompare
26 {
27 	public class Driver
28 	{
Main(string [] args)29 		public static int Main (string [] args)
30 		{
31 			bool showHelp = false;
32 			AbiMode = false;
33 			FollowForwarders = false;
34 			string output = null;
35 
36 			var acoll = new AssemblyCollection ();
37 
38 			var options = new Mono.Options.OptionSet {
39 				"usage: mono-api-info [OPTIONS+] ASSEMBLY+",
40 				"",
41 				"Expose IL structure of CLR assemblies as XML.",
42 				"",
43 				"Available Options:",
44 				{ "abi",
45 					"Generate ABI, not API; contains only classes with instance fields which are not [NonSerialized].",
46 					v => AbiMode = v != null },
47 				{ "f|follow-forwarders",
48 					"Follow type forwarders.",
49 					v => FollowForwarders = v != null },
50 				{ "d|L|lib|search-directory=",
51 					"Check for assembly references in {DIRECTORY}.",
52 					v => TypeHelper.Resolver.AddSearchDirectory (v) },
53 				{ "r=",
54 					"Read and register the file {ASSEMBLY}, and add the directory containing ASSEMBLY to the search path.",
55 					v => TypeHelper.Resolver.ResolveFile (v) },
56 				{ "o=",
57 					"The output file. If not specified the output will be written to stdout.",
58 					v => output = v },
59 				{ "h|?|help",
60 					"Show this message and exit.",
61 					v => showHelp = v != null },
62 				{ "contract-api",
63 					"Produces contract API with all members at each level of inheritance hierarchy",
64 					v => FullAPISet = v != null },
65 			};
66 
67 			var asms = options.Parse (args);
68 
69 			if (showHelp || asms.Count == 0) {
70 				options.WriteOptionDescriptions (Console.Out);
71 				Console.WriteLine ();
72 				return showHelp? 0 :1;
73 			}
74 
75 			string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
76 			string pf = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
77 			TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"assembly\GAC\MSDATASRC\7.0.3300.0__b03f5f7f11d50a3a"));
78 
79 			foreach (string arg in asms) {
80 				acoll.Add (arg);
81 
82 				if (arg.Contains ("v3.0")) {
83 					TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727"));
84 				} else if (arg.Contains ("v3.5")) {
85 					TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v2.0.50727"));
86 					TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v3.0\Windows Communication Foundation"));
87 				} else if (arg.Contains ("v4.0")) {
88 					if (arg.Contains ("Silverlight")) {
89 						TypeHelper.Resolver.AddSearchDirectory (Path.Combine (pf, @"Microsoft Silverlight\4.0.51204.0"));
90 					} else {
91 						TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319"));
92 						TypeHelper.Resolver.AddSearchDirectory (Path.Combine (windir, @"Microsoft.NET\Framework\v4.0.30319\WPF"));
93 					}
94 				} else {
95 					TypeHelper.Resolver.AddSearchDirectory (Path.GetDirectoryName (arg));
96 				}
97 			}
98 
99 			StreamWriter outputStream = null;
100 			if (!string.IsNullOrEmpty (output))
101 				outputStream = new StreamWriter (output);
102 			try {
103 				TextWriter outStream = outputStream ?? Console.Out;
104 				var settings = new XmlWriterSettings ();
105 				settings.Indent = true;
106 				var textWriter = XmlWriter.Create (outStream, settings);
107 				var writer = new WellFormedXmlWriter (textWriter);
108 				writer.WriteStartDocument ();
109 				acoll.Writer = writer;
110 				acoll.DoOutput ();
111 				writer.WriteEndDocument ();
112 				writer.Flush ();
113 			} finally {
114 				if (outputStream != null)
115 					outputStream.Dispose ();
116 			}
117 			return 0;
118 		}
119 
120 		internal static bool AbiMode { get; private set; }
121 		internal static bool FollowForwarders { get; private set; }
122 		internal static bool FullAPISet { get; set; }
123 	}
124 
125 	public class Utils {
126 		static char[] CharsToCleanup = new char[] { '<', '>', '/' };
127 
CleanupTypeName(TypeReference type)128 		public static string CleanupTypeName (TypeReference type)
129 		{
130 			return CleanupTypeName (type.FullName);
131 		}
132 
CleanupTypeName(string t)133 		public static string CleanupTypeName (string t)
134 		{
135 			if (t.IndexOfAny (CharsToCleanup) == -1)
136 				return t;
137 			var sb = new StringBuilder (t.Length);
138 			for (int i = 0; i < t.Length; i++) {
139 				var ch = t [i];
140 				switch (ch) {
141 				case '<':
142 					sb.Append ('[');
143 					break;
144 				case '>':
145 					sb.Append (']');
146 					break;
147 				case '/':
148 					sb.Append ('+');
149 					break;
150 				default:
151 					sb.Append (ch);
152 					break;
153 				}
154 			}
155 			return sb.ToString ();
156 		}
157 	}
158 
159 	class AssemblyCollection
160 	{
161 		XmlWriter writer;
162 		List<AssemblyDefinition> assemblies = new List<AssemblyDefinition> ();
163 
AssemblyCollection()164 		public AssemblyCollection ()
165 		{
166 		}
167 
Add(string name)168 		public bool Add (string name)
169 		{
170 			AssemblyDefinition ass = LoadAssembly (name);
171 			if (ass == null) {
172 				Console.Error.WriteLine ("Cannot load assembly file " + name);
173 				return false;
174 			}
175 
176 			assemblies.Add (ass);
177 			return true;
178 		}
179 
DoOutput()180 		public void DoOutput ()
181 		{
182 			if (writer == null)
183 				throw new InvalidOperationException ("Document not set");
184 
185 			writer.WriteStartElement ("assemblies");
186 			foreach (AssemblyDefinition a in assemblies) {
187 				AssemblyData data = new AssemblyData (writer, a);
188 				data.DoOutput ();
189 			}
190 			writer.WriteEndElement ();
191 		}
192 
193 		public XmlWriter Writer {
194 			set { writer = value; }
195 		}
196 
LoadAssembly(string assembly)197 		AssemblyDefinition LoadAssembly (string assembly)
198 		{
199 			try {
200 				if (File.Exists (assembly))
201 					return TypeHelper.Resolver.ResolveFile (assembly);
202 
203 				return TypeHelper.Resolver.Resolve (AssemblyNameReference.Parse (assembly), new ReaderParameters ());
204 			} catch (Exception e) {
205 				Console.WriteLine (e);
206 				return null;
207 			}
208 		}
209 	}
210 
211 	abstract class BaseData
212 	{
213 		protected XmlWriter writer;
214 
BaseData(XmlWriter writer)215 		protected BaseData (XmlWriter writer)
216 		{
217 			this.writer = writer;
218 		}
219 
DoOutput()220 		public abstract void DoOutput ();
221 
AddAttribute(string name, string value)222 		protected void AddAttribute (string name, string value)
223 		{
224 			writer.WriteAttributeString (name, value);
225 		}
226 	}
227 
228 	class TypeForwardedToData : BaseData
229 	{
230 		AssemblyDefinition ass;
231 
TypeForwardedToData(XmlWriter writer, AssemblyDefinition ass)232 		public TypeForwardedToData (XmlWriter writer, AssemblyDefinition ass)
233 			: base (writer)
234 		{
235 			this.ass = ass;
236 		}
237 
DoOutput()238 		public override void DoOutput ()
239 		{
240 			foreach (ExportedType type in ass.MainModule.ExportedTypes) {
241 
242 				if (((uint)type.Attributes & 0x200000u) == 0)
243 					continue;
244 
245 				writer.WriteStartElement ("attribute");
246 				AddAttribute ("name", typeof (TypeForwardedToAttribute).FullName);
247 				writer.WriteStartElement ("properties");
248 				writer.WriteStartElement ("property");
249 				AddAttribute ("name", "Destination");
250 				AddAttribute ("value", Utils.CleanupTypeName (type.FullName));
251 				writer.WriteEndElement (); // properties
252 				writer.WriteEndElement (); // properties
253 				writer.WriteEndElement (); // attribute
254 			}
255 		}
256 
OutputForwarders(XmlWriter writer, AssemblyDefinition ass)257 		public static void OutputForwarders (XmlWriter writer, AssemblyDefinition ass)
258 		{
259 			TypeForwardedToData tftd = new TypeForwardedToData (writer, ass);
260 			tftd.DoOutput ();
261 		}
262 	}
263 
264 	class AssemblyData : BaseData
265 	{
266 		AssemblyDefinition ass;
267 
AssemblyData(XmlWriter writer, AssemblyDefinition ass)268 		public AssemblyData (XmlWriter writer, AssemblyDefinition ass)
269 			: base (writer)
270 		{
271 			this.ass = ass;
272 		}
273 
DoOutput()274 		public override void DoOutput ()
275 		{
276 			if (writer == null)
277 				throw new InvalidOperationException ("Document not set");
278 
279 			writer.WriteStartElement ("assembly");
280 			AssemblyNameDefinition aname = ass.Name;
281 			AddAttribute ("name", aname.Name);
282 			AddAttribute ("version", aname.Version.ToString ());
283 
284 			AttributeData.OutputAttributes (writer, ass);
285 
286 			var types = new List<TypeDefinition> ();
287 			if (ass.MainModule.Types != null) {
288 				types.AddRange (ass.MainModule.Types);
289 			}
290 
291 			if (Driver.FollowForwarders && ass.MainModule.ExportedTypes != null) {
292 				foreach (var t in ass.MainModule.ExportedTypes) {
293 					var forwarded = t.Resolve ();
294 					if (forwarded == null) {
295 						throw new Exception ("Could not resolve forwarded type " + t.FullName + " in " + ass.Name);
296 					}
297 					types.Add (forwarded);
298 				}
299 			}
300 
301 			if (types.Count == 0) {
302 				writer.WriteEndElement (); // assembly
303 				return;
304 			}
305 
306 			types.Sort (TypeReferenceComparer.Default);
307 
308 			writer.WriteStartElement ("namespaces");
309 
310 			string current_namespace = "$%&$&";
311 			bool in_namespace = false;
312 			foreach (TypeDefinition t in types) {
313 				if (string.IsNullOrEmpty (t.Namespace))
314 					continue;
315 
316 				if (!Driver.AbiMode && ((t.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.Public))
317 					continue;
318 
319 				if (t.DeclaringType != null)
320 					continue; // enforce !nested
321 
322 				if (t.Namespace != current_namespace) {
323 					current_namespace = t.Namespace;
324 					if (in_namespace) {
325 						writer.WriteEndElement (); // classes
326 						writer.WriteEndElement (); // namespace
327 					} else {
328 						in_namespace = true;
329 					}
330 					writer.WriteStartElement ("namespace");
331 					AddAttribute ("name", current_namespace);
332 					writer.WriteStartElement ("classes");
333 				}
334 
335 				TypeData bd = new TypeData (writer, t);
336 				bd.DoOutput ();
337 
338 			}
339 
340 			if (in_namespace) {
341 				writer.WriteEndElement (); // classes
342 				writer.WriteEndElement (); // namespace
343 			}
344 
345 			writer.WriteEndElement (); // namespaces
346 
347 			writer.WriteEndElement (); // assembly
348 		}
349 	}
350 
351 	abstract class MemberData : BaseData
352 	{
353 		MemberReference [] members;
354 
MemberData(XmlWriter writer, MemberReference [] members)355 		public MemberData (XmlWriter writer, MemberReference [] members)
356 			: base (writer)
357 		{
358 			this.members = members;
359 		}
360 
GetAdditionalCustomAttributeProvider(MemberReference member)361 		protected virtual ICustomAttributeProvider GetAdditionalCustomAttributeProvider (MemberReference member)
362 		{
363 			return null;
364 		}
365 
DoOutput()366 		public override void DoOutput ()
367 		{
368 			writer.WriteStartElement (ParentTag);
369 
370 			foreach (MemberReference member in members) {
371 				writer.WriteStartElement (Tag);
372 				AddAttribute ("name", GetName (member));
373 				if (!NoMemberAttributes)
374 					AddAttribute ("attrib", GetMemberAttributes (member));
375 				AddExtraAttributes (member);
376 
377 				AttributeData.OutputAttributes (writer, (ICustomAttributeProvider) member, GetAdditionalCustomAttributeProvider (member));
378 
379 				AddExtraData (member);
380 				writer.WriteEndElement (); // Tag
381 			}
382 
383 			writer.WriteEndElement (); // ParentTag
384 		}
385 
AddExtraData(MemberReference memberDefenition)386 		protected virtual void AddExtraData (MemberReference memberDefenition)
387 		{
388 		}
389 
AddExtraAttributes(MemberReference memberDefinition)390 		protected virtual void AddExtraAttributes (MemberReference memberDefinition)
391 		{
392 		}
393 
GetName(MemberReference memberDefenition)394 		protected virtual string GetName (MemberReference memberDefenition)
395 		{
396 			return "NoNAME";
397 		}
398 
GetMemberAttributes(MemberReference memberDefenition)399 		protected virtual string GetMemberAttributes (MemberReference memberDefenition)
400 		{
401 			return null;
402 		}
403 
404 		public virtual bool NoMemberAttributes {
405 			get { return false; }
406 			set {}
407 		}
408 
409 		public virtual string ParentTag {
410 			get { return "NoPARENTTAG"; }
411 		}
412 
413 		public virtual string Tag {
414 			get { return "NoTAG"; }
415 		}
416 
OutputGenericParameters(XmlWriter writer, IGenericParameterProvider provider)417 		public static void OutputGenericParameters (XmlWriter writer, IGenericParameterProvider provider)
418 		{
419 			if (provider.GenericParameters.Count == 0)
420 				return;
421 
422 			var gparameters = provider.GenericParameters;
423 
424 			writer.WriteStartElement ("generic-parameters");
425 
426 			foreach (GenericParameter gp in gparameters) {
427 				writer.WriteStartElement ("generic-parameter");
428 				writer.WriteAttributeString ("name", gp.Name);
429 				writer.WriteAttributeString ("attributes", ((int) gp.Attributes).ToString ());
430 
431 				AttributeData.OutputAttributes (writer, gp);
432 
433 				var constraints = gp.Constraints;
434 				if (constraints.Count == 0) {
435 					writer.WriteEndElement (); // generic-parameter
436 					continue;
437 				}
438 
439 				writer.WriteStartElement ("generic-parameter-constraints");
440 
441 				foreach (TypeReference constraint in constraints) {
442 					writer.WriteStartElement ("generic-parameter-constraint");
443 					writer.WriteAttributeString ("name", Utils.CleanupTypeName (constraint));
444 					writer.WriteEndElement (); // generic-parameter-constraint
445 				}
446 
447 				writer.WriteEndElement (); // generic-parameter-constraints
448 
449 				writer.WriteEndElement (); // generic-parameter
450 			}
451 
452 			writer.WriteEndElement (); // generic-parameters
453 		}
454 	}
455 
456 	class TypeData : MemberData
457 	{
458 		TypeDefinition type;
459 
TypeData(XmlWriter writer, TypeDefinition type)460 		public TypeData (XmlWriter writer, TypeDefinition type)
461 			: base (writer, null)
462 		{
463 			this.type = type;
464 		}
DoOutput()465 		public override void DoOutput ()
466 		{
467 			if (writer == null)
468 				throw new InvalidOperationException ("Document not set");
469 
470 			writer.WriteStartElement ("class");
471 			AddAttribute ("name", type.Name);
472 			string classType = GetClassType (type);
473 			AddAttribute ("type", classType);
474 
475 			if (type.BaseType != null)
476 				AddAttribute ("base", Utils.CleanupTypeName (type.BaseType));
477 
478 			if (type.IsSealed)
479 				AddAttribute ("sealed", "true");
480 
481 			if (type.IsAbstract)
482 				AddAttribute ("abstract", "true");
483 
484 			if ( (type.Attributes & TypeAttributes.Serializable) != 0 || type.IsEnum)
485 				AddAttribute ("serializable", "true");
486 
487 			string charSet = GetCharSet (type);
488 			AddAttribute ("charset", charSet);
489 
490 			string layout = GetLayout (type);
491 			if (layout != null)
492 				AddAttribute ("layout", layout);
493 
494 			if (type.PackingSize >= 0) {
495 				AddAttribute ("pack", type.PackingSize.ToString ());
496 			}
497 
498 			if (type.ClassSize >= 0) {
499 				AddAttribute ("size", type.ClassSize.ToString ());
500 			}
501 
502 			if (type.IsEnum) {
503 				var value_type = GetEnumValueField (type);
504 				if (value_type == null)
505 					throw new NotSupportedException ();
506 
507 				AddAttribute ("enumtype", Utils.CleanupTypeName (value_type.FieldType));
508 			}
509 
510 			AttributeData.OutputAttributes (writer, type);
511 
512 			var ifaces =  TypeHelper.GetInterfaces (type).
513 				Where ((iface) => TypeHelper.IsPublic (iface)). // we're only interested in public interfaces
514 				OrderBy (s => s.FullName, StringComparer.Ordinal);
515 
516 			if (ifaces.Any ()) {
517 				writer.WriteStartElement ("interfaces");
518 				foreach (TypeReference iface in ifaces) {
519 					writer.WriteStartElement ("interface");
520 					AddAttribute ("name", Utils.CleanupTypeName (iface));
521 					writer.WriteEndElement (); // interface
522 				}
523 				writer.WriteEndElement (); // interfaces
524 			}
525 
526 			MemberData.OutputGenericParameters (writer, type);
527 
528 			ArrayList members = new ArrayList ();
529 
530 			FieldDefinition [] fields = GetFields (type);
531 			if (fields.Length > 0) {
532 				Array.Sort (fields, MemberReferenceComparer.Default);
533 				FieldData fd = new FieldData (writer, fields);
534 				members.Add (fd);
535 			}
536 
537 			if (!Driver.AbiMode) {
538 
539 				MethodDefinition [] ctors = GetConstructors (type);
540 				if (ctors.Length > 0) {
541 					Array.Sort (ctors, MethodDefinitionComparer.Default);
542 					members.Add (new ConstructorData (writer, ctors));
543 				}
544 
545 				PropertyDefinition[] properties = GetProperties (type, Driver.FullAPISet);
546 				if (properties.Length > 0) {
547 					Array.Sort (properties, PropertyDefinitionComparer.Default);
548 					members.Add (new PropertyData (writer, properties));
549 				}
550 
551 				EventDefinition [] events = GetEvents (type);
552 				if (events.Length > 0) {
553 					Array.Sort (events, MemberReferenceComparer.Default);
554 					members.Add (new EventData (writer, events));
555 				}
556 
557 				MethodDefinition [] methods = GetMethods (type, Driver.FullAPISet);
558 				if (methods.Length > 0) {
559 					Array.Sort (methods, MethodDefinitionComparer.Default);
560 					members.Add (new MethodData (writer, methods));
561 				}
562 			}
563 
564 			foreach (MemberData md in members)
565 				md.DoOutput ();
566 
567 			var nested = type.NestedTypes;
568 			//remove non public(familiy) and nested in second degree
569 			for (int i = nested.Count - 1; i >= 0; i--) {
570 				TypeDefinition t = nested [i];
571 				if ((t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic ||
572 					(t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily ||
573 					(t.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem) {
574 					// public
575 					if (t.DeclaringType == type)
576 						continue; // not nested of nested
577 				}
578 
579 				nested.RemoveAt (i);
580 			}
581 
582 			if (nested.Count > 0) {
583 				var nestedArray = nested.ToArray ();
584 				Array.Sort (nestedArray, TypeReferenceComparer.Default);
585 
586 				writer.WriteStartElement ("classes");
587 				foreach (TypeDefinition t in nestedArray) {
588 					TypeData td = new TypeData (writer, t);
589 					td.DoOutput ();
590 				}
591 				writer.WriteEndElement (); // classes
592 			}
593 
594 			writer.WriteEndElement (); // class
595 		}
596 
GetEnumValueField(TypeDefinition type)597 		static FieldReference GetEnumValueField (TypeDefinition type)
598 		{
599 			foreach (FieldDefinition field in type.Fields)
600 				if (field.IsSpecialName && field.Name == "value__")
601 					return field;
602 
603 			return null;
604 		}
605 
GetMemberAttributes(MemberReference member)606 		protected override string GetMemberAttributes (MemberReference member)
607 		{
608 			if (member != type)
609 				throw new InvalidOperationException ("odd");
610 
611 			return ((int) type.Attributes).ToString (CultureInfo.InvariantCulture);
612 		}
613 
MustDocumentMethod(MethodDefinition method)614 		public static bool MustDocumentMethod (MethodDefinition method) {
615 			// All other methods
616 			MethodAttributes maskedAccess = method.Attributes & MethodAttributes.MemberAccessMask;
617 			return maskedAccess == MethodAttributes.Public
618 				|| maskedAccess == MethodAttributes.Family
619 				|| maskedAccess == MethodAttributes.FamORAssem;
620 		}
621 
GetClassType(TypeDefinition t)622 		static string GetClassType (TypeDefinition t)
623 		{
624 			if (t.IsEnum)
625 				return "enum";
626 
627 			if (t.IsValueType)
628 				return "struct";
629 
630 			if (t.IsInterface)
631 				return "interface";
632 
633 			if (TypeHelper.IsDelegate(t))
634 				return "delegate";
635 
636 			if (t.IsPointer)
637 				return "pointer";
638 
639 			return "class";
640 		}
641 
GetCharSet(TypeDefinition type)642 		static string GetCharSet (TypeDefinition type)
643 		{
644 			TypeAttributes maskedStringFormat = type.Attributes & TypeAttributes.StringFormatMask;
645 			if (maskedStringFormat == TypeAttributes.AnsiClass)
646 				return CharSet.Ansi.ToString ();
647 
648 			if (maskedStringFormat == TypeAttributes.AutoClass)
649 				return CharSet.Auto.ToString ();
650 
651 			if (maskedStringFormat == TypeAttributes.UnicodeClass)
652 				return CharSet.Unicode.ToString ();
653 
654 			return CharSet.None.ToString ();
655 		}
656 
GetLayout(TypeDefinition type)657 		static string GetLayout (TypeDefinition type)
658 		{
659 			TypeAttributes maskedLayout = type.Attributes & TypeAttributes.LayoutMask;
660 			if (maskedLayout == TypeAttributes.AutoLayout)
661 				return LayoutKind.Auto.ToString ();
662 
663 			if (maskedLayout == TypeAttributes.ExplicitLayout)
664 				return LayoutKind.Explicit.ToString ();
665 
666 			if (maskedLayout == TypeAttributes.SequentialLayout)
667 				return LayoutKind.Sequential.ToString ();
668 
669 			return null;
670 		}
671 
GetFields(TypeDefinition type)672 		FieldDefinition [] GetFields (TypeDefinition type) {
673 			ArrayList list = new ArrayList ();
674 
675 			var fields = type.Fields;
676 			foreach (FieldDefinition field in fields) {
677 				if (field.IsSpecialName)
678 					continue;
679 
680 				if (Driver.AbiMode && field.IsStatic)
681 					continue;
682 
683 				// we're only interested in public or protected members
684 				FieldAttributes maskedVisibility = (field.Attributes & FieldAttributes.FieldAccessMask);
685 				if (Driver.AbiMode && !field.IsNotSerialized) {
686 					list.Add (field);
687 				} else {
688 					if (maskedVisibility == FieldAttributes.Public
689 						|| maskedVisibility == FieldAttributes.Family
690 						|| maskedVisibility == FieldAttributes.FamORAssem) {
691 						list.Add (field);
692 					}
693 				}
694 			}
695 
696 			return (FieldDefinition []) list.ToArray (typeof (FieldDefinition));
697 		}
698 
699 
GetProperties(TypeDefinition type, bool fullAPI)700 		internal static PropertyDefinition [] GetProperties (TypeDefinition type, bool fullAPI) {
701 			var list = new List<PropertyDefinition> ();
702 
703 			var t = type;
704 			do {
705 				var properties = t.Properties;//type.GetProperties (flags);
706 				foreach (PropertyDefinition property in properties) {
707 					MethodDefinition getMethod = property.GetMethod;
708 					MethodDefinition setMethod = property.SetMethod;
709 
710 					bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod);
711 					bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod);
712 
713 					// if neither the getter or setter should be documented, then
714 					// skip the property
715 					if (hasGetter || hasSetter) {
716 
717 						if (t != type && list.Any (l => l.Name == property.Name))
718 							continue;
719 
720 						list.Add (property);
721 					}
722 				}
723 
724 				if (!fullAPI)
725 					break;
726 
727 				if (t.IsInterface || t.IsEnum)
728 					break;
729 
730 				if (t.BaseType == null || t.BaseType.FullName == "System.Object")
731 					t = null;
732 				else
733 					t = t.BaseType.Resolve ();
734 
735 			} while (t != null);
736 
737 			return list.ToArray ();
738 		}
739 
GetMethods(TypeDefinition type, bool fullAPI)740 		private MethodDefinition[] GetMethods (TypeDefinition type, bool fullAPI)
741 		{
742 			var list = new List<MethodDefinition> ();
743 
744 			var t = type;
745 			do {
746 				var methods = t.Methods;//type.GetMethods (flags);
747 				foreach (MethodDefinition method in methods) {
748 					if (method.IsSpecialName && !method.Name.StartsWith ("op_", StringComparison.Ordinal))
749 						continue;
750 
751 					// we're only interested in public or protected members
752 					if (!MustDocumentMethod (method))
753 						continue;
754 
755 					if (t == type && IsFinalizer (method)) {
756 						string name = method.DeclaringType.Name;
757 						int arity = name.IndexOf ('`');
758 						if (arity > 0)
759 							name = name.Substring (0, arity);
760 
761 						method.Name = "~" + name;
762 					}
763 
764 					if (t != type && list.Any (l => l.DeclaringType != method.DeclaringType && l.Name == method.Name && l.Parameters.Count == method.Parameters.Count &&
765 					                           l.Parameters.SequenceEqual (method.Parameters, new ParameterComparer ())))
766 						continue;
767 
768 					list.Add (method);
769 				}
770 
771 				if (!fullAPI)
772 					break;
773 
774 				if (t.IsInterface || t.IsEnum)
775 					break;
776 
777 				if (t.BaseType == null || t.BaseType.FullName == "System.Object")
778 					t = null;
779 				else
780 					t = t.BaseType.Resolve ();
781 
782 			} while (t != null);
783 
784 			return list.ToArray ();
785 		}
786 
787 		sealed class ParameterComparer : IEqualityComparer<ParameterDefinition>
788 		{
Equals(ParameterDefinition x, ParameterDefinition y)789 			public bool Equals (ParameterDefinition x, ParameterDefinition y)
790 			{
791 				return x.ParameterType.Name == y.ParameterType.Name;
792 			}
793 
GetHashCode(ParameterDefinition obj)794 			public int GetHashCode (ParameterDefinition obj)
795 			{
796 				return obj.ParameterType.Name.GetHashCode ();
797 			}
798 		}
799 
IsFinalizer(MethodDefinition method)800 		static bool IsFinalizer (MethodDefinition method)
801 		{
802 			if (method.Name != "Finalize")
803 				return false;
804 
805 			if (!method.IsVirtual)
806 				return false;
807 
808 			if (method.Parameters.Count != 0)
809 				return false;
810 
811 			return true;
812 		}
813 
GetConstructors(TypeDefinition type)814 		private MethodDefinition [] GetConstructors (TypeDefinition type)
815 		{
816 			ArrayList list = new ArrayList ();
817 
818 			var ctors = type.Methods.Where (m => m.IsConstructor);//type.GetConstructors (flags);
819 			foreach (MethodDefinition constructor in ctors) {
820 				// we're only interested in public or protected members
821 				if (!MustDocumentMethod(constructor))
822 					continue;
823 
824 				list.Add (constructor);
825 			}
826 
827 			return (MethodDefinition []) list.ToArray (typeof (MethodDefinition));
828 		}
829 
GetEvents(TypeDefinition type)830 		private EventDefinition[] GetEvents (TypeDefinition type)
831 		{
832 			ArrayList list = new ArrayList ();
833 
834 			var events = type.Events;//type.GetEvents (flags);
835 			foreach (EventDefinition eventDef in events) {
836 				MethodDefinition addMethod = eventDef.AddMethod;//eventInfo.GetAddMethod (true);
837 
838 				if (addMethod == null || !MustDocumentMethod (addMethod))
839 					continue;
840 
841 				list.Add (eventDef);
842 			}
843 
844 			return (EventDefinition []) list.ToArray (typeof (EventDefinition));
845 		}
846 	}
847 
848 	class FieldData : MemberData
849 	{
FieldData(XmlWriter writer, FieldDefinition [] members)850 		public FieldData (XmlWriter writer, FieldDefinition [] members)
851 			: base (writer, members)
852 		{
853 		}
854 
GetName(MemberReference memberDefenition)855 		protected override string GetName (MemberReference memberDefenition)
856 		{
857 			FieldDefinition field = (FieldDefinition) memberDefenition;
858 			return field.Name;
859 		}
860 
GetMemberAttributes(MemberReference memberDefenition)861 		protected override string GetMemberAttributes (MemberReference memberDefenition)
862 		{
863 			FieldDefinition field = (FieldDefinition) memberDefenition;
864 			return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture);
865 		}
866 
AddExtraAttributes(MemberReference memberDefinition)867 		protected override void AddExtraAttributes (MemberReference memberDefinition)
868 		{
869 			base.AddExtraAttributes (memberDefinition);
870 
871 			FieldDefinition field = (FieldDefinition) memberDefinition;
872 			AddAttribute ("fieldtype", Utils.CleanupTypeName (field.FieldType));
873 
874 			if (field.IsLiteral) {
875 				object value = field.Constant;//object value = field.GetValue (null);
876 				string stringValue = null;
877 				//if (value is Enum) {
878 				//    // FIXME: when Mono bug #60090 has been
879 				//    // fixed, we should just be able to use
880 				//    // Convert.ToString
881 				//    stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture);
882 				//}
883 				//else {
884 				stringValue = Convert.ToString (value, CultureInfo.InvariantCulture);
885 				//}
886 
887 				if (stringValue != null)
888 					AddAttribute ("value", stringValue);
889 			}
890 		}
891 
892 		public override string ParentTag {
893 			get { return "fields"; }
894 		}
895 
896 		public override string Tag {
897 			get { return "field"; }
898 		}
899 	}
900 
901 	class PropertyData : MemberData
902 	{
PropertyData(XmlWriter writer, PropertyDefinition [] members)903 		public PropertyData (XmlWriter writer, PropertyDefinition [] members)
904 			: base (writer, members)
905 		{
906 		}
907 
GetName(MemberReference memberDefenition)908 		protected override string GetName (MemberReference memberDefenition)
909 		{
910 			PropertyDefinition prop = (PropertyDefinition) memberDefenition;
911 			return prop.Name;
912 		}
913 
GetMethods(PropertyDefinition prop, out bool haveParameters)914 		MethodDefinition [] GetMethods (PropertyDefinition prop, out bool haveParameters)
915 		{
916 			MethodDefinition _get = prop.GetMethod;
917 			MethodDefinition _set = prop.SetMethod;
918 			bool haveGet = (_get != null && TypeData.MustDocumentMethod(_get));
919 			bool haveSet = (_set != null && TypeData.MustDocumentMethod(_set));
920 			haveParameters = haveGet || (haveSet && _set.Parameters.Count > 1);
921 			MethodDefinition [] methods;
922 
923 			if (haveGet && haveSet) {
924 				methods = new MethodDefinition [] { _get, _set };
925 			} else if (haveGet) {
926 				methods = new MethodDefinition [] { _get };
927 			} else if (haveSet) {
928 				methods = new MethodDefinition [] { _set };
929 			} else {
930 				//odd
931 				return null;
932 			}
933 
934 			return methods;
935 		}
936 
AddExtraAttributes(MemberReference memberDefinition)937 		protected override void AddExtraAttributes (MemberReference memberDefinition)
938 		{
939 			base.AddExtraAttributes (memberDefinition);
940 
941 			PropertyDefinition prop = (PropertyDefinition) memberDefinition;
942 			AddAttribute ("ptype", Utils.CleanupTypeName (prop.PropertyType));
943 
944 			bool haveParameters;
945 			MethodDefinition [] methods = GetMethods ((PropertyDefinition) memberDefinition, out haveParameters);
946 
947 			if (methods != null && haveParameters) {
948 				string parms = Parameters.GetSignature (methods [0].Parameters);
949 				if (!string.IsNullOrEmpty (parms))
950 					AddAttribute ("params", parms);
951 			}
952 
953 		}
954 
AddExtraData(MemberReference memberDefenition)955 		protected override void AddExtraData (MemberReference memberDefenition)
956 		{
957 			base.AddExtraData (memberDefenition);
958 
959 			bool haveParameters;
960 			MethodDefinition [] methods = GetMethods ((PropertyDefinition) memberDefenition, out haveParameters);
961 
962 			if (methods == null)
963 				return;
964 
965 			MethodData data = new MethodData (writer, methods);
966 			//data.NoMemberAttributes = true;
967 			data.DoOutput ();
968 		}
969 
GetMemberAttributes(MemberReference memberDefenition)970 		protected override string GetMemberAttributes (MemberReference memberDefenition)
971 		{
972 			PropertyDefinition prop = (PropertyDefinition) memberDefenition;
973 			return ((int) prop.Attributes).ToString (CultureInfo.InvariantCulture);
974 		}
975 
976 		public override string ParentTag {
977 			get { return "properties"; }
978 		}
979 
980 		public override string Tag {
981 			get { return "property"; }
982 		}
983 	}
984 
985 	class EventData : MemberData
986 	{
EventData(XmlWriter writer, EventDefinition [] members)987 		public EventData (XmlWriter writer, EventDefinition [] members)
988 			: base (writer, members)
989 		{
990 		}
991 
GetName(MemberReference memberDefenition)992 		protected override string GetName (MemberReference memberDefenition)
993 		{
994 			EventDefinition evt = (EventDefinition) memberDefenition;
995 			return evt.Name;
996 		}
997 
GetMemberAttributes(MemberReference memberDefenition)998 		protected override string GetMemberAttributes (MemberReference memberDefenition)
999 		{
1000 			EventDefinition evt = (EventDefinition) memberDefenition;
1001 			return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture);
1002 		}
1003 
AddExtraAttributes(MemberReference memberDefinition)1004 		protected override void AddExtraAttributes (MemberReference memberDefinition)
1005 		{
1006 			base.AddExtraAttributes (memberDefinition);
1007 
1008 			EventDefinition evt = (EventDefinition) memberDefinition;
1009 			AddAttribute ("eventtype", Utils.CleanupTypeName (evt.EventType));
1010 		}
1011 
1012 		public override string ParentTag {
1013 			get { return "events"; }
1014 		}
1015 
1016 		public override string Tag {
1017 			get { return "event"; }
1018 		}
1019 	}
1020 
1021 	class MethodData : MemberData
1022 	{
1023 		bool noAtts;
1024 
MethodData(XmlWriter writer, MethodDefinition [] members)1025 		public MethodData (XmlWriter writer, MethodDefinition [] members)
1026 			: base (writer, members)
1027 		{
1028 		}
1029 
GetName(MemberReference memberDefenition)1030 		protected override string GetName (MemberReference memberDefenition)
1031 		{
1032 			MethodDefinition method = (MethodDefinition) memberDefenition;
1033 			string name = method.Name;
1034 			string parms = Parameters.GetSignature (method.Parameters);
1035 
1036 			return string.Format ("{0}({1})", name, parms);
1037 		}
1038 
GetMemberAttributes(MemberReference memberDefenition)1039 		protected override string GetMemberAttributes (MemberReference memberDefenition)
1040 		{
1041 			MethodDefinition method = (MethodDefinition) memberDefenition;
1042 			return ((int)( method.Attributes)).ToString (CultureInfo.InvariantCulture);
1043 		}
1044 
GetAdditionalCustomAttributeProvider(MemberReference member)1045 		protected override ICustomAttributeProvider GetAdditionalCustomAttributeProvider (MemberReference member)
1046 		{
1047 			var mbase = (MethodDefinition) member;
1048 			return mbase.MethodReturnType;
1049 		}
1050 
AddExtraAttributes(MemberReference memberDefinition)1051 		protected override void AddExtraAttributes (MemberReference memberDefinition)
1052 		{
1053 			base.AddExtraAttributes (memberDefinition);
1054 
1055 			if (!(memberDefinition is MethodDefinition))
1056 				return;
1057 
1058 			MethodDefinition mbase = (MethodDefinition) memberDefinition;
1059 
1060 			if (mbase.IsAbstract)
1061 				AddAttribute ("abstract", "true");
1062 			if (mbase.IsVirtual)
1063 				AddAttribute ("virtual", "true");
1064 			if (mbase.IsFinal && mbase.IsVirtual && mbase.IsReuseSlot)
1065 				AddAttribute ("sealed", "true");
1066 			if (mbase.IsStatic)
1067 				AddAttribute ("static", "true");
1068 			var baseMethod = TypeHelper.GetBaseMethodInTypeHierarchy (mbase);
1069 			if (baseMethod != null && baseMethod != mbase) {
1070 				// This indicates whether this method is an override of another method.
1071 				// This information is not necessarily available in the api info for any
1072 				// particular assembly, because a method is only overriding another if
1073 				// there is a base virtual function with the same signature, and that
1074 				// base method can come from another assembly.
1075 				AddAttribute ("is-override", "true");
1076 			}
1077 			string rettype = Utils.CleanupTypeName (mbase.MethodReturnType.ReturnType);
1078 			if (rettype != "System.Void" || !mbase.IsConstructor)
1079 				AddAttribute ("returntype", (rettype));
1080 //
1081 //			if (mbase.MethodReturnType.HasCustomAttributes)
1082 //				AttributeData.OutputAttributes (writer, mbase.MethodReturnType);
1083 		}
1084 
AddExtraData(MemberReference memberDefenition)1085 		protected override void AddExtraData (MemberReference memberDefenition)
1086 		{
1087 			base.AddExtraData (memberDefenition);
1088 
1089 			if (!(memberDefenition is MethodDefinition))
1090 				return;
1091 
1092 			MethodDefinition mbase = (MethodDefinition)memberDefenition;
1093 
1094 			ParameterData parms = new ParameterData (writer, mbase.Parameters) {
1095 				HasExtensionParameter = mbase.CustomAttributes.Any (l => l.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
1096 			};
1097 
1098 			parms.DoOutput ();
1099 
1100 			MemberData.OutputGenericParameters (writer, mbase);
1101 		}
1102 
1103 		public override bool NoMemberAttributes {
1104 			get { return noAtts; }
1105 			set { noAtts = value; }
1106 		}
1107 
1108 		public override string ParentTag {
1109 			get { return "methods"; }
1110 		}
1111 
1112 		public override string Tag {
1113 			get { return "method"; }
1114 		}
1115 	}
1116 
1117 	class ConstructorData : MethodData
1118 	{
ConstructorData(XmlWriter writer, MethodDefinition [] members)1119 		public ConstructorData (XmlWriter writer, MethodDefinition [] members)
1120 			: base (writer, members)
1121 		{
1122 		}
1123 
1124 		public override string ParentTag {
1125 			get { return "constructors"; }
1126 		}
1127 
1128 		public override string Tag {
1129 			get { return "constructor"; }
1130 		}
1131 	}
1132 
1133 	class ParameterData : BaseData
1134 	{
1135 		private IList<ParameterDefinition> parameters;
1136 
ParameterData(XmlWriter writer, IList<ParameterDefinition> parameters)1137 		public ParameterData (XmlWriter writer, IList<ParameterDefinition> parameters)
1138 			: base (writer)
1139 		{
1140 			this.parameters = parameters;
1141 		}
1142 
1143 		public bool HasExtensionParameter { get; set; }
1144 
DoOutput()1145 		public override void DoOutput ()
1146 		{
1147 			bool first = true;
1148 			writer.WriteStartElement ("parameters");
1149 			foreach (ParameterDefinition parameter in parameters) {
1150 				writer.WriteStartElement ("parameter");
1151 				AddAttribute ("name", parameter.Name);
1152 				AddAttribute ("position", parameter.Method.Parameters.IndexOf(parameter).ToString(CultureInfo.InvariantCulture));
1153 				AddAttribute ("attrib", ((int) parameter.Attributes).ToString());
1154 
1155 				string direction = first && HasExtensionParameter ? "this" : "in";
1156 				first = false;
1157 
1158 				var pt = parameter.ParameterType;
1159 				var brt = pt as ByReferenceType;
1160 				if (brt != null) {
1161 					direction = parameter.IsOut ? "out" : "ref";
1162 					pt = brt.ElementType;
1163 				}
1164 
1165 				AddAttribute ("type", Utils.CleanupTypeName (pt));
1166 
1167 				if (parameter.IsOptional) {
1168 					AddAttribute ("optional", "true");
1169 					if (parameter.HasConstant)
1170 						AddAttribute ("defaultValue", parameter.Constant == null ? "NULL" : parameter.Constant.ToString ());
1171 				}
1172 
1173 				if (direction != "in")
1174 					AddAttribute ("direction", direction);
1175 
1176 				AttributeData.OutputAttributes (writer, parameter);
1177 				writer.WriteEndElement (); // parameter
1178 			}
1179 			writer.WriteEndElement (); // parameters
1180 		}
1181 	}
1182 
1183 	class AttributeData
1184 	{
DoOutput(XmlWriter writer, IList<ICustomAttributeProvider> providers)1185 		public static void DoOutput (XmlWriter writer, IList<ICustomAttributeProvider> providers)
1186 		{
1187 			if (writer == null)
1188 				throw new InvalidOperationException ("Document not set");
1189 
1190 			if (providers == null || providers.Count == 0)
1191 				return;
1192 
1193 			if (!providers.Any ((provider) => provider != null && provider.HasCustomAttributes))
1194 				return;
1195 
1196 			writer.WriteStartElement ("attributes");
1197 
1198 			foreach (var provider in providers) {
1199 				if (provider == null)
1200 					continue;
1201 
1202 				if (!provider.HasCustomAttributes)
1203 					continue;
1204 
1205 
1206 				var ass = provider as AssemblyDefinition;
1207 				if (ass != null && !Driver.FollowForwarders)
1208 					TypeForwardedToData.OutputForwarders (writer, ass);
1209 
1210 				var attributes = provider.CustomAttributes.
1211 					Where ((att) => !SkipAttribute (att)).
1212 					OrderBy ((a) => a.Constructor.DeclaringType.FullName, StringComparer.Ordinal);
1213 
1214 				foreach (var att in attributes) {
1215 					string attName = Utils.CleanupTypeName (att.Constructor.DeclaringType);
1216 
1217 					writer.WriteStartElement ("attribute");
1218 					writer.WriteAttributeString ("name", attName);
1219 
1220 					var attribute_mapping = CreateAttributeMapping (att);
1221 
1222 					if (attribute_mapping != null) {
1223 						var mapping = attribute_mapping.Where ((attr) => attr.Key != "TypeId");
1224 						if (mapping.Any ()) {
1225 							writer.WriteStartElement ("properties");
1226 							foreach (var kvp in mapping) {
1227 								string name = kvp.Key;
1228 								object o = kvp.Value;
1229 
1230 								writer.WriteStartElement ("property");
1231 								writer.WriteAttributeString ("name", name);
1232 
1233 								if (o == null) {
1234 									writer.WriteAttributeString ("value", "null");
1235 								} else {
1236 									string value = o.ToString ();
1237 									if (attName.EndsWith ("GuidAttribute", StringComparison.Ordinal))
1238 										value = value.ToUpper ();
1239 									writer.WriteAttributeString ("value", value);
1240 								}
1241 
1242 								writer.WriteEndElement (); // property
1243 							}
1244 							writer.WriteEndElement (); // properties
1245 						}
1246 					}
1247 					writer.WriteEndElement (); // attribute
1248 				}
1249 			}
1250 
1251 			writer.WriteEndElement (); // attributes
1252 		}
1253 
CreateAttributeMapping(CustomAttribute attribute)1254 		static Dictionary<string, object> CreateAttributeMapping (CustomAttribute attribute)
1255 		{
1256 			Dictionary<string, object> mapping = null;
1257 
1258 			PopulateMapping (ref mapping, attribute);
1259 
1260 			var constructor = attribute.Constructor.Resolve ();
1261 			if (constructor == null || !constructor.HasParameters)
1262 				return mapping;
1263 
1264 			PopulateMapping (ref mapping, constructor, attribute);
1265 
1266 			return mapping;
1267 		}
1268 
PopulateMapping(ref Dictionary<string, object> mapping, CustomAttribute attribute)1269 		static void PopulateMapping (ref Dictionary<string, object> mapping, CustomAttribute attribute)
1270 		{
1271 			if (!attribute.HasProperties)
1272 				return;
1273 
1274 			foreach (var named_argument in attribute.Properties) {
1275 				var name = named_argument.Name;
1276 				var arg = named_argument.Argument;
1277 
1278 				if (arg.Value is CustomAttributeArgument)
1279 					arg = (CustomAttributeArgument) arg.Value;
1280 
1281 				if (mapping == null)
1282 					mapping = new Dictionary<string, object> (StringComparer.Ordinal);
1283 				mapping.Add (name, GetArgumentValue (arg.Type, arg.Value));
1284 			}
1285 		}
1286 
CreateArgumentFieldMapping(MethodDefinition constructor)1287 		static Dictionary<FieldReference, int> CreateArgumentFieldMapping (MethodDefinition constructor)
1288 		{
1289 			Dictionary<FieldReference, int> field_mapping = null;
1290 
1291 			int? argument = null;
1292 
1293 			foreach (Instruction instruction in constructor.Body.Instructions) {
1294 				switch (instruction.OpCode.Code) {
1295 				case Code.Ldarg_1:
1296 					argument = 1;
1297 					break;
1298 				case Code.Ldarg_2:
1299 					argument = 2;
1300 					break;
1301 				case Code.Ldarg_3:
1302 					argument = 3;
1303 					break;
1304 				case Code.Ldarg:
1305 				case Code.Ldarg_S:
1306 					argument = ((ParameterDefinition) instruction.Operand).Index + 1;
1307 					break;
1308 
1309 				case Code.Stfld:
1310 					FieldReference field = (FieldReference) instruction.Operand;
1311 					if (field.DeclaringType.FullName != constructor.DeclaringType.FullName)
1312 						continue;
1313 
1314 					if (!argument.HasValue)
1315 						break;
1316 
1317 					if (field_mapping == null)
1318 						field_mapping = new Dictionary<FieldReference, int> ();
1319 
1320 					if (!field_mapping.ContainsKey (field))
1321 						field_mapping.Add (field, (int) argument - 1);
1322 
1323 					argument = null;
1324 					break;
1325 				}
1326 			}
1327 
1328 			return field_mapping;
1329 		}
1330 
CreatePropertyFieldMapping(TypeDefinition type)1331 		static Dictionary<PropertyDefinition, FieldReference> CreatePropertyFieldMapping (TypeDefinition type)
1332 		{
1333 			Dictionary<PropertyDefinition, FieldReference> property_mapping = null;
1334 
1335 			foreach (PropertyDefinition property in type.Properties) {
1336 				if (property.GetMethod == null)
1337 					continue;
1338 				if (!property.GetMethod.HasBody)
1339 					continue;
1340 
1341 				foreach (Instruction instruction in property.GetMethod.Body.Instructions) {
1342 					if (instruction.OpCode.Code != Code.Ldfld)
1343 						continue;
1344 
1345 					FieldReference field = (FieldReference) instruction.Operand;
1346 					if (field.DeclaringType.FullName != type.FullName)
1347 						continue;
1348 
1349 					if (property_mapping == null)
1350 						property_mapping = new Dictionary<PropertyDefinition, FieldReference> ();
1351 					property_mapping.Add (property, field);
1352 					break;
1353 				}
1354 			}
1355 
1356 			return property_mapping;
1357 		}
1358 
PopulateMapping(ref Dictionary<string, object> mapping, MethodDefinition constructor, CustomAttribute attribute)1359 		static void PopulateMapping (ref Dictionary<string, object> mapping, MethodDefinition constructor, CustomAttribute attribute)
1360 		{
1361 			if (!constructor.HasBody)
1362 				return;
1363 
1364 			// Custom handling for attributes with arguments which cannot be easily extracted
1365 			var ca = attribute.ConstructorArguments;
1366 			switch (constructor.DeclaringType.FullName) {
1367 			case "System.Runtime.CompilerServices.DecimalConstantAttribute":
1368 				var dca = constructor.Parameters[2].ParameterType == constructor.Module.TypeSystem.Int32 ?
1369 					new DecimalConstantAttribute ((byte) ca[0].Value, (byte) ca[1].Value, (int) ca[2].Value, (int) ca[3].Value, (int) ca[4].Value) :
1370 					new DecimalConstantAttribute ((byte) ca[0].Value, (byte) ca[1].Value, (uint) ca[2].Value, (uint) ca[3].Value, (uint) ca[4].Value);
1371 
1372 				if (mapping == null)
1373 					mapping = new Dictionary<string, object> (StringComparer.Ordinal);
1374 				mapping.Add ("Value", dca.Value);
1375 				return;
1376 			case "System.ComponentModel.BindableAttribute":
1377 				if (ca.Count != 1)
1378 					break;
1379 
1380 				if (mapping == null)
1381 					mapping = new Dictionary<string, object> (StringComparer.Ordinal);
1382 
1383 				if (constructor.Parameters[0].ParameterType == constructor.Module.TypeSystem.Boolean) {
1384 					mapping.Add ("Bindable", ca[0].Value);
1385 				} else if (constructor.Parameters[0].ParameterType.FullName == "System.ComponentModel.BindableSupport") {
1386 					if ((int)ca[0].Value == 0)
1387 						mapping.Add ("Bindable", false);
1388 					else if ((int)ca[0].Value == 1)
1389 						mapping.Add ("Bindable", true);
1390 					else
1391 						throw new NotImplementedException ();
1392 				} else {
1393 					throw new NotImplementedException ();
1394 				}
1395 
1396 				return;
1397 			}
1398 
1399 			var field_mapping = CreateArgumentFieldMapping (constructor);
1400 			if (field_mapping != null) {
1401 				var property_mapping = CreatePropertyFieldMapping ((TypeDefinition) constructor.DeclaringType);
1402 
1403 				if (property_mapping != null) {
1404 					foreach (var pair in property_mapping) {
1405 						int argument;
1406 						if (!field_mapping.TryGetValue (pair.Value, out argument))
1407 							continue;
1408 
1409 						var ca_arg = ca [argument];
1410 						if (ca_arg.Value is CustomAttributeArgument)
1411 							ca_arg = (CustomAttributeArgument)ca_arg.Value;
1412 
1413 						if (mapping == null)
1414 							mapping = new Dictionary<string, object> (StringComparer.Ordinal);
1415 						mapping.Add (pair.Key.Name, GetArgumentValue (ca_arg.Type, ca_arg.Value));
1416 					}
1417 				}
1418 			}
1419 		}
1420 
GetArgumentValue(TypeReference reference, object value)1421 		static object GetArgumentValue (TypeReference reference, object value)
1422 		{
1423 			var type = reference.Resolve ();
1424 			if (type == null)
1425 				return value;
1426 
1427 			if (type.IsEnum) {
1428 				if (IsFlaggedEnum (type))
1429 					return GetFlaggedEnumValue (type, value);
1430 
1431 				return GetEnumValue (type, value);
1432 			}
1433 
1434 			return value;
1435 		}
1436 
IsFlaggedEnum(TypeDefinition type)1437 		static bool IsFlaggedEnum (TypeDefinition type)
1438 		{
1439 			if (!type.IsEnum)
1440 				return false;
1441 
1442 			if (!type.HasCustomAttributes)
1443 				return false;
1444 
1445 			foreach (CustomAttribute attribute in type.CustomAttributes)
1446 				if (attribute.Constructor.DeclaringType.FullName == "System.FlagsAttribute")
1447 					return true;
1448 
1449 			return false;
1450 		}
1451 
GetFlaggedEnumValue(TypeDefinition type, object value)1452 		static object GetFlaggedEnumValue (TypeDefinition type, object value)
1453 		{
1454 			if (value is ulong)
1455 				return GetFlaggedEnumValue (type, (ulong)value);
1456 
1457 			long flags = Convert.ToInt64 (value);
1458 			var signature = new StringBuilder ();
1459 
1460 			for (int i = type.Fields.Count - 1; i >= 0; i--) {
1461 				FieldDefinition field = type.Fields [i];
1462 
1463 				if (!field.HasConstant)
1464 					continue;
1465 
1466 				long flag = Convert.ToInt64 (field.Constant);
1467 
1468 				if (flag == 0)
1469 					continue;
1470 
1471 				if ((flags & flag) == flag) {
1472 					if (signature.Length != 0)
1473 						signature.Append (", ");
1474 
1475 					signature.Append (field.Name);
1476 					flags -= flag;
1477 				}
1478 			}
1479 
1480 			return signature.ToString ();
1481 		}
1482 
GetFlaggedEnumValue(TypeDefinition type, ulong flags)1483 		static object GetFlaggedEnumValue (TypeDefinition type, ulong flags)
1484 		{
1485 			var signature = new StringBuilder ();
1486 
1487 			for (int i = type.Fields.Count - 1; i >= 0; i--) {
1488 				FieldDefinition field = type.Fields [i];
1489 
1490 				if (!field.HasConstant)
1491 					continue;
1492 
1493 				ulong flag = Convert.ToUInt64 (field.Constant);
1494 
1495 				if (flag == 0)
1496 					continue;
1497 
1498 				if ((flags & flag) == flag) {
1499 					if (signature.Length != 0)
1500 						signature.Append (", ");
1501 
1502 					signature.Append (field.Name);
1503 					flags -= flag;
1504 				}
1505 			}
1506 
1507 			return signature.ToString ();
1508 		}
1509 
GetEnumValue(TypeDefinition type, object value)1510 		static object GetEnumValue (TypeDefinition type, object value)
1511 		{
1512 			foreach (FieldDefinition field in type.Fields) {
1513 				if (!field.HasConstant)
1514 					continue;
1515 
1516 				if (Comparer.Default.Compare (field.Constant, value) == 0)
1517 					return field.Name;
1518 			}
1519 
1520 			return value;
1521 		}
1522 
SkipAttribute(CustomAttribute attribute)1523 		static bool SkipAttribute (CustomAttribute attribute)
1524 		{
1525 			if (!TypeHelper.IsPublic (attribute))
1526 				return true;
1527 
1528 			return attribute.Constructor.DeclaringType.Name.EndsWith ("TODOAttribute", StringComparison.Ordinal);
1529 		}
1530 
OutputAttributes(XmlWriter writer, params ICustomAttributeProvider[] providers)1531 		public static void OutputAttributes (XmlWriter writer, params ICustomAttributeProvider[] providers)
1532 		{
1533 			AttributeData.DoOutput (writer, providers);
1534 		}
1535 	}
1536 
1537 	static class Parameters {
1538 
GetSignature(IList<ParameterDefinition> infos)1539 		public static string GetSignature (IList<ParameterDefinition> infos)
1540 		{
1541 			if (infos == null || infos.Count == 0)
1542 				return string.Empty;
1543 
1544 			var signature = new StringBuilder ();
1545 			for (int i = 0; i < infos.Count; i++) {
1546 
1547 				if (i > 0)
1548 					signature.Append (", ");
1549 
1550 				ParameterDefinition info = infos [i];
1551 
1552 				string modifier = string.Empty;
1553 				if (info.ParameterType.IsByReference) {
1554 					if ((info.Attributes & ParameterAttributes.In) != 0)
1555 						modifier = "in";
1556 					else if ((info.Attributes & ParameterAttributes.Out) != 0)
1557 						modifier = "out";
1558 				}
1559 
1560 				if (modifier.Length > 0) {
1561 					signature.Append (modifier);
1562 					signature.Append (" ");
1563 				}
1564 
1565 				signature.Append (Utils.CleanupTypeName (info.ParameterType));
1566 			}
1567 
1568 			return signature.ToString ();
1569 		}
1570 
1571 	}
1572 
1573 	class TypeReferenceComparer : IComparer<TypeReference>
1574 	{
1575 		public static TypeReferenceComparer Default = new TypeReferenceComparer ();
1576 
Compare(TypeReference a, TypeReference b)1577 		public int Compare (TypeReference a, TypeReference b)
1578 		{
1579 			int result = String.Compare (a.Namespace, b.Namespace, StringComparison.Ordinal);
1580 			if (result != 0)
1581 				return result;
1582 
1583 			return String.Compare (a.Name, b.Name, StringComparison.Ordinal);
1584 		}
1585 	}
1586 
1587 	class MemberReferenceComparer : IComparer
1588 	{
1589 		public static MemberReferenceComparer Default = new MemberReferenceComparer ();
1590 
Compare(object a, object b)1591 		public int Compare (object a, object b)
1592 		{
1593 			MemberReference ma = (MemberReference) a;
1594 			MemberReference mb = (MemberReference) b;
1595 			return String.Compare (ma.Name, mb.Name, StringComparison.Ordinal);
1596 		}
1597 	}
1598 
1599 	class PropertyDefinitionComparer : IComparer<PropertyDefinition>
1600 	{
1601 		public static PropertyDefinitionComparer Default = new PropertyDefinitionComparer ();
1602 
Compare(PropertyDefinition ma, PropertyDefinition mb)1603 		public int Compare (PropertyDefinition ma, PropertyDefinition mb)
1604 		{
1605 			int res = String.Compare (ma.Name, mb.Name, StringComparison.Ordinal);
1606 			if (res != 0)
1607 				return res;
1608 
1609 			if (!ma.HasParameters && !mb.HasParameters)
1610 				return 0;
1611 
1612 			if (!ma.HasParameters)
1613 				return -1;
1614 
1615 			if (!mb.HasParameters)
1616 				return 1;
1617 
1618 			return MethodDefinitionComparer.Compare (ma.Parameters, mb.Parameters);
1619 		}
1620 	}
1621 
1622 	class MethodDefinitionComparer : IComparer
1623 	{
1624 		public static MethodDefinitionComparer Default = new MethodDefinitionComparer ();
1625 
Compare(object a, object b)1626 		public int Compare (object a, object b)
1627 		{
1628 			MethodDefinition ma = (MethodDefinition) a;
1629 			MethodDefinition mb = (MethodDefinition) b;
1630 			int res = String.Compare (ma.Name, mb.Name, StringComparison.Ordinal);
1631 			if (res != 0)
1632 				return res;
1633 
1634 			if (!ma.HasParameters && !mb.HasParameters)
1635 				return 0;
1636 
1637 			if (!ma.HasParameters)
1638 				return -1;
1639 
1640 			if (!mb.HasParameters)
1641 				return 1;
1642 
1643 			res = Compare (ma.Parameters, mb.Parameters);
1644 			if (res != 0)
1645 				return res;
1646 
1647 			if (ma.HasGenericParameters != mb.HasGenericParameters)
1648 				return ma.HasGenericParameters ? -1 : 1;
1649 
1650 			if (ma.HasGenericParameters && mb.HasGenericParameters) {
1651 				res = ma.GenericParameters.Count - mb.GenericParameters.Count;
1652 				if (res != 0)
1653 					return res;
1654 			}
1655 
1656 			// operators can differ by only return type
1657 			return string.CompareOrdinal (ma.ReturnType.FullName, mb.ReturnType.FullName);
1658 		}
1659 
Compare(IList<ParameterDefinition> pia, IList<ParameterDefinition> pib)1660 		public static int Compare (IList<ParameterDefinition> pia, IList<ParameterDefinition> pib)
1661 		{
1662 			var res = pia.Count - pib.Count;
1663 			if (res != 0)
1664 				return res;
1665 
1666 			string siga = Parameters.GetSignature (pia);
1667 			string sigb = Parameters.GetSignature (pib);
1668 			return String.Compare (siga, sigb, StringComparison.Ordinal);
1669 		}
1670 	}
1671 }
1672 
1673