* linker/Mono.Linker.Steps/MarkStep.cs: Visibility changes needed by
[mcs.git] / tools / tuner / Mono.Tuner / MoonlightA11yDescriptorGenerator.cs
blobf7ac404af97d4f33644fb610dc4c8df49cedfa58
1 //
2 // MoonlightA11yDescriptorGenerator.cs
3 //
4 // Author:
5 // Andrés G. Aragoneses (aaragoneses@novell.com)
6 //
7 // (C) 2009 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:
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.
29 using System;
30 using System.Collections;
32 using System.IO;
33 using System.Text.RegularExpressions;
34 using System.Text;
36 using System.Xml;
37 using System.Xml.XPath;
39 using Mono.Linker;
40 using Mono.Linker.Steps;
42 using Mono.Cecil;
44 namespace Mono.Tuner {
46 public class MoonlightA11yDescriptorGenerator : BaseStep {
48 XmlTextWriter writer = null;
49 protected override void ProcessAssembly (AssemblyDefinition assembly)
51 if (assembly.Name.Name == "MoonAtkBridge" || assembly.Name.Name == "System.Windows" ||
52 assembly.Name.Name.Contains ("Dummy"))
53 return;
55 if (writer == null) {
56 writer = new XmlTextWriter (System.Console.Out);
57 writer.Formatting = Formatting.Indented;
58 writer.WriteStartElement("linker");
61 IDictionary types = ScanAssembly (assembly);
62 if (types != null && types.Count > 0) {
63 writer.WriteStartElement("assembly");
64 writer.WriteAttributeString ("fullname", assembly.Name.Name);
66 foreach (TypeDefinition type in types.Keys) {
67 IList members = (IList)types [type];
68 if (members != null && members.Count > 0) {
69 writer.WriteStartElement("type");
70 writer.WriteAttributeString ("fullname", type.FullName);
72 foreach (IAnnotationProvider member in members) {
73 MethodDefinition method = member as MethodDefinition;
74 if (method != null) {
75 writer.WriteStartElement("method");
76 writer.WriteAttributeString ("signature",
77 method.ReturnType.ReturnType.FullName + " " +
78 method.Name + GetMethodParams (method));
79 writer.WriteEndElement ();
80 continue;
83 FieldDefinition field = member as FieldDefinition;
84 if (field != null) {
85 writer.WriteStartElement("field");
86 writer.WriteAttributeString ("signature", field.DeclaringType.FullName + " " + field.Name);
87 writer.WriteEndElement ();
90 writer.WriteEndElement ();
94 writer.WriteEndElement ();
99 protected override void EndProcess ()
101 if (writer != null) {
102 writer.WriteEndElement ();
103 writer.Close ();
104 writer = null;
108 //this is almost the ToString method of MethodDefinition...
109 private string GetMethodParams (MethodDefinition method)
111 string @params = "(";
112 if (method.HasParameters) {
113 for (int i = 0; i < method.Parameters.Count; i++) {
114 if (i > 0)
115 @params += ",";
117 @params += method.Parameters [i].ParameterType.FullName;
120 @params += ")";
121 return @params;
124 Hashtable /*Dictionary<TypeDefinition,List<IAnnotationProvider>*/ ScanAssembly (AssemblyDefinition assembly)
126 if (Annotations.GetAction (assembly) != AssemblyAction.Link)
127 return null;
129 Hashtable members_used = new Hashtable ();
130 foreach (TypeDefinition type in assembly.MainModule.Types) {
131 IList used_providers = FilterPublicMembers (ScanType (type));
132 if (used_providers.Count > 0)
133 members_used [type] = used_providers;
134 else if (IsInternal (type, true) &&
135 Annotations.IsMarked (type))
136 throw new NotSupportedException (String.Format ("The type {0} is used while its API is not", type.ToString ()));
138 return members_used;
141 static IList ScanType (TypeDefinition type)
143 return ExtractUsedProviders (type.Methods, type.Constructors, type.Fields);
146 static IList FilterPublicMembers (IList members)
148 IList new_list = new ArrayList ();
149 foreach (MemberReference item in members)
150 if (IsInternal (item, true))
151 new_list.Add (item);
153 return new_list;
156 static string FindMasterInfoFile (string name)
158 string [] master_infos = Directory.GetFiles (Environment.CurrentDirectory, "*.info");
160 if (master_infos.Length == 0)
161 throw new Exception ("No masterinfo files found in current directory");
163 foreach (string file in master_infos) {
164 if (file.EndsWith (name + ".info"))
165 return file;
168 return null;
171 const string xpath_init = "assemblies/assembly/namespaces/namespace[@name='{0}']/classes/class[@name='{1}']";
173 static string GetXPathSearchForType (TypeDefinition type)
175 TypeDefinition parent_type = type;
176 string xpath = String.Empty;
177 while (parent_type.DeclaringType != null) {
178 xpath = String.Format ("/classes/class[@name='{0}']", parent_type.Name) + xpath;
179 parent_type = parent_type.DeclaringType;
181 return String.Format (xpath_init, parent_type.Namespace, parent_type.Name) + xpath;
184 static bool IsInternal (MemberReference member, bool master_info)
186 TypeDefinition type = null;
187 string master_info_file = null;
189 if (member is TypeDefinition) {
190 type = member as TypeDefinition;
191 if (!master_info)
192 return (!type.IsNested && !type.IsPublic) ||
193 (type.IsNested && (!type.IsNestedPublic || IsInternal (type.DeclaringType, false)));
195 master_info_file = FindMasterInfoFile (type.Module.Assembly.Name.Name);
196 if (master_info_file == null)
197 return IsInternal (member, false);
199 return !NodeExists (master_info_file, GetXPathSearchForType (type));
202 type = member.DeclaringType.Resolve ();
204 if (IsInternal (type, master_info))
205 return true;
207 MethodDefinition method = member as MethodDefinition;
208 FieldDefinition field = member as FieldDefinition;
210 if (field == null && method == null)
211 throw new System.NotSupportedException ("Members to scan should be methods or fields");
213 if (!master_info) {
215 if (method != null)
216 return !method.IsPublic;
218 return !field.IsPublic;
221 master_info_file = FindMasterInfoFile (type.Module.Assembly.Name.Name);
222 if (master_info_file == null)
223 return IsInternal (member, false);
225 string xpath_type = GetXPathSearchForType (type);
226 string name;
227 if (field != null)
228 name = field.Name;
229 else {
230 name = method.ToString ();
232 //lame, I know...
233 name = WackyOutArgs (WackyCommas (name.Substring (name.IndexOf ("::") + 2)
234 .Replace ("/", "+") // nested classes
235 .Replace ('<', '[').Replace ('>', ']'))); //generic params
238 if (field != null || !IsPropertyMethod (method))
239 return !NodeExists (master_info_file, xpath_type + String.Format ("/*/*[@name='{0}']", name));
241 return !NodeExists (master_info_file, xpath_type + String.Format ("/properties/*/*/*[@name='{0}']", name));
244 //at some point I want to get rid of this method and ask cecil's maintainer to spew commas in a uniform way...
245 static string WackyCommas (string method)
247 string outstring = String.Empty;
248 bool square_bracket = false;
249 foreach (char c in method) {
250 if (c == '[')
251 square_bracket = true;
252 else if (c == ']')
253 square_bracket = false;
255 outstring = outstring + c;
257 if (c == ',' && !square_bracket)
258 outstring = outstring + " ";
260 return outstring;
263 //ToString() spews & but not 'out' keyword
264 static string WackyOutArgs (string method)
266 return Regex.Replace (method, @"\w+&", delegate (Match m) { return "out " + m.ToString (); });
269 //copied from MarkStep (violating DRY unless I can put this in a better place... Cecil?)
270 static bool IsPropertyMethod (MethodDefinition md)
272 return (md.SemanticsAttributes & MethodSemanticsAttributes.Getter) != 0 ||
273 (md.SemanticsAttributes & MethodSemanticsAttributes.Setter) != 0;
276 //OPTIMIZEME!: maybe hold a dictionary of opened FileStreams?
277 static bool NodeExists (string file, string xpath)
279 //Console.WriteLine ("Looking for node {0} in file {1}", xpath, file.Substring (file.LastIndexOf ("/") + 1));
280 using (FileStream stream = new FileStream (file, FileMode.Open)) {
281 XPathDocument document = new XPathDocument (stream);
282 XPathNavigator navigator = document.CreateNavigator ();
283 XPathNavigator node = navigator.SelectSingleNode (xpath);
284 return (node != null);
288 static IList /*List<IAnnotationProvider>*/ ExtractUsedProviders (params IList[] members)
290 IList used = new ArrayList ();
291 if (members == null || members.Length == 0)
292 return used;
294 foreach (IList members_list in members)
295 foreach (IAnnotationProvider provider in members_list)
296 if (Annotations.IsMarked (provider))
297 used.Add (provider);
299 return used;