2010-06-21 Atsushi Enomoto <atsushi@ximian.com>
[mcs.git] / tools / ictool / ictool.cs
blob9b4e0dd44729a6dad5ed973368749b12524d2f37
1 //
2 // file: ictool.cs
3 // author: Dan Lewis (dihlewis@yahoo.co.uk)
4 // (C) 2002
5 //
6 // description:
7 //
8 // Tool for generating C prototypes and structures suitable for use by the runtime
9 // from a list of supplied assemblies. See ictool-config.xml for configuration details.
12 using System;
13 using System.IO;
14 using System.Xml;
15 using System.Reflection;
16 using System.Collections;
18 public class ICTool {
19 public static void Main (string[] args) {
20 string filename = "ictool-config.xml";
21 if (args.Length == 1) {
22 filename = args[0];
24 else if (args.Length > 1) {
25 Console.Error.WriteLine ("Usage: ictool.exe [config.xml]");
26 Environment.Exit (-1);
29 try {
30 Stream config = File.OpenRead (filename);
31 Configure (config);
33 catch (Exception e) {
34 Console.Error.WriteLine ("Error: could not read configuration file.");
35 Console.Error.WriteLine (e);
36 Environment.Exit (-1);
39 EmitPrototypes ();
40 EmitStructures ();
43 // private
45 private static void EmitPrototypes () {
46 StreamWriter methods_file = GetOutputFile ("methods");
47 StreamWriter map_file = GetOutputFile ("map");
49 // includes
51 methods_file.WriteLine ("#include \"{0}\"\n", output_files["types"]);
52 map_file.WriteLine ("#include \"{0}\"\n", output_files["methods"]);
54 map_file.Write (
55 "static gpointer icall_map [] = {\n\t"
58 ArrayList map_lines = new ArrayList ();
60 BindingFlags binding =
61 BindingFlags.DeclaredOnly |
62 BindingFlags.Instance |
63 BindingFlags.Static |
64 BindingFlags.Public |
65 BindingFlags.NonPublic;
67 foreach (Type type in types.Values) {
68 bool has_icall = false;
69 MethodInfo[] methods = type.GetMethods (binding);
71 foreach (MethodInfo method in methods) {
72 if (IsInternalCall (method)) {
73 has_icall = true;
74 break;
78 if (!has_icall)
79 continue;
81 methods_file.WriteLine ("\n/* {0} */\n", type.FullName);
82 //map_lines.Add (String.Format ("\n/* {0} */\n", type.FullName));
84 foreach (MethodInfo method in methods) {
85 if (!IsInternalCall (method))
86 continue;
88 // function name
90 string func_name = String.Format ("ves_icall_{0}_{1}",
92 type.FullName,
93 method.Name
96 func_name = func_name.Replace ('.', '_');
98 // map file
100 map_lines.Add (String.Format (
101 "\"{0}::{1}\", {2}",
103 type.FullName.Replace ('.', '_'),
104 method.Name,
105 func_name
108 // methods file
110 ArrayList args = new ArrayList ();
112 // FIXME: return types that are structs need to be inserted
113 // into the argument list as a destination pointer
115 // object/value instance pointer
117 if (IsInstanceMethod (method)) {
118 args.Add (String.Format (
119 "{0}{1}",
121 peer_map.GetPeer (method.DeclaringType).GetTypedef (1),
122 "this"
126 // arguments
128 foreach (ParameterInfo param in method.GetParameters ()) {
129 Type arg_type = param.ParameterType;
131 int refs = 0;
132 if (arg_type.IsByRef) {
133 arg_type = arg_type.GetElementType ();
134 ++ refs;
137 Peer arg_peer = peer_map.GetPeer (arg_type);
138 if (!arg_peer.IsValueType)
139 ++ refs;
141 args.Add (String.Format ("{0}{1}", arg_peer.GetTypedef (refs), param.Name));
144 Peer ret = peer_map.GetPeer (method.ReturnType);
145 methods_file.WriteLine ("static {0}", ret.GetTypedef (ret.IsValueType ? 0 : 1));
146 methods_file.WriteLine ("{0} ({1});",
148 func_name,
149 Join (", ", args)
151 methods_file.WriteLine ();
156 methods_file.Close ();
158 // write map file and close it
160 map_file.Write (
161 "{0}\n}};\n", Join (",\n\t", map_lines)
164 map_file.Close ();
167 private static bool IsInternalCall (MethodInfo meth) {
168 return (meth.GetMethodImplementationFlags () & MethodImplAttributes.InternalCall) != 0;
171 private static bool IsInstanceMethod (MethodInfo meth) {
172 return (meth.CallingConvention & CallingConventions.HasThis) != 0;
175 private static void EmitStructures () {
176 StreamWriter file = GetOutputFile ("types");
178 // build dependency graph
180 DependencyGraph dg = new DependencyGraph ();
181 foreach (Peer peer in peer_map.Peers) {
182 dg.AddNode (peer);
184 // peer depends on nearest base
186 if (peer.NearestBase != null)
187 dg.AddEdge (peer.NearestBase, peer);
189 // peer depends on any value types used for fields
191 foreach (PeerField field in peer.Fields) {
192 if (field.Peer.IsValueType)
193 dg.AddEdge (field.Peer, peer);
197 // write structures in order
199 foreach (Peer peer in dg.TopologicalSort ()) {
200 if (peer.IsOpaque)
201 continue;
203 if (peer.IsEnum) {
204 file.WriteLine ("typedef {0} {1};", peer.UnderlyingPeer.Name, peer.Name);
205 file.WriteLine ("enum _{0} {{", peer.Name);
207 ArrayList enum_lines = new ArrayList ();
208 foreach (string name in peer.EnumConstants.Keys) {
209 enum_lines.Add (String.Format ("\t{0}_{1} = {2}",
210 peer.Name,
211 name,
212 peer.EnumConstants[name]
216 file.WriteLine ("{0}\n}};\n", Join (",\n", enum_lines));
218 else {
219 file.WriteLine ("typedef struct _{0} {{", peer.Name);
221 // base type
223 if (peer.NearestBase != null) {
224 file.WriteLine ("\t{0} __base;", peer.NearestBase.Name);
225 file.WriteLine ();
228 // fields
230 foreach (PeerField field in peer.Fields) {
231 bool use_struct = true;
232 if (field.Peer.IsValueType || field.Peer.IsOpaque)
233 use_struct = false;
235 file.WriteLine ("\t{0}{1}{2};",
236 use_struct ? "struct _" : "",
237 field.Peer.GetTypedef (field.Peer.IsValueType ? 0 : 1),
238 field.Name
242 file.WriteLine ("}} {0};\n", peer.Name);
247 private static void LoadAssemblies () {
248 types = new Hashtable ();
250 foreach (string filename in assemblies) {
251 Assembly assembly;
253 // find assembly
255 FileInfo info = null;
256 foreach (string path in assembly_paths) {
257 info = new FileInfo (Path.Combine (path, filename));
258 if (info.Exists)
259 break;
262 if (!info.Exists) {
263 Console.Error.WriteLine ("Error: assembly {0} not found.", filename);
264 Environment.Exit (-1);
267 // load assembly
269 assembly = Assembly.LoadFrom (info.FullName);
271 // load types
273 ArrayList loaded_types;
275 try {
276 loaded_types = new ArrayList (assembly.GetTypes ());
278 catch (ReflectionTypeLoadException e) {
279 loaded_types = new ArrayList ();
280 foreach (Type type in e.Types) {
281 if (type != null)
282 loaded_types.Add (type);
285 foreach (Exception f in e.LoaderExceptions) {
286 if (f is TypeLoadException) {
287 Console.Error.WriteLine ("Warning: {0} could not be loaded from assembly {1}.",
288 ((TypeLoadException)f).TypeName,
289 filename
292 else
293 Console.Error.WriteLine (f);
297 // add to type dictionary
299 foreach (Type type in loaded_types) {
300 if (!types.Contains (type.FullName))
301 types.Add (type.FullName, type);
306 private static void Configure (Stream input) {
307 XmlDocument doc = new XmlDocument ();
308 doc.Load (input);
310 // assemblies
312 assembly_paths = new ArrayList ();
313 assembly_paths.Add (".");
315 foreach (XmlNode node in doc.SelectNodes ("config/assemblypath")) {
316 assembly_paths.Add (node.Attributes["path"].Value);
319 assemblies = new ArrayList ();
320 foreach (XmlNode node in doc.SelectNodes ("config/assembly")) {
321 assemblies.Add (node.Attributes["file"].Value);
324 LoadAssemblies ();
326 // outputfiles
328 output_path = ".";
329 XmlNode path_node = doc.SelectSingleNode ("config/outputpath");
330 if (path_node != null)
331 output_path = path_node.Attributes["path"].Value;
333 output_files = new Hashtable ();
334 output_includes = new Hashtable ();
335 foreach (XmlNode node in doc.SelectNodes ("config/outputfile")) {
336 string name = node.Attributes["name"].Value;
337 output_files.Add (name, node.Attributes["file"].Value);
339 foreach (XmlNode child in node.ChildNodes) {
340 if (child.Name == "include")
341 output_includes[name] = child.InnerText;
345 // typemap
347 peer_map = new PeerMap ();
348 foreach (XmlNode node in doc.SelectNodes ("config/typemap/namespace")) {
349 string ns = node.Attributes["name"].Value;
351 foreach (XmlNode child in node.ChildNodes) {
352 if (child.Name == "type") {
353 string name = child.Attributes["name"].Value;
354 string peer_name = child.Attributes["peer"].Value;
356 bool opaque = false;
357 if (child.Attributes["opaque"] != null && child.Attributes["opaque"].Value == "true")
358 opaque = true;
360 String fullname = String.Format ("{0}.{1}", ns, name);
362 Type type;
363 if (child.Attributes["default"] != null && child.Attributes["default"].Value == "true")
364 type = Type.GetType (fullname);
365 else
366 type = (Type)types [fullname];
368 if (type != null)
369 peer_map.Add (new Peer (type, peer_name, opaque));
374 peer_map.ResolvePeers ();
377 private static StreamWriter GetOutputFile (string name) {
378 string filename = Path.Combine (output_path, (string)output_files[name]);
379 StreamWriter file = File.CreateText (filename);
380 file.AutoFlush = true;
382 file.Write (
384 // (verbatim string)
386 @"/**
387 * {0}
389 * This file was automatically generated on {1} by ictool.exe from
390 * the following assemblies:
391 * {2}
396 output_files[name],
397 DateTime.Now.ToString ("d"),
398 Join (", ", assemblies)
401 if (output_includes.Contains (name)) {
402 file.WriteLine (output_includes [name]);
403 file.WriteLine ();
406 return file;
409 private static string Join (string separator, ICollection values) {
410 // note to microsoft: please implement this in String :)
412 string[] strs = new string[values.Count];
414 int i = 0;
415 foreach (object value in values)
416 strs[i ++] = value.ToString ();
418 return String.Join (separator, strs);
421 private static ArrayList assembly_paths;
422 private static ArrayList assemblies;
423 private static string output_path;
424 private static Hashtable output_files;
425 private static Hashtable output_includes;
426 private static PeerMap peer_map;
427 private static Hashtable types;