1 // cilc -- a CIL-to-C binding generator
2 // Copyright (C) 2003, 2004 Alp Toker <alp@atoker.com>
3 // Licensed under the terms of the GNU GPL
7 using System.Reflection;
8 using System.Collections;
14 static CodeWriter C, H, Cindex, Hindex, Hdecls;
15 static string ns, dllname;
16 static string cur_type, CurType;
17 static string target_dir;
19 static ArrayList funcs_done = new ArrayList ();
21 public static int Main (string[] args)
23 if (args.Length < 1 || args.Length > 2) {
24 Console.WriteLine ("Mono CIL-to-C binding generator");
25 Console.WriteLine ("Usage: cilc [options] assembly target");
32 Generate (args[0], Path.GetFileNameWithoutExtension (args[0]));
34 Generate (args[0], args[1]);
39 public static void Generate (string assembly, string target)
41 target_dir = target + Path.DirectorySeparatorChar;
43 if (Directory.Exists (target_dir)) {
44 Console.WriteLine ("Error: Target directory " + target_dir + " already exists.");
48 Directory.CreateDirectory (target_dir);
50 //TODO: parse given .h files here and register the type names
52 Assembly a = Assembly.LoadFrom (assembly);
53 dllname = Path.GetFileName (assembly);
56 string soname = NsToFlat (Path.GetFileNameWithoutExtension (assembly)).ToLower ();
59 CodeWriter makefile = new CodeWriter (target_dir + "Makefile");
60 makefile.Indenter = "\t";
61 makefile.WriteLine (@"OBJS = $(shell ls *.c | sed -e 's/\.c/.o/')");
62 makefile.WriteLine (@"CFLAGS = -static -fpic $(shell pkg-config --cflags glib-2.0 gobject-2.0 mono) -I.");
63 makefile.WriteLine ();
64 makefile.WriteLine ("all: lib" + soname + ".so");
65 makefile.WriteLine ();
66 makefile.WriteLine ("lib" + soname + ".so: $(OBJS)");
68 makefile.WriteLine ("gcc -Wall -fpic -shared `pkg-config --libs glib-2.0 gobject-2.0 mono` -lpthread $(OBJS) -o lib" + soname + ".so");
70 makefile.WriteLine ();
71 makefile.WriteLine ("clean:");
73 makefile.WriteLine ("rm -rf core *~ *.o *.so");
78 Console.WriteLine ("Type registry missed hits (by namespace):");
79 MakeReport (registry_hits);
82 static void MakeReport (Hashtable ctable)
84 string[] reg_keys = (string[]) (new ArrayList (ctable.Keys)).ToArray (typeof (string));
85 int[] reg_vals = (int[]) (new ArrayList (ctable.Values)).ToArray (typeof (int));
86 Array.Sort (reg_vals, reg_keys);
88 Array.Reverse (reg_vals);
89 Array.Reverse (reg_keys);
91 for (int i = 0 ; i != reg_keys.Length ; i++) {
92 Console.WriteLine (" " + reg_keys[i] + ": " + reg_vals[i]);
96 static void AssemblyGen (Assembly a)
98 Type[] types = a.GetTypes ();
99 Hashtable ns_types = new Hashtable ();
101 foreach (Type t in types) {
103 Console.WriteLine ("Ignoring non-public type: " + t.Name);
108 Console.WriteLine ("Ignoring unrecognised type: " + t.Name);
114 if (!ns_types.Contains (t.Namespace))
115 ns_types[t.Namespace] = new ArrayList ();
117 ((ArrayList) ns_types[t.Namespace]).Add (t);
120 namespaces = (string[]) (new ArrayList (ns_types.Keys)).ToArray (typeof (string));
122 foreach (DictionaryEntry de in ns_types)
123 NamespaceGen ((string) de.Key, (Type[]) ((ArrayList) de.Value).ToArray (typeof (Type)));
126 static string[] namespaces;
128 static void NamespaceGen (string given_ns, Type[] types)
130 //ns = types[0].Namespace;
133 Hindex = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + ".h");
134 Hdecls = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + "-decls.h");
135 Cindex = new CodeWriter (target_dir + NsToFlat (ns).ToLower () + ".c");
137 string Hindex_id = "__" + NsToFlat (ns).ToUpper () + "_H__";
138 Hindex.WriteLine ("#ifndef " + Hindex_id);
139 Hindex.WriteLine ("#define " + Hindex_id);
142 string Hdecls_id = "__" + NsToFlat (ns).ToUpper () + "_DECLS_H__";
143 Hdecls.WriteLine ("#ifndef " + Hdecls_id);
144 Hdecls.WriteLine ("#define " + Hdecls_id);
147 Cindex.WriteLine ("#include <glib.h>");
148 Cindex.WriteLine ("#include <mono/jit/jit.h>");
151 Cindex.WriteLine ("MonoDomain *" + NsToC (ns) + "_get_mono_domain (void)");
152 Cindex.WriteLine ("{");
153 Cindex.WriteLine ("static MonoDomain *domain = NULL;");
154 Cindex.WriteLine ("if (domain != NULL) return domain;");
155 Cindex.WriteLine ("domain = mono_jit_init (\"cilc\");");
158 Cindex.WriteLine ("return domain;");
159 Cindex.WriteLine ("}");
162 Cindex.WriteLine ("MonoAssembly *" + NsToC (ns) + "_get_mono_assembly (void)");
163 Cindex.WriteLine ("{");
164 Cindex.WriteLine ("static MonoAssembly *assembly = NULL;");
165 Cindex.WriteLine ("assembly = mono_domain_assembly_open (" + NsToC (ns) + "_get_mono_domain (), \"" + dllname + "\");");
168 Cindex.WriteLine ("return assembly;");
169 Cindex.WriteLine ("}");
172 foreach (Type t in types)
176 Hindex.WriteLine ("#endif /* " + Hindex_id + " */");
179 Hdecls.WriteLine ("#endif /* " + Hdecls_id + " */");
186 static void TypeGen (Type t)
188 //TODO: we only handle ordinary classes for now
190 else if (t.IsSubclassOf (typeof (Delegate))) {
191 Console.WriteLine ("Ignoring delegate: " + t.Name);
196 cur_type = NsToC (ns) + "_" + CamelToC (t.Name);
197 CurType = NsToFlat (ns) + t.Name;
200 string fname = NsToFlat (ns).ToLower () + t.Name.ToLower ();
201 C = new CodeWriter (target_dir + fname + ".c");
202 H = new CodeWriter (target_dir + fname + ".h");
203 Hindex.WriteLine ("#include <" + fname + ".h" + ">");
206 string H_id = "__" + NsToFlat (ns).ToUpper () + "_" + t.Name.ToUpper () + "_H__";
207 H.WriteLine ("#ifndef " + H_id);
208 H.WriteLine ("#define " + H_id);
211 H.WriteLine ("#include <glib.h>");
212 H.WriteLine ("#include <glib-object.h>");
213 H.WriteLine ("#include <mono/metadata/object.h>");
214 H.WriteLine ("#include <mono/metadata/debug-helpers.h>");
215 H.WriteLine ("#include <mono/metadata/appdomain.h>");
218 if (IsRegistered (t.BaseType))
219 H.WriteLine ("#include \"" + NsToFlat (t.BaseType.Namespace).ToLower () + t.BaseType.Name.ToLower () + ".h\"");
221 //H.WriteLine ("#include \"" + NsToFlat (t.Namespace).ToLower () + "-decls.h\"");
222 foreach (string ext_ns in namespaces)
223 H.WriteLine ("#include \"" + NsToFlat (ext_ns).ToLower () + "-decls.h\"");
227 H.WriteLine ("#ifdef __cplusplus");
228 H.WriteLine ("extern \"C\" {");
229 H.WriteLine ("#endif /* __cplusplus */");
232 C.WriteLine ("#include \"" + fname + ".h" + "\"");
241 H.WriteLine ("#ifdef __cplusplus");
243 H.WriteLine ("#endif /* __cplusplus */");
246 H.WriteLine ("#endif /* " + H_id + " */");
252 static void EnumGen (Type t)
254 //TODO: we needn't split out each enum into its own file
255 //TODO: just use glib-mkenums
257 C.WriteLine ("GType " + cur_type + "_get_type (void)");
259 C.WriteLine ("static GType etype = 0;");
260 C.WriteLine ("etype = g_enum_register_static (\"" + "\", NULL);");
261 C.WriteLine ("return etype;");
265 static void ClassGen (Type t)
267 //TODO: what flags do we want for GetEvents and GetConstructors?
271 events = t.GetEvents (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
274 H.WriteLine ("G_BEGIN_DECLS");
278 string NS = NsToC (ns).ToUpper ();
279 string T = CamelToC (t.Name).ToUpper ();
280 string NST = NS + "_" + T;
281 string NSTT = NS + "_TYPE_" + T;
283 H.WriteLine ("#define " + NSTT + " (" + cur_type + "_get_type ())");
284 H.WriteLine ("#define " + NST + "(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), " + NSTT + ", " + CurType + "))");
285 H.WriteLine ("#define " + NST + "_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), " + NSTT + ", " + CurType + "Class))");
286 H.WriteLine ("#define " + NS + "_IS_" + T + "(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), " + NSTT + "))");
287 H.WriteLine ("#define " + NS + "_IS_" + T + "_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), " + NSTT + "))");
288 H.WriteLine ("#define " + NST + "_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), " + NSTT + ", " + CurType + "Class))");
291 if (!C.IsDuplicate) {
292 Hdecls.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
293 Hdecls.WriteLine ("typedef struct _" + CurType + "Class " + CurType + "Class;");
298 //H.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
301 //H.WriteLine ("typedef struct _" + CurType + "Class " + CurType + "Class;");
302 H.WriteLine ("typedef struct _" + CurType + "Private " + CurType + "Private;");
304 H.WriteLine ("struct _" + CurType);
308 if (IsRegistered (t.BaseType))
309 ParentName = NsToFlat (t.BaseType.Namespace) + t.BaseType.Name;
311 ParentName = "GObject";
313 //H.WriteLine ("GObject parent_instance;");
314 H.WriteLine (ParentName + " parent_instance;");
315 H.WriteLine (CurType + "Private *priv;");
318 H.WriteLine ("struct _" + CurType + "Class");
320 H.WriteLine (ParentName + "Class parent_class;" + " /* inherits " + t.BaseType.Namespace + " " + t.BaseType.Name + " */");
323 //TODO: event arguments
324 foreach (EventInfo ei in events)
325 H.WriteLine ("void (* " + CamelToC (ei.Name) + ") (" + CurType + " *thiz" + ");");
333 C.WriteLine ("struct _" + CurType + "Private");
335 C.WriteLine ("MonoObject *mono_object;");
341 if (events.Length != 0) {
342 C.WriteLine ("enum {");
344 foreach (EventInfo ei in events)
345 C.WriteLine (CamelToC (ei.Name).ToUpper () + ",");
347 C.WriteLine ("LAST_SIGNAL");
353 //TODO: if the class inherits a known GLib Object, use its raw handle
354 C.WriteLine ("static gpointer parent_class;");
356 if (events.Length == 0)
357 C.WriteLine ("static guint signals[0];");
359 C.WriteLine ("static guint signals[LAST_SIGNAL] = { 0 };");
362 C.WriteLine ("static MonoClass *" + cur_type + "_get_mono_class (void)");
364 C.WriteLine ("MonoAssembly *assembly;");
365 C.WriteLine ("static MonoClass *class = NULL;");
366 C.WriteLine ("if (class != NULL) return class;");
368 C.WriteLine ("assembly = (MonoAssembly*) " + NsToC (ns) + "_get_mono_assembly ();");
369 C.WriteLine ("class = (MonoClass*) mono_class_from_name ((MonoImage*) mono_assembly_get_image (assembly)" + ", \"" + ns + "\", \"" + t.Name + "\");");
371 C.WriteLine ("mono_class_init (class);");
374 C.WriteLine ("return class;");
379 //generate the GObject init function
380 C.WriteLine ("static void " + cur_type + "_init (" + CurType + " *thiz" + ")");
382 C.WriteLine ("thiz->priv = g_new0 (" + CurType + "Private, 1);");
388 //generate the GObject class init function
389 C.WriteLine ("static void " + cur_type + "_class_init (" + CurType + "Class *klass" + ")");
392 C.WriteLine ("GObjectClass *object_class = G_OBJECT_CLASS (klass);");
393 C.WriteLine ("parent_class = g_type_class_peek_parent (klass);");
394 //C.WriteLine ("object_class->finalize = _finalize;");
396 foreach (EventInfo ei in events)
404 //generate the GObject get_type function
405 C.WriteLine ("GType " + cur_type + "_get_type (void)", H, ";");
407 C.WriteLine ("static GType object_type = 0;");
408 C.WriteLine ("g_type_init ();");
410 C.WriteLine ("if (object_type) return object_type;");
412 C.WriteLine ("static const GTypeInfo object_info =");
414 C.WriteLine ("sizeof (" + CurType + "Class),");
415 C.WriteLine ("(GBaseInitFunc) NULL,");
416 C.WriteLine ("(GBaseFinalizeFunc) NULL,");
417 C.WriteLine ("(GClassInitFunc) " + cur_type + "_class_init,");
418 C.WriteLine ("NULL, /* class_finalize */");
419 C.WriteLine ("NULL, /* class_data */");
420 C.WriteLine ("sizeof (" + CurType + "),");
421 C.WriteLine ("0, /* n_preallocs */");
422 C.WriteLine ("(GInstanceInitFunc) " + cur_type + "_init,");
426 string parent_type = "G_TYPE_OBJECT";
427 if (IsRegistered (t.BaseType))
428 parent_type = NsToC (t.BaseType.Namespace).ToUpper () + "_TYPE_" + CamelToC (t.BaseType.Name).ToUpper ();
430 C.WriteLine ("object_type = g_type_register_static (" + parent_type + ", \"" + CurType + "\", &object_info, 0);");
432 C.WriteLine ("return object_type;");
436 //generate constructors
437 ConstructorInfo[] constructors;
438 constructors = t.GetConstructors ();
439 foreach (ConstructorInfo c in constructors)
440 ConstructorGen (c, t);
442 //generate static methods
443 MethodInfo[] methods;
444 methods = t.GetMethods (BindingFlags.Public|BindingFlags.Static|BindingFlags.DeclaredOnly);
445 foreach (MethodInfo m in methods)
448 //generate instance methods
449 methods = t.GetMethods (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
450 foreach (MethodInfo m in methods)
454 H.WriteLine ("G_END_DECLS");
457 //FIXME: this won't work in the general case. arraylist should contain just type names, not Types
458 static ArrayList registered_types = new ArrayList ();
459 static Hashtable registry_hits = new Hashtable ();
461 static bool IsRegistered (Type t)
463 bool isreg = registered_types.Contains (t);
465 //TODO: use our list of supported primitive types instead
466 if (!isreg && !t.IsPrimitive) {
467 if (!registry_hits.Contains (t.Namespace)) {
469 registry_hits[t.Namespace] = count;
472 registry_hits[t.Namespace] = (int) registry_hits[t.Namespace] + 1;
478 static void RegisterCsType (Type t)
480 if (IsRegistered (t))
483 registered_types.Add (t);
486 static string CsTypeToC (Type t)
488 //TODO: use this method everywhere
490 //if (t.Namespace == ns)
491 if (IsRegistered (t))
492 return NsToFlat (t.Namespace) + t.Name + " *";
494 string ptype = "MonoClass *";
498 case "System.String":
499 ptype = "const gchar *";
510 static void EventGen (EventInfo ei, Type t)
512 //Console.WriteLine ("TODO: event: " + ei.Name);
513 //Console.WriteLine ("\t" + CamelToC (ei.Name));
514 string name = CamelToC (ei.Name);
517 C.WriteLine ("signals[" + name.ToUpper () + "] = g_signal_new (");
518 C.WriteLine ("\"" + name + "\",");
519 C.WriteLine ("G_OBJECT_CLASS_TYPE (object_class),");
520 C.WriteLine ("G_SIGNAL_RUN_LAST,");
521 C.WriteLine ("G_STRUCT_OFFSET (" + CurType + "Class" + ", " + name + "),");
522 C.WriteLine ("NULL, NULL,");
523 C.WriteLine ("g_cclosure_marshal_VOID__VOID,");
524 C.WriteLine ("G_TYPE_NONE, 0");
528 static void ConstructorGen (ConstructorInfo c, Type t)
530 ParameterInfo[] parameters = c.GetParameters ();
531 FunctionGen (parameters, (MethodBase) c, t, null, true);
534 static void MethodGen (MethodInfo m, Type t)
536 ParameterInfo[] parameters = m.GetParameters ();
537 FunctionGen (parameters, (MethodBase) m, t, m.ReturnType, false);
540 static string ToValidFuncName (string name)
542 //avoid generated function name conflicts with internal functions
544 switch (name.ToLower ()) {
548 return "class_initialize";
550 return "retrieve_type";
556 static void FunctionGen (ParameterInfo[] parameters, MethodBase m, Type t, Type ret_type, bool ctor)
560 bool has_return = false;
575 mytype = CurType + " *";
578 Console.WriteLine (ret_type);
579 if (ret_type != null && ret_type != typeof (Void)) {
581 //TODO: return simple gint or gchar if possible
582 mytype = "MonoObject *";
586 string params_arg = "NULL";
587 if (parameters.Length != 0)
588 params_arg = "_mono_params";
590 string instance = "thiz";
591 string instance_arg = instance + "->priv->mono_object";
593 //TODO: also check, !static
595 myargs = mytype + instance;
596 if (parameters.Length > 0) myargs += ", ";
601 myname = cur_type + "_";
605 myname += ToValidFuncName (CamelToC (m.Name));
607 //handle overloaded methods
608 //TODO: generate an alias function for the default ctor etc.
610 //TODO: how do we choose the default ctor/method overload? perhaps the
611 //first/shortest, but we need scope for this
612 //perhaps use DefaultMemberAttribute, Type.GetDefaultMembers
614 if (funcs_done.Contains (myname)) {
615 for (int i = 0 ; i < parameters.Length ; i++) {
616 ParameterInfo p = parameters[i];
620 else if (i != parameters.Length - 1)
627 if (funcs_done.Contains (myname))
630 funcs_done.Add (myname);
632 //handle the parameters
633 string param_assign = "";
634 string mycsargs = "";
636 for (int i = 0 ; i < parameters.Length ; i++) {
637 ParameterInfo p = parameters[i];
638 mycsargs += GetMonoType (Type.GetTypeCode (p.ParameterType));
639 myargs += CsTypeToC (p.ParameterType) + p.Name;
640 if (i != parameters.Length - 1) {
646 if (myargs == "") myargs = "void";
651 C.WriteLine (mytype + myname + " (" + myargs + ")", H, ";");
653 C.WriteLine ("void " + myname + " (" + myargs + ")", H, ";");
657 C.WriteLine ("static MonoMethod *_mono_method = NULL;");
658 if (parameters.Length != 0) C.WriteLine ("gpointer " + params_arg + "[" + parameters.Length + "];");
660 C.WriteLine (CurType + " *" + instance + ";");
662 C.WriteLine (instance + " = g_object_new (" + NsToC (ns).ToUpper () + "_TYPE_" + CamelToC (t.Name).ToUpper () + ", NULL);");
667 C.WriteLine ("if (_mono_method == NULL) {");
669 //if (ctor) C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":.ctor()\", FALSE);");
670 if (ctor) C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":.ctor(" + mycsargs + ")\", FALSE);");
672 C.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":" + m.Name + "(" + mycsargs + ")" + "\", FALSE);");
675 C.WriteLine ("_mono_method = mono_method_desc_search_in_class (_mono_method_desc, " + cur_type + "_get_mono_class ());");
680 //assign the parameters
681 for (int i = 0 ; i < parameters.Length ; i++) {
682 ParameterInfo p = parameters[i];
683 C.WriteLine (params_arg + "[" + i + "] = " + GetMonoVal (p.Name, p.ParameterType.ToString ()) + ";");
685 if (parameters.Length != 0) C.WriteLine ();
687 if (ctor) C.WriteLine (instance_arg + " = (MonoObject*) mono_object_new ((MonoDomain*) " + NsToC (ns) + "_get_mono_domain ()" + ", " + cur_type + "_get_mono_class ());");
692 C.WriteLine ("mono_runtime_invoke (_mono_method, " + instance_arg + ", " + params_arg + ", NULL);");
694 C.WriteLine ("mono_runtime_invoke (_mono_method, " + "NULL" + ", " + params_arg + ", NULL);");
696 if (ctor) C.WriteLine ("return " + instance + ";");
701 static string GetMonoType (TypeCode tc)
703 //see mcs/class/corlib/System/TypeCode.cs
704 //see mono/mono/dis/get.c
711 case TypeCode.String:
715 return tc.ToString ();
719 static string GetMonoVal (string name, string type)
722 case "System.String":
723 return "(gpointer*) mono_string_new ((MonoDomain*) mono_domain_get (), " + name + ")";
733 static string NsToC (string s)
735 s = s.Replace ('.', '_');
739 static string NsToFlat (string s)
741 s = s.Replace (".", "");
745 static string CamelToC (string s)
747 //converts camel case to c-style
751 bool prev_is_cap = true;
753 foreach (char c in s) {
754 char cl = c.ToString ().ToLower ()[0];
755 bool is_cap = c != cl;
757 if (!prev_is_cap && is_cap) {
762 prev_is_cap = is_cap;
774 private StreamWriter w;
776 public CodeWriter (string fname)
781 public bool IsDuplicate = false;
783 void Init (string fname)
785 if (File.Exists (fname)) {
786 string newfname = fname + ".x";
787 Console.WriteLine ("Warning: File " + fname + " already exists, using " + newfname);
793 FileStream fs = new FileStream (fname, FileMode.OpenOrCreate, FileAccess.Write);
794 w = new StreamWriter (fs);
797 public string Indenter = " ";
798 string cur_indent = "";
801 public void Indent ()
805 for (int i = 0; i != level ; i++) cur_indent += Indenter;
808 public void Outdent ()
812 for (int i = 0; i != level ; i++) cur_indent += Indenter;
815 public void Write (string text)
820 public void WriteLine (string text)
822 char[] opentags = {'{', '('};
823 char[] closetags = {'}', ')'};
825 if (text.TrimStart (closetags) != text)
828 w.Write (cur_indent);
831 if (text.TrimEnd (opentags) != text)
835 public void WriteLine (string text, CodeWriter cc)
837 WriteLine (text, "", cc, "");
840 public void WriteLine (string text, CodeWriter cc, string suffix)
842 WriteLine (text, "", cc, suffix);
845 public void WriteLine (string text, string prefix, CodeWriter cc)
847 WriteLine (text, prefix, cc, "");
850 public void WriteLine (string text, string prefix, CodeWriter cc, string suffix)
853 cc.WriteLine (prefix + text + suffix);
856 public void WriteComment (string text)
858 w.WriteLine ("/* " + text + " */");
861 public void WriteLine ()