update MEF to preview 9
[mcs.git] / tools / mdoc / Mono.Documentation / exceptions.cs
blobad1ba9d76ae832a258c585cd8964d6e9761301b0
1 //
2 // Mono.Documentation/exceptions.cs
3 //
4 // Authors:
5 // Jonathan Pryor (jonpryor@vt.edu)
6 //
7 // (C) 2008 Novell, Inc.
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 using System;
29 using System.Collections.Generic;
30 using System.Collections.ObjectModel;
31 using System.Linq;
33 using Mono.Cecil;
34 using Mono.Cecil.Cil;
36 namespace Mono.Documentation {
38 [Flags]
39 public enum ExceptionLocations {
40 Member = 0x0,
41 Assembly = 0x1,
42 DependentAssemblies = 0x2,
43 AddedMembers = 0x4,
46 public class ExceptionSources {
47 internal ExceptionSources (TypeReference exception)
49 Exception = exception;
50 SourcesList = new List<IMemberReference> ();
51 Sources = new ReadOnlyCollection<IMemberReference> (SourcesList);
54 public TypeReference Exception { get; private set; }
55 public ReadOnlyCollection<IMemberReference> Sources { get; private set; }
56 internal List<IMemberReference> SourcesList;
60 public class ExceptionLookup {
62 SlashDocMemberFormatter xdoc = new SlashDocMemberFormatter ();
64 // xdoc(MemberRef) -> xdoc(TypeRef) -> ExceptionSource
65 // where ExceptionSource.Exception == xdoc(TypeRef)
66 Dictionary<string, Dictionary<string, ExceptionSources>> db = new Dictionary<string, Dictionary<string, ExceptionSources>> ();
68 ExceptionLocations locations;
70 public ExceptionLookup (ExceptionLocations locations)
72 this.locations = locations;
75 public IEnumerable<ExceptionSources> this [IMemberReference member] {
76 get {
77 if (member == null)
78 throw new ArgumentNullException ("member");
79 IMemberReference memberDef = member.Resolve ();
80 if (memberDef == null) {
81 ArrayType array = member.DeclaringType as ArrayType;
82 if (array != null && array.Rank > 1) {
83 // Multi-dimensional array; the member is runtime generated,
84 // doesn't "really" exist (in a form that we can resolve),
85 // so we can't do anything further.
86 return new ExceptionSources[0];
88 throw new NotSupportedException (string.Format (
89 "Unable to resolve member {0}::{1}.",
90 member.DeclaringType.FullName, member.Name));
92 string memberDecl = xdoc.GetDeclaration (member.Resolve ());
93 Dictionary<string, ExceptionSources> e;
94 if (!db.TryGetValue (memberDecl, out e)) {
95 e = new Dictionary<string, ExceptionSources> ();
96 var bodies = GetMethodBodies (member);
97 foreach (var body in bodies) {
98 if (body == null)
99 continue;
100 FillExceptions (body, e);
102 db.Add (memberDecl, e);
104 return e.Values;
108 MethodBody[] GetMethodBodies (IMemberReference member)
110 if (member is MethodReference) {
111 return new[]{ (((MethodReference) member).Resolve ()).Body };
113 if (member is PropertyReference) {
114 PropertyDefinition prop = ((PropertyReference) member).Resolve ();
115 return new[]{
116 prop.GetMethod != null ? prop.GetMethod.Body : null,
117 prop.SetMethod != null ? prop.SetMethod.Body : null,
120 if (member is FieldReference)
121 return new MethodBody[]{};
122 if (member is EventReference) {
123 EventDefinition ev = ((EventReference) member).Resolve ();
124 return new[]{
125 ev.AddMethod != null ? ev.AddMethod.Body : null,
126 ev.InvokeMethod != null ? ev.InvokeMethod.Body : null,
127 ev.RemoveMethod != null ? ev.RemoveMethod.Body : null,
130 throw new NotSupportedException ("Unsupported member type: " + member.GetType().FullName);
133 void FillExceptions (MethodBody body, Dictionary<string, ExceptionSources> exceptions)
135 for (int i = 0; i < body.Instructions.Count; ++i) {
136 Instruction instruction = body.Instructions [i];
137 switch (instruction.OpCode.Code) {
138 case Code.Call:
139 case Code.Callvirt: {
140 if ((locations & ExceptionLocations.Assembly) == 0 &&
141 (locations & ExceptionLocations.DependentAssemblies) == 0)
142 break;
143 IMemberReference memberRef = ((IMemberReference) instruction.Operand);
144 if (((locations & ExceptionLocations.Assembly) != 0 &&
145 body.Method.DeclaringType.Scope.Name == memberRef.DeclaringType.Scope.Name) ||
146 ((locations & ExceptionLocations.DependentAssemblies) != 0 &&
147 body.Method.DeclaringType.Scope.Name != memberRef.DeclaringType.Scope.Name)) {
148 IEnumerable<ExceptionSources> memberExceptions = this [memberRef];
149 AddExceptions (body, instruction,
150 memberExceptions.Select (es => es.Exception),
151 memberExceptions.SelectMany (es => es.Sources),
152 exceptions);
154 break;
156 case Code.Newobj: {
157 MethodReference ctor = (MethodReference) instruction.Operand;
158 if (IsExceptionConstructor (ctor)) {
159 AddExceptions (body, instruction,
160 new TypeReference[]{ctor.DeclaringType},
161 new IMemberReference[]{body.Method},
162 exceptions);
164 break;
170 void AddExceptions (MethodBody body, Instruction instruction, IEnumerable<TypeReference> add, IEnumerable<IMemberReference> sources,
171 Dictionary<string, ExceptionSources> exceptions)
173 var handlers = body.ExceptionHandlers.Cast<ExceptionHandler> ()
174 .Where (eh => instruction.Offset >= eh.TryStart.Offset &&
175 instruction.Offset <= eh.TryEnd.Offset);
176 foreach (var ex in add) {
177 if (!handlers.Any (h => IsExceptionCaught (ex, h.CatchType))) {
178 ExceptionSources s;
179 string eName = xdoc.GetDeclaration (ex);
180 if (!exceptions.TryGetValue (eName, out s)) {
181 s = new ExceptionSources (ex);
182 exceptions.Add (eName, s);
184 s.SourcesList.AddRange (sources);
189 static bool IsExceptionConstructor (MethodReference ctor)
191 return GetBases (ctor.DeclaringType)
192 .Any (t => t.FullName == "System.Exception");
195 bool IsExceptionCaught (TypeReference exception, TypeReference catcher)
197 return GetBases (exception).Select (e => xdoc.GetDeclaration (e))
198 .Union (GetBases (catcher).Select (e => xdoc.GetDeclaration (e)))
199 .Any ();
202 static IEnumerable<TypeReference> GetBases (TypeReference type)
204 yield return type;
205 TypeDefinition def = type.Resolve ();
206 while (def != null && def.BaseType != null) {
207 yield return def.BaseType;
208 def = def.BaseType.Resolve ();