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