fixed tests
[mono.git] / mcs / tools / cilc / cilc.cs
1 // cilc -- a CIL-to-C binding generator
2 // Copyright (C) 2003, 2004, 2005, 2006 Alp Toker <alp@atoker.com>
3 // Licensed under the terms of the MIT License
4
5 using System;
6 using System.IO;
7 using System.Reflection;
8 using System.Collections;
9 using System.Diagnostics;
10 using System.Text.RegularExpressions;
11
12 public class cilc
13 {
14         private cilc () {}
15
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;
20
21         static ArrayList funcs_done = new ArrayList ();
22
23         public static int Main (string[] args)
24         {
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;
29                 }
30
31                 ns = "Unnamed";
32
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));
41
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]);
52                 }
53
54                 return 0;
55         }
56
57
58         public static void SmartBind (string aname)
59         {
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;
65                 }
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 ();
74         }
75
76         public static string Run (string cmd, string args)
77         {
78                 ProcessStartInfo psi = new ProcessStartInfo (cmd, args);
79                 psi.UseShellExecute = false;
80                 psi.RedirectStandardInput = true;
81                 psi.RedirectStandardOutput = true;
82                 psi.RedirectStandardError = true;
83
84                 Process p = Process.Start (psi);
85
86                 string line = p.StandardOutput.ReadLine ();
87
88                 p.WaitForExit ();
89
90                 return line;
91         }
92
93         public static int RunWithReport (string cmd, string args)
94         {
95                 ProcessStartInfo psi = new ProcessStartInfo (cmd, args);
96                 psi.UseShellExecute = false;
97                 psi.RedirectStandardOutput = true;
98                 psi.RedirectStandardError = true;
99
100                 Process p = Process.Start (psi);
101
102                 string line;
103                 while ((line = p.StandardOutput.ReadLine ()) != null)
104                         if (verbose)
105                                 Console.WriteLine (line);
106                         else
107                                 Console.Write (".");
108
109                 Console.WriteLine ();
110
111                 Console.Write (p.StandardError.ReadToEnd ());
112
113                 p.WaitForExit ();
114
115                 return p.ExitCode;
116         }
117
118         static bool verbose = false;
119
120         static string extpkgs = "";
121         static string[] extsubpkgs = {};
122         static string[] extincludes = {};
123
124         public static void RegisterPkg (string pkg, string subpkgs)
125         {
126                 extpkgs += " " + pkg;
127
128                 string cflags = Run ("pkg-config", "--cflags-only-I " + pkg);
129
130                 extsubpkgs = subpkgs.Trim ().Split (',');
131                 extincludes = new string[extsubpkgs.Length];
132
133                 for (int i = 0 ; i != extsubpkgs.Length ; i++)
134                         extincludes[i] =  extsubpkgs[i] + "/" + extsubpkgs[i] + ".h";
135
136                 //string cmd = "gcc";
137                 //string args = "-E " + cflags + " " + includedir + "/" + hname;
138
139                 string cppincludes = "";
140                 foreach (string include in extincludes)
141                         cppincludes += " -include " + include;
142
143                 string cmd = "cpp";
144                 string args = cflags + cppincludes + " /dev/null";
145
146                 ProcessStartInfo psi = new ProcessStartInfo (cmd, args);
147                 psi.UseShellExecute = false;
148                 psi.RedirectStandardOutput = true;
149                 psi.RedirectStandardError = true;
150
151                 Process p = Process.Start (psi);
152
153                 string line;
154
155                 Regex re_type = new Regex (@"typedef struct (_\w+) (\w+);");
156                 Regex re_enum = new Regex (@"} (\w+);");
157
158                 while ((line = p.StandardOutput.ReadLine ()) != null) {
159                         line = line.Trim ();
160
161                         Match m;
162
163                         m = re_type.Match (line);
164                         if (m.Success) {
165                                 string G = m.Groups[2].Value;
166
167                                 if (!GIsValid (G))
168                                         continue;
169
170                                 if (G.EndsWith ("Class"))
171                                         continue;
172
173                                 RegisterG (G);
174                                 continue;
175                         }
176
177                         m = re_enum.Match (line);
178                         if (m.Success) {
179                                 string G = m.Groups[1].Value;
180
181                                 if (!GIsValid (G))
182                                         continue;
183
184                                 RegisterG (G);
185                                 RegisterByVal (G);
186                                 continue;
187                         }
188                 }
189
190                 p.WaitForExit ();
191                 Console.Write (p.StandardError.ReadToEnd ());
192                 Console.WriteLine ();
193         }
194
195         static bool GIsValid (string G) {
196                 foreach (string extsubpkg in extsubpkgs)
197                         if (G.ToLower ().StartsWith (extsubpkg))
198                                 return true;
199
200                 return false;
201         }
202
203         public static void Generate (string assembly, string target)
204         {
205                 target_dir = target + Path.DirectorySeparatorChar;
206
207                 if (Directory.Exists (target_dir)) {
208                         Console.WriteLine ("Error: Target directory " + target_dir + " already exists.");
209                         return;
210                 }
211
212                 Directory.CreateDirectory (target_dir);
213
214                 Assembly a = Assembly.LoadFrom (assembly);
215
216                 Console.WriteLine ();
217                 Console.WriteLine ("References (not followed):");
218                 foreach (AssemblyName reference in a.GetReferencedAssemblies ())
219                         Console.WriteLine ("  " + reference.Name);
220                 Console.WriteLine ();
221
222                 dllname = Path.GetFileName (assembly);
223                 AssemblyGen (a);
224
225                 //we might not want to do this in future
226                 File.Copy (dllname, target_dir + dllname);
227
228                 string soname = "lib" + NsToFlat (Path.GetFileNameWithoutExtension (assembly)).ToLower () + ".so";
229
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"));
233
234                 makefile.Write (sr.ReadToEnd ());
235                 sr.Close ();
236                 makefile.Close ();
237
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/')");
244
245                 if (extpkgs != "") {
246                         makefile_defs.WriteLine ("EXTRAINCLUDES = $(shell pkg-config --cflags" + extpkgs + ")");
247                         makefile_defs.WriteLine ("EXTRALIBS = $(shell pkg-config --libs" + extpkgs + ")");
248                 }
249                 makefile_defs.Close ();
250
251                 Console.WriteLine ();
252
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.");
257                         }
258                 }
259
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 ();
265         }
266
267         static void MakeReport (Hashtable ctable, string desc, int num)
268         {
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);
273
274                 Array.Reverse (reg_vals);
275                 Array.Reverse (reg_keys);
276
277                 for (int i = 0 ; i != reg_keys.Length && i != num ; i++) {
278                         Console.WriteLine ("  " + reg_keys[i] + ": " + reg_vals[i]);
279                 }
280         }
281
282         static int warnings_ignored = 0;
283
284         static void AssemblyGen (Assembly a)
285         {
286                 Type[] types = a.GetTypes ();
287                 Hashtable ns_types = new Hashtable ();
288
289                 foreach (Type t in types) {
290                         if (t.IsNotPublic) {
291                                 //Console.WriteLine ("Ignoring non-public type: " + t.Name);
292                                 //warnings_ignored++;
293                                 continue;
294                         }
295
296                         if (!t.IsClass && !t.IsInterface && !t.IsEnum) {
297                                 //Console.WriteLine ("Ignoring unrecognised type: " + t.Name);
298                                 warnings_ignored++;
299                                 continue;
300                         }
301
302                         RegisterType (t);
303
304                         if (t.IsEnum)
305                                 RegisterByVal (t);
306
307                         string tns = t.Namespace == null ? "" : t.Namespace;
308
309                         if (!ns_types.Contains (tns))
310                                 ns_types[tns] = new ArrayList ();
311
312                         ((ArrayList) ns_types[tns]).Add (t);
313                 }
314
315                 namespaces = (string[]) (new ArrayList (ns_types.Keys)).ToArray (typeof (string));
316
317                 foreach (DictionaryEntry de in ns_types)
318                         NamespaceGen ((string) de.Key, (Type[]) ((ArrayList) de.Value).ToArray (typeof (Type)));
319         }
320
321         static string[] namespaces;
322
323         static void NamespaceGen (string given_ns, Type[] types)
324         {
325                 //ns = types[0].Namespace;
326                 ns = given_ns;
327
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");
331
332                 string Hindex_id = "__" + NsToFlat (ns).ToUpper () + "_H__";
333                 Hindex.WriteLine ("#ifndef " + Hindex_id);
334                 Hindex.WriteLine ("#define " + Hindex_id);
335                 Hindex.WriteLine ();
336
337                 string Hdecls_id = "__" + NsToFlat (ns).ToUpper () + "_DECLS_H__";
338                 Hdecls.WriteLine ("#ifndef " + Hdecls_id);
339                 Hdecls.WriteLine ("#define " + Hdecls_id);
340                 Hdecls.WriteLine ();
341
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 ();
354
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 ();
366
367                 Cindex.WriteLine ("return domain;");
368                 Cindex.WriteLine ("}");
369                 Cindex.WriteLine ();
370
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 ();
377
378                 Cindex.WriteLine ("return assembly;");
379                 Cindex.WriteLine ("}");
380                 Cindex.WriteLine ();
381
382
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 ("}");
388
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 ();
411
412
413                 Console.Write ("Generating sources in " + ns);
414                 foreach (Type t in types) {
415                         TypeGen (t);
416                         Console.Write (".");
417                 }
418
419                 Console.WriteLine ();
420
421                 Hindex.WriteLine ();
422                 Hindex.WriteLine ("#endif /* " + Hindex_id + " */");
423
424                 Hdecls.WriteLine ();
425                 Hdecls.WriteLine ("#endif /* " + Hdecls_id + " */");
426
427                 Cindex.Close ();
428                 Hindex.Close ();
429                 Hdecls.Close ();
430         }
431
432         static void TypeGen (Type t)
433         {
434                 //TODO: we only handle ordinary classes for now
435                 /*
436                          else if (t.IsSubclassOf (typeof (Delegate))) {
437                          Console.WriteLine ("Ignoring delegate: " + t.Name);
438                          return;
439                          }
440                          */
441
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);
449
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" + ">");
455
456
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 ();
461
462                 H.WriteLine ("#include <glib.h>");
463                 H.WriteLine ("#include <glib-object.h>");
464
465                 foreach (string include in extincludes)
466                         H.WriteLine ("#include <" + include + ">");
467
468                 H.WriteLine ();
469
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\"");
472
473                 foreach (string ext_ns in namespaces)
474                         H.WriteLine ("#include \"" + NsToFlat (ext_ns).ToLower () + "types.h\"");
475
476                 H.WriteLine ();
477
478                 H.WriteLine ("#ifdef __cplusplus");
479                 H.WriteLine ("extern \"C\" {", false);
480                 H.WriteLine ("#endif /* __cplusplus */");
481                 H.WriteLine ();
482
483                 C.WriteLine ("#include \"" + fname + ".h" + "\"");
484
485                 Type[] ifaces;
486                 ifaces = t.GetInterfaces ();
487                 foreach (Type iface in ifaces) {
488                         if (!IsRegistered (iface))
489                                 continue;
490
491                         string iface_fname = NsToFlat (ns).ToLower () + iface.Name.ToLower ();
492                         C.WriteLine ("#include \"" + iface_fname + ".h" + "\"");
493                 }
494
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 ();
499
500                 if (t.IsClass)
501                         ClassGen (t);
502                 else if (t.IsInterface)
503                         ClassGen (t);
504                 else if (t.IsEnum)
505                         EnumGen (t);
506
507                 H.WriteLine ();
508                 H.WriteLine ("#ifdef __cplusplus");
509                 H.WriteLine ("}", false);
510                 H.WriteLine ("#endif /* __cplusplus */");
511                 H.WriteLine ();
512
513                 H.WriteLine ("#endif /* " + H_id + " */");
514
515                 C.Close ();
516                 H.Close ();
517         }
518
519         static void EnumGen (Type t)
520         {
521                 //TODO: we needn't split out each enum into its own file
522
523                 string gname = CsTypeToG (t);
524
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 ("_", "-") + "\" },");
536                 }
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 ("}");
545         }
546
547         static void ClassGen (Type t)
548         {
549                 //TODO: what flags do we want for GetEvents and GetConstructors?
550
551                 //events as signals
552                 EventInfo[] events;
553                 events = t.GetEvents (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
554
555                 //events as signals
556                 MethodInfo[] methods;
557                 methods = t.GetMethods (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
558
559                 Type[] ifaces;
560                 ifaces = t.GetInterfaces ();
561
562                 H.WriteLine ("G_BEGIN_DECLS");
563                 H.WriteLine ();
564
565                 {
566                         string NS = NsToC (ns).ToUpper ();
567                         string T = CamelToC (t.Name).ToUpper ();
568                         string NST = NS + "_" + T;
569                         string NSTT = NS + "_TYPE_" + T;
570
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 + "))");
582                 }
583
584                 if (!C.IsDuplicate) {
585                         Hdecls.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
586                         Hdecls.WriteLine ("typedef struct _" + CurTypeClass + " " + CurTypeClass + ";");
587                         Hdecls.WriteLine ();
588                 }
589
590                 H.WriteLine ();
591
592                 string ParentName;
593                 string ParentNameClass;
594
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";
604                 }
605
606                 //H.WriteLine ("typedef struct _" + CurType + " " + CurType + ";");
607
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 ("{");
615
616                         H.WriteLine (ParentName + " parent_instance;");
617                         H.WriteLine (CurType + "Private *priv;");
618                         H.WriteLine ("};");
619                         H.WriteLine ();
620                 }
621
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 + " */");
628
629                 if (events.Length != 0) {
630                         H.WriteLine ();
631                         H.WriteLine ("/* signals */");
632
633                         //FIXME: event arguments
634                         foreach (EventInfo ei in events)
635                                 H.WriteLine ("void (* " + CamelToC (ei.Name) + ") (" + CurType + " *thiz" + ");");
636                 }
637
638                 if (t.IsInterface) {
639                         if (methods.Length != 0) {
640                                 H.WriteLine ();
641                                 H.WriteLine ("/* vtable */");
642
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" + ");");
647                         }
648                 }
649
650                 H.WriteLine ("};");
651                 H.WriteLine ();
652
653                 //generate c file
654
655                 //private struct
656                 C.WriteLine ("struct _" + CurType + "Private");
657                 C.WriteLine ("{");
658                 C.WriteLine ("MonoObject *mono_object;");
659                 C.WriteLine ("};");
660
661                 C.WriteLine ();
662
663                 //events
664                 if (events.Length != 0) {
665                         C.WriteLine ("enum {");
666
667                         foreach (EventInfo ei in events)
668                                 C.WriteLine (CamelToC (ei.Name).ToUpper () + ",");
669
670                         C.WriteLine ("LAST_SIGNAL");
671                         C.WriteLine ("};");
672                         C.WriteLine ();
673                 }
674
675                 C.WriteLine ("static gpointer parent_class;");
676
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 ();
682
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;");
688
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 + "\");");
691
692                 C.WriteLine ("mono_class_init (class);");
693                 C.WriteLine ();
694
695                 C.WriteLine ("return class;");
696                 C.WriteLine ("}");
697
698                 C.WriteLine ();
699
700                 wrap_gobject = TypeIsGObject (t);
701
702                 //TODO: generate thin wrappers for interfaces
703
704                 //generate constructors
705                 ConstructorInfo[] constructors;
706                 constructors = t.GetConstructors ();
707                 foreach (ConstructorInfo c in constructors)
708                         ConstructorGen (c, t);
709
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);
715
716                 //generate instance methods
717                 methods = t.GetMethods (BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
718                 foreach (MethodInfo m in methods)
719                         MethodGen (m, t);
720
721                 C.WriteLine ();
722
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 ("}");
729
730                         C.WriteLine ();
731
732                         //generate the GObject class init function
733                         C.WriteLine ("static void " + cur_type + "_class_init (" + CurTypeClass + " *klass" + ")");
734                         C.WriteLine ("{");
735
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;");
739
740                         foreach (EventInfo ei in events)
741                                 EventGen (ei, t);
742
743                         C.WriteLine ("}");
744
745                         C.WriteLine ();
746                 }
747
748                 if (ifaces.Length != 0) {
749                         foreach (Type iface in ifaces) {
750                                 if (!IsRegistered (iface))
751                                         continue;
752
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 + ";");
758                                 }
759                                 //TODO: properties etc.
760                                 C.WriteLine ("}");
761                                 C.WriteLine ();
762                         }
763
764                 }
765
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 ();
796
797                 foreach (Type iface in ifaces) {
798                         if (!IsRegistered (iface))
799                                 continue;
800
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 ();
808                 }
809
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 ();
814
815                 C.WriteLine ("object_type = g_type_register_static (" + parent_type + ", \"" + CurType + "\", &object_info, 0);");
816                 }
817
818                 foreach (Type iface in ifaces) {
819                         if (!IsRegistered (iface))
820                                 continue;
821
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" + ");");
823                 }
824                 C.WriteLine ();
825                 C.WriteLine ("return object_type;");
826                 C.WriteLine ("}");
827
828                 H.WriteLine ();
829                 H.WriteLine ("G_END_DECLS");
830         }
831
832         static bool TypeIsGObject (Type t)
833         {
834                 if (t == null)
835                         return false;
836
837                 if (t.FullName == "GLib.Object")
838                         return true;
839
840                 return TypeIsGObject (t.BaseType);
841         }
842
843
844         //FIXME: clean up this mess with hits as the type registry uses strings
845
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 ();
850
851         static bool IsRegisteredByVal (Type t)
852         {
853                 return byval_types.Contains (CsTypeToFlat (t));
854         }
855
856         static bool IsExternal (Type t)
857         {
858                 return external_types.Contains (CsTypeToFlat (t));
859         }
860
861         static void RegisterByVal (string tn)
862         {
863                 //TODO: warn on dupes
864                 byval_types.Add (tn);
865         }
866
867         static void RegisterByVal (Type t)
868         {
869                 RegisterByVal (CsTypeToFlat (t));
870         }
871
872         static bool IsRegistered (String tn)
873         {
874                 return registered_types.Contains (tn);
875         }
876
877         static bool IsRegistered (Type t)
878         {
879                 return IsRegistered (t, true);
880         }
881
882         static bool IsRegistered (Type t, bool log_hits)
883         {
884                 return IsRegistered (CsTypeToFlat (t), true);
885         }
886
887         static bool IsRegistered (string tn, bool log_hits)
888         {
889                 //bool isreg = registered_types.Contains (t);
890                 bool isreg = registered_types.Contains (tn);
891
892                 if (!isreg && log_hits) {
893                         HitRegistry (tn);
894                 }
895
896                 return isreg;
897         }
898
899         static void HitRegistry (string tn)
900         {
901                 //FIXME: ignore handled primitive types here
902
903                 if (!registry_hits.Contains (tn)) {
904                         int count = 0;
905                         registry_hits[tn] = count;
906                 }
907
908                 registry_hits[tn] = (int) registry_hits[tn] + 1;
909         }
910
911         static bool RegisterG (string G)
912         {
913                 if (IsRegistered (G, false)) {
914                         Console.WriteLine ("Warning: unmanaged type " + G + " already registered! Can't re-register.");
915                         return false;
916                 }
917
918                 external_types.Add (G);
919                 registered_types.Add (G);
920                 return true;
921         }
922
923         static string NewG (string G)
924         {
925                 if (IsRegistered (G, false))
926                 {
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
930                 }
931
932                 registered_types.Add (G);
933                 return (G);
934         }
935
936         static string CsTypeToFlat (Type t) //TODO: use this everywhere
937         {
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
940
941                 return NsToFlat (t.Namespace) + t.Name;
942         }
943
944         static void RegisterType (Type t)
945         {
946                 NewG (CsTypeToFlat (t));
947         }
948
949         /*
950         static string NewG (Type t)
951         {
952                 return NewG (CsTypeToFlat (t));
953         }
954         */
955
956         static string CsTypeToG (Type t)
957         {
958                 if (IsRegistered (t))
959                         return CsTypeToFlat (t);
960
961                 return "GObject";
962         }
963
964         static string GToGI (string G)
965         {
966                 string possGC = G + "Iface";
967                 //TODO: conflict resolution
968
969                 return possGC;
970         }
971
972         //static string CsTypeToGC (String tn)
973         static string GToGC (string G)
974         {
975                 string possGC = G + "Class";
976
977                 if (IsRegistered (possGC))
978                         return GToGC (G + "Object");
979                 else
980                         return possGC;
981         }
982
983         static string CsTypeToC (Type t)
984         {
985                 //TODO: use this method everywhere
986
987                 switch (t.FullName)
988                 {
989                         case "System.String":
990                                 return "const gchar *";
991
992                         case "System.Int32":
993                                 return "gint ";
994
995                         case "System.UInt32":
996                                 return "guint ";
997
998                         case "System.Boolean":
999                                 return "gboolean ";
1000
1001                         case "System.IntPtr":
1002                                 return "gpointer ";
1003                         
1004                         case "System.Char":
1005                                 return "guint16 ";
1006
1007                         case "System.SByte":
1008                                 return "gint8 ";
1009
1010                         case "System.Byte":
1011                                 return "guint8 ";
1012
1013                         case "System.Double":
1014                                 return "gdouble ";
1015
1016                         //questionable
1017                         case "System.EventHandler":
1018                                 case "System.MulticastDelegate":
1019                                 return "GCallback ";
1020                 }
1021
1022                 if (IsRegistered (t) && IsRegisteredByVal (t))
1023                         return CsTypeToFlat (t) + " ";
1024
1025                 if (t == typeof (void))
1026                         return "void ";
1027
1028                 return CsTypeToG (t) + " *";
1029         }
1030
1031         static void EventGen (EventInfo ei, Type t)
1032         {
1033                 //Console.WriteLine ("TODO: event: " + ei.Name);
1034                 //Console.WriteLine ("\t" + CamelToC (ei.Name));
1035                 string name = CamelToC (ei.Name);
1036
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 (");");
1047         }
1048
1049         static void ConstructorGen (ConstructorInfo c, Type t)
1050         {
1051                 ParameterInfo[] parameters = c.GetParameters ();
1052                 FunctionGen (parameters, (MethodBase) c, t, null, true);
1053         }
1054
1055         static void MethodGen (MethodInfo m, Type t)
1056         {
1057                 ParameterInfo[] parameters = m.GetParameters ();
1058                 FunctionGen (parameters, (MethodBase) m, t, m.ReturnType, false);
1059         }
1060
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"};
1062
1063         static string KeywordAvoid (string s)
1064         {
1065                 if (Array.IndexOf (keywords, s.ToLower ()) != -1)
1066                         return KeywordAvoid ("_" + s);
1067
1068                 return s;
1069         }
1070
1071         static string ToValidFuncName (string name)
1072         {
1073                 //avoid generated function name conflicts with internal functions
1074
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;
1084                 }
1085         }
1086
1087         static bool wrap_gobject = false;
1088
1089         static void FunctionGen (ParameterInfo[] parameters, MethodBase m, Type t, Type ret_type, bool ctor)
1090         {
1091                 string myargs = "";
1092                 bool has_return = !ctor && ret_type != null && ret_type != typeof (void);
1093                 bool stat = m.IsStatic;
1094
1095                 string mytype, rettype;
1096
1097                 mytype = CurType + " *";
1098
1099                 if (ctor) {
1100                         has_return = true;
1101                         rettype = mytype;
1102                         stat = true;
1103                 } else
1104                         rettype = CsTypeToC (ret_type);
1105
1106                 string params_arg = "NULL";
1107                 if (parameters.Length != 0)
1108                         params_arg = "_mono_params";
1109
1110                 string instance = "thiz";
1111                 string mono_obj = "NULL";
1112
1113                 if (ctor || !stat)
1114                         mono_obj = "_mono_object";
1115
1116                 //if (ctor || !stat)
1117                 //      mono_obj = instance + "->priv->mono_object";
1118
1119                 if (!stat) {
1120                         myargs = mytype + instance;
1121                         if (parameters.Length > 0) myargs += ", ";
1122                 }
1123
1124                 string myname;
1125
1126                 myname = cur_type + "_";
1127                 if (ctor)
1128                         myname += "new";
1129                 else
1130                         myname += ToValidFuncName (CamelToC (m.Name));
1131
1132                 //handle overloaded methods
1133                 //TODO: generate an alias function for the default ctor etc.
1134
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
1138
1139                 if (funcs_done.Contains (myname)) {
1140                         for (int i = 0 ; i < parameters.Length ; i++) {
1141                                 ParameterInfo p = parameters[i];
1142
1143                                 if (i == 0)
1144                                         myname += "_with_";
1145                                 else
1146                                         myname += "_and_";
1147
1148                                 myname += KeywordAvoid (p.Name);
1149                         }
1150                 }
1151
1152                 if (funcs_done.Contains (myname))
1153                         return;
1154
1155                 funcs_done.Add (myname);
1156
1157                 //handle the parameters
1158                 string mycsargs = "";
1159
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 += ", ";
1167                         }
1168                 }
1169
1170                 if (myargs == "")
1171                         myargs = "void";
1172
1173                 C.WriteLine ();
1174
1175                 C.WriteLine (rettype + myname + " (" + myargs + ")", H, ";");
1176
1177                 C.WriteLine ("{");
1178
1179                 C.WriteLine ("static MonoMethod *_mono_method = NULL;");
1180
1181                 if (ctor || !stat)
1182                         C.WriteLine ("MonoObject *" + mono_obj + ";");
1183
1184                 if (parameters.Length != 0) C.WriteLine ("gpointer " + params_arg + "[" + parameters.Length + "];");
1185
1186                 if (ctor) {
1187                         C.WriteLine (CurType + " *" + instance + ";");
1188                 }
1189
1190                 if (!ctor && !stat) {
1191                         C.WriteLine ();
1192                         C.WriteLine (mono_obj + " = g_object_get_data (G_OBJECT (" + instance + "), \"mono-object\");");
1193                 }
1194
1195                 C.WriteLine ();
1196
1197                 C.WriteLine ("if (_mono_method == NULL) {");
1198
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);");
1203
1204
1205                 C.WriteLine ("_mono_method = mono_method_desc_search_in_class (_mono_method_desc, " + cur_type + "_get_mono_class ());");
1206
1207                 C.WriteLine ("}");
1208                 C.WriteLine ();
1209
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)) + ";");
1214                 }
1215
1216                 if (parameters.Length != 0)
1217                         C.WriteLine ();
1218
1219                 if (ctor)
1220                         C.WriteLine (mono_obj + " = (MonoObject*) mono_object_new ((MonoDomain*) " + NsToC (ns) + "_get_mono_domain ()" + ", " + cur_type + "_get_mono_class ());");
1221
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
1227
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 *")
1235                                 {
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);");
1241                                 }
1242                                 else
1243                                         C.WriteLine ("mono_runtime_invoke (_mono_method, " + mono_obj + ", " + params_arg + ", NULL);");
1244                 }
1245
1246                 if (ctor) {
1247                         C.WriteLine ();
1248
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);");
1254
1255                         C.WriteLine ("g_object_set_data (G_OBJECT (" + instance + "), \"mono-object\", " + mono_obj + ");");
1256                         C.WriteLine ();
1257                         C.WriteLine ("return " + instance + ";");
1258                 }
1259
1260                 C.WriteLine ("}");
1261         }
1262
1263         static string GetMonoType (TypeCode tc)
1264         {
1265                 //see mcs/class/corlib/System/TypeCode.cs
1266                 //see mono/mono/dis/get.c
1267
1268                 switch (tc)
1269                 {
1270                         case TypeCode.Int32:
1271                                 return "int";
1272
1273                         case TypeCode.String:
1274                                 return "string";
1275
1276                         default: //TODO: construct signature based on mono docs
1277                                 return tc.ToString ().ToLower ();
1278                 }
1279         }
1280
1281         static string GetMonoVal (Type t, string name)
1282         {
1283                 string type = t.FullName;
1284
1285                 if (TypeIsGObject (t))
1286                         return "(gpointer*) " + NsToC (ns) + "_cilc_glib_gobject_get_mobject (G_OBJECT (" + name + "))";
1287
1288                 switch (type) {
1289                         case "System.String":
1290                                 return "(gpointer*) mono_string_new ((MonoDomain*) mono_domain_get (), " + name + ")";
1291
1292                         case "System.Int32":
1293                                 return "&" + name;
1294
1295                         default:
1296                         return "&" + name;
1297                 }
1298         }
1299
1300         static string NsToC (string s)
1301         {
1302                 if (s == null)
1303                         return "";
1304
1305                 s = s.Replace ('.', '_');
1306                 return CamelToC (s);
1307         }
1308
1309         static string NsToFlat (string s)
1310         {
1311                 if (s == null)
1312                         return "";
1313
1314                 s = s.Replace (".", "");
1315                 return s;
1316         }
1317
1318         static string CamelToC (string s)
1319         {
1320                 //converts camel case to c-style
1321
1322                 string o = "";
1323
1324                 bool prev_is_cap = true;
1325
1326                 foreach  (char c in s) {
1327                         char cl = c.ToString ().ToLower ()[0];
1328                         bool is_cap = c != cl;
1329
1330                         if (!prev_is_cap && is_cap) {
1331                                 o += "_";
1332                         }
1333
1334                         o += cl;
1335                         prev_is_cap = is_cap;
1336
1337                         if (c == '_')
1338                                 prev_is_cap = true;
1339                 }
1340
1341                 return o;
1342         }
1343 }
1344
1345 class CodeWriter
1346 {
1347         private StreamWriter w;
1348
1349         public CodeWriter (string fname)
1350         {
1351                 Init (fname);
1352         }
1353
1354         public bool IsDuplicate = false;
1355
1356         void Init (string fname)
1357         {
1358                 if (File.Exists (fname)) {
1359                         string newfname = fname + ".x";
1360                         //Console.WriteLine ("Warning: File " + fname + " already exists, using " + newfname);
1361                         IsDuplicate = true;
1362                         Init (newfname);
1363                         return;
1364                 }
1365
1366                 FileStream fs = new FileStream (fname, FileMode.OpenOrCreate, FileAccess.Write);
1367                 w = new StreamWriter (fs);
1368         }
1369
1370         public string Indenter = "  ";
1371         string cur_indent = "";
1372         int level = 0;
1373
1374         public void Indent ()
1375         {
1376                 level++;
1377                 cur_indent = "";
1378                 for (int i = 0; i != level ; i++) cur_indent += Indenter;
1379         }
1380
1381         public void Outdent ()
1382         {
1383                 level--;
1384                 cur_indent = "";
1385                 for (int i = 0; i != level ; i++) cur_indent += Indenter;
1386         }
1387
1388         public void Write (string text)
1389         {
1390                 w.Write (text);
1391         }
1392
1393         public void WriteLine (string text)
1394         {
1395                 WriteLine (text, true);
1396         }
1397
1398         public void WriteLine (string text, bool autoindent)
1399         {
1400                 char[] opentags = {'{', '('};
1401                 char[] closetags = {'}', ')'};
1402
1403                 if (autoindent && text.TrimStart (closetags) != text)
1404                         Outdent ();
1405
1406                 w.Write (cur_indent);
1407                 w.WriteLine (text);
1408
1409                 if (autoindent && text.TrimEnd (opentags) != text)
1410                         Indent ();
1411         }
1412
1413         public void WriteLine (string text, CodeWriter cc)
1414         {
1415                 WriteLine (text, "", cc, "");
1416         }
1417
1418         public void WriteLine (string text, CodeWriter cc, string suffix)
1419         {
1420                 WriteLine (text, "", cc, suffix);
1421         }
1422
1423         public void WriteLine (string text, string prefix, CodeWriter cc)
1424         {
1425                 WriteLine (text, prefix, cc, "");
1426         }
1427
1428         public void WriteLine (string text, string prefix, CodeWriter cc, string suffix)
1429         {
1430                 WriteLine (text);
1431                 cc.WriteLine (prefix + text + suffix);
1432         }
1433
1434         public void WriteComment (string text)
1435         {
1436                 w.WriteLine ("/* " + text + " */");
1437         }
1438
1439         public void WriteLine ()
1440         {
1441                 w.WriteLine ("");
1442         }
1443
1444         public void Close ()
1445         {
1446                 w.Flush ();
1447                 w.Close ();
1448         }
1449 }