df553db5abe7470398c3518866735a875abd8bc3
[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 using System.Text;
32
33 using Mono.Linker;
34 using Mono.Linker.Steps;
35
36 using Mono.Cecil;
37 using Mono.Cecil.Cil;
38
39 namespace Mono.Tuner {
40
41         public class CheckVisibility : BaseStep {
42
43                 bool throw_on_error;
44
45                 protected override void Process ()
46                 {
47                         throw_on_error = GetThrowOnVisibilityErrorParameter ();
48                 }
49
50                 bool GetThrowOnVisibilityErrorParameter ()
51                 {
52                         try {
53                                 return bool.Parse (Context.GetParameter ("throw_on_visibility_error"));
54                         } catch {
55                                 return false;
56                         }
57                 }
58
59                 protected override void ProcessAssembly (AssemblyDefinition assembly)
60                 {
61                         if (assembly.Name.Name == "mscorlib" || assembly.Name.Name == "smcs")
62                                 return;
63
64                         if (Annotations.GetAction (assembly) != AssemblyAction.Link)
65                                 return;
66
67                         Report ("in assembly {0}", assembly.Name);
68
69                         foreach (ModuleDefinition module in assembly.Modules)
70                                 foreach (TypeDefinition type in module.Types)
71                                         CheckType (type);
72                 }
73
74                 void CheckType (TypeDefinition type)
75                 {
76                         if (!IsVisibleFrom (type, type.BaseType)) {
77                                 ReportError ("Base type `{0}` of type `{1}` is not visible",
78                                         type.BaseType, type);
79                         }
80
81                         CheckInterfaces (type);
82
83                         CheckFields (type);
84                         CheckMethods (type);
85                 }
86
87                 void CheckInterfaces (TypeDefinition type)
88                 {
89                         foreach (var iface in type.Interfaces) {
90                                 if (!IsVisibleFrom (type, iface.InterfaceType)) {
91                                         ReportError ("Interface `{0}` implemented by `{1}` is not visible",
92                                                 iface, type);
93                                 }
94                         }
95                 }
96
97                 static bool IsPublic (TypeDefinition type)
98                 {
99                         return (type.DeclaringType == null && type.IsPublic) || type.IsNestedPublic;
100                 }
101
102                 static bool AreInDifferentAssemblies (TypeDefinition type, TypeDefinition target)
103                 {
104                         if (type.Module.Assembly.Name.FullName == target.Module.Assembly.Name.FullName)
105                                 return false;
106
107                         return !IsInternalVisibleTo (target.Module.Assembly, type.Module.Assembly);
108                 }
109
110                 static bool IsInternalVisibleTo (AssemblyDefinition assembly, AssemblyDefinition candidate)
111                 {
112                         foreach (CustomAttribute attribute in assembly.CustomAttributes) {
113                                 if (!IsInternalsVisibleToAttribute (attribute))
114                                         continue;
115
116                                 if (attribute.ConstructorArguments.Count == 0)
117                                         continue;
118
119                                 string signature = (string) attribute.ConstructorArguments [0].Value;
120
121                                 if (InternalsVisibleToSignatureMatch (signature, candidate.Name))
122                                         return true;
123                         }
124
125                         return false;
126                 }
127
128                 static bool InternalsVisibleToSignatureMatch (string signature, AssemblyNameReference reference)
129                 {
130                         int pos = signature.IndexOf (",");
131                         if (pos == -1)
132                                 return signature == reference.Name;
133
134                         string assembly_name = signature.Substring (0, pos);
135
136                         pos = signature.IndexOf ("=");
137                         if (pos == -1)
138                                 throw new ArgumentException ();
139
140                         string public_key = signature.Substring (pos + 1).ToLower ();
141
142                         return assembly_name == reference.Name && public_key == ToPublicKeyString (reference.PublicKey);
143                 }
144
145                 static string ToPublicKeyString (byte [] public_key)
146                 {
147                         StringBuilder signature = new StringBuilder (public_key.Length);
148                         for (int i = 0; i < public_key.Length; i++)
149                                 signature.Append (public_key [i].ToString ("x2"));
150
151                         return signature.ToString ();
152                 }
153
154                 static bool IsInternalsVisibleToAttribute (CustomAttribute attribute)
155                 {
156                         return attribute.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.InternalsVisibleToAttribute";
157                 }
158
159                 bool IsVisibleFrom (TypeDefinition type, TypeReference reference)
160                 {
161                         if (reference == null)
162                                 return true;
163
164                         if (reference is GenericParameter || reference.GetElementType () is GenericParameter)
165                                 return true;
166
167                         TypeDefinition other = reference.Resolve ();
168                         if (other == null)
169                                 return true;
170
171                         if (!AreInDifferentAssemblies (type, other))
172                                 return true;
173
174                         if (IsPublic (other))
175                                 return true;
176
177                         return false;
178                 }
179
180                 bool IsVisibleFrom (TypeDefinition type, MethodReference reference)
181                 {
182                         if (reference == null)
183                                 return true;
184
185                         MethodDefinition meth = reference.Resolve ();
186                         if (meth == null)
187                                 return true;
188
189                         TypeDefinition dec = (TypeDefinition) meth.DeclaringType;
190                         if (!IsVisibleFrom (type, dec))
191                                 return false;
192
193                         if (meth.IsPublic)
194                                 return true;
195
196                         if (type == dec || IsNestedIn (type, dec))
197                                 return true;
198
199                         if (meth.IsFamily && InHierarchy (type, dec))
200                                 return true;
201
202                         if (meth.IsFamilyOrAssembly && (!AreInDifferentAssemblies (type, dec) || InHierarchy (type, dec)))
203                                 return true;
204
205                         if (meth.IsFamilyAndAssembly && (!AreInDifferentAssemblies (type, dec) && InHierarchy (type, dec)))
206                                 return true;
207
208                         if (!AreInDifferentAssemblies (type, dec) && meth.IsAssembly)
209                                 return true;
210
211                         return false;
212                 }
213
214                 bool IsVisibleFrom (TypeDefinition type, FieldReference reference)
215                 {
216                         if (reference == null)
217                                 return true;
218
219                         FieldDefinition field = reference.Resolve ();
220                         if (field == null)
221                                 return true;
222
223                         TypeDefinition dec = (TypeDefinition) field.DeclaringType;
224                         if (!IsVisibleFrom (type, dec))
225                                 return false;
226
227                         if (field.IsPublic)
228                                 return true;
229
230                         if (type == dec || IsNestedIn (type, dec))
231                                 return true;
232
233                         if (field.IsFamily && InHierarchy (type, dec))
234                                 return true;
235
236                         if (field.IsFamilyOrAssembly && (!AreInDifferentAssemblies (type, dec) || InHierarchy (type, dec)))
237                                 return true;
238
239                         if (field.IsFamilyAndAssembly && (!AreInDifferentAssemblies (type, dec) && InHierarchy (type, dec)))
240                                 return true;
241
242                         if (!AreInDifferentAssemblies (type, dec) && field.IsAssembly)
243                                 return true;
244
245                         return false;
246                 }
247
248                 static bool IsNestedIn (TypeDefinition type, TypeDefinition other)
249                 {
250                         TypeDefinition declaring = type.DeclaringType;
251
252                         if (declaring == null)
253                                 return false;
254
255                         if (declaring == other)
256                                 return true;
257
258                         if (declaring.DeclaringType == null)
259                                 return false;
260
261                         return IsNestedIn (declaring, other);
262                 }
263
264                 static bool InHierarchy (TypeDefinition type, TypeDefinition other)
265                 {
266                         if (type.BaseType == null)
267                                 return false;
268
269                         TypeDefinition baseType = type.BaseType.Resolve ();
270
271                         if (baseType == other)
272                                 return true;
273
274                         return InHierarchy (baseType, other);
275                 }
276
277                 static void Report (string pattern, params object [] parameters)
278                 {
279                         Console.WriteLine ("[check] " + pattern, parameters);
280                 }
281
282                 void ReportError (string pattern, params object [] parameters)
283                 {
284                         Report (pattern, parameters);
285
286                         if (throw_on_error)
287                                 throw new VisibilityErrorException (string.Format (pattern, parameters));
288                 }
289
290                 void CheckFields (TypeDefinition type)
291                 {
292                         foreach (FieldDefinition field in type.Fields) {
293                                 if (!IsVisibleFrom (type, field.FieldType)) {
294                                         ReportError ("Field `{0}` of type `{1}` is not visible from `{2}`",
295                                                 field.Name, field.FieldType, type);
296                                 }
297                         }
298                 }
299
300                 void CheckMethods (TypeDefinition type)
301                 {
302                         CheckMethods (type, type.Methods);
303                 }
304
305                 void CheckMethods (TypeDefinition type, ICollection methods)
306                 {
307                         foreach (MethodDefinition method in methods) {
308                                 if (!IsVisibleFrom (type, method.ReturnType)) {
309                                         ReportError ("Method return type `{0}` in method `{1}` is not visible",
310                                                 method.ReturnType, method);
311                                 }
312
313                                 foreach (ParameterDefinition parameter in method.Parameters) {
314                                         if (!IsVisibleFrom (type, parameter.ParameterType)) {
315                                                 ReportError ("Parameter `{0}` of type `{1}` in method `{2}` is not visible.",
316                                                         parameter.Index, parameter.ParameterType, method);
317                                         }
318                                 }
319
320                                 if (method.HasBody)
321                                         CheckBody (method);
322                         }
323                 }
324
325                 void CheckBody (MethodDefinition method)
326                 {
327                         TypeDefinition type = (TypeDefinition) method.DeclaringType;
328
329                         foreach (VariableDefinition variable in method.Body.Variables) {
330                                 if (!IsVisibleFrom ((TypeDefinition) method.DeclaringType, variable.VariableType)) {
331                                         ReportError ("Variable `{0}` of type `{1}` from method `{2}` is not visible",
332                                                 variable.Index, variable.VariableType, method);
333                                 }
334                         }
335
336                         foreach (Instruction instr in method.Body.Instructions) {
337                                 switch (instr.OpCode.OperandType) {
338                                 case OperandType.InlineType:
339                                 case OperandType.InlineMethod:
340                                 case OperandType.InlineField:
341                                 case OperandType.InlineTok:
342                                         bool error = false;
343                                         TypeReference type_ref = instr.Operand as TypeReference;
344                                         if (type_ref != null)
345                                                 error = !IsVisibleFrom (type, type_ref);
346
347                                         MethodReference meth_ref = instr.Operand as MethodReference;
348                                         if (meth_ref != null)
349                                                 error = !IsVisibleFrom (type, meth_ref);
350
351                                         FieldReference field_ref = instr.Operand as FieldReference;
352                                         if (field_ref != null)
353                                                 error = !IsVisibleFrom (type, field_ref);
354
355                                         if (error) {
356                                                 ReportError ("Operand `{0}` of type {1} at offset 0x{2} in method `{3}` is not visible",
357                                                         instr.Operand, instr.OpCode.OperandType, instr.Offset.ToString ("x4"), method);
358                                         }
359
360                                         break;
361                                 default:
362                                         continue;
363                                 }
364                         }
365                 }
366
367                 class VisibilityErrorException : Exception {
368
369                         public VisibilityErrorException (string message)
370                                 : base (message)
371                         {
372                         }
373                 }
374         }
375 }