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