2010-06-21 Atsushi Enomoto <atsushi@ximian.com>
[mcs.git] / tools / tuner / Mono.Tuner / MoonlightA11yDescriptorGenerator.cs
blob01118798c988e4c20ff3076cb73ecdcb7b7a5f7a
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;
31 using System.Collections.Generic;
33 using System.IO;
34 using System.Text.RegularExpressions;
35 using System.Text;
37 using System.Xml;
38 using System.Xml.XPath;
40 using Mono.Linker;
41 using Mono.Linker.Steps;
43 using Mono.Cecil;
45 namespace Mono.Tuner {
47 public class MoonlightA11yDescriptorGenerator : BaseStep {
49 XmlTextWriter writer = null;
50 protected override void ProcessAssembly (AssemblyDefinition assembly)
52 if (assembly.Name.Name == "MoonAtkBridge" || assembly.Name.Name == "System.Windows" ||
53 assembly.Name.Name.Contains ("Dummy"))
54 return;
56 if (writer == null) {
57 if (!Directory.Exists (Context.OutputDirectory))
58 Directory.CreateDirectory (Context.OutputDirectory);
60 string file_name = "descriptors.xml";
61 string file_path = Path.Combine (Context.OutputDirectory, file_name);
62 if (File.Exists (file_path))
63 File.Delete (file_path);
64 FileStream xml_file = new FileStream (file_path, FileMode.OpenOrCreate);
65 Console.WriteLine ("Created file {0}", file_name);
66 Console.Write ("Writing contents...");
68 writer = new XmlTextWriter (xml_file, System.Text.Encoding.UTF8);
69 writer.Formatting = Formatting.Indented;
70 writer.WriteStartElement("linker");
73 SortedDictionary <TypeDefinition, IList> types = ScanAssembly (assembly);
74 if (types != null && types.Count > 0) {
75 writer.WriteStartElement("assembly");
76 writer.WriteAttributeString ("fullname", assembly.Name.Name);
78 foreach (TypeDefinition type in types.Keys) {
79 IList members = types [type];
80 if (members != null && members.Count > 0) {
81 writer.WriteStartElement("type");
82 writer.WriteAttributeString ("fullname", type.FullName);
84 foreach (IAnnotationProvider member in members) {
85 MethodDefinition method = member as MethodDefinition;
86 if (method != null) {
87 writer.WriteStartElement("method");
88 writer.WriteAttributeString ("signature",
89 method.ReturnType.ReturnType.FullName + " " +
90 method.Name + GetMethodParams (method));
91 writer.WriteEndElement ();
92 continue;
95 FieldDefinition field = member as FieldDefinition;
96 if (field != null) {
97 writer.WriteStartElement("field");
98 writer.WriteAttributeString ("signature", field.DeclaringType.FullName + " " + field.Name);
99 writer.WriteEndElement ();
102 writer.WriteEndElement ();
106 writer.WriteEndElement ();
107 Console.WriteLine ();
112 protected override void EndProcess ()
114 Console.WriteLine ();
116 foreach (FileStream stream in streams)
117 stream.Close ();
119 if (writer != null) {
120 writer.WriteEndElement ();
121 writer.Close ();
122 writer = null;
126 //this is almost the ToString method of MethodDefinition...
127 private string GetMethodParams (MethodDefinition method)
129 string @params = "(";
130 if (method.HasParameters) {
131 for (int i = 0; i < method.Parameters.Count; i++) {
132 if (i > 0)
133 @params += ",";
135 @params += method.Parameters [i].ParameterType.FullName;
138 @params += ")";
139 return @params;
142 SortedDictionary<TypeDefinition, IList> /*,List<IAnnotationProvider>>*/ ScanAssembly (AssemblyDefinition assembly)
144 if (Annotations.GetAction (assembly) != AssemblyAction.Link)
145 return null;
147 SortedDictionary<TypeDefinition, IList> members_used = new SortedDictionary<TypeDefinition, IList> (new TypeComparer ());
148 foreach (TypeDefinition type in assembly.MainModule.Types) {
149 IList used_providers = FilterPublicMembers (ScanType (type));
150 if (used_providers.Count > 0)
151 members_used [type] = used_providers;
152 else if (IsInternal (type, true) &&
153 Annotations.IsMarked (type))
154 throw new NotSupportedException (String.Format ("The type {0} is used while its API is not", type.ToString ()));
156 return members_used;
159 static IList ScanType (TypeDefinition type)
161 return ExtractUsedProviders (type.Methods, type.Constructors, type.Fields);
164 static IList FilterPublicMembers (IList members)
166 IList new_list = new ArrayList ();
167 foreach (MemberReference item in members)
168 if (IsInternal (item, true))
169 new_list.Add (item);
171 return new_list;
174 static string [] master_infos = Directory.GetFiles (Environment.CurrentDirectory, "*.info");
176 static string FindMasterInfoFile (string name)
178 if (master_infos.Length == 0)
179 throw new Exception ("No masterinfo files found in current directory");
181 foreach (string file in master_infos) {
182 if (file.EndsWith (name + ".info"))
183 return file;
186 return null;
189 const string xpath_init = "assemblies/assembly/namespaces/namespace[@name='{0}']/classes/class[@name='{1}']";
191 static string GetXPathSearchForType (TypeDefinition type)
193 TypeDefinition parent_type = type;
194 string xpath = String.Empty;
195 while (parent_type.DeclaringType != null) {
196 xpath = String.Format ("/classes/class[@name='{0}']", parent_type.Name) + xpath;
197 parent_type = parent_type.DeclaringType;
199 return String.Format (xpath_init, parent_type.Namespace, parent_type.Name) + xpath;
202 static bool IsInternal (MemberReference member, bool master_info)
204 TypeDefinition type = null;
205 string master_info_file = null;
207 if (member is TypeDefinition) {
208 type = member as TypeDefinition;
209 if (!master_info)
210 return (!type.IsNested && !type.IsPublic) ||
211 (type.IsNested && (!type.IsNestedPublic || IsInternal (type.DeclaringType, false)));
213 master_info_file = FindMasterInfoFile (type.Module.Assembly.Name.Name);
214 if (master_info_file == null)
215 return IsInternal (member, false);
217 return !NodeExists (master_info_file, GetXPathSearchForType (type));
220 type = member.DeclaringType.Resolve ();
222 if (IsInternal (type, master_info))
223 return true;
225 MethodDefinition method = member as MethodDefinition;
226 FieldDefinition field = member as FieldDefinition;
228 if (field == null && method == null)
229 throw new System.NotSupportedException ("Members to scan should be methods or fields");
231 if (!master_info) {
233 if (method != null)
234 return !method.IsPublic;
236 return !field.IsPublic;
239 master_info_file = FindMasterInfoFile (type.Module.Assembly.Name.Name);
240 if (master_info_file == null)
241 return IsInternal (member, false);
243 string xpath_type = GetXPathSearchForType (type);
244 string name;
245 if (field != null)
246 name = field.Name;
247 else {
248 name = method.ToString ();
250 //lame, I know...
251 name = WackyOutArgs (WackyCommas (name.Substring (name.IndexOf ("::") + 2)
252 .Replace ("/", "+") // nested classes
253 .Replace ('<', '[').Replace ('>', ']'))); //generic params
256 if (field != null || !IsPropertyMethod (method))
257 return !NodeExists (master_info_file, xpath_type + String.Format ("/*/*[@name='{0}']", name));
259 return !NodeExists (master_info_file, xpath_type + String.Format ("/properties/*/*/*[@name='{0}']", name));
262 //at some point I want to get rid of this method and ask cecil's maintainer to spew commas in a uniform way...
263 static string WackyCommas (string method)
265 string outstring = String.Empty;
266 bool square_bracket = false;
267 foreach (char c in method) {
268 if (c == '[')
269 square_bracket = true;
270 else if (c == ']')
271 square_bracket = false;
273 outstring = outstring + c;
275 if (c == ',' && !square_bracket)
276 outstring = outstring + " ";
278 return outstring;
281 //ToString() spews & but not 'out' keyword
282 static string WackyOutArgs (string method)
284 return Regex.Replace (method, @"\w+&", delegate (Match m) { return "out " + m.ToString (); });
287 //copied from MarkStep (violating DRY unless I can put this in a better place... Cecil?)
288 static bool IsPropertyMethod (MethodDefinition md)
290 return (md.SemanticsAttributes & MethodSemanticsAttributes.Getter) != 0 ||
291 (md.SemanticsAttributes & MethodSemanticsAttributes.Setter) != 0;
294 static Dictionary<string, XPathNavigator> navs = new Dictionary<string, XPathNavigator> ();
295 static List<FileStream> streams = new List<FileStream> ();
297 static bool NodeExists (string file, string xpath)
299 Console.Write (".");
300 //Console.WriteLine ("Looking for node {0} in file {1}", xpath, file.Substring (file.LastIndexOf ("/") + 1));
302 XPathNavigator nav = null;
303 if (!navs.TryGetValue (file, out nav)) {
304 FileStream stream = new FileStream (file, FileMode.Open);
305 XPathDocument document = new XPathDocument (stream);
306 nav = document.CreateNavigator ();
307 streams.Add (stream);
308 navs [file] = nav;
310 return nav.SelectSingleNode (xpath) != null;
313 static IList /*List<IAnnotationProvider>*/ ExtractUsedProviders (params IList[] members)
315 IList used = new ArrayList ();
316 if (members == null || members.Length == 0)
317 return used;
319 foreach (IList members_list in members)
320 foreach (IAnnotationProvider provider in members_list)
321 if (Annotations.IsMarked (provider))
322 used.Add (provider);
324 return used;
327 class TypeComparer : IComparer <TypeDefinition> {
329 public int Compare (TypeDefinition x, TypeDefinition y)
331 return string.Compare (x.ToString (), y.ToString ());