5 // Jb Evain (jbevain@novell.com)
7 // (C) 2007 Novell, Inc.
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
30 using System.Collections;
34 using Mono.Linker.Steps;
39 namespace Mono.Tuner {
41 public class CheckVisibility : BaseStep {
45 protected override void Process ()
47 throw_on_error = GetThrowOnVisibilityErrorParameter ();
50 bool GetThrowOnVisibilityErrorParameter ()
53 return bool.Parse (Context.GetParameter ("throw_on_visibility_error"));
59 protected override void ProcessAssembly (AssemblyDefinition assembly)
61 if (assembly.Name.Name == "mscorlib" || assembly.Name.Name == "smcs")
64 if (Annotations.GetAction (assembly) != AssemblyAction.Link)
67 Report ("in assembly {0}", assembly.Name);
69 foreach (ModuleDefinition module in assembly.Modules)
70 foreach (TypeDefinition type in module.Types)
74 void CheckType (TypeDefinition type)
76 if (!IsVisibleFrom (type, type.BaseType)) {
77 ReportError ("Base type `{0}` of type `{1}` is not visible",
81 CheckInterfaces (type);
87 void CheckInterfaces (TypeDefinition type)
89 foreach (var iface in type.Interfaces) {
90 if (!IsVisibleFrom (type, iface.InterfaceType)) {
91 ReportError ("Interface `{0}` implemented by `{1}` is not visible",
97 static bool IsPublic (TypeDefinition type)
99 return (type.DeclaringType == null && type.IsPublic) || type.IsNestedPublic;
102 static bool AreInDifferentAssemblies (TypeDefinition type, TypeDefinition target)
104 if (type.Module.Assembly.Name.FullName == target.Module.Assembly.Name.FullName)
107 return !IsInternalVisibleTo (target.Module.Assembly, type.Module.Assembly);
110 static bool IsInternalVisibleTo (AssemblyDefinition assembly, AssemblyDefinition candidate)
112 foreach (CustomAttribute attribute in assembly.CustomAttributes) {
113 if (!IsInternalsVisibleToAttribute (attribute))
116 if (attribute.ConstructorArguments.Count == 0)
119 string signature = (string) attribute.ConstructorArguments [0].Value;
121 if (InternalsVisibleToSignatureMatch (signature, candidate.Name))
128 static bool InternalsVisibleToSignatureMatch (string signature, AssemblyNameReference reference)
130 int pos = signature.IndexOf (",");
132 return signature == reference.Name;
134 string assembly_name = signature.Substring (0, pos);
136 pos = signature.IndexOf ("=");
138 throw new ArgumentException ();
140 string public_key = signature.Substring (pos + 1).ToLower ();
142 return assembly_name == reference.Name && public_key == ToPublicKeyString (reference.PublicKey);
145 static string ToPublicKeyString (byte [] public_key)
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"));
151 return signature.ToString ();
154 static bool IsInternalsVisibleToAttribute (CustomAttribute attribute)
156 return attribute.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.InternalsVisibleToAttribute";
159 bool IsVisibleFrom (TypeDefinition type, TypeReference reference)
161 if (reference == null)
164 if (reference is GenericParameter || reference.GetElementType () is GenericParameter)
167 TypeDefinition other = reference.Resolve ();
171 if (!AreInDifferentAssemblies (type, other))
174 if (IsPublic (other))
180 bool IsVisibleFrom (TypeDefinition type, MethodReference reference)
182 if (reference == null)
185 MethodDefinition meth = reference.Resolve ();
189 TypeDefinition dec = (TypeDefinition) meth.DeclaringType;
190 if (!IsVisibleFrom (type, dec))
196 if (type == dec || IsNestedIn (type, dec))
199 if (meth.IsFamily && InHierarchy (type, dec))
202 if (meth.IsFamilyOrAssembly && (!AreInDifferentAssemblies (type, dec) || InHierarchy (type, dec)))
205 if (meth.IsFamilyAndAssembly && (!AreInDifferentAssemblies (type, dec) && InHierarchy (type, dec)))
208 if (!AreInDifferentAssemblies (type, dec) && meth.IsAssembly)
214 bool IsVisibleFrom (TypeDefinition type, FieldReference reference)
216 if (reference == null)
219 FieldDefinition field = reference.Resolve ();
223 TypeDefinition dec = (TypeDefinition) field.DeclaringType;
224 if (!IsVisibleFrom (type, dec))
230 if (type == dec || IsNestedIn (type, dec))
233 if (field.IsFamily && InHierarchy (type, dec))
236 if (field.IsFamilyOrAssembly && (!AreInDifferentAssemblies (type, dec) || InHierarchy (type, dec)))
239 if (field.IsFamilyAndAssembly && (!AreInDifferentAssemblies (type, dec) && InHierarchy (type, dec)))
242 if (!AreInDifferentAssemblies (type, dec) && field.IsAssembly)
248 static bool IsNestedIn (TypeDefinition type, TypeDefinition other)
250 TypeDefinition declaring = type.DeclaringType;
252 if (declaring == null)
255 if (declaring == other)
258 if (declaring.DeclaringType == null)
261 return IsNestedIn (declaring, other);
264 static bool InHierarchy (TypeDefinition type, TypeDefinition other)
266 if (type.BaseType == null)
269 TypeDefinition baseType = type.BaseType.Resolve ();
271 if (baseType == other)
274 return InHierarchy (baseType, other);
277 static void Report (string pattern, params object [] parameters)
279 Console.WriteLine ("[check] " + pattern, parameters);
282 void ReportError (string pattern, params object [] parameters)
284 Report (pattern, parameters);
287 throw new VisibilityErrorException (string.Format (pattern, parameters));
290 void CheckFields (TypeDefinition type)
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);
300 void CheckMethods (TypeDefinition type)
302 CheckMethods (type, type.Methods);
305 void CheckMethods (TypeDefinition type, ICollection methods)
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);
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);
325 void CheckBody (MethodDefinition method)
327 TypeDefinition type = (TypeDefinition) method.DeclaringType;
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);
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:
343 TypeReference type_ref = instr.Operand as TypeReference;
344 if (type_ref != null)
345 error = !IsVisibleFrom (type, type_ref);
347 MethodReference meth_ref = instr.Operand as MethodReference;
348 if (meth_ref != null)
349 error = !IsVisibleFrom (type, meth_ref);
351 FieldReference field_ref = instr.Operand as FieldReference;
352 if (field_ref != null)
353 error = !IsVisibleFrom (type, field_ref);
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);
367 class VisibilityErrorException : Exception {
369 public VisibilityErrorException (string message)