importing messaging-2008 branch to trunk.
[mono.git] / mcs / tools / tuner / Mono.Tuner / CheckVisibility.cs
1 //
2 // CheckVisibility.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //
7 // (C) 2007 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.
27 //
28
29 using System;
30 using System.Collections;
31
32 using Mono.Linker;
33 using Mono.Linker.Steps;
34
35 using Mono.Cecil;
36 using Mono.Cecil.Cil;
37
38 namespace Mono.Tuner {
39
40         public class CheckVisibility : BaseStep {
41
42                 protected override void ProcessAssembly (AssemblyDefinition assembly)
43                 {
44                         if (assembly.Name.Name == "mscorlib" || assembly.Name.Name == "smcs")
45                                 return;
46
47                         if (Annotations.GetAction (assembly) != AssemblyAction.Link)
48                                 return;
49
50                         Report ("in assembly {0}", assembly.Name);
51
52                         foreach (ModuleDefinition module in assembly.Modules)
53                                 foreach (TypeDefinition type in module.Types)
54                                         CheckType (type);
55                 }
56
57                 void CheckType (TypeDefinition type)
58                 {
59                         if (!IsVisibleFrom (type, type.BaseType)) {
60                                 Report ("Base type `{0}` of type `{1}` is not visible",
61                                         type.BaseType, type);
62                         }
63
64                         CheckInterfaces (type);
65
66                         CheckFields (type);
67                         CheckConstructors (type);
68                         CheckMethods (type);
69                 }
70
71                 void CheckInterfaces (TypeDefinition type)
72                 {
73                         foreach (TypeReference iface in type.Interfaces) {
74                                 if (!IsVisibleFrom (type, iface)) {
75                                         Report ("Interface `{0}` implemented by `{1}` is not visible",
76                                                 iface, type);
77                                 }
78                         }
79                 }
80
81                 static bool IsPublic (TypeDefinition type)
82                 {
83                         return (type.DeclaringType == null && type.IsPublic) || type.IsNestedPublic;
84                 }
85
86                 static bool AreInDifferentAssemblies (TypeDefinition lhs, TypeDefinition rhs)
87                 {
88                         return lhs.Module.Assembly.Name.FullName != rhs.Module.Assembly.Name.FullName;
89                 }
90
91                 bool IsVisibleFrom (TypeDefinition type, TypeReference reference)
92                 {
93                         if (reference == null)
94                                 return true;
95
96                         if (reference is GenericParameter || reference.GetOriginalType () is GenericParameter)
97                                 return true;
98
99                         TypeDefinition other = Context.Resolver.Resolve (reference);
100                         if (other == null)
101                                 return true;
102
103                         if (!AreInDifferentAssemblies (type, other))
104                                 return true;
105
106                         if (IsPublic (other))
107                                 return true;
108
109                         return false;
110                 }
111
112                 bool IsVisibleFrom (TypeDefinition type, MethodReference reference)
113                 {
114                         if (reference == null)
115                                 return true;
116
117                         MethodDefinition meth = null;
118                         try {
119                                 meth = Context.Resolver.Resolve (reference);
120                         } catch (ResolutionException) {}
121
122                         if (meth == null)
123                                 return true;
124
125                         TypeDefinition dec = (TypeDefinition) meth.DeclaringType;
126                         if (!IsVisibleFrom (type, dec))
127                                 return false;
128
129                         if (meth.IsPublic)
130                                 return true;
131
132                         if (type == dec || type.DeclaringType == dec)
133                                 return true;
134
135                         if (meth.IsFamily && InHierarchy (type, dec))
136                                 return true;
137
138                         if (meth.IsFamilyOrAssembly && (!AreInDifferentAssemblies (type, dec) || InHierarchy (type, dec)))
139                                 return true;
140
141                         if (!AreInDifferentAssemblies (type, dec) && meth.IsAssembly)
142                                 return true;
143
144                         return false;
145                 }
146
147                 bool IsVisibleFrom (TypeDefinition type, FieldReference reference)
148                 {
149                         if (reference == null)
150                                 return true;
151
152                         FieldDefinition field = null;
153                         try {
154                                 field = Context.Resolver.Resolve (reference);
155                         } catch (ResolutionException) {}
156
157                         if (field == null)
158                                 return true;
159
160                         TypeDefinition dec = (TypeDefinition) field.DeclaringType;
161                         if (!IsVisibleFrom (type, dec))
162                                 return false;
163
164                         if (field.IsPublic)
165                                 return true;
166
167                         if (type == dec || type.DeclaringType == dec)
168                                 return true;
169
170                         if (field.IsFamily && InHierarchy (type, dec))
171                                 return true;
172
173                         if (field.IsFamilyOrAssembly && (!AreInDifferentAssemblies (type, dec) || InHierarchy (type, dec)))
174                                 return true;
175
176                         if (!AreInDifferentAssemblies (type, dec) && field.IsAssembly)
177                                 return true;
178
179                         return false;
180                 }
181
182                 bool InHierarchy (TypeDefinition type, TypeDefinition other)
183                 {
184                         if (type.BaseType == null)
185                                 return false;
186
187                         TypeDefinition baseType = Context.Resolver.Resolve (type.BaseType);
188
189                         if (baseType == other)
190                                 return true;
191
192                         return InHierarchy (baseType, other);
193                 }
194
195                 static void Report (string pattern, params object [] parameters)
196                 {
197                         Console.WriteLine ("[check] " + pattern, parameters);
198                 }
199
200                 void CheckFields (TypeDefinition type)
201                 {
202                         foreach (FieldDefinition field in type.Fields) {
203                                 if (!IsVisibleFrom (type, field.FieldType)) {
204                                         Report ("Field `{0}` of type `{1}` is not visible from `{2}`",
205                                                 field.Name, field.FieldType, type);
206                                 }
207                         }
208                 }
209
210                 void CheckConstructors (TypeDefinition type)
211                 {
212                         CheckMethods (type, type.Constructors);
213                 }
214
215                 void CheckMethods (TypeDefinition type)
216                 {
217                         CheckMethods (type, type.Methods);
218                 }
219
220                 void CheckMethods (TypeDefinition type, ICollection methods)
221                 {
222                         foreach (MethodDefinition method in methods) {
223                                 if (!IsVisibleFrom (type, method.ReturnType.ReturnType)) {
224                                         Report ("Method return type `{0}` in method `{1}` is not visible",
225                                                 method.ReturnType.ReturnType, method);
226                                 }
227
228                                 foreach (ParameterDefinition parameter in method.Parameters) {
229                                         if (!IsVisibleFrom (type, parameter.ParameterType)) {
230                                                 Report ("Parameter `{0}` of type `{1}` in method `{2}` is not visible.",
231                                                         parameter.Sequence, parameter.ParameterType, method);
232                                         }
233                                 }
234
235                                 if (method.HasBody)
236                                         CheckBody (method);
237                         }
238                 }
239
240                 void CheckBody (MethodDefinition method)
241                 {
242                         TypeDefinition type = (TypeDefinition) method.DeclaringType;
243
244                         foreach (VariableDefinition variable in method.Body.Variables) {
245                                 if (!IsVisibleFrom ((TypeDefinition) method.DeclaringType, variable.VariableType)) {
246                                         Report ("Variable `{0}` of type `{1}` from method `{2}` is not visible",
247                                                 variable.Index, variable.VariableType, method);
248                                 }
249                         }
250
251                         foreach (Instruction instr in method.Body.Instructions) {
252                                 switch (instr.OpCode.OperandType) {
253                                 case OperandType.InlineType:
254                                 case OperandType.InlineMethod:
255                                 case OperandType.InlineField:
256                                 case OperandType.InlineTok:
257                                         bool error = false;
258                                         TypeReference type_ref = instr.Operand as TypeReference;
259                                         if (type_ref != null)
260                                                 error = !IsVisibleFrom (type, type_ref);
261
262                                         MethodReference meth_ref = instr.Operand as MethodReference;
263                                         if (meth_ref != null)
264                                                 error = !IsVisibleFrom (type, meth_ref);
265
266                                         FieldReference field_ref = instr.Operand as FieldReference;
267                                         if (field_ref != null)
268                                                 error = !IsVisibleFrom (type, field_ref);
269
270                                         if (error) {
271                                                 Report ("Operand `{0}` of type {1} at offset 0x{2} in method `{3}` is not visible",
272                                                         instr.Operand, instr.OpCode.OperandType, instr.Offset.ToString ("x4"), method);
273                                         }
274
275                                         break;
276                                 default:
277                                         continue;
278                                 }
279                         }
280                 }
281         }
282 }