2009-09-24 Zoltan Varga <vargaz@gmail.com>
[mcs.git] / tools / cilc / cilc.cs
blobf344c8d604fef6ee1b62d834a3cff4098ffe44d2
1 // cilc -- a CIL-to-C binding generator
2 // Copyright (C) 2003, 2004, 2005, 2006, 2007 Alp Toker <alp@atoker.com>
3 // Licensed under the terms of the MIT License
5 using System;
6 using System.IO;
7 using System.Reflection;
8 using System.Collections;
9 using System.Diagnostics;
10 using System.Text.RegularExpressions;
12 public class cilc
14 private cilc () {}
16 static CodeWriter C, H, Cindex, Hindex, Hdecls;
17 static string ns, dllname;
18 static string cur_type, CurType, CurTypeClass;
19 static string target_dir;
21 static ArrayList funcs_done = new ArrayList ();
23 public static int Main (string[] args)
25 if (args.Length < 1 || args.Length > 4) {
26 Console.WriteLine ("Mono CIL-to-C binding generator");
27 Console.WriteLine ("Usage: cilc assembly [target] [pkg ns[,ns...]]");
28 return 1;
31 ns = "Unnamed";
33 RegisterByVal (typeof (uint));
34 RegisterByVal (typeof (int));
35 RegisterByVal (typeof (IntPtr));
36 RegisterByVal (typeof (bool));
37 RegisterByVal (typeof (char));
38 RegisterByVal (typeof (sbyte));
39 RegisterByVal (typeof (byte));
40 RegisterByVal (typeof (double));
42 if (args.Length == 1) {
43 SmartBind (args[0]);
44 } else if (args.Length == 2) {
45 Generate (args[0], args[1]);
46 } else if (args.Length == 3) {
47 RegisterPkg (args[1], args[2]);
48 SmartBind (args[0]);
49 } else if (args.Length == 4) {
50 RegisterPkg (args[2], args[3]);
51 Generate (args[0], args[1]);
54 return 0;
58 public static void SmartBind (string aname)
60 string tmpdir = Path.GetTempPath () + Path.GetTempFileName ();
61 string cwd = Directory.GetCurrentDirectory ();
62 if (Directory.Exists (tmpdir) || File.Exists (tmpdir)) {
63 Console.WriteLine ("Error: Temporary directory " + tmpdir + " already exists.");
64 return;
66 Generate (aname, tmpdir);
67 Console.Write ("Compiling unmanaged binding");
68 RunWithReport ("make", "-C \"" + tmpdir + "\" bundle=true");
69 Console.WriteLine ();
70 Console.Write ("Installing to current directory");
71 RunWithReport ("make", "-C \"" + tmpdir + "\" install prefix=\"" + cwd + "\"");
72 Directory.Delete (tmpdir, true);
73 Console.WriteLine ();
76 public static string Run (string cmd, string args)
78 ProcessStartInfo psi = new ProcessStartInfo (cmd, args);
79 psi.UseShellExecute = false;
80 psi.RedirectStandardInput = true;
81 psi.RedirectStandardOutput = true;
82 psi.RedirectStandardError = true;
84 Process p = Process.Start (psi);
86 string line = p.StandardOutput.ReadLine ();
88 p.WaitForExit ();
90 return line;
93 public static int RunWithReport (string cmd, string args)
95 ProcessStartInfo psi = new ProcessStartInfo (cmd, args);
96 psi.UseShellExecute = false;
97 psi.RedirectStandardOutput = true;
98 psi.RedirectStandardError = true;
100 Process p = Process.Start (psi);
102 string line;
103 while ((line = p.StandardOutput.ReadLine ()) != null)
104 if (verbose)
105 Console.WriteLine (line);
106 else
107 Console.Write (".");
109 Console.WriteLine ();
111 Console.Write (p.StandardError.ReadToEnd ());
113 p.WaitForExit ();
115 return p.ExitCode;
118 static bool verbose = false;
120 static string extpkgs = String.Empty;
121 static string[] extsubpkgs = {};
122 static string[] extincludes = {};
124 public static void RegisterPkg (string pkg, string subpkgs)
126 extpkgs += " " + pkg;
128 string cflags = Run ("pkg-config", "--cflags-only-I " + pkg);
130 extsubpkgs = subpkgs.Trim ().Split (',');
131 extincludes = new string[extsubpkgs.Length];
133 for (int i = 0 ; i != extsubpkgs.Length ; i++)
134 extincludes[i] = extsubpkgs[i] + "/" + extsubpkgs[i] + ".h";
136 //string cmd = "gcc";
137 //string args = "-E " + cflags + " " + includedir + "/" + hname;
139 string cppincludes = String.Empty;
140 foreach (string include in extincludes)
141 cppincludes += " -include " + include;
143 string cmd = "cpp";
144 string args = cflags + cppincludes + " /dev/null";
146 ProcessStartInfo psi = new ProcessStartInfo (cmd, args);
147 psi.UseShellExecute = false;
148 psi.RedirectStandardOutput = true;
149 psi.RedirectStandardError = true;
151 Process p = Process.Start (psi);
153 string line;
155 Regex re_type = new Regex (@"typedef struct (_\w+) (\w+);");
156 Regex re_enum = new Regex (@"} (\w+);");
158 while ((line = p.StandardOutput.ReadLine ()) != null) {
159 line = line.Trim ();
161 Match m;
163 m = re_type.Match (line);
164 if (m.Success) {
165 string G = m.Groups[2].Value;
167 if (!GIsValid (G))
168 continue;
170 if (G.EndsWith ("Class"))
171 continue;
173 RegisterG (G);
174 continue;
177 m = re_enum.Match (line);
178 if (m.Success) {
179 string G = m.Groups[1].Value;
181 if (!GIsValid (G))
182 continue;
184 RegisterG (G);
185 RegisterByVal (G);
186 continue;
190 p.WaitForExit ();
191 Console.Write (p.StandardError.ReadToEnd ());
192 Console.WriteLine ();
195 static bool GIsValid (string G) {
196 foreach (string extsubpkg in extsubpkgs)
197 if (G.ToLower ().StartsWith (extsubpkg))
198 return true;
200 return false;
203 public static void Generate (string assembly, string target)
205 target_dir = target + Path.DirectorySeparatorChar;
207 if (Directory.Exists (target_dir)) {
208 Console.WriteLine ("Error: Target directory " + target_dir + " already exists.");
209 return;
212 Directory.CreateDirectory (target_dir);
214 Assembly a = Assembly.LoadFrom (assembly);
216 Console.WriteLine ();
217 Console.WriteLine ("References (not followed):");
218 foreach (AssemblyName reference in a.GetReferencedAssemblies ())
219 Console.WriteLine (" " + reference.Name);
220 Console.WriteLine ();
222 dllname = Path.GetFileName (assembly);
223 AssemblyGen (a);
225 //we might not want to do this in future
226 File.Copy (dllname, target_dir + dllname);
228 string soname = "lib" + NsToFlat (Path.GetFileNameWithoutExtension (assembly)).ToLower () + ".so";
230 //create the static makefile
231 StreamWriter makefile = new StreamWriter (File.Create (target_dir + "Makefile"));
232 StreamReader sr = new StreamReader (Assembly.GetAssembly (typeof(cilc)).GetManifestResourceStream ("res-Makefile"));
234 makefile.Write (sr.ReadToEnd ());
235 sr.Close ();
236 makefile.Close ();
238 //create makefile defs
239 CodeWriter makefile_defs = new CodeWriter (target_dir + "defs.mk");
240 makefile_defs.Indenter = "\t";
241 makefile_defs.WriteLine ("ASSEMBLY = " + assembly);
242 makefile_defs.WriteLine ("SONAME = " + soname);
243 makefile_defs.WriteLine (@"OBJS = $(shell ls *.c | sed -e 's/\.c/.o/')");
245 if (extpkgs != String.Empty) {
246 makefile_defs.WriteLine ("EXTRAINCLUDES = $(shell pkg-config --cflags" + extpkgs + ")");
247 makefile_defs.WriteLine ("EXTRALIBS = $(shell pkg-config --libs" + extpkgs + ")");
249 makefile_defs.Close ();
251 Console.WriteLine ();
253 //identify hits on types that were registered too late
254 foreach (string tn in registered_types) {
255 if (registry_hits.Contains (tn)) {
256 Console.WriteLine ("Warning: " + tn + " was incorrectly registered after it was needed instead of before. Consider re-ordering.");
260 MakeReport (registry_hits, "Type registry missed hits", 20);
261 Console.WriteLine ();
262 //TODO: this count is now wrong
263 Console.WriteLine (registered_types.Count + " types generated/seen in " + namespaces.Length + " namespaces; " + warnings_ignored + " types ignored");
264 Console.WriteLine ();
267 static void MakeReport (Hashtable ctable, string desc, int num)
269 Console.WriteLine (desc + " (top " + (registry_hits.Count > num ? num : registry_hits.Count) + " of " + registry_hits.Count + "):");
270 string[] reg_keys = (string[]) (new ArrayList (ctable.Keys)).ToArray (typeof (string));
271 int[] reg_vals = (int[]) (new ArrayList (ctable.Values)).ToArray (typeof (int));
272 Array.Sort (reg_vals, reg_keys);
274 Array.Reverse (reg_vals);
275 Array.Reverse (reg_keys);
277 for (int i = 0 ; i != reg_keys.Length && i != num ; i++) {
278 Console.WriteLine (" " + reg_keys[i] + ": " + reg_vals[i]);
282 static int warnings_ignored = 0;
284 static void AssemblyGen (Assembly a)
286 Type[] types = a.GetTypes ();
287 Hashtable ns_types = new Hashtable ();
289 foreach (Type t in types) {
290 if (t.IsNotPublic) {
291 //Console.WriteLine ("Ignoring non-public type: " + t.Name);
292 //warnings_ignored++;
293 continue;
296 if (!t.IsClass && !t.IsInterface && !t.IsEnum) {
297 //Console.WriteLine ("Ignoring unrecognised type: " + t.Name);
298 warnings_ignored++;
299 continue;
302 RegisterType (t);
304 if (t.IsEnum)
305 RegisterByVal (t);
307 string tns = t.Namespace == null ? String.Empty : t.Namespace;
309 if (!ns_types.Contains (tns))
310 ns_types[tns] = new ArrayList ();
312 ((ArrayList) ns_types[tns]).Add (t);
315 namespaces = (string[]) (new ArrayList (ns_types.Keys)).ToArray (typeof (string));
317 foreach (DictionaryEntry de in ns_types)
318 NamespaceGen ((string) de.Key, (Type[]) ((ArrayList) de.Value).ToArray (typeof (Type)));
321 static string[] namespaces;
323 static void NamespaceGen (string given_ns, Type[] types)
325 //ns = types[0].Namespace;
326 ns = given_ns;
328 Hindex = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + ".h");
329 Hdecls = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + "types.h");
330 Cindex = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + ".c");
332 string Hindex_id = "__" + NsToFlat (ns).ToUpper () + "_H__";
333 Hindex.WriteLine ("#ifndef " + Hindex_id);
334 Hindex.WriteLine ("#define " + Hindex_id);
335 Hindex.WriteLine ();
337 string Hdecls_id = "__" + NsToFlat (ns).ToUpper () + "_DECLS_H__";
338 Hdecls.WriteLine ("#ifndef " + Hdecls_id);
339 Hdecls.WriteLine ("#define " + Hdecls_id);
340 Hdecls.WriteLine ();
342 Cindex.WriteLine ("#include <glib.h>");
343 Cindex.WriteLine ("#include <glib-object.h>");
344 Cindex.WriteLine ("#include <mono/jit/jit.h>");
345 Cindex.WriteLine ();
346 Cindex.WriteLine ("#include <mono/metadata/object.h>");
347 Cindex.WriteLine ("#include <mono/metadata/debug-helpers.h>");
348 Cindex.WriteLine ("#include <mono/metadata/appdomain.h>");
349 Cindex.WriteLine ();
350 Cindex.WriteLine ("#ifdef CILC_BUNDLE");
351 Cindex.WriteLine ("#include \"bundle.h\"");
352 Cindex.WriteLine ("#endif");
353 Cindex.WriteLine ();
355 Cindex.WriteLine ("MonoDomain *" + NsToC (ns) + "_get_mono_domain (void)");
356 Cindex.WriteLine ("{");
357 Cindex.WriteLine ("static MonoDomain *domain = NULL;");
358 Cindex.WriteLine ("if (domain != NULL) return domain;");
359 Cindex.WriteLine ("mono_config_parse (NULL);");
360 Cindex.WriteLine ("domain = mono_jit_init (\"cilc\");");
361 Cindex.WriteLine ();
362 Cindex.WriteLine ("#ifdef CILC_BUNDLE");
363 Cindex.WriteLine ("mono_register_bundled_assemblies (bundled);");
364 Cindex.WriteLine ("#endif");
365 Cindex.WriteLine ();
367 Cindex.WriteLine ("return domain;");
368 Cindex.WriteLine ("}");
369 Cindex.WriteLine ();
371 Cindex.WriteLine ("MonoAssembly *" + NsToC (ns) + "_get_mono_assembly (void)");
372 Cindex.WriteLine ("{");
373 Cindex.WriteLine ("static MonoAssembly *assembly = NULL;");
374 Cindex.WriteLine ("if (assembly != NULL) return assembly;");
375 Cindex.WriteLine ("assembly = mono_domain_assembly_open (" + NsToC (ns) + "_get_mono_domain (), \"" + dllname + "\");");
376 Cindex.WriteLine ();
378 Cindex.WriteLine ("return assembly;");
379 Cindex.WriteLine ("}");
380 Cindex.WriteLine ();
383 Cindex.WriteLine ("MonoObject *" + NsToC (ns) + "_cilc_glib_gobject_get_mobject (GObject *_handle)");
384 Cindex.WriteLine ("{");
385 //FIXME: instantiate monobject if it doesn't exist
386 Cindex.WriteLine ("return g_object_get_data (G_OBJECT (" + "_handle" + "), \"mono-object\");");
387 Cindex.WriteLine ("}");
389 Cindex.WriteLine ("gpointer " + NsToC (ns) + "_cilc_glib_mobject_get_gobject (MonoObject *_mono_object)");
390 Cindex.WriteLine ("{");
391 Cindex.WriteLine ("static MonoAssembly *_mono_assembly = NULL;");
392 Cindex.WriteLine ("static MonoMethod *_mono_method = NULL;");
393 Cindex.WriteLine ("static MonoClass *_mono_class = NULL;");
394 Cindex.WriteLine ("gpointer *retval;");
395 Cindex.WriteLine ();
396 Cindex.WriteLine ("if (_mono_assembly == NULL) {");
397 Cindex.WriteLine ("_mono_assembly = mono_domain_assembly_open (" + NsToC (ns) + "_get_mono_domain (), \"" + "glib-sharp" + "\");");
398 Cindex.WriteLine ("}");
399 Cindex.WriteLine ("if (_mono_class == NULL) {");
400 Cindex.WriteLine ("_mono_class = (MonoClass*) mono_class_from_name ((MonoImage*) mono_assembly_get_image (_mono_assembly), \"GLib\", \"Object\");");
401 Cindex.WriteLine ("}");
402 Cindex.WriteLine ("if (_mono_method == NULL) {");
403 Cindex.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":get_Handle()\", FALSE);");
404 Cindex.WriteLine ("_mono_method = mono_method_desc_search_in_class (_mono_method_desc, _mono_class);");
405 Cindex.WriteLine ("}");
406 Cindex.WriteLine ();
407 Cindex.WriteLine ("retval = (gpointer *) mono_object_unbox (mono_runtime_invoke (_mono_method, _mono_object, NULL, NULL));");
408 Cindex.WriteLine ("return (gpointer ) *retval;");
409 Cindex.WriteLine ("}");
410 Cindex.WriteLine ();
413 Console.Write ("Generating sources in " + ns);
414 foreach (Type t in types) {
415 TypeGen (t);
416 Console.Write (".");
419 Console.WriteLine ();
421 Hindex.WriteLine ();
422 Hindex.WriteLine ("#endif /* " + Hindex_id + " */");
424 Hdecls.WriteLine ();
425 Hdecls.WriteLine ("#endif /* " + Hdecls_id + " */");
427 Cindex.Close ();
428 Hindex.Close ();
429 Hdecls.Close ();
432 static void TypeGen (Type t)
434 //TODO: we only handle ordinary classes for now
436 else if (t.IsSubclassOf (typeof (Delegate))) {
437 Console.WriteLine ("Ignoring delegate: " + t.Name);
438 return;
442 cur_type = NsToC (ns) + "_" + CamelToC (t.Name);
443 //CurType = NsToFlat (ns) + t.Name;
444 CurType = CsTypeToG (t);
445 if (t.IsInterface)
446 CurTypeClass = GToGI (CurType);
447 else
448 CurTypeClass = GToGC (CurType);
450 //ns = t.Namespace;
451 string fname = NsToFlat (ns).ToLower () + t.Name.ToLower ();
452 C = new CodeWriter (target_dir + fname + ".c");
453 H = new CodeWriter (target_dir + fname + ".h");
454 Hindex.WriteLine ("#include <" + fname + ".h" + ">");
457 string H_id = "__" + NsToFlat (ns).ToUpper () + "_" + t.Name.ToUpper () + "_H__";
458 H.WriteLine ("#ifndef " + H_id);
459 H.WriteLine ("#define " + H_id);
460 H.WriteLine ();
462 H.WriteLine ("#include <glib.h>");
463 H.WriteLine ("#include <glib-object.h>");
465 foreach (string include in extincludes)
466 H.WriteLine ("#include <" + include + ">");
468 H.WriteLine ();
470 if (t.BaseType != null && IsRegistered (t.BaseType) && !IsExternal (t.BaseType))
471 H.WriteLine ("#include \"" + NsToFlat (t.BaseType.Namespace).ToLower () + t.BaseType.Name.ToLower () + ".h\"");
473 foreach (string ext_ns in namespaces)
474 H.WriteLine ("#include \"" + NsToFlat (ext_ns).ToLower () + "types.h\"");
476 H.WriteLine ();
478 H.WriteLine ("#ifdef __cplusplus");
479 H.WriteLine ("extern \"C\" {", false);
480 H.WriteLine ("#endif /* __cplusplus */");
481 H.WriteLine ();
483 C.WriteLine ("#include \"" + fname + ".h" + "\"");
485 Type[] ifaces;
486 ifaces = t.GetInterfaces ();
487 foreach (Type iface in ifaces) {
488 if (!IsRegistered (iface))
489 continue;
491 string iface_fname = NsToFlat (ns).ToLower () + iface.Name.ToLower ();
492 C.WriteLine ("#include \"" + iface_fname + ".h" + "\"");
495 C.WriteLine ("#include <mono/metadata/object.h>");
496 C.WriteLine ("#include <mono/metadata/debug-helpers.h>");
497 C.WriteLine ("#include <mono/metadata/appdomain.h>");
498 C.WriteLine ();
500 if (t.IsClass)
501 ClassGen (t);
502 else if (t.IsInterface)
503 ClassGen (t);
504 else if (t.IsEnum)
505 EnumGen (t);
507 H.WriteLine ();
508 H.WriteLine ("#ifdef __cplusplus");
509 H.WriteLine ("}", false);
510 H.WriteLine ("#endif /* __cplusplus */");
511 H.WriteLine ();
513 H.WriteLine ("#endif /* " + H_id + " */");
515 C.Close ();
516 H.Close ();
519 static void EnumGen (Type t)
521 //TODO: we needn't split out each enum into its own file
523 string gname = CsTypeToG (t);
525 Hdecls.WriteLine ("typedef enum");
526 Hdecls.WriteLine ("{");
527 C.WriteLine ("GType " + cur_type + "_get_type (void)", H, ";");
528 C.WriteLine ("{");
529 C.WriteLine ("static GType etype = 0;");
530 C.WriteLine ("if (etype == 0) {");
531 C.WriteLine ("static const GEnumValue values[] = {");
532 foreach (FieldInfo fi in t.GetFields (BindingFlags.Static|BindingFlags.Public)) {
533 string finame = (cur_type + "_" + CamelToC (fi.Name)).ToUpper ();
534 Hdecls.WriteLine (finame + ",");
535 C.WriteLine ("{ " + finame + ", \"" + finame + "\", \"" + CamelToC (fi.Name).Replace ("_", "-") + "\" },");
537 Hdecls.WriteLine ("} " + gname + ";");
538 Hdecls.WriteLine ();
539 C.WriteLine ("{ 0, NULL, NULL }");
540 C.WriteLine ("};");
541 C.WriteLine ("etype = g_enum_register_static (\"" + gname + "\", values);");
542 C.WriteLine ("}");
543 C.WriteLine ("return etype;");
544 C.WriteLine ("}");
547 static void ClassGen (Type t)
549 //TODO: what flags do we want for GetEvents and GetConstructors?
551 //events as signals
552 EventInfo[] events;
553 events = t.GetEvents (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
555 //events as signals
556 MethodInfo[] methods;
557 methods = t.GetMethods (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
559 Type[] ifaces;
560 ifaces = t.GetInterfaces ();
562 H.WriteLine ("G_BEGIN_DECLS");
563 H.WriteLine ();
566 string NS = NsToC (ns).ToUpper ();
567 string T = CamelToC (t.Name).ToUpper ();
568 string NST = NS + "_" + T;
569 string NSTT = NS + "_TYPE_" + T;
571 H.WriteLine ("#define " + NSTT + " (" + cur_type + "_get_type ())");
572 H.WriteLine ("#define " + NST + "(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), " + NSTT + ", " + CurType + "))");
573 if (!t.IsInterface)
574 H.WriteLine ("#define " + NST + "_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), " + NSTT + ", " + CurTypeClass + "))");
575 H.WriteLine ("#define " + NS + "_IS_" + T + "(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), " + NSTT + "))");
576 if (!t.IsInterface)
577 H.WriteLine ("#define " + NS + "_IS_" + T + "_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), " + NSTT + "))");
578 if (t.IsInterface)
579 H.WriteLine ("#define " + NST + "_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), " + NSTT + ", " + CurTypeClass + "))");
580 else
581 H.WriteLine ("#define " + NST + "_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), " + NSTT + ", " + CurTypeClass + "))");
584 if (!C.IsDuplicate) {
585 Hdecls.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
586 Hdecls.WriteLine ("typedef struct _" + CurTypeClass + " " + CurTypeClass + ";");
587 Hdecls.WriteLine ();
590 H.WriteLine ();
592 string ParentName;
593 string ParentNameClass;
595 if (t.BaseType != null) {
596 ParentName = CsTypeToG (t.BaseType);
597 ParentNameClass = GToGC (ParentName);
598 } else {
599 ParentName = "GType";
600 if (t.IsInterface)
601 ParentNameClass = ParentName + "Interface";
602 else
603 ParentNameClass = ParentName + "Class";
606 //H.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
608 //H.WriteLine ();
609 //H.WriteLine ("typedef struct _" + CurType + "Class " + CurType + "Class;");
610 if (!t.IsInterface) {
611 H.WriteLine ("typedef struct _" + CurType + "Private " + CurType + "Private;");
612 H.WriteLine ();
613 H.WriteLine ("struct _" + CurType);
614 H.WriteLine ("{");
616 H.WriteLine (ParentName + " parent_instance;");
617 H.WriteLine (CurType + "Private *priv;");
618 H.WriteLine ("};");
619 H.WriteLine ();
622 H.WriteLine ("struct _" + CurTypeClass);
623 H.WriteLine ("{");
624 //H.WriteLine (ParentNameClass + " parent_class;");
625 H.WriteLine (ParentNameClass + " parent;");
626 if (t.BaseType != null)
627 H.WriteLine ("/* inherits " + t.BaseType.Namespace + " " + t.BaseType.Name + " */");
629 if (events.Length != 0) {
630 H.WriteLine ();
631 H.WriteLine ("/* signals */");
633 //FIXME: event arguments
634 foreach (EventInfo ei in events)
635 H.WriteLine ("void (* " + CamelToC (ei.Name) + ") (" + CurType + " *thiz" + ");");
638 if (t.IsInterface) {
639 if (methods.Length != 0) {
640 H.WriteLine ();
641 H.WriteLine ("/* vtable */");
643 //FIXME: method arguments
644 //string funcname = ToValidFuncName (CamelToC (imi.Name));
645 foreach (MethodInfo mi in methods)
646 H.WriteLine ("void (* " + CamelToC (mi.Name) + ") (" + CurType + " *thiz" + ");");
650 H.WriteLine ("};");
651 H.WriteLine ();
653 //generate c file
655 //private struct
656 C.WriteLine ("struct _" + CurType + "Private");
657 C.WriteLine ("{");
658 C.WriteLine ("MonoObject *mono_object;");
659 C.WriteLine ("};");
661 C.WriteLine ();
663 //events
664 if (events.Length != 0) {
665 C.WriteLine ("enum {");
667 foreach (EventInfo ei in events)
668 C.WriteLine (CamelToC (ei.Name).ToUpper () + ",");
670 C.WriteLine ("LAST_SIGNAL");
671 C.WriteLine ("};");
672 C.WriteLine ();
675 C.WriteLine ("static gpointer parent_class;");
677 if (events.Length == 0)
678 C.WriteLine ("static guint signals[0];");
679 else
680 C.WriteLine ("static guint signals[LAST_SIGNAL] = { 0 };");
681 C.WriteLine ();
683 C.WriteLine ("static MonoClass *" + cur_type + "_get_mono_class (void)");
684 C.WriteLine ("{");
685 C.WriteLine ("MonoAssembly *assembly;");
686 C.WriteLine ("static MonoClass *class = NULL;");
687 C.WriteLine ("if (class != NULL) return class;");
689 C.WriteLine ("assembly = (MonoAssembly*) " + NsToC (ns) + "_get_mono_assembly ();");
690 C.WriteLine ("class = (MonoClass*) mono_class_from_name ((MonoImage*) mono_assembly_get_image (assembly)" + ", \"" + ns + "\", \"" + t.Name + "\");");
692 C.WriteLine ("mono_class_init (class);");
693 C.WriteLine ();
695 C.WriteLine ("return class;");
696 C.WriteLine ("}");
698 C.WriteLine ();
700 wrap_gobject = TypeIsGObject (t);
702 //TODO: generate thin wrappers for interfaces
704 //generate constructors
705 ConstructorInfo[] constructors;
706 constructors = t.GetConstructors ();
707 foreach (ConstructorInfo c in constructors)
708 ConstructorGen (c, t);
710 //generate static methods
711 //MethodInfo[] methods;
712 methods = t.GetMethods (BindingFlags.Public|BindingFlags.Static|BindingFlags.DeclaredOnly);
713 foreach (MethodInfo m in methods)
714 MethodGen (m, t);
716 //generate instance methods
717 methods = t.GetMethods (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
718 foreach (MethodInfo m in methods)
719 MethodGen (m, t);
721 C.WriteLine ();
723 if (t.IsClass) {
724 //generate the GObject init function
725 C.WriteLine ("static void " + cur_type + "_init (" + CurType + " *thiz" + ")");
726 C.WriteLine ("{");
727 C.WriteLine ("thiz->priv = g_new0 (" + CurType + "Private, 1);");
728 C.WriteLine ("}");
730 C.WriteLine ();
732 //generate the GObject class init function
733 C.WriteLine ("static void " + cur_type + "_class_init (" + CurTypeClass + " *klass" + ")");
734 C.WriteLine ("{");
736 C.WriteLine ("GObjectClass *object_class = G_OBJECT_CLASS (klass);");
737 C.WriteLine ("parent_class = g_type_class_peek_parent (klass);");
738 //C.WriteLine ("object_class->finalize = _finalize;");
740 foreach (EventInfo ei in events)
741 EventGen (ei, t);
743 C.WriteLine ("}");
745 C.WriteLine ();
748 if (ifaces.Length != 0) {
749 foreach (Type iface in ifaces) {
750 if (!IsRegistered (iface))
751 continue;
753 C.WriteLine ("static void " + NsToC (iface.Namespace) + "_" + CamelToC (iface.Name) + "_interface_init (" + GToGI (CsTypeToG (iface)) + " *iface" + ")");
754 C.WriteLine ("{");
755 foreach (MethodInfo imi in iface.GetMethods (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly)) {
756 string funcname = ToValidFuncName (CamelToC (imi.Name));
757 C.WriteLine ("iface->" + funcname + " = " + cur_type + "_" + funcname + ";");
759 //TODO: properties etc.
760 C.WriteLine ("}");
761 C.WriteLine ();
766 //generate the GObject get_type function
767 C.WriteLine ("GType " + cur_type + "_get_type (void)", H, ";");
768 C.WriteLine ("{");
769 C.WriteLine ("static GType object_type = 0;");
770 C.WriteLine ("g_type_init ();");
771 C.WriteLine ();
772 C.WriteLine ("if (object_type) return object_type;");
773 C.WriteLine ();
774 C.WriteLine ("static const GTypeInfo object_info =");
775 C.WriteLine ("{");
776 C.WriteLine ("sizeof (" + CurTypeClass + "),");
777 C.WriteLine ("(GBaseInitFunc) NULL, /* base_init */");
778 C.WriteLine ("(GBaseFinalizeFunc) NULL, /* base_finalize */");
779 if (t.IsClass)
780 C.WriteLine ("(GClassInitFunc) " + cur_type + "_class_init, /* class_init */");
781 else
782 C.WriteLine ("NULL, /* class_init */");
783 C.WriteLine ("NULL, /* class_finalize */");
784 C.WriteLine ("NULL, /* class_data */");
785 if (t.IsClass)
786 C.WriteLine ("sizeof (" + CurType + "),");
787 else
788 C.WriteLine ("0,");
789 C.WriteLine ("0, /* n_preallocs */");
790 if (t.IsClass)
791 C.WriteLine ("(GInstanceInitFunc) " + cur_type + "_init, /* instance_init */");
792 else
793 C.WriteLine ("NULL, /* instance_init */");
794 C.WriteLine ("};");
795 C.WriteLine ();
797 foreach (Type iface in ifaces) {
798 if (!IsRegistered (iface))
799 continue;
801 C.WriteLine ("static const GInterfaceInfo " + CamelToC (iface.Namespace) + "_" + CamelToC (iface.Name) + "_info" + " =");
802 C.WriteLine ("{");
803 C.WriteLine ("(GInterfaceInitFunc) " + NsToC (iface.Namespace) + "_" + CamelToC (iface.Name) + "_interface_init, /* interface_init */");
804 C.WriteLine ("(GInterfaceFinalizeFunc) NULL, /* interface_finalize */");
805 C.WriteLine ("NULL, /* interface_data */");
806 C.WriteLine ("};");
807 C.WriteLine ();
810 if (t.BaseType != null) {
811 string parent_type = "G_TYPE_OBJECT";
812 if (IsRegistered (t.BaseType))
813 parent_type = NsToC (t.BaseType.Namespace).ToUpper () + "_TYPE_" + CamelToC (t.BaseType.Name).ToUpper ();
815 C.WriteLine ("object_type = g_type_register_static (" + parent_type + ", \"" + CurType + "\", &object_info, 0);");
818 foreach (Type iface in ifaces) {
819 if (!IsRegistered (iface))
820 continue;
822 C.WriteLine ("g_type_add_interface_static (object_type, " + NsToC (iface.Namespace).ToUpper () + "_TYPE_" + CamelToC (iface.Name).ToUpper () + ", &" + NsToC (iface.Namespace) + "_" + CamelToC (iface.Name) + "_info" + ");");
824 C.WriteLine ();
825 C.WriteLine ("return object_type;");
826 C.WriteLine ("}");
828 H.WriteLine ();
829 H.WriteLine ("G_END_DECLS");
832 static bool TypeIsGObject (Type t)
834 if (t == null)
835 return false;
837 if (t.FullName == "GLib.Object")
838 return true;
840 return TypeIsGObject (t.BaseType);
844 //FIXME: clean up this mess with hits as the type registry uses strings
846 static ArrayList registered_types = new ArrayList ();
847 static ArrayList byval_types = new ArrayList ();
848 static ArrayList external_types = new ArrayList ();
849 static Hashtable registry_hits = new Hashtable ();
851 static bool IsRegisteredByVal (Type t)
853 return byval_types.Contains (CsTypeToFlat (t));
856 static bool IsExternal (Type t)
858 return external_types.Contains (CsTypeToFlat (t));
861 static void RegisterByVal (string tn)
863 //TODO: warn on dupes
864 byval_types.Add (tn);
867 static void RegisterByVal (Type t)
869 RegisterByVal (CsTypeToFlat (t));
872 static bool IsRegistered (String tn)
874 return registered_types.Contains (tn);
877 static bool IsRegistered (Type t)
879 return IsRegistered (t, true);
882 static bool IsRegistered (Type t, bool log_hits)
884 return IsRegistered (CsTypeToFlat (t), true);
887 static bool IsRegistered (string tn, bool log_hits)
889 //bool isreg = registered_types.Contains (t);
890 bool isreg = registered_types.Contains (tn);
892 if (!isreg && log_hits) {
893 HitRegistry (tn);
896 return isreg;
899 static void HitRegistry (string tn)
901 //FIXME: ignore handled primitive types here
903 if (!registry_hits.Contains (tn)) {
904 int count = 0;
905 registry_hits[tn] = count;
908 registry_hits[tn] = (int) registry_hits[tn] + 1;
911 static bool RegisterG (string G)
913 if (IsRegistered (G, false)) {
914 Console.WriteLine ("Warning: unmanaged type " + G + " already registered! Can't re-register.");
915 return false;
918 external_types.Add (G);
919 registered_types.Add (G);
920 return true;
923 static string NewG (string G)
925 if (IsRegistered (G, false))
927 Console.WriteLine ("Warning: type " + G + " already registered! Appending 'Extra' and trying again");
928 Console.WriteLine ();
929 return NewG (G + "Extra"); //FIXME: handle this properly
932 registered_types.Add (G);
933 return (G);
936 static string CsTypeToFlat (Type t) //TODO: use this everywhere
938 //TODO: check registry to see if t.Name's name has been changed during NewG.
939 //if it's not in the registry, continue as usual
941 return NsToFlat (t.Namespace) + t.Name;
944 static void RegisterType (Type t)
946 NewG (CsTypeToFlat (t));
950 static string NewG (Type t)
952 return NewG (CsTypeToFlat (t));
956 static string CsTypeToG (Type t)
958 if (IsRegistered (t))
959 return CsTypeToFlat (t);
961 return "GObject";
964 static string GToGI (string G)
966 string possGC = G + "Iface";
967 //TODO: conflict resolution
969 return possGC;
972 //static string CsTypeToGC (String tn)
973 static string GToGC (string G)
975 string possGC = G + "Class";
977 if (IsRegistered (possGC))
978 return GToGC (G + "Object");
979 else
980 return possGC;
983 static string CsTypeToC (Type t)
985 //TODO: use this method everywhere
987 switch (t.FullName)
989 case "System.String":
990 return "const gchar *";
992 case "System.Int32":
993 return "gint ";
995 case "System.UInt32":
996 return "guint ";
998 case "System.Boolean":
999 return "gboolean ";
1001 case "System.IntPtr":
1002 return "gpointer ";
1004 case "System.Char":
1005 return "guint16 ";
1007 case "System.SByte":
1008 return "gint8 ";
1010 case "System.Byte":
1011 return "guint8 ";
1013 case "System.Double":
1014 return "gdouble ";
1016 //questionable
1017 case "System.EventHandler":
1018 case "System.MulticastDelegate":
1019 return "GCallback ";
1022 if (IsRegistered (t) && IsRegisteredByVal (t))
1023 return CsTypeToFlat (t) + " ";
1025 if (t == typeof (void))
1026 return "void ";
1028 return CsTypeToG (t) + " *";
1031 static void EventGen (EventInfo ei, Type t)
1033 //Console.WriteLine ("TODO: event: " + ei.Name);
1034 //Console.WriteLine ("\t" + CamelToC (ei.Name));
1035 string name = CamelToC (ei.Name);
1037 C.WriteLine ();
1038 C.WriteLine ("signals[" + name.ToUpper () + "] = g_signal_new (");
1039 C.WriteLine ("\"" + name + "\",");
1040 C.WriteLine ("G_OBJECT_CLASS_TYPE (object_class),");
1041 C.WriteLine ("G_SIGNAL_RUN_LAST,");
1042 C.WriteLine ("G_STRUCT_OFFSET (" + CurTypeClass + ", " + name + "),");
1043 C.WriteLine ("NULL, NULL,");
1044 C.WriteLine ("g_cclosure_marshal_VOID__VOID,");
1045 C.WriteLine ("G_TYPE_NONE, 0");
1046 C.WriteLine (");");
1049 static void ConstructorGen (ConstructorInfo c, Type t)
1051 ParameterInfo[] parameters = c.GetParameters ();
1052 FunctionGen (parameters, (MethodBase) c, t, null, true);
1055 static void MethodGen (MethodInfo m, Type t)
1057 ParameterInfo[] parameters = m.GetParameters ();
1058 FunctionGen (parameters, (MethodBase) m, t, m.ReturnType, false);
1061 static readonly string[] keywords = {"auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while"};
1063 static string KeywordAvoid (string s)
1065 if (Array.IndexOf (keywords, s.ToLower ()) != -1)
1066 return KeywordAvoid ("_" + s);
1068 return s;
1071 static string ToValidFuncName (string name)
1073 //avoid generated function name conflicts with internal functions
1075 switch (name.ToLower ()) {
1076 case "init":
1077 return "initialize";
1078 case "class_init":
1079 return "class_initialize";
1080 case "get_type":
1081 return "retrieve_type";
1082 default:
1083 return name;
1087 static bool wrap_gobject = false;
1089 static void FunctionGen (ParameterInfo[] parameters, MethodBase m, Type t, Type ret_type, bool ctor)
1091 string myargs = String.Empty;
1092 bool has_return = !ctor && ret_type != null && ret_type != typeof (void);
1093 bool stat = m.IsStatic;
1095 string mytype, rettype;
1097 mytype = CurType + " *";
1099 if (ctor) {
1100 has_return = true;
1101 rettype = mytype;
1102 stat = true;
1103 } else
1104 rettype = CsTypeToC (ret_type);
1106 string params_arg = "NULL";
1107 if (parameters.Length != 0)
1108 params_arg = "_mono_params";
1110 string instance = "thiz";
1111 string mono_obj = "NULL";
1113 if (ctor || !stat)
1114 mono_obj = "_mono_object";
1116 //if (ctor || !stat)
1117 // mono_obj = instance + "->priv->mono_object";
1119 if (!stat) {
1120 myargs = mytype + instance;
1121 if (parameters.Length > 0) myargs += ", ";
1124 string myname;
1126 myname = cur_type + "_";
1127 if (ctor)
1128 myname += "new";
1129 else
1130 myname += ToValidFuncName (CamelToC (m.Name));
1132 //handle overloaded methods
1133 //TODO: generate an alias function for the default ctor etc.
1135 //TODO: how do we choose the default ctor/method overload? perhaps the
1136 //first/shortest, but we need scope for this
1137 //perhaps use DefaultMemberAttribute, Type.GetDefaultMembers
1139 if (funcs_done.Contains (myname)) {
1140 for (int i = 0 ; i < parameters.Length ; i++) {
1141 ParameterInfo p = parameters[i];
1143 if (i == 0)
1144 myname += "_with_";
1145 else
1146 myname += "_and_";
1148 myname += KeywordAvoid (p.Name);
1152 if (funcs_done.Contains (myname))
1153 return;
1155 funcs_done.Add (myname);
1157 //handle the parameters
1158 string mycsargs = String.Empty;
1160 for (int i = 0 ; i < parameters.Length ; i++) {
1161 ParameterInfo p = parameters[i];
1162 mycsargs += GetMonoType (Type.GetTypeCode (p.ParameterType));
1163 myargs += CsTypeToC (p.ParameterType) + KeywordAvoid (p.Name);
1164 if (i != parameters.Length - 1) {
1165 mycsargs += ",";
1166 myargs += ", ";
1170 if (myargs == String.Empty)
1171 myargs = "void";
1173 C.WriteLine ();
1175 C.WriteLine (rettype + myname + " (" + myargs + ")", H, ";");
1177 C.WriteLine ("{");
1179 C.WriteLine ("static MonoMethod *_mono_method = NULL;");
1181 if (ctor || !stat)
1182 C.WriteLine ("MonoObject *" + mono_obj + ";");
1184 if (parameters.Length != 0) C.WriteLine ("gpointer " + params_arg + "[" + parameters.Length + "];");
1186 if (ctor) {
1187 C.WriteLine (CurType + " *" + instance + ";");
1190 if (!ctor && !stat) {
1191 C.WriteLine ();
1192 C.WriteLine (mono_obj + " = g_object_get_data (G_OBJECT (" + instance + "), \"mono-object\");");
1195 C.WriteLine ();
1197 C.WriteLine ("if (_mono_method == NULL) {");
1199 if (ctor)
1200 C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":.ctor(" + mycsargs + ")\", FALSE);");
1201 else
1202 C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":" + m.Name + "(" + mycsargs + ")" + "\", FALSE);");
1205 C.WriteLine ("_mono_method = mono_method_desc_search_in_class (_mono_method_desc, " + cur_type + "_get_mono_class ());");
1207 C.WriteLine ("}");
1208 C.WriteLine ();
1210 //assign the parameters
1211 for (int i = 0 ; i < parameters.Length ; i++) {
1212 ParameterInfo p = parameters[i];
1213 C.WriteLine (params_arg + "[" + i + "] = " + GetMonoVal (p.ParameterType, KeywordAvoid (p.Name)) + ";");
1216 if (parameters.Length != 0)
1217 C.WriteLine ();
1219 if (ctor)
1220 C.WriteLine (mono_obj + " = (MonoObject*) mono_object_new ((MonoDomain*) " + NsToC (ns) + "_get_mono_domain ()" + ", " + cur_type + "_get_mono_class ());");
1222 //delegates are a special case as we want their constructor to take a function pointer
1223 if (ctor && t.IsSubclassOf (typeof (MulticastDelegate))) {
1224 C.WriteLine ("mono_delegate_ctor (" + mono_obj + ", object, method);");
1225 } else {
1226 //code to invoke the method
1228 if (!ctor && has_return)
1229 if (IsRegisteredByVal (ret_type)) {
1230 C.WriteLine ("{");
1231 C.WriteLine (rettype + "* retval = (" + rettype + "*) mono_object_unbox (mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL));");
1232 C.WriteLine ("return (" + rettype + ") *retval;");
1233 C.WriteLine ("}");
1234 } else if (rettype == "const gchar *")
1236 //convert the MonoString to a UTF8 before returning
1237 C.WriteLine ("return (" + rettype + ") mono_string_to_utf8 ((MonoString*) mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL));");
1238 } else {
1239 //TODO: this isn't right
1240 C.WriteLine ("return (" + rettype + ") mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL);");
1242 else
1243 C.WriteLine ("mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL);");
1246 if (ctor) {
1247 C.WriteLine ();
1249 //TODO: use ->priv, not data for better performance if not wrapping a gobject
1250 if (wrap_gobject)
1251 C.WriteLine (instance + " = (" + CurType + " *) " + NsToC (ns) + "_cilc_glib_mobject_get_gobject (" + mono_obj + ");");
1252 else
1253 C.WriteLine (instance + " = (" + CurType + " *) g_object_new (" + NsToC (ns).ToUpper () + "_TYPE_" + CamelToC (t.Name).ToUpper () + ", NULL);");
1255 C.WriteLine ("g_object_set_data (G_OBJECT (" + instance + "), \"mono-object\", " + mono_obj + ");");
1256 C.WriteLine ();
1257 C.WriteLine ("return " + instance + ";");
1260 C.WriteLine ("}");
1263 static string GetMonoType (TypeCode tc)
1265 //see mcs/class/corlib/System/TypeCode.cs
1266 //see mono/mono/dis/get.c
1268 switch (tc)
1270 case TypeCode.Int32:
1271 return "int";
1273 case TypeCode.String:
1274 return "string";
1276 default: //TODO: construct signature based on mono docs
1277 return tc.ToString ().ToLower ();
1281 static string GetMonoVal (Type t, string name)
1283 string type = t.FullName;
1285 if (TypeIsGObject (t))
1286 return "(gpointer*) " + NsToC (ns) + "_cilc_glib_gobject_get_mobject (G_OBJECT (" + name + "))";
1288 switch (type) {
1289 case "System.String":
1290 return "(gpointer*) mono_string_new ((MonoDomain*) mono_domain_get (), " + name + ")";
1292 case "System.Int32":
1293 return "&" + name;
1295 default:
1296 return "&" + name;
1300 static string NsToC (string s)
1302 if (s == null)
1303 return String.Empty;
1305 s = s.Replace ('.', '_');
1306 return CamelToC (s);
1309 static string NsToFlat (string s)
1311 if (s == null)
1312 return String.Empty;
1314 s = s.Replace (".", String.Empty);
1315 return s;
1318 static string CamelToC (string s)
1320 //converts camel case to c-style
1322 string o = String.Empty;
1324 bool prev_is_cap = true;
1326 foreach (char c in s) {
1327 char cl = c.ToString ().ToLower ()[0];
1328 bool is_cap = c != cl;
1330 if (!prev_is_cap && is_cap) {
1331 o += "_";
1334 o += cl;
1335 prev_is_cap = is_cap;
1337 if (c == '_')
1338 prev_is_cap = true;
1341 return o;