merge -r 58784:58785
[mono.git] / mcs / class / Mono.Posix / Mono.Unix.Native / make-map.cs
1 //
2 // MakeMap.cs: Builds a C map of constants defined on C# land
3 //
4 // Authors:
5 //  Miguel de Icaza (miguel@novell.com)
6 //  Jonathan Pryor (jonpryor@vt.edu)
7 //
8 // (C) 2003 Novell, Inc.
9 // (C) 2004 Jonathan Pryor
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32 using System;
33 using System.Collections;
34 using System.IO;
35 using System.Globalization;
36 using System.Reflection;
37 using System.Runtime.InteropServices;
38
39 delegate void CreateFileHandler (string assembly_name, string file_prefix);
40 delegate void AssemblyAttributesHandler (Assembly assembly);
41 delegate void TypeHandler (Type t, string ns, string fn);
42 delegate void CloseFileHandler (string file_prefix);
43
44 class MakeMap {
45
46         public static int Main (string [] args)
47         {
48                 FileGenerator[] generators = new FileGenerator[]{
49                         new HeaderFileGenerator (),
50                         new SourceFileGenerator (),
51                         new ConvertFileGenerator (),
52                         new ConvertDocFileGenerator (),
53                         new MphPrototypeFileGenerator (),
54                 };
55
56                 MakeMap composite = new MakeMap ();
57                 foreach (FileGenerator g in generators) {
58                         composite.FileCreators += new CreateFileHandler (g.CreateFile);
59                         composite.AssemblyAttributesHandler += 
60                                 new AssemblyAttributesHandler (g.WriteAssemblyAttributes);
61                         composite.TypeHandler += new TypeHandler (g.WriteType);
62                         composite.FileClosers += new CloseFileHandler (g.CloseFile);
63                 }
64
65                 return composite.Run (args);
66         }
67
68         event CreateFileHandler FileCreators;
69         event AssemblyAttributesHandler AssemblyAttributesHandler;
70         event TypeHandler TypeHandler;
71         event CloseFileHandler FileClosers;
72
73         int Run (string[] args)
74         {
75                 if (args.Length != 2){
76                         Console.WriteLine ("Usage is: make-map assembly output");
77                         return 1;
78                 }
79                 
80                 string assembly_name = args[0];
81                 string output = args[1];
82
83                 FileCreators (assembly_name, output);
84
85                 Assembly assembly = Assembly.LoadFrom (assembly_name);
86                 AssemblyAttributesHandler (assembly);
87                 
88                 Type [] exported_types = assembly.GetTypes ();
89                 Array.Sort (exported_types, new TypeFullNameComparer ());
90                         
91                 foreach (Type t in exported_types) {
92                         string ns = t.Namespace;
93                         if (ns == null || !ns.StartsWith ("Mono"))
94                                 continue;
95                         string fn = GetNativeName (t.FullName);
96                         ns = GetNativeName (ns);
97
98                         TypeHandler (t, ns, fn);
99                 }
100                 FileClosers (output);
101
102                 return 0;
103         }
104
105         private class TypeFullNameComparer : IComparer {
106                 public int Compare (object o1, object o2)
107                 {
108                         Type t1 = o1 as Type;
109                         Type t2 = o2 as Type;
110                         if (t1 == t2)
111                                 return 0;
112                         if (t1 == null)
113                                 return 1;
114                         if (t2 == null)
115                                 return -1;
116                         return CultureInfo.InvariantCulture.CompareInfo.Compare (
117                                         t1.FullName, t2.FullName, CompareOptions.Ordinal);
118                 }
119         }
120
121         private class _MemberNameComparer : IComparer {
122                 public int Compare (object o1, object o2)
123                 {
124                         MemberInfo m1 = o1 as MemberInfo;
125                         MemberInfo m2 = o2 as MemberInfo;
126                         if (m1 == m2)
127                                 return 0;
128                         if (m1 == null)
129                                 return 1;
130                         if (m2 == null)
131                                 return -1;
132                         return CultureInfo.InvariantCulture.CompareInfo.Compare (
133                                         m1.Name, m2.Name, CompareOptions.Ordinal);
134                 }
135         }
136
137         private class _OrdinalStringComparer : IComparer {
138                 public int Compare (object o1, object o2)
139                 {
140                         string s1 = o1 as string;
141                         string s2 = o2 as string;
142                         if (object.ReferenceEquals (s1, s2))
143                                 return 0;
144                         if (s1 == null)
145                                 return 1;
146                         if (s2 == null)
147                                 return -1;
148                         return CultureInfo.InvariantCulture.CompareInfo.Compare (s1, s2, 
149                                         CompareOptions.Ordinal);
150                 }
151         }
152
153         internal static IComparer MemberNameComparer = new _MemberNameComparer ();
154         internal static IComparer OrdinalStringComparer = new _OrdinalStringComparer ();
155
156         internal static string GetNativeName (string fn)
157         {
158                 fn = fn.Replace ('.', '_');
159                 if (fn.StartsWith ("Mono_Unix_Native"))
160                         return fn.Replace ("Mono_Unix_Native", "Mono_Posix");
161                 return fn.Replace ("Mono_Unix", "Mono_Posix");
162         }
163 }
164
165 abstract class FileGenerator {
166         public abstract void CreateFile (string assembly_name, string file_prefix);
167
168         public virtual void WriteAssemblyAttributes (Assembly assembly)
169         {
170         }
171
172         public abstract void WriteType (Type t, string ns, string fn);
173         public abstract void CloseFile (string file_prefix);
174
175         protected static void WriteHeader (StreamWriter s, string assembly)
176         {
177                 WriteHeader (s, assembly, false);
178         }
179
180         protected static void WriteHeader (StreamWriter s, string assembly, bool noConfig)
181         {
182                 s.WriteLine (
183                         "/*\n" +
184                         " * This file was automatically generated by make-map from {0}.\n" +
185                         " *\n" +
186                         " * DO NOT MODIFY.\n" +
187                         " */",
188                         assembly);
189                 if (!noConfig) {
190                         s.WriteLine ("#include <config.h>");
191                 }
192                 s.WriteLine ();
193         }
194
195         protected static bool CanMapType (Type t, out bool bits)
196         {
197                 object [] attributes = t.GetCustomAttributes (false);
198                 bool map = false;
199                 bits = false;
200                 
201                 foreach (object attr in attributes) {
202                         if (attr.GetType ().Name == "MapAttribute")
203                                 map = true;
204                         if (attr.GetType ().Name == "FlagsAttribute")
205                                 bits = true;
206                 }
207                 return map;
208         }
209
210         protected static string GetNativeType (Type t)
211         {
212                 string ut = t.Name;
213                 if (t.IsEnum)
214                         ut = Enum.GetUnderlyingType (t).Name;
215                 Type et = t.GetElementType ();
216                 if (et != null && et.IsEnum)
217                         ut = Enum.GetUnderlyingType (et).Name;
218
219                 string type = null;
220
221                 switch (ut) {
222                         case "Boolean":       type = "int";             break;
223                         case "Byte":          type = "unsigned char";   break;
224                         case "SByte":         type = "signed char";     break;
225                         case "Int16":         type = "short";           break;
226                         case "UInt16":        type = "unsigned short";  break;
227                         case "Int32":         type = "int";             break;
228                         case "UInt32":        type = "unsigned int";    break;
229                         case "UInt32[]":      type = "unsigned int*";   break;
230                         case "Int64":         type = "gint64";          break;
231                         case "UInt64":        type = "guint64";         break;
232                         case "IntPtr":        type = "void*";           break;
233                         case "Byte[]":        type = "void*";           break;
234                         case "String":        type = "const char*";     break;
235                         case "StringBuilder": type = "char*";           break;
236                         case "Void":          type = "void";            break;
237                         case "HandleRef":     type = "void*";           break;
238                 }
239                 if (type != null)
240                         return string.Format ("{0}{1}", type,
241                                         t.IsByRef ? "*" : "");
242                 return GetTypeName (t);
243         }
244
245         private static string GetTypeName (Type t)
246         {
247                 if (t.Namespace.StartsWith ("System"))
248                         return "int /* warning: unknown mapping for type: " + t.Name + " */";
249                 string ts = "struct " +
250                         MakeMap.GetNativeName (t.FullName).Replace ("+", "_").Replace ("&", "*");
251                 return ts;
252         }
253 }
254
255 class HeaderFileGenerator : FileGenerator {
256         StreamWriter sh;
257
258         public override void CreateFile (string assembly_name, string file_prefix)
259         {
260                 sh = File.CreateText (file_prefix + ".h");
261                 WriteHeader (sh, assembly_name);
262                 sh.WriteLine ("#ifndef INC_Mono_Posix_" + file_prefix + "_H");
263                 sh.WriteLine ("#define INC_Mono_Posix_" + file_prefix + "_H\n");
264                 sh.WriteLine ("#include <glib/gtypes.h>\n");
265                 sh.WriteLine ("G_BEGIN_DECLS\n");
266         }
267
268         public override void WriteType (Type t, string ns, string fn)
269         {
270                 bool bits;
271                 if (!CanMapType (t, out bits))
272                         return;
273                 string etype = GetNativeType (t);
274
275                 WriteLiteralValues (sh, t, fn);
276                 sh.WriteLine ("int {1}_From{2} ({0} x, {0} *r);", etype, ns, t.Name);
277                 sh.WriteLine ("int {1}_To{2} ({0} x, {0} *r);", etype, ns, t.Name);
278                 sh.WriteLine ();
279         }
280
281         static void WriteLiteralValues (StreamWriter sh, Type t, string n)
282         {
283                 object inst = Activator.CreateInstance (t);
284                 FieldInfo[] fields = t.GetFields ();
285                 Array.Sort (fields, MakeMap.MemberNameComparer);
286                 foreach (FieldInfo fi in fields) {
287                         if (!fi.IsLiteral)
288                                 continue;
289                         sh.WriteLine ("#define {0}_{1} 0x{2:x}", n, fi.Name, fi.GetValue (inst));
290                 }
291         }
292
293         public override void CloseFile (string file_prefix)
294         {
295                 sh.WriteLine ("G_END_DECLS\n");
296                 sh.WriteLine ("#endif /* ndef INC_Mono_Posix_" + file_prefix + "_H */\n");
297                 sh.Close ();
298         }
299 }
300
301 class SourceFileGenerator : FileGenerator {
302         StreamWriter sc;
303
304         public override void CreateFile (string assembly_name, string file_prefix)
305         {
306                 sc = File.CreateText (file_prefix + ".c");
307                 WriteHeader (sc, assembly_name);
308
309                 if (file_prefix.IndexOf ("/") != -1)
310                         file_prefix = file_prefix.Substring (file_prefix.IndexOf ("/") + 1);
311                 sc.WriteLine ("#include \"{0}.h\"", file_prefix);
312                 sc.WriteLine ();
313         }
314
315         public override void WriteAssemblyAttributes (Assembly assembly)
316         {
317                 object [] x = assembly.GetCustomAttributes (false);
318                 Console.WriteLine ("Got: " + x.Length);
319                 foreach (object aattr in assembly.GetCustomAttributes (false)) {
320                         Console.WriteLine ("Got: " + aattr.GetType ().Name);
321                         if (aattr.GetType ().Name == "HeaderAttribute"){
322                                 WriteDefines (sc, aattr);
323                                 WriteIncludes (sc, aattr);
324                         }
325                 }
326         }
327
328         static void WriteDefines (TextWriter writer, object o)
329         {
330                 PropertyInfo prop = o.GetType ().GetProperty ("Defines");
331                 if (prop == null)
332                         throw new Exception ("Cannot find 'Defines' property");
333
334                 MethodInfo method = prop.GetGetMethod ();
335                 string [] defines = method.Invoke (o, null).ToString ().Split (',');
336                 foreach (string def in defines) {
337                         writer.WriteLine ("#ifndef {0}", def);
338                         writer.WriteLine ("#define {0}", def);
339                         writer.WriteLine ("#endif /* ndef {0} */", def);
340                 }
341         }
342
343         static void WriteIncludes (TextWriter writer, object o)
344         {
345                 PropertyInfo prop = o.GetType ().GetProperty ("Includes");
346                 if (prop == null)
347                         throw new Exception ("Cannot find 'Includes' property");
348
349                 MethodInfo method = prop.GetGetMethod ();
350                 string [] includes = method.Invoke (o, null).ToString ().Split (',');;
351                 foreach (string inc in includes){
352                         if (inc.Length > 3 && 
353                                         string.CompareOrdinal (inc, 0, "ah:", 0, 3) == 0) {
354                                 string i = inc.Substring (3);
355                                 writer.WriteLine ("#ifdef HAVE_" + (i.ToUpper ().Replace ("/", "_").Replace (".", "_")));
356                                 writer.WriteLine ("#include <{0}>", i);
357                                 writer.WriteLine ("#endif");
358                         } else 
359                                 writer.WriteLine ("#include <{0}>", inc);
360                 }
361                 writer.WriteLine ();
362         }
363
364         public override void WriteType (Type t, string ns, string fn)
365         {
366                 bool bits;
367                 if (!CanMapType (t, out bits))
368                         return;
369                 string etype = GetNativeType (t);
370
371                 WriteFromManagedType (t, ns, fn, etype, bits);
372                 WriteToManagedType (t, ns, fn, etype, bits);
373         }
374
375         private void WriteFromManagedType (Type t, string ns, string fn, string etype, bool bits)
376         {
377                 sc.WriteLine ("int {1}_From{2} ({0} x, {0} *r)", etype, ns, t.Name);
378                 sc.WriteLine ("{");
379                 sc.WriteLine ("\t*r = 0;");
380                 // For many values, 0 is a valid value, but doesn't have it's own symbol.
381                 // Examples: Error (0 means "no error"), WaitOptions (0 means "no options").
382                 // Make 0 valid for all conversions.
383                 sc.WriteLine ("\tif (x == 0)\n\t\treturn 0;");
384                 FieldInfo[] fields = t.GetFields ();
385                 Array.Sort (fields, MakeMap.MemberNameComparer);
386                 foreach (FieldInfo fi in fields) {
387                         if (!fi.IsLiteral)
388                                 continue;
389                         if (Attribute.GetCustomAttribute (fi, 
390                                 typeof(ObsoleteAttribute), false) != null) {
391                                 sc.WriteLine ("\t/* {0}_{1} is obsolete; ignoring */", fn, fi.Name);
392                                 continue;
393                         }
394                         if (bits)
395                                 // properly handle case where [Flags] enumeration has helper
396                                 // synonyms.  e.g. DEFFILEMODE and ACCESSPERMS for mode_t.
397                                 sc.WriteLine ("\tif ((x & {0}_{1}) == {0}_{1})", fn, fi.Name);
398                         else
399                                 sc.WriteLine ("\tif (x == {0}_{1})", fn, fi.Name);
400                         sc.WriteLine ("#ifdef {0}", fi.Name);
401                         if (bits)
402                                 sc.WriteLine ("\t\t*r |= {1};", fn, fi.Name);
403                         else
404                                 sc.WriteLine ("\t\t{{*r = {1}; return 0;}}", fn, fi.Name);
405                         sc.WriteLine ("#else /* def {0} */\n\t\t{{errno = EINVAL; return -1;}}", fi.Name);
406                         sc.WriteLine ("#endif /* ndef {0} */", fi.Name);
407                 }
408                 if (bits)
409                         sc.WriteLine ("\treturn 0;");
410                 else
411                         sc.WriteLine ("\terrno = EINVAL; return -1;"); // return error if not matched
412                 sc.WriteLine ("}\n");
413         }
414
415         private void WriteToManagedType (Type t, string ns, string fn, string etype, bool bits)
416         {
417                 sc.WriteLine ("int {1}_To{2} ({0} x, {0} *r)", etype, ns, t.Name);
418                 sc.WriteLine ("{");
419                 sc.WriteLine ("\t*r = 0;", etype);
420                 // For many values, 0 is a valid value, but doesn't have it's own symbol.
421                 // Examples: Error (0 means "no error"), WaitOptions (0 means "no options").
422                 // Make 0 valid for all conversions.
423                 sc.WriteLine ("\tif (x == 0)\n\t\treturn 0;");
424                 FieldInfo[] fields = t.GetFields ();
425                 Array.Sort (fields, MakeMap.MemberNameComparer);
426                 foreach (FieldInfo fi in fields) {
427                         if (!fi.IsLiteral)
428                                 continue;
429                         sc.WriteLine ("#ifdef {0}", fi.Name);
430                         if (bits)
431                                 // properly handle case where [Flags] enumeration has helper
432                                 // synonyms.  e.g. DEFFILEMODE and ACCESSPERMS for mode_t.
433                                 sc.WriteLine ("\tif ((x & {1}) == {1})\n\t\t*r |= {0}_{1};", fn, fi.Name);
434                         else
435                                 sc.WriteLine ("\tif (x == {1})\n\t\t{{*r = {0}_{1}; return 0;}}", fn, fi.Name);
436                         sc.WriteLine ("#endif /* ndef {0} */", fi.Name);
437                 }
438                 if (bits)
439                         sc.WriteLine ("\treturn 0;");
440                 else
441                         sc.WriteLine ("\terrno = EINVAL; return -1;");
442                 sc.WriteLine ("}\n");
443         }
444
445         public override void CloseFile (string file_prefix)
446         {
447                 sc.Close ();
448         }
449 }
450
451 class ConvertFileGenerator : FileGenerator {
452         StreamWriter scs;
453
454         public override void CreateFile (string assembly_name, string file_prefix)
455         {
456                 scs = File.CreateText (file_prefix + ".cs");
457                 WriteHeader (scs, assembly_name, true);
458                 scs.WriteLine ("using System;");
459                 scs.WriteLine ("using System.Runtime.InteropServices;");
460                 scs.WriteLine ("using Mono.Unix.Native;\n");
461                 scs.WriteLine ("namespace Mono.Unix.Native {\n");
462                 scs.WriteLine ("\t[CLSCompliant (false)]");
463                 scs.WriteLine ("\tpublic sealed /* static */ partial class NativeConvert");
464                 scs.WriteLine ("\t{");
465                 scs.WriteLine ("\t\tprivate NativeConvert () {}\n");
466                 scs.WriteLine ("\t\tprivate const string LIB = \"MonoPosixHelper\";\n");
467                 scs.WriteLine ("\t\tprivate static void ThrowArgumentException (object value)");
468                 scs.WriteLine ("\t\t{");
469                 scs.WriteLine ("\t\t\tthrow new ArgumentOutOfRangeException (\"value\", value,");
470                 scs.WriteLine ("\t\t\t\tLocale.GetText (\"Current platform doesn't support this value.\"));");
471                 scs.WriteLine ("\t\t}\n");
472         }
473
474         public override void WriteType (Type t, string ns, string fn)
475         {
476                 bool bits;
477                 if (!CanMapType (t, out bits))
478                         return;
479
480                 string mtype = Enum.GetUnderlyingType(t).Name;
481                 ObsoleteAttribute oa = (ObsoleteAttribute) Attribute.GetCustomAttribute (t, 
482                                         typeof(ObsoleteAttribute), false);
483                 string obsolete = "";
484                 if (oa != null) {
485                         obsolete = "[Obsolete (\"" + oa.Message + "\")]\n\t\t";
486                 }
487                 scs.WriteLine ("\t\t[DllImport (LIB, " + 
488                         "EntryPoint=\"{0}_From{1}\")]\n" +
489                         "\t\tprivate static extern int From{1} ({1} value, out {2} rval);\n",
490                         ns, t.Name, mtype);
491                 scs.WriteLine ("\t\t{3}public static bool TryFrom{1} ({1} value, out {2} rval)\n" +
492                         "\t\t{{\n" +
493                         "\t\t\treturn From{1} (value, out rval) == 0;\n" +
494                         "\t\t}}\n", ns, t.Name, mtype, obsolete);
495                 scs.WriteLine ("\t\t{2}public static {0} From{1} ({1} value)", mtype, t.Name, obsolete);
496                 scs.WriteLine ("\t\t{");
497                 scs.WriteLine ("\t\t\t{0} rval;", mtype);
498                 scs.WriteLine ("\t\t\tif (From{0} (value, out rval) == -1)\n" + 
499                                 "\t\t\t\tThrowArgumentException (value);", t.Name);
500                 scs.WriteLine ("\t\t\treturn rval;");
501                 scs.WriteLine ("\t\t}\n");
502                 scs.WriteLine ("\t\t[DllImport (LIB, " + 
503                         "EntryPoint=\"{0}_To{1}\")]\n" +
504                         "\t\tprivate static extern int To{1} ({2} value, out {1} rval);\n",
505                         ns, t.Name, mtype);
506                 scs.WriteLine ("\t\t{2}public static bool TryTo{1} ({0} value, out {1} rval)\n" +
507                         "\t\t{{\n" +
508                         "\t\t\treturn To{1} (value, out rval) == 0;\n" +
509                         "\t\t}}\n", mtype, t.Name, obsolete);
510                 scs.WriteLine ("\t\t{2}public static {1} To{1} ({0} value)", mtype, t.Name, obsolete);
511                 scs.WriteLine ("\t\t{");
512                 scs.WriteLine ("\t\t\t{0} rval;", t.Name);
513                 scs.WriteLine ("\t\t\tif (To{0} (value, out rval) == -1)\n" + 
514                                 "\t\t\t\tThrowArgumentException (value);", t.Name);
515                 scs.WriteLine ("\t\t\treturn rval;");
516                 scs.WriteLine ("\t\t}\n");
517         }
518
519         public override void CloseFile (string file_prefix)
520         {
521                 scs.WriteLine ("\t}");
522                 scs.WriteLine ("}\n");
523                 scs.Close ();
524         }
525 }
526
527 class ConvertDocFileGenerator : FileGenerator {
528         StreamWriter scs;
529
530         public override void CreateFile (string assembly_name, string file_prefix)
531         {
532                 scs = File.CreateText (file_prefix + ".xml");
533                 scs.WriteLine ("    <!-- BEGIN GENERATED CONTENT");
534                 WriteHeader (scs, assembly_name, true);
535                 scs.WriteLine ("      -->");
536         }
537
538         public override void WriteType (Type t, string ns, string fn)
539         {
540                 bool bits;
541                 if (!CanMapType (t, out bits))
542                         return;
543
544                 string type = GetCSharpType (t);
545                 string mtype = Enum.GetUnderlyingType(t).FullName;
546                 string member = t.Name;
547                 string ftype = t.FullName;
548
549                 string to_returns = "";
550                 string to_remarks = "";
551                 string to_exception = "";
552
553                 if (bits) {
554                         to_returns = "<returns>An approximation of the equivalent managed value.</returns>";
555                         to_remarks = @"<para>The current conversion functions are unable to determine
556         if a value in a <c>[Flags]</c>-marked enumeration <i>does not</i> 
557         exist on the current platform.  As such, if <paramref name=""value"" /> 
558         contains a flag value which the current platform doesn't support, it 
559         will not be present in the managed value returned.</para>
560         <para>This should only be a problem if <paramref name=""value"" /> 
561         <i>was not</i> previously returned by 
562         <see cref=""M:Mono.Unix.Native.NativeConvert.From" + member + "\" />.</para>\n";
563                 }
564                 else {
565                         to_returns = "<returns>The equivalent managed value.</returns>";
566                         to_exception = @"
567         <exception cref=""T:System.ArgumentOutOfRangeException"">
568           <paramref name=""value"" /> has no equivalent managed value.
569         </exception>
570 ";
571                 }
572                 scs.WriteLine (@"
573     <Member MemberName=""TryFrom{1}"">
574       <MemberSignature Language=""C#"" Value=""public static bool TryFrom{1} ({0} value, out {2} rval);"" />
575       <MemberType>Method</MemberType>
576       <ReturnValue>
577         <ReturnType>System.Boolean</ReturnType>
578       </ReturnValue>
579       <Parameters>
580         <Parameter Name=""value"" Type=""{0}"" />
581         <Parameter Name=""rval"" Type=""{3}&amp;"" RefType=""out"" />
582       </Parameters>
583       <Docs>
584         <param name=""value"">The managed value to convert.</param>
585         <param name=""rval"">The OS-specific equivalent value.</param>
586         <summary>Converts a <see cref=""T:{0}"" /> 
587           enumeration value to an OS-specific value.</summary>
588         <returns><see langword=""true"" /> if the conversion was successful; 
589         otherwise, <see langword=""false"" />.</returns>
590         <remarks><para>This is an exception-safe alternative to 
591         <see cref=""M:Mono.Unix.Native.NativeConvert.From{1}"" />.</para>
592         <para>If successful, this method stores the OS-specific equivalent
593         value of <paramref name=""value"" /> into <paramref name=""rval"" />.
594         Otherwise, <paramref name=""rval"" /> will contain <c>0</c>.</para>
595         </remarks>
596         <altmember cref=""M:Mono.Unix.Native.NativeConvert.From{1}"" />
597         <altmember cref=""M:Mono.Unix.Native.NativeConvert.To{1}"" />
598         <altmember cref=""M:Mono.Unix.Native.NativeConvert.TryTo{1}"" />
599       </Docs>
600     </Member>
601     <Member MemberName=""From{1}"">
602       <MemberSignature Language=""C#"" Value=""public static {2} From{1} ({0} value);"" />
603       <MemberType>Method</MemberType>
604       <ReturnValue>
605         <ReturnType>{3}</ReturnType>
606       </ReturnValue>
607       <Parameters>
608         <Parameter Name=""value"" Type=""{0}"" />
609       </Parameters>
610       <Docs>
611         <param name=""value"">The managed value to convert.</param>
612         <summary>Converts a <see cref=""T:{0}"" /> 
613           to an OS-specific value.</summary>
614         <returns>The equivalent OS-specific value.</returns>
615         <exception cref=""T:System.ArgumentOutOfRangeException"">
616           <paramref name=""value"" /> has no equivalent OS-specific value.
617         </exception>
618         <remarks></remarks>
619         <altmember cref=""M:Mono.Unix.Native.NativeConvert.To{1}"" />
620         <altmember cref=""M:Mono.Unix.Native.NativeConvert.TryFrom{1}"" />
621         <altmember cref=""M:Mono.Unix.Native.NativeConvert.TryTo{1}"" />
622       </Docs>
623     </Member>
624     <Member MemberName=""TryTo{1}"">
625       <MemberSignature Language=""C#"" Value=""public static bool TryTo{1} ({2} value, out {0} rval);"" />
626       <MemberType>Method</MemberType>
627       <ReturnValue>
628         <ReturnType>System.Boolean</ReturnType>
629       </ReturnValue>
630       <Parameters>
631         <Parameter Name=""value"" Type=""{3}"" />
632         <Parameter Name=""rval"" Type=""{0}&amp;"" RefType=""out"" />
633       </Parameters>
634       <Docs>
635         <param name=""value"">The OS-specific value to convert.</param>
636         <param name=""rval"">The managed equivalent value</param>
637         <summary>Converts an OS-specific value to a 
638           <see cref=""T:{0}"" />.</summary>
639         <returns><see langword=""true"" /> if the conversion was successful; 
640         otherwise, <see langword=""false"" />.</returns>
641         <remarks><para>This is an exception-safe alternative to 
642         <see cref=""M:Mono.Unix.Native.NativeConvert.To{1}"" />.</para>
643         <para>If successful, this method stores the managed equivalent
644         value of <paramref name=""value"" /> into <paramref name=""rval"" />.
645         Otherwise, <paramref name=""rval"" /> will contain a <c>0</c>
646         cast to a <see cref=""T:{0}"" />.</para>
647         " + to_remarks + 
648 @"        </remarks>
649         <altmember cref=""M:Mono.Unix.Native.NativeConvert.From{1}"" />
650         <altmember cref=""M:Mono.Unix.Native.NativeConvert.To{1}"" />
651         <altmember cref=""M:Mono.Unix.Native.NativeConvert.TryFrom{1}"" />
652       </Docs>
653     </Member>
654     <Member MemberName=""To{1}"">
655       <MemberSignature Language=""C#"" Value=""public static {0} To{1} ({2} value);"" />
656       <MemberType>Method</MemberType>
657       <ReturnValue>
658         <ReturnType>{0}</ReturnType>
659       </ReturnValue>
660       <Parameters>
661         <Parameter Name=""value"" Type=""{3}"" />
662       </Parameters>
663       <Docs>
664         <param name=""value"">The OS-specific value to convert.</param>
665         <summary>Converts an OS-specific value to a 
666           <see cref=""T:{0}"" />.</summary>
667                                         " + to_returns + "\n" + 
668                         to_exception + 
669 @"        <remarks>
670         " + to_remarks + @"
671         </remarks>
672         <altmember cref=""M:Mono.Unix.Native.NativeConvert.From{1}"" />
673         <altmember cref=""M:Mono.Unix.Native.NativeConvert.TryFrom{1}"" />
674         <altmember cref=""M:Mono.Unix.Native.NativeConvert.TryTo{1}"" />
675       </Docs>
676     </Member>
677 ", ftype, member, type, mtype
678                 );
679         }
680
681         private string GetCSharpType (Type t)
682         {
683                 string ut = t.Name;
684                 if (t.IsEnum)
685                         ut = Enum.GetUnderlyingType (t).Name;
686                 Type et = t.GetElementType ();
687                 if (et != null && et.IsEnum)
688                         ut = Enum.GetUnderlyingType (et).Name;
689
690                 string type = null;
691
692                 switch (ut) {
693                         case "Boolean":       type = "bool";    break;
694                         case "Byte":          type = "byte";    break;
695                         case "SByte":         type = "sbyte";   break;
696                         case "Int16":         type = "short";   break;
697                         case "UInt16":        type = "ushort";  break;
698                         case "Int32":         type = "int";     break;
699                         case "UInt32":        type = "uint";    break;
700                         case "Int64":         type = "long";    break;
701                         case "UInt64":        type = "ulong";   break;
702                 }
703
704                 return type;
705         }
706
707         public override void CloseFile (string file_prefix)
708         {
709                 scs.WriteLine ("    <!-- END GENERATED CONTENT -->");
710                 scs.Close ();
711         }
712 }
713
714 class MphPrototypeFileGenerator : FileGenerator {
715         StreamWriter icall;
716         Hashtable methods = new Hashtable ();
717         Hashtable structs = new Hashtable ();
718
719         public override void CreateFile (string assembly_name, string file_prefix)
720         {
721                 icall = File.CreateText (file_prefix + "-icalls.h");
722                 WriteHeader (icall, assembly_name);
723                 icall.WriteLine ("#ifndef INC_Mono_Posix_" + file_prefix + "_ICALLS_H");
724                 icall.WriteLine ("#define INC_Mono_Posix_" + file_prefix + "_ICALLS_H\n");
725                 icall.WriteLine ("#include <glib/gtypes.h>\n");
726                 icall.WriteLine ("G_BEGIN_DECLS\n");
727
728                 // Kill warning about unused method
729                 DumpTypeInfo (null);
730         }
731
732         public override void WriteType (Type t, string ns, string fn)
733         {
734                 BindingFlags bf = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
735                 foreach (MethodInfo m in t.GetMethods (bf)) {
736                         if ((m.Attributes & MethodAttributes.PinvokeImpl) == 0)
737                                 continue;
738                         DllImportAttribute dia = GetDllImportInfo (m);
739                         if (dia == null) {
740                                 Console.WriteLine ("Unable to emit native prototype for P/Invoke " + 
741                                                 "method: {0}", m);
742                                 continue;
743                         }
744                         // we shouldn't declare prototypes for POSIX, etc. functions.
745                         if (dia.Value != "MonoPosixHelper" || IsOnExcludeList (dia.EntryPoint))
746                                 continue;
747                         methods [dia.EntryPoint] = m;
748                         RecordStructs (m);
749                 }
750         }
751
752         private static DllImportAttribute GetDllImportInfo (MethodInfo method)
753         {
754                 // .NET 2.0 synthesizes pseudo-attributes such as DllImport
755                 DllImportAttribute dia = (DllImportAttribute) Attribute.GetCustomAttribute (method, 
756                                         typeof(DllImportAttribute), false);
757                 if (dia != null)
758                         return dia;
759
760                 // We're not on .NET 2.0; assume we're on Mono and use some internal
761                 // methods...
762                 Type MonoMethod = Type.GetType ("System.Reflection.MonoMethod", false);
763                 if (MonoMethod == null) {
764                         Console.WriteLine ("cannot find MonoMethod");
765                         return null;
766                 }
767                 MethodInfo GetDllImportAttribute = 
768                         MonoMethod.GetMethod ("GetDllImportAttribute", 
769                                         BindingFlags.Static | BindingFlags.NonPublic);
770                 if (GetDllImportAttribute == null) {
771                         Console.WriteLine ("cannot find GetDllImportAttribute");
772                         return null;
773                 }
774                 IntPtr mhandle = method.MethodHandle.Value;
775                 return (DllImportAttribute) GetDllImportAttribute.Invoke (null, 
776                                 new object[]{mhandle});
777         }
778
779         private static string[] ExcludeList = new string[]{
780                 "Mono_Posix_Stdlib_snprintf",
781         };
782
783         private bool IsOnExcludeList (string method)
784         {
785                 int idx = Array.BinarySearch (ExcludeList, method);
786                 return (idx >= 0 && idx < ExcludeList.Length) ? true : false;
787         }
788
789         private void RecordStructs (MethodInfo method)
790         {
791                 ParameterInfo[] parameters = method.GetParameters ();
792                 foreach (ParameterInfo pi in parameters) {
793                         string s = GetNativeType (pi.ParameterType);
794                         if (s.StartsWith ("struct"))
795                                 structs [s] = s;
796                 }
797         }
798
799         public override void CloseFile (string file_prefix)
800         {
801                 icall.WriteLine ("/*\n * Structure Declarations\n */");
802                 foreach (string s in Sort (structs.Keys))
803                         icall.WriteLine ("{0};", s.Replace ("*", ""));
804
805                 icall.WriteLine ();
806
807                 icall.WriteLine ("/*\n * Function Declarations\n */");
808                 foreach (string method in Sort (methods.Keys)) {
809                         WriteMethodDeclaration ((MethodInfo) methods [method], method);
810                 }
811
812                 icall.WriteLine ("\nG_END_DECLS\n");
813                 icall.WriteLine ("#endif /* ndef INC_Mono_Posix_" + file_prefix + "_ICALLS_H */\n");
814                 icall.Close ();
815         }
816
817         private static IEnumerable Sort (ICollection c)
818         {
819                 ArrayList al = new ArrayList (c);
820                 al.Sort (MakeMap.OrdinalStringComparer);
821                 return al;
822         }
823
824         private void WriteMethodDeclaration (MethodInfo method, string entryPoint)
825         {
826                 icall.Write ("{0} ", GetNativeType (method.ReturnType));
827                 icall.Write ("{0} ", entryPoint);
828                 ParameterInfo[] parameters = method.GetParameters();
829                 if (parameters.Length == 0) {
830                         icall.WriteLine ("(void);");
831                         return;
832                 }
833                 if (parameters.Length > 0) {
834                         icall.Write ("(");
835                         WriteParameterDeclaration (parameters [0]);
836                 }
837                 for (int i = 1; i < parameters.Length; ++i) {
838                         icall.Write (", ");
839                         WriteParameterDeclaration (parameters [i]);
840                 }
841                 icall.WriteLine (");");
842         }
843
844         private void DumpTypeInfo (Type t)
845         {
846                 if (t == null)
847                         return;
848
849                 icall.WriteLine ("\t\t/* Type Info for " + t.FullName + ":");
850                 foreach (MemberInfo mi in typeof(Type).GetMembers()) {
851                         icall.WriteLine ("\t\t\t{0}={1}", mi.Name, GetMemberValue (mi, t));
852                 }
853                 icall.WriteLine ("\t\t */");
854         }
855
856         private static string GetMemberValue (MemberInfo mi, Type t)
857         {
858                 try {
859                 switch (mi.MemberType) {
860                         case MemberTypes.Constructor:
861                         case MemberTypes.Method: {
862                                 MethodBase b = (MethodBase) mi;
863                                 if (b.GetParameters().Length == 0)
864                                         return b.Invoke (t, new object[]{}).ToString();
865                                 return "<<cannot invoke>>";
866                         }
867                         case MemberTypes.Field:
868                                 return ((FieldInfo) mi).GetValue (t).ToString ();
869                         case MemberTypes.Property: {
870                                 PropertyInfo pi = (PropertyInfo) mi;
871                                 if (!pi.CanRead)
872                                         return "<<cannot read>>";
873                                 return pi.GetValue (t, null).ToString ();
874                         }
875                         default:
876                                 return "<<unknown value>>";
877                 }
878                 }
879                 catch (Exception e) {
880                         return "<<exception reading member: " + e.Message + ">>";
881                 }
882         }
883
884         private void WriteParameterDeclaration (ParameterInfo pi)
885         {
886                 // DumpTypeInfo (pi.ParameterType);
887                 icall.Write ("{0} {1}", GetNativeType (pi.ParameterType), pi.Name);
888         }
889 }
890
891 // vim: noexpandtab