1 // cilc -- a CIL-to-C binding generator
2 // Copyright (C) 2003, 2004, 2005 Alp Toker <alp@atoker.com>
3 // Licensed under the terms of the GNU GPL
7 using System.Reflection;
8 using System.Collections;
9 using System.Diagnostics;
10 using System.Text.RegularExpressions;
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...]]");
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) {
44 } else if (args.Length == 2) {
45 Generate (args[0], args[1]);
46 } else if (args.Length == 3) {
47 RegisterPkg (args[1], args[2]);
49 } else if (args.Length == 4) {
50 RegisterPkg (args[2], args[3]);
51 Generate (args[0], args[1]);
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.");
66 Generate (aname, tmpdir);
67 Console.Write ("Compiling unmanaged binding");
68 RunWithReport ("make", "-C \"" + tmpdir + "\" bundle=true");
70 Console.Write ("Installing to current directory");
71 RunWithReport ("make", "-C \"" + tmpdir + "\" install prefix=\"" + cwd + "\"");
72 Directory.Delete (tmpdir, true);
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 ();
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);
103 while ((line = p.StandardOutput.ReadLine ()) != null)
105 Console.WriteLine (line);
109 Console.WriteLine ();
111 Console.Write (p.StandardError.ReadToEnd ());
118 static bool verbose = false;
120 static string extpkgs = "";
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 = "";
140 foreach (string include in extincludes)
141 cppincludes += " -include " + include;
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);
155 Regex re_type = new Regex (@"typedef struct (_\w+) (\w+);");
156 Regex re_enum = new Regex (@"} (\w+);");
158 while ((line = p.StandardOutput.ReadLine ()) != null) {
163 m = re_type.Match (line);
165 string G = m.Groups[2].Value;
170 if (G.EndsWith ("Class"))
177 m = re_enum.Match (line);
179 string G = m.Groups[1].Value;
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))
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.");
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);
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 ());
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/')");
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) {
291 //Console.WriteLine ("Ignoring non-public type: " + t.Name);
292 //warnings_ignored++;
296 if (!t.IsClass && !t.IsEnum) {
297 //Console.WriteLine ("Ignoring unrecognised type: " + t.Name);
307 string tns = t.Namespace == null ? "" : 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;
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);
337 string Hdecls_id = "__" + NsToFlat (ns).ToUpper () + "_DECLS_H__";
338 Hdecls.WriteLine ("#ifndef " + Hdecls_id);
339 Hdecls.WriteLine ("#define " + Hdecls_id);
342 Cindex.WriteLine ("#include <glib.h>");
343 Cindex.WriteLine ("#include <glib-object.h>");
344 Cindex.WriteLine ("#include <mono/jit/jit.h>");
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>");
350 Cindex.WriteLine ("#ifdef CILC_BUNDLE");
351 Cindex.WriteLine ("#include \"bundle.h\"");
352 Cindex.WriteLine ("#endif");
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\");");
362 Cindex.WriteLine ("#ifdef CILC_BUNDLE");
363 Cindex.WriteLine ("mono_register_bundled_assemblies (bundled);");
364 Cindex.WriteLine ("#endif");
367 Cindex.WriteLine ("return domain;");
368 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 + "\");");
378 Cindex.WriteLine ("return assembly;");
379 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;");
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 ("}");
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 ("}");
413 Console.Write ("Generating sources in " + ns);
414 foreach (Type t in types) {
419 Console.WriteLine ();
422 Hindex.WriteLine ("#endif /* " + Hindex_id + " */");
425 Hdecls.WriteLine ("#endif /* " + Hdecls_id + " */");
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);
442 cur_type = NsToC (ns) + "_" + CamelToC (t.Name);
443 //CurType = NsToFlat (ns) + t.Name;
444 CurType = CsTypeToG (t);
445 CurTypeClass = GToGC (CurType);
448 string fname = NsToFlat (ns).ToLower () + t.Name.ToLower ();
449 C = new CodeWriter (target_dir + fname + ".c");
450 H = new CodeWriter (target_dir + fname + ".h");
451 Hindex.WriteLine ("#include <" + fname + ".h" + ">");
454 string H_id = "__" + NsToFlat (ns).ToUpper () + "_" + t.Name.ToUpper () + "_H__";
455 H.WriteLine ("#ifndef " + H_id);
456 H.WriteLine ("#define " + H_id);
459 H.WriteLine ("#include <glib.h>");
460 H.WriteLine ("#include <glib-object.h>");
462 foreach (string include in extincludes)
463 H.WriteLine ("#include <" + include + ">");
467 if (IsRegistered (t.BaseType) && !IsExternal (t.BaseType))
468 H.WriteLine ("#include \"" + NsToFlat (t.BaseType.Namespace).ToLower () + t.BaseType.Name.ToLower () + ".h\"");
470 foreach (string ext_ns in namespaces)
471 H.WriteLine ("#include \"" + NsToFlat (ext_ns).ToLower () + "types.h\"");
475 H.WriteLine ("#ifdef __cplusplus");
476 H.WriteLine ("extern \"C\" {", false);
477 H.WriteLine ("#endif /* __cplusplus */");
480 C.WriteLine ("#include \"" + fname + ".h" + "\"");
481 C.WriteLine ("#include <mono/metadata/object.h>");
482 C.WriteLine ("#include <mono/metadata/debug-helpers.h>");
483 C.WriteLine ("#include <mono/metadata/appdomain.h>");
492 H.WriteLine ("#ifdef __cplusplus");
493 H.WriteLine ("}", false);
494 H.WriteLine ("#endif /* __cplusplus */");
497 H.WriteLine ("#endif /* " + H_id + " */");
503 static void EnumGen (Type t)
505 //TODO: we needn't split out each enum into its own file
507 string gname = CsTypeToG (t);
509 Hdecls.WriteLine ("typedef enum");
510 Hdecls.WriteLine ("{");
511 C.WriteLine ("GType " + cur_type + "_get_type (void)", H, ";");
513 C.WriteLine ("static GType etype = 0;");
514 C.WriteLine ("if (etype == 0) {");
515 C.WriteLine ("static const GEnumValue values[] = {");
516 foreach (FieldInfo fi in t.GetFields (BindingFlags.Static|BindingFlags.Public)) {
517 string finame = (cur_type + "_" + CamelToC (fi.Name)).ToUpper ();
518 Hdecls.WriteLine (finame + ",");
519 C.WriteLine ("{ " + finame + ", \"" + finame + "\", \"" + CamelToC (fi.Name).Replace ("_", "-") + "\" },");
521 Hdecls.WriteLine ("} " + gname + ";");
523 C.WriteLine ("{ 0, NULL, NULL }");
525 C.WriteLine ("etype = g_enum_register_static (\"" + gname + "\", values);");
527 C.WriteLine ("return etype;");
531 static void ClassGen (Type t)
533 //TODO: what flags do we want for GetEvents and GetConstructors?
537 events = t.GetEvents (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
540 ifaces = t.GetInterfaces ();
542 H.WriteLine ("G_BEGIN_DECLS");
546 string NS = NsToC (ns).ToUpper ();
547 string T = CamelToC (t.Name).ToUpper ();
548 string NST = NS + "_" + T;
549 string NSTT = NS + "_TYPE_" + T;
551 H.WriteLine ("#define " + NSTT + " (" + cur_type + "_get_type ())");
552 H.WriteLine ("#define " + NST + "(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), " + NSTT + ", " + CurType + "))");
553 H.WriteLine ("#define " + NST + "_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), " + NSTT + ", " + CurTypeClass + "))");
554 H.WriteLine ("#define " + NS + "_IS_" + T + "(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), " + NSTT + "))");
555 H.WriteLine ("#define " + NS + "_IS_" + T + "_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), " + NSTT + "))");
556 H.WriteLine ("#define " + NST + "_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), " + NSTT + ", " + CurTypeClass + "))");
559 if (!C.IsDuplicate) {
560 Hdecls.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
561 Hdecls.WriteLine ("typedef struct _" + CurTypeClass + " " + CurTypeClass + ";");
566 //H.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
569 //H.WriteLine ("typedef struct _" + CurType + "Class " + CurType + "Class;");
570 H.WriteLine ("typedef struct _" + CurType + "Private " + CurType + "Private;");
572 H.WriteLine ("struct _" + CurType);
575 string ParentName = CsTypeToG (t.BaseType);
576 string ParentNameClass = GToGC (ParentName);
578 H.WriteLine (ParentName + " parent_instance;");
579 H.WriteLine (CurType + "Private *priv;");
582 H.WriteLine ("struct _" + CurTypeClass);
584 H.WriteLine (ParentNameClass + " parent_class;" + " /* inherits " + t.BaseType.Namespace + " " + t.BaseType.Name + " */");
586 if (events.Length != 0)
589 //TODO: event arguments
590 foreach (EventInfo ei in events)
591 H.WriteLine ("void (* " + CamelToC (ei.Name) + ") (" + CurType + " *thiz" + ");");
599 C.WriteLine ("struct _" + CurType + "Private");
601 C.WriteLine ("MonoObject *mono_object;");
607 if (events.Length != 0) {
608 C.WriteLine ("enum {");
610 foreach (EventInfo ei in events)
611 C.WriteLine (CamelToC (ei.Name).ToUpper () + ",");
613 C.WriteLine ("LAST_SIGNAL");
618 C.WriteLine ("static gpointer parent_class;");
620 if (events.Length == 0)
621 C.WriteLine ("static guint signals[0];");
623 C.WriteLine ("static guint signals[LAST_SIGNAL] = { 0 };");
626 C.WriteLine ("static MonoClass *" + cur_type + "_get_mono_class (void)");
628 C.WriteLine ("MonoAssembly *assembly;");
629 C.WriteLine ("static MonoClass *class = NULL;");
630 C.WriteLine ("if (class != NULL) return class;");
632 C.WriteLine ("assembly = (MonoAssembly*) " + NsToC (ns) + "_get_mono_assembly ();");
633 C.WriteLine ("class = (MonoClass*) mono_class_from_name ((MonoImage*) mono_assembly_get_image (assembly)" + ", \"" + ns + "\", \"" + t.Name + "\");");
635 C.WriteLine ("mono_class_init (class);");
638 C.WriteLine ("return class;");
643 //generate the GObject init function
644 C.WriteLine ("static void " + cur_type + "_init (" + CurType + " *thiz" + ")");
646 C.WriteLine ("thiz->priv = g_new0 (" + CurType + "Private, 1);");
652 //generate the GObject class init function
653 C.WriteLine ("static void " + cur_type + "_class_init (" + CurTypeClass + " *klass" + ")");
656 C.WriteLine ("GObjectClass *object_class = G_OBJECT_CLASS (klass);");
657 C.WriteLine ("parent_class = g_type_class_peek_parent (klass);");
658 //C.WriteLine ("object_class->finalize = _finalize;");
660 foreach (EventInfo ei in events)
666 C.WriteLine ("static void " + cur_type + "_interface_init (gpointer g_iface, gpointer iface_data)");
669 foreach (Type iface in ifaces) {
670 C.WriteLine ("//" + CsTypeToG (iface) + "Interface" + " *iface = (" + CsTypeToG (iface) + "Interface" + ")g_iface;");
671 foreach (MethodInfo imi in iface.GetMethods (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly)) {
672 string funcname = ToValidFuncName (CamelToC (imi.Name));
673 C.WriteLine ("//iface->" + funcname + " = " + cur_type + "_" + funcname + ";");
675 //TODO: properties etc.
682 //generate the GObject get_type function
683 C.WriteLine ("GType " + cur_type + "_get_type (void)", H, ";");
685 C.WriteLine ("static GType object_type = 0;");
686 C.WriteLine ("g_type_init ();");
688 C.WriteLine ("if (object_type) return object_type;");
690 C.WriteLine ("static const GTypeInfo object_info =");
692 C.WriteLine ("sizeof (" + CurTypeClass + "),");
693 C.WriteLine ("(GBaseInitFunc) NULL, /* base_init */");
694 C.WriteLine ("(GBaseFinalizeFunc) NULL, /* base_finalize */");
695 C.WriteLine ("(GClassInitFunc) " + cur_type + "_class_init, /* class_init */");
696 C.WriteLine ("NULL, /* class_finalize */");
697 C.WriteLine ("NULL, /* class_data */");
698 C.WriteLine ("sizeof (" + CurType + "),");
699 C.WriteLine ("0, /* n_preallocs */");
700 C.WriteLine ("(GInstanceInitFunc) " + cur_type + "_init,");
704 foreach (Type iface in ifaces) {
705 C.WriteLine ("//static const GInterfaceInfo bar_ifoo_info = {};");
708 string parent_type = "G_TYPE_OBJECT";
709 if (IsRegistered (t.BaseType))
710 parent_type = NsToC (t.BaseType.Namespace).ToUpper () + "_TYPE_" + CamelToC (t.BaseType.Name).ToUpper ();
712 C.WriteLine ("object_type = g_type_register_static (" + parent_type + ", \"" + CurType + "\", &object_info, 0);");
714 foreach (Type iface in ifaces)
715 C.WriteLine ("//g_type_add_interface_static (object_type, BAR_IFOO_TYPE, bar_ifoo_info);");
717 C.WriteLine ("return object_type;");
720 wrap_gobject = TypeIsGObject (t);
722 //generate constructors
723 ConstructorInfo[] constructors;
724 constructors = t.GetConstructors ();
725 foreach (ConstructorInfo c in constructors)
726 ConstructorGen (c, t);
728 //generate static methods
729 MethodInfo[] methods;
730 methods = t.GetMethods (BindingFlags.Public|BindingFlags.Static|BindingFlags.DeclaredOnly);
731 foreach (MethodInfo m in methods)
734 //generate instance methods
735 methods = t.GetMethods (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
736 foreach (MethodInfo m in methods)
740 H.WriteLine ("G_END_DECLS");
743 static bool TypeIsGObject (Type t)
748 if (t.FullName == "GLib.Object")
751 return TypeIsGObject (t.BaseType);
755 //FIXME: clean up this mess with hits as the type registry uses strings
757 static ArrayList registered_types = new ArrayList ();
758 static ArrayList byval_types = new ArrayList ();
759 static ArrayList external_types = new ArrayList ();
760 static Hashtable registry_hits = new Hashtable ();
762 static bool IsRegisteredByVal (Type t)
764 return byval_types.Contains (CsTypeToFlat (t));
767 static bool IsExternal (Type t)
769 return external_types.Contains (CsTypeToFlat (t));
772 static void RegisterByVal (string tn)
774 //TODO: warn on dupes
775 byval_types.Add (tn);
778 static void RegisterByVal (Type t)
780 RegisterByVal (CsTypeToFlat (t));
783 static bool IsRegistered (String tn)
785 return registered_types.Contains (tn);
788 static bool IsRegistered (Type t)
790 return IsRegistered (t, true);
793 static bool IsRegistered (Type t, bool log_hits)
795 return IsRegistered (CsTypeToFlat (t), true);
798 static bool IsRegistered (string tn, bool log_hits)
800 //bool isreg = registered_types.Contains (t);
801 bool isreg = registered_types.Contains (tn);
803 if (!isreg && log_hits) {
810 static void HitRegistry (string tn)
812 //FIXME: ignore handled primitive types here
814 if (!registry_hits.Contains (tn)) {
816 registry_hits[tn] = count;
819 registry_hits[tn] = (int) registry_hits[tn] + 1;
822 static bool RegisterG (string G)
824 if (IsRegistered (G, false)) {
825 Console.WriteLine ("Warning: unmanaged type " + G + " already registered! Can't re-register.");
829 external_types.Add (G);
830 registered_types.Add (G);
834 static string NewG (string G)
836 if (IsRegistered (G, false))
838 Console.WriteLine ("Warning: type " + G + " already registered! Appending 'Extra' and trying again");
839 Console.WriteLine ();
840 return NewG (G + "Extra"); //FIXME: handle this properly
843 registered_types.Add (G);
847 static string CsTypeToFlat (Type t) //TODO: use this everywhere
849 //TODO: check registry to see if t.Name's name has been changed during NewG.
850 //if it's not in the registry, continue as usual
852 return NsToFlat (t.Namespace) + t.Name;
855 static void RegisterType (Type t)
857 NewG (CsTypeToFlat (t));
861 static string NewG (Type t)
863 return NewG (CsTypeToFlat (t));
867 static string CsTypeToG (Type t)
869 if (IsRegistered (t))
870 return CsTypeToFlat (t);
875 //static string CsTypeToGC (String tn)
876 static string GToGC (string G)
878 string possGC = G + "Class";
880 if (IsRegistered (possGC))
881 return GToGC (G + "Object");
886 static string CsTypeToC (Type t)
888 //TODO: use this method everywhere
892 case "System.String":
893 return "const gchar *";
898 case "System.UInt32":
901 case "System.Boolean":
904 case "System.IntPtr":
916 case "System.Double":
920 case "System.EventHandler":
921 case "System.MulticastDelegate":
925 if (IsRegistered (t) && IsRegisteredByVal (t))
926 return CsTypeToFlat (t) + " ";
928 if (t == typeof (void))
931 return CsTypeToG (t) + " *";
934 static void EventGen (EventInfo ei, Type t)
936 //Console.WriteLine ("TODO: event: " + ei.Name);
937 //Console.WriteLine ("\t" + CamelToC (ei.Name));
938 string name = CamelToC (ei.Name);
941 C.WriteLine ("signals[" + name.ToUpper () + "] = g_signal_new (");
942 C.WriteLine ("\"" + name + "\",");
943 C.WriteLine ("G_OBJECT_CLASS_TYPE (object_class),");
944 C.WriteLine ("G_SIGNAL_RUN_LAST,");
945 C.WriteLine ("G_STRUCT_OFFSET (" + CurTypeClass + ", " + name + "),");
946 C.WriteLine ("NULL, NULL,");
947 C.WriteLine ("g_cclosure_marshal_VOID__VOID,");
948 C.WriteLine ("G_TYPE_NONE, 0");
952 static void ConstructorGen (ConstructorInfo c, Type t)
954 ParameterInfo[] parameters = c.GetParameters ();
955 FunctionGen (parameters, (MethodBase) c, t, null, true);
958 static void MethodGen (MethodInfo m, Type t)
960 ParameterInfo[] parameters = m.GetParameters ();
961 FunctionGen (parameters, (MethodBase) m, t, m.ReturnType, false);
964 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"};
966 static string KeywordAvoid (string s)
968 if (Array.IndexOf (keywords, s.ToLower ()) != -1)
969 return KeywordAvoid ("_" + s);
974 static string ToValidFuncName (string name)
976 //avoid generated function name conflicts with internal functions
978 switch (name.ToLower ()) {
982 return "class_initialize";
984 return "retrieve_type";
990 static bool wrap_gobject = false;
992 static void FunctionGen (ParameterInfo[] parameters, MethodBase m, Type t, Type ret_type, bool ctor)
995 bool has_return = !ctor && ret_type != null && ret_type != typeof (void);
996 bool stat = m.IsStatic;
998 string mytype, rettype;
1000 mytype = CurType + " *";
1007 rettype = CsTypeToC (ret_type);
1009 string params_arg = "NULL";
1010 if (parameters.Length != 0)
1011 params_arg = "_mono_params";
1013 string instance = "thiz";
1014 string mono_obj = "NULL";
1017 mono_obj = "_mono_object";
1019 //if (ctor || !stat)
1020 // mono_obj = instance + "->priv->mono_object";
1023 myargs = mytype + instance;
1024 if (parameters.Length > 0) myargs += ", ";
1029 myname = cur_type + "_";
1033 myname += ToValidFuncName (CamelToC (m.Name));
1035 //handle overloaded methods
1036 //TODO: generate an alias function for the default ctor etc.
1038 //TODO: how do we choose the default ctor/method overload? perhaps the
1039 //first/shortest, but we need scope for this
1040 //perhaps use DefaultMemberAttribute, Type.GetDefaultMembers
1042 if (funcs_done.Contains (myname)) {
1043 for (int i = 0 ; i < parameters.Length ; i++) {
1044 ParameterInfo p = parameters[i];
1051 myname += KeywordAvoid (p.Name);
1055 if (funcs_done.Contains (myname))
1058 funcs_done.Add (myname);
1060 //handle the parameters
1061 string mycsargs = "";
1063 for (int i = 0 ; i < parameters.Length ; i++) {
1064 ParameterInfo p = parameters[i];
1065 mycsargs += GetMonoType (Type.GetTypeCode (p.ParameterType));
1066 myargs += CsTypeToC (p.ParameterType) + KeywordAvoid (p.Name);
1067 if (i != parameters.Length - 1) {
1078 C.WriteLine (rettype + myname + " (" + myargs + ")", H, ";");
1082 C.WriteLine ("static MonoMethod *_mono_method = NULL;");
1085 C.WriteLine ("MonoObject *" + mono_obj + ";");
1087 if (parameters.Length != 0) C.WriteLine ("gpointer " + params_arg + "[" + parameters.Length + "];");
1090 C.WriteLine (CurType + " *" + instance + ";");
1093 if (!ctor && !stat) {
1095 C.WriteLine (mono_obj + " = g_object_get_data (G_OBJECT (" + instance + "), \"mono-object\");");
1100 C.WriteLine ("if (_mono_method == NULL) {");
1103 C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":.ctor(" + mycsargs + ")\", FALSE);");
1105 C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":" + m.Name + "(" + mycsargs + ")" + "\", FALSE);");
1108 C.WriteLine ("_mono_method = mono_method_desc_search_in_class (_mono_method_desc, " + cur_type + "_get_mono_class ());");
1113 //assign the parameters
1114 for (int i = 0 ; i < parameters.Length ; i++) {
1115 ParameterInfo p = parameters[i];
1116 C.WriteLine (params_arg + "[" + i + "] = " + GetMonoVal (p.ParameterType, KeywordAvoid (p.Name)) + ";");
1119 if (parameters.Length != 0)
1123 C.WriteLine (mono_obj + " = (MonoObject*) mono_object_new ((MonoDomain*) " + NsToC (ns) + "_get_mono_domain ()" + ", " + cur_type + "_get_mono_class ());");
1125 //delegates are a special case as we want their constructor to take a function pointer
1126 if (ctor && t.IsSubclassOf (typeof (MulticastDelegate))) {
1127 C.WriteLine ("mono_delegate_ctor (" + mono_obj + ", object, method);");
1129 //code to invoke the method
1131 if (!ctor && has_return)
1132 if (IsRegisteredByVal (ret_type)) {
1134 C.WriteLine (rettype + "* retval = (" + rettype + "*) mono_object_unbox (mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL));");
1135 C.WriteLine ("return (" + rettype + ") *retval;");
1137 } else if (rettype == "const gchar *")
1139 //convert the MonoString to a UTF8 before returning
1140 C.WriteLine ("return (" + rettype + ") mono_string_to_utf8 ((MonoString*) mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL));");
1142 //TODO: this isn't right
1143 C.WriteLine ("return (" + rettype + ") mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL);");
1146 C.WriteLine ("mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL);");
1152 //TODO: use ->priv, not data for better performance if not wrapping a gobject
1154 C.WriteLine (instance + " = (" + CurType + " *) " + NsToC (ns) + "_cilc_glib_mobject_get_gobject (" + mono_obj + ");");
1156 C.WriteLine (instance + " = (" + CurType + " *) g_object_new (" + NsToC (ns).ToUpper () + "_TYPE_" + CamelToC (t.Name).ToUpper () + ", NULL);");
1158 C.WriteLine ("g_object_set_data (G_OBJECT (" + instance + "), \"mono-object\", " + mono_obj + ");");
1160 C.WriteLine ("return " + instance + ";");
1166 static string GetMonoType (TypeCode tc)
1168 //see mcs/class/corlib/System/TypeCode.cs
1169 //see mono/mono/dis/get.c
1173 case TypeCode.Int32:
1176 case TypeCode.String:
1179 default: //TODO: construct signature based on mono docs
1180 return tc.ToString ().ToLower ();
1184 static string GetMonoVal (Type t, string name)
1186 string type = t.FullName;
1188 if (TypeIsGObject (t))
1189 return "(gpointer*) " + NsToC (ns) + "_cilc_glib_gobject_get_mobject (G_OBJECT (" + name + "))";
1192 case "System.String":
1193 return "(gpointer*) mono_string_new ((MonoDomain*) mono_domain_get (), " + name + ")";
1195 case "System.Int32":
1203 static string NsToC (string s)
1208 s = s.Replace ('.', '_');
1209 return CamelToC (s);
1212 static string NsToFlat (string s)
1217 s = s.Replace (".", "");
1221 static string CamelToC (string s)
1223 //converts camel case to c-style
1227 bool prev_is_cap = true;
1229 foreach (char c in s) {
1230 char cl = c.ToString ().ToLower ()[0];
1231 bool is_cap = c != cl;
1233 if (!prev_is_cap && is_cap) {
1238 prev_is_cap = is_cap;
1250 private StreamWriter w;
1252 public CodeWriter (string fname)
1257 public bool IsDuplicate = false;
1259 void Init (string fname)
1261 if (File.Exists (fname)) {
1262 string newfname = fname + ".x";
1263 //Console.WriteLine ("Warning: File " + fname + " already exists, using " + newfname);
1269 FileStream fs = new FileStream (fname, FileMode.OpenOrCreate, FileAccess.Write);
1270 w = new StreamWriter (fs);
1273 public string Indenter = " ";
1274 string cur_indent = "";
1277 public void Indent ()
1281 for (int i = 0; i != level ; i++) cur_indent += Indenter;
1284 public void Outdent ()
1288 for (int i = 0; i != level ; i++) cur_indent += Indenter;
1291 public void Write (string text)
1296 public void WriteLine (string text)
1298 WriteLine (text, true);
1301 public void WriteLine (string text, bool autoindent)
1303 char[] opentags = {'{', '('};
1304 char[] closetags = {'}', ')'};
1306 if (autoindent && text.TrimStart (closetags) != text)
1309 w.Write (cur_indent);
1312 if (autoindent && text.TrimEnd (opentags) != text)
1316 public void WriteLine (string text, CodeWriter cc)
1318 WriteLine (text, "", cc, "");
1321 public void WriteLine (string text, CodeWriter cc, string suffix)
1323 WriteLine (text, "", cc, suffix);
1326 public void WriteLine (string text, string prefix, CodeWriter cc)
1328 WriteLine (text, prefix, cc, "");
1331 public void WriteLine (string text, string prefix, CodeWriter cc, string suffix)
1334 cc.WriteLine (prefix + text + suffix);
1337 public void WriteComment (string text)
1339 w.WriteLine ("/* " + text + " */");
1342 public void WriteLine ()
1347 public void Close ()