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);
84 CheckConstructors (type);
88 void CheckInterfaces (TypeDefinition type)
90 foreach (TypeReference iface in type.Interfaces) {
91 if (!IsVisibleFrom (type, iface)) {
92 ReportError ("Interface `{0}` implemented by `{1}` is not visible",
98 static bool IsPublic (TypeDefinition type)
100 return (type.DeclaringType == null && type.IsPublic) || type.IsNestedPublic;
103 static bool AreInDifferentAssemblies (TypeDefinition type, TypeDefinition target)
105 if (type.Module.Assembly.Name.FullName == target.Module.Assembly.Name.FullName)
108 return !IsInternalVisibleTo (target.Module.Assembly, type.Module.Assembly);
111 static bool IsInternalVisibleTo (AssemblyDefinition assembly, AssemblyDefinition candidate)
113 foreach (CustomAttribute attribute in assembly.CustomAttributes) {
114 if (!IsInternalsVisibleToAttribute (attribute))
117 if (attribute.ConstructorParameters.Count == 0)
120 string signature = (string) attribute.ConstructorParameters [0];
122 if (InternalsVisibleToSignatureMatch (signature, candidate.Name))
129 static bool InternalsVisibleToSignatureMatch (string signature, AssemblyNameReference reference)
131 int pos = signature.IndexOf (",");
133 return signature == reference.Name;
135 string assembly_name = signature.Substring (0, pos);
137 pos = signature.IndexOf ("=");
139 throw new ArgumentException ();
141 string public_key = signature.Substring (pos + 1).ToLower ();
143 return assembly_name == reference.Name && public_key == ToPublicKeyString (reference.PublicKey);
146 static string ToPublicKeyString (byte [] public_key)
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"));
152 return signature.ToString ();
155 static bool IsInternalsVisibleToAttribute (CustomAttribute attribute)
157 return attribute.Constructor.DeclaringType.FullName == "System.Runtime.CompilerServices.InternalsVisibleToAttribute";
160 bool IsVisibleFrom (TypeDefinition type, TypeReference reference)
162 if (reference == null)
165 if (reference is GenericParameter || reference.GetOriginalType () is GenericParameter)
168 TypeDefinition other = reference.Resolve ();
172 if (!AreInDifferentAssemblies (type, other))
175 if (IsPublic (other))
181 bool IsVisibleFrom (TypeDefinition type, MethodReference reference)
183 if (reference == null)
186 MethodDefinition meth = reference.Resolve ();
190 TypeDefinition dec = (TypeDefinition) meth.DeclaringType;
191 if (!IsVisibleFrom (type, dec))
197 if (type == dec || IsNestedIn (type, dec))
200 if (meth.IsFamily && InHierarchy (type, dec))
203 if (meth.IsFamilyOrAssembly && (!AreInDifferentAssemblies (type, dec) || InHierarchy (type, dec)))
206 if (meth.IsFamilyAndAssembly && (!AreInDifferentAssemblies (type, dec) && InHierarchy (type, dec)))
209 if (!AreInDifferentAssemblies (type, dec) && meth.IsAssembly)
215 bool IsVisibleFrom (TypeDefinition type, FieldReference reference)
217 if (reference == null)
220 FieldDefinition field = reference.Resolve ();
224 TypeDefinition dec = (TypeDefinition) field.DeclaringType;
225 if (!IsVisibleFrom (type, dec))
231 if (type == dec || IsNestedIn (type, dec))
234 if (field.IsFamily && InHierarchy (type, dec))
237 if (field.IsFamilyOrAssembly && (!AreInDifferentAssemblies (type, dec) || InHierarchy (type, dec)))
240 if (field.IsFamilyAndAssembly && (!AreInDifferentAssemblies (type, dec) && InHierarchy (type, dec)))
243 if (!AreInDifferentAssemblies (type, dec) && field.IsAssembly)
249 static bool IsNestedIn (TypeDefinition type, TypeDefinition other)
251 TypeDefinition declaring = type.DeclaringType;
253 if (declaring == null)
256 if (declaring == other)
259 if (declaring.DeclaringType == null)
262 return IsNestedIn (declaring, other);
265 static bool InHierarchy (TypeDefinition type, TypeDefinition other)
267 if (type.BaseType == null)
270 TypeDefinition baseType = type.BaseType.Resolve ();
272 if (baseType == other)
275 return InHierarchy (baseType, other);
278 static void Report (string pattern, params object [] parameters)
280 Console.WriteLine ("[check] " + pattern, parameters);
283 void ReportError (string pattern, params object [] parameters)
285 Report (pattern, parameters);
288 throw new VisibilityErrorException (string.Format (pattern, parameters));
291 void CheckFields (TypeDefinition type)
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);
301 void CheckConstructors (TypeDefinition type)
303 CheckMethods (type, type.Constructors);
306 void CheckMethods (TypeDefinition type)
308 CheckMethods (type, type.Methods);
311 void CheckMethods (TypeDefinition type, ICollection methods)
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);
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);
331 void CheckBody (MethodDefinition method)
333 TypeDefinition type = (TypeDefinition) method.DeclaringType;
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);
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:
349 TypeReference type_ref = instr.Operand as TypeReference;
350 if (type_ref != null)
351 error = !IsVisibleFrom (type, type_ref);
353 MethodReference meth_ref = instr.Operand as MethodReference;
354 if (meth_ref != null)
355 error = !IsVisibleFrom (type, meth_ref);
357 FieldReference field_ref = instr.Operand as FieldReference;
358 if (field_ref != null)
359 error = !IsVisibleFrom (type, field_ref);
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);
373 class VisibilityErrorException : Exception {
375 public VisibilityErrorException (string message)