[msvc] Update csproj files (#4711)
[mono.git] / mcs / tools / ictool / ictool.cs
1 //\r
2 // file:        ictool.cs\r
3 // author:      Dan Lewis (dihlewis@yahoo.co.uk)\r
4 //              (C) 2002\r
5 //\r
6 // description:\r
7 //\r
8 // Tool for generating C prototypes and structures suitable for use by the runtime\r
9 // from a list of supplied assemblies. See ictool-config.xml for configuration details.\r
10 //\r
11 \r
12 using System;\r
13 using System.IO;\r
14 using System.Xml;\r
15 using System.Reflection;\r
16 using System.Collections;\r
17 \r
18 public class ICTool {\r
19         public static void Main (string[] args) {\r
20                 string filename = "ictool-config.xml";\r
21                 if (args.Length == 1) {\r
22                         filename = args[0];\r
23                 }\r
24                 else if (args.Length > 1) {\r
25                         Console.Error.WriteLine ("Usage: ictool.exe [config.xml]");\r
26                         Environment.Exit (-1);\r
27                 }\r
28 \r
29                 try {\r
30                         Stream config = File.OpenRead (filename);\r
31                         Configure (config);\r
32                 }\r
33                 catch (Exception e) {\r
34                         Console.Error.WriteLine ("Error: could not read configuration file.");\r
35                         Console.Error.WriteLine (e);\r
36                         Environment.Exit (-1);\r
37                 }\r
38 \r
39                 EmitPrototypes ();\r
40                 EmitStructures ();\r
41         }\r
42 \r
43         // private\r
44 \r
45         private static void EmitPrototypes () {\r
46                 StreamWriter methods_file = GetOutputFile ("methods");\r
47                 StreamWriter map_file = GetOutputFile ("map");\r
48 \r
49                 // includes\r
50 \r
51                 methods_file.WriteLine ("#include \"{0}\"\n", output_files["types"]);\r
52                 map_file.WriteLine ("#include \"{0}\"\n", output_files["methods"]);\r
53 \r
54                 map_file.Write (\r
55                         "static gpointer icall_map [] = {\n\t"\r
56                 );\r
57 \r
58                 ArrayList map_lines = new ArrayList ();\r
59 \r
60                 BindingFlags binding =\r
61                         BindingFlags.DeclaredOnly |\r
62                         BindingFlags.Instance |\r
63                         BindingFlags.Static |\r
64                         BindingFlags.Public |\r
65                         BindingFlags.NonPublic;\r
66 \r
67                 foreach (Type type in types.Values) {\r
68                         bool has_icall = false;\r
69                         MethodInfo[] methods = type.GetMethods (binding);\r
70 \r
71                         foreach (MethodInfo method in methods) {\r
72                                 if (IsInternalCall (method)) {\r
73                                         has_icall = true;\r
74                                         break;\r
75                                 }\r
76                         }\r
77 \r
78                         if (!has_icall)\r
79                                 continue;\r
80 \r
81                         methods_file.WriteLine ("\n/* {0} */\n", type.FullName);\r
82                         //map_lines.Add (String.Format ("\n/* {0} */\n", type.FullName));\r
83                         \r
84                         foreach (MethodInfo method in methods) {\r
85                                 if (!IsInternalCall (method))\r
86                                         continue;\r
87 \r
88                                 // function name\r
89 \r
90                                 string func_name = String.Format ("ves_icall_{0}_{1}",\r
91                                         \r
92                                         type.FullName,\r
93                                         method.Name\r
94                                 );\r
95 \r
96                                 func_name = func_name.Replace ('.', '_');\r
97 \r
98                                 // map file\r
99 \r
100                                 map_lines.Add (String.Format (\r
101                                         "\"{0}::{1}\", {2}",\r
102 \r
103                                         type.FullName.Replace ('.', '_'),\r
104                                         method.Name,\r
105                                         func_name\r
106                                 ));\r
107 \r
108                                 // methods file\r
109 \r
110                                 ArrayList args = new ArrayList ();\r
111 \r
112                                 // FIXME: return types that are structs need to be inserted\r
113                                 // into the argument list as a destination pointer\r
114 \r
115                                 // object/value instance pointer\r
116                                 \r
117                                 if (IsInstanceMethod (method)) {\r
118                                         args.Add (String.Format (\r
119                                                 "{0}{1}",\r
120 \r
121                                                 peer_map.GetPeer (method.DeclaringType).GetTypedef (1),\r
122                                                 "this"\r
123                                         ));\r
124                                 }\r
125 \r
126                                 // arguments\r
127 \r
128                                 foreach (ParameterInfo param in method.GetParameters ()) {\r
129                                         Type arg_type = param.ParameterType;\r
130 \r
131                                         int refs = 0;\r
132                                         if (arg_type.IsByRef) {\r
133                                                 arg_type = arg_type.GetElementType ();\r
134                                                 ++ refs;\r
135                                         }\r
136 \r
137                                         Peer arg_peer = peer_map.GetPeer (arg_type);\r
138                                         if (!arg_peer.IsValueType)\r
139                                                 ++ refs;\r
140 \r
141                                         args.Add (String.Format ("{0}{1}", arg_peer.GetTypedef (refs), param.Name));\r
142                                 }\r
143 \r
144                                 Peer ret = peer_map.GetPeer (method.ReturnType);\r
145                                 methods_file.WriteLine ("static {0}", ret.GetTypedef (ret.IsValueType ? 0 : 1));\r
146                                 methods_file.WriteLine ("{0} ({1});",\r
147                                         \r
148                                         func_name,\r
149                                         Join (", ", args)\r
150                                 );\r
151                                 methods_file.WriteLine ();\r
152                         }\r
153 \r
154                 }\r
155 \r
156                 methods_file.Close ();\r
157 \r
158                 // write map file and close it\r
159 \r
160                 map_file.Write (\r
161                         "{0}\n}};\n", Join (",\n\t", map_lines)\r
162                 );\r
163 \r
164                 map_file.Close ();\r
165         }\r
166 \r
167         private static bool IsInternalCall (MethodInfo meth) {\r
168                 return (meth.GetMethodImplementationFlags () & MethodImplAttributes.InternalCall) != 0;\r
169         }\r
170 \r
171         private static bool IsInstanceMethod (MethodInfo meth) {\r
172                 return (meth.CallingConvention & CallingConventions.HasThis) != 0;\r
173         }\r
174 \r
175         private static void EmitStructures () {\r
176                 StreamWriter file = GetOutputFile ("types");\r
177 \r
178                 // build dependency graph\r
179                 \r
180                 DependencyGraph dg = new DependencyGraph ();\r
181                 foreach (Peer peer in peer_map.Peers) {\r
182                         dg.AddNode (peer);\r
183 \r
184                         // peer depends on nearest base\r
185 \r
186                         if (peer.NearestBase != null)\r
187                                 dg.AddEdge (peer.NearestBase, peer);\r
188 \r
189                         // peer depends on any value types used for fields\r
190 \r
191                         foreach (PeerField field in peer.Fields) {\r
192                                 if (field.Peer.IsValueType)\r
193                                         dg.AddEdge (field.Peer, peer);\r
194                         }\r
195                 }\r
196 \r
197                 // write structures in order\r
198 \r
199                 foreach (Peer peer in dg.TopologicalSort ()) {\r
200                         if (peer.IsOpaque)\r
201                                 continue;\r
202 \r
203                         if (peer.IsEnum) {\r
204                                 file.WriteLine ("typedef {0} {1};", peer.UnderlyingPeer.Name, peer.Name);\r
205                                 file.WriteLine ("enum _{0} {{", peer.Name);\r
206 \r
207                                 ArrayList enum_lines = new ArrayList ();\r
208                                 foreach (string name in peer.EnumConstants.Keys) {\r
209                                         enum_lines.Add (String.Format ("\t{0}_{1} = {2}",\r
210                                                 peer.Name,\r
211                                                 name,\r
212                                                 peer.EnumConstants[name]\r
213                                         ));\r
214                                 }\r
215                                 \r
216                                 file.WriteLine ("{0}\n}};\n", Join (",\n", enum_lines));\r
217                         }\r
218                         else {\r
219                                 file.WriteLine ("typedef struct _{0} {{", peer.Name);\r
220 \r
221                                 // base type\r
222                                 \r
223                                 if (peer.NearestBase != null) {\r
224                                         file.WriteLine ("\t{0} __base;", peer.NearestBase.Name);\r
225                                         file.WriteLine ();\r
226                                 }\r
227 \r
228                                 // fields\r
229                                 \r
230                                 foreach (PeerField field in peer.Fields) {\r
231                                         bool use_struct = true;\r
232                                         if (field.Peer.IsValueType || field.Peer.IsOpaque)\r
233                                                 use_struct = false;\r
234                                 \r
235                                         file.WriteLine ("\t{0}{1}{2};",\r
236                                                 use_struct ? "struct _" : "",\r
237                                                 field.Peer.GetTypedef (field.Peer.IsValueType ? 0 : 1),\r
238                                                 field.Name\r
239                                         );\r
240                                 }\r
241 \r
242                                 file.WriteLine ("}} {0};\n", peer.Name);\r
243                         }\r
244                 }\r
245         }\r
246 \r
247         private static void LoadAssemblies () {\r
248                 types = new Hashtable ();\r
249 \r
250                 foreach (string filename in assemblies) {\r
251                         Assembly assembly;\r
252 \r
253                         // find assembly\r
254 \r
255                         FileInfo info = null;\r
256                         foreach (string path in assembly_paths) {\r
257                                 info = new FileInfo (Path.Combine (path, filename));\r
258                                 if (info.Exists)\r
259                                         break;\r
260                         }\r
261 \r
262                         if (!info.Exists) {\r
263                                 Console.Error.WriteLine ("Error: assembly {0} not found.", filename);\r
264                                 Environment.Exit (-1);\r
265                         }\r
266 \r
267                         // load assembly\r
268 \r
269                         assembly = Assembly.LoadFrom (info.FullName);\r
270 \r
271                         // load types\r
272 \r
273                         ArrayList loaded_types;\r
274                         \r
275                         try {\r
276                                 loaded_types = new ArrayList (assembly.GetTypes ());\r
277                         }\r
278                         catch (ReflectionTypeLoadException e) {\r
279                                 loaded_types = new ArrayList ();\r
280                                 foreach (Type type in e.Types) {\r
281                                         if (type != null)\r
282                                                 loaded_types.Add (type);\r
283                                 }\r
284 \r
285                                 foreach (Exception f in e.LoaderExceptions) {\r
286                                         if (f is TypeLoadException) {\r
287                                                 Console.Error.WriteLine ("Warning: {0} could not be loaded from assembly {1}.",\r
288                                                         ((TypeLoadException)f).TypeName,\r
289                                                         filename\r
290                                                 );\r
291                                         }\r
292                                         else\r
293                                                 Console.Error.WriteLine (f);\r
294                                 }\r
295                         }\r
296 \r
297                         // add to type dictionary\r
298 \r
299                         foreach (Type type in loaded_types) {\r
300                                 if (!types.Contains (type.FullName))\r
301                                         types.Add (type.FullName, type);\r
302                         }\r
303                 }\r
304         }\r
305 \r
306         private static void Configure (Stream input) {\r
307                 XmlDocument doc = new XmlDocument ();\r
308                 doc.Load (input);\r
309 \r
310                 // assemblies\r
311 \r
312                 assembly_paths = new ArrayList ();\r
313                 assembly_paths.Add (".");\r
314 \r
315                 foreach (XmlNode node in doc.SelectNodes ("config/assemblypath")) {\r
316                         assembly_paths.Add (node.Attributes["path"].Value);\r
317                 }\r
318 \r
319                 assemblies = new ArrayList ();\r
320                 foreach (XmlNode node in doc.SelectNodes ("config/assembly")) {\r
321                         assemblies.Add (node.Attributes["file"].Value);\r
322                 }\r
323 \r
324                 LoadAssemblies ();\r
325 \r
326                 // outputfiles\r
327 \r
328                 output_path = ".";\r
329                 XmlNode path_node = doc.SelectSingleNode ("config/outputpath");\r
330                 if (path_node != null)\r
331                         output_path = path_node.Attributes["path"].Value;\r
332 \r
333                 output_files = new Hashtable ();\r
334                 output_includes = new Hashtable ();\r
335                 foreach (XmlNode node in doc.SelectNodes ("config/outputfile")) {\r
336                         string name = node.Attributes["name"].Value;\r
337                         output_files.Add (name, node.Attributes["file"].Value);\r
338 \r
339                         foreach (XmlNode child in node.ChildNodes) {\r
340                                 if (child.Name == "include")\r
341                                         output_includes[name] = child.InnerText;\r
342                         }\r
343                 }\r
344 \r
345                 // typemap\r
346 \r
347                 peer_map = new PeerMap ();\r
348                 foreach (XmlNode node in doc.SelectNodes ("config/typemap/namespace")) {\r
349                         string ns = node.Attributes["name"].Value;\r
350 \r
351                         foreach (XmlNode child in node.ChildNodes) {\r
352                                 if (child.Name == "type") {\r
353                                         string name = child.Attributes["name"].Value;\r
354                                         string peer_name = child.Attributes["peer"].Value;\r
355 \r
356                                         bool opaque = false;\r
357                                         if (child.Attributes["opaque"] != null && child.Attributes["opaque"].Value == "true")\r
358                                                 opaque = true;\r
359 \r
360                                         String fullname = String.Format ("{0}.{1}", ns, name);\r
361                                         \r
362                                         Type type;\r
363                                         if (child.Attributes["default"] != null && child.Attributes["default"].Value == "true")\r
364                                                 type = Type.GetType (fullname);\r
365                                         else\r
366                                                 type = (Type)types [fullname];\r
367 \r
368                                         if (type != null)\r
369                                                 peer_map.Add (new Peer (type, peer_name, opaque));\r
370                                 }\r
371                         }\r
372                 }\r
373 \r
374                 peer_map.ResolvePeers ();\r
375         }\r
376 \r
377         private static StreamWriter GetOutputFile (string name) {\r
378                 string filename = Path.Combine (output_path, (string)output_files[name]);\r
379                 StreamWriter file = File.CreateText (filename);\r
380                 file.AutoFlush = true;\r
381 \r
382                 file.Write (\r
383 \r
384 // (verbatim string)\r
385                 \r
386 @"/**\r
387  *  {0}\r
388  *\r
389  *  This file was automatically generated on {1} by ictool.exe from\r
390  *  the following assemblies:\r
391  *    {2}\r
392  */\r
393  \r
394 ",\r
395 \r
396                         output_files[name],\r
397                         DateTime.Now.ToString ("d"),\r
398                         Join (", ", assemblies)\r
399                 );\r
400 \r
401                 if (output_includes.Contains (name)) {\r
402                         file.WriteLine (output_includes [name]);\r
403                         file.WriteLine ();\r
404                 }\r
405 \r
406                 return file;\r
407         }\r
408 \r
409         private static string Join (string separator, ICollection values) {\r
410                 // note to microsoft: please implement this in String :)\r
411 \r
412                 string[] strs = new string[values.Count];\r
413 \r
414                 int i = 0;\r
415                 foreach (object value in values)\r
416                         strs[i ++] = value.ToString ();\r
417 \r
418                 return String.Join (separator, strs);\r
419         }\r
420         \r
421         private static ArrayList assembly_paths;\r
422         private static ArrayList assemblies;\r
423         private static string output_path;\r
424         private static Hashtable output_files;\r
425         private static Hashtable output_includes;\r
426         private static PeerMap peer_map;\r
427         private static Hashtable types;\r
428 }\r