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
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
= 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
;
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/')");
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
) {
291 //Console.WriteLine ("Ignoring non-public type: " + t.Name);
292 //warnings_ignored++;
296 if (!t
.IsClass
&& !t
.IsInterface
&& !t
.IsEnum
) {
297 //Console.WriteLine ("Ignoring unrecognised type: " + t.Name);
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;
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
);
446 CurTypeClass
= GToGI (CurType
);
448 CurTypeClass
= GToGC (CurType
);
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
);
462 H
.WriteLine ("#include <glib.h>");
463 H
.WriteLine ("#include <glib-object.h>");
465 foreach (string include
in extincludes
)
466 H
.WriteLine ("#include <" + include
+ ">");
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\"");
478 H
.WriteLine ("#ifdef __cplusplus");
479 H
.WriteLine ("extern \"C\" {", false);
480 H
.WriteLine ("#endif /* __cplusplus */");
483 C
.WriteLine ("#include \"" + fname
+ ".h" + "\"");
486 ifaces
= t
.GetInterfaces ();
487 foreach (Type iface
in ifaces
) {
488 if (!IsRegistered (iface
))
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>");
502 else if (t
.IsInterface
)
508 H
.WriteLine ("#ifdef __cplusplus");
509 H
.WriteLine ("}", false);
510 H
.WriteLine ("#endif /* __cplusplus */");
513 H
.WriteLine ("#endif /* " + H_id
+ " */");
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
, ";");
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
+ ";");
539 C
.WriteLine ("{ 0, NULL, NULL }");
541 C
.WriteLine ("etype = g_enum_register_static (\"" + gname
+ "\", values);");
543 C
.WriteLine ("return etype;");
547 static void ClassGen (Type t
)
549 //TODO: what flags do we want for GetEvents and GetConstructors?
553 events
= t
.GetEvents (BindingFlags
.Public
|BindingFlags
.Instance
|BindingFlags
.DeclaredOnly
);
556 MethodInfo
[] methods
;
557 methods
= t
.GetMethods (BindingFlags
.Public
|BindingFlags
.Instance
|BindingFlags
.DeclaredOnly
);
560 ifaces
= t
.GetInterfaces ();
562 H
.WriteLine ("G_BEGIN_DECLS");
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
+ "))");
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
+ "))");
577 H
.WriteLine ("#define " + NS
+ "_IS_" + T
+ "_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), " + NSTT
+ "))");
579 H
.WriteLine ("#define " + NST
+ "_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), " + NSTT
+ ", " + CurTypeClass
+ "))");
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
+ ";");
593 string ParentNameClass
;
595 if (t
.BaseType
!= null) {
596 ParentName
= CsTypeToG (t
.BaseType
);
597 ParentNameClass
= GToGC (ParentName
);
599 ParentName
= "GType";
601 ParentNameClass
= ParentName
+ "Interface";
603 ParentNameClass
= ParentName
+ "Class";
606 //H.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
609 //H.WriteLine ("typedef struct _" + CurType + "Class " + CurType + "Class;");
610 if (!t
.IsInterface
) {
611 H
.WriteLine ("typedef struct _" + CurType
+ "Private " + CurType
+ "Private;");
613 H
.WriteLine ("struct _" + CurType
);
616 H
.WriteLine (ParentName
+ " parent_instance;");
617 H
.WriteLine (CurType
+ "Private *priv;");
622 H
.WriteLine ("struct _" + CurTypeClass
);
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) {
631 H
.WriteLine ("/* signals */");
633 //FIXME: event arguments
634 foreach (EventInfo ei
in events
)
635 H
.WriteLine ("void (* " + CamelToC (ei
.Name
) + ") (" + CurType
+ " *thiz" + ");");
639 if (methods
.Length
!= 0) {
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" + ");");
656 C
.WriteLine ("struct _" + CurType
+ "Private");
658 C
.WriteLine ("MonoObject *mono_object;");
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");
675 C
.WriteLine ("static gpointer parent_class;");
677 if (events
.Length
== 0)
678 C
.WriteLine ("static guint signals[0];");
680 C
.WriteLine ("static guint signals[LAST_SIGNAL] = { 0 };");
683 C
.WriteLine ("static MonoClass *" + cur_type
+ "_get_mono_class (void)");
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);");
695 C
.WriteLine ("return class;");
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
)
716 //generate instance methods
717 methods
= t
.GetMethods (BindingFlags
.Public
|BindingFlags
.Instance
|BindingFlags
.DeclaredOnly
);
718 foreach (MethodInfo m
in methods
)
724 //generate the GObject init function
725 C
.WriteLine ("static void " + cur_type
+ "_init (" + CurType
+ " *thiz" + ")");
727 C
.WriteLine ("thiz->priv = g_new0 (" + CurType
+ "Private, 1);");
732 //generate the GObject class init function
733 C
.WriteLine ("static void " + cur_type
+ "_class_init (" + CurTypeClass
+ " *klass" + ")");
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
)
748 if (ifaces
.Length
!= 0) {
749 foreach (Type iface
in ifaces
) {
750 if (!IsRegistered (iface
))
753 C
.WriteLine ("static void " + NsToC (iface
.Namespace
) + "_" + CamelToC (iface
.Name
) + "_interface_init (" + GToGI (CsTypeToG (iface
)) + " *iface" + ")");
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.
766 //generate the GObject get_type function
767 C
.WriteLine ("GType " + cur_type
+ "_get_type (void)", H
, ";");
769 C
.WriteLine ("static GType object_type = 0;");
770 C
.WriteLine ("g_type_init ();");
772 C
.WriteLine ("if (object_type) return object_type;");
774 C
.WriteLine ("static const GTypeInfo object_info =");
776 C
.WriteLine ("sizeof (" + CurTypeClass
+ "),");
777 C
.WriteLine ("(GBaseInitFunc) NULL, /* base_init */");
778 C
.WriteLine ("(GBaseFinalizeFunc) NULL, /* base_finalize */");
780 C
.WriteLine ("(GClassInitFunc) " + cur_type
+ "_class_init, /* class_init */");
782 C
.WriteLine ("NULL, /* class_init */");
783 C
.WriteLine ("NULL, /* class_finalize */");
784 C
.WriteLine ("NULL, /* class_data */");
786 C
.WriteLine ("sizeof (" + CurType
+ "),");
789 C
.WriteLine ("0, /* n_preallocs */");
791 C
.WriteLine ("(GInstanceInitFunc) " + cur_type
+ "_init, /* instance_init */");
793 C
.WriteLine ("NULL, /* instance_init */");
797 foreach (Type iface
in ifaces
) {
798 if (!IsRegistered (iface
))
801 C
.WriteLine ("static const GInterfaceInfo " + CamelToC (iface
.Namespace
) + "_" + CamelToC (iface
.Name
) + "_info" + " =");
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 */");
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
))
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" + ");");
825 C
.WriteLine ("return object_type;");
829 H
.WriteLine ("G_END_DECLS");
832 static bool TypeIsGObject (Type t
)
837 if (t
.FullName
== "GLib.Object")
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
) {
899 static void HitRegistry (string tn
)
901 //FIXME: ignore handled primitive types here
903 if (!registry_hits
.Contains (tn
)) {
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.");
918 external_types
.Add (G
);
919 registered_types
.Add (G
);
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
);
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
);
964 static string GToGI (string G
)
966 string possGC
= G
+ "Iface";
967 //TODO: conflict resolution
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");
983 static string CsTypeToC (Type t
)
985 //TODO: use this method everywhere
989 case "System.String":
990 return "const gchar *";
995 case "System.UInt32":
998 case "System.Boolean":
1001 case "System.IntPtr":
1007 case "System.SByte":
1013 case "System.Double":
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))
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
);
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");
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
);
1071 static string ToValidFuncName (string name
)
1073 //avoid generated function name conflicts with internal functions
1075 switch (name
.ToLower ()) {
1077 return "initialize";
1079 return "class_initialize";
1081 return "retrieve_type";
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
+ " *";
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";
1114 mono_obj
= "_mono_object";
1116 //if (ctor || !stat)
1117 // mono_obj = instance + "->priv->mono_object";
1120 myargs
= mytype
+ instance
;
1121 if (parameters
.Length
> 0) myargs
+= ", ";
1126 myname
= cur_type
+ "_";
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
];
1148 myname
+= KeywordAvoid (p
.Name
);
1152 if (funcs_done
.Contains (myname
))
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) {
1170 if (myargs
== String
.Empty
)
1175 C
.WriteLine (rettype
+ myname
+ " (" + myargs
+ ")", H
, ";");
1179 C
.WriteLine ("static MonoMethod *_mono_method = NULL;");
1182 C
.WriteLine ("MonoObject *" + mono_obj
+ ";");
1184 if (parameters
.Length
!= 0) C
.WriteLine ("gpointer " + params_arg
+ "[" + parameters
.Length
+ "];");
1187 C
.WriteLine (CurType
+ " *" + instance
+ ";");
1190 if (!ctor
&& !stat
) {
1192 C
.WriteLine (mono_obj
+ " = g_object_get_data (G_OBJECT (" + instance
+ "), \"mono-object\");");
1197 C
.WriteLine ("if (_mono_method == NULL) {");
1200 C
.WriteLine ("MonoMethodDesc *_mono_method_desc = mono_method_desc_new (\":.ctor(" + mycsargs
+ ")\", FALSE);");
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 ());");
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)
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);");
1226 //code to invoke the method
1228 if (!ctor
&& has_return
)
1229 if (IsRegisteredByVal (ret_type
)) {
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;");
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));");
1239 //TODO: this isn't right
1240 C
.WriteLine ("return (" + rettype
+ ") mono_runtime_invoke (_mono_method, " + mono_obj
+ ", " + params_arg
+ ", NULL);");
1243 C
.WriteLine ("mono_runtime_invoke (_mono_method, " + mono_obj
+ ", " + params_arg
+ ", NULL);");
1249 //TODO: use ->priv, not data for better performance if not wrapping a gobject
1251 C
.WriteLine (instance
+ " = (" + CurType
+ " *) " + NsToC (ns
) + "_cilc_glib_mobject_get_gobject (" + mono_obj
+ ");");
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
+ ");");
1257 C
.WriteLine ("return " + instance
+ ";");
1263 static string GetMonoType (TypeCode tc
)
1265 //see mcs/class/corlib/System/TypeCode.cs
1266 //see mono/mono/dis/get.c
1270 case TypeCode
.Int32
:
1273 case TypeCode
.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
+ "))";
1289 case "System.String":
1290 return "(gpointer*) mono_string_new ((MonoDomain*) mono_domain_get (), " + name
+ ")";
1292 case "System.Int32":
1300 static string NsToC (string s
)
1303 return String
.Empty
;
1305 s
= s
.Replace ('.', '_');
1306 return CamelToC (s
);
1309 static string NsToFlat (string s
)
1312 return String
.Empty
;
1314 s
= s
.Replace (".", String
.Empty
);
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
) {
1335 prev_is_cap
= is_cap
;