2 // MakeMap.cs: Builds a C map of constants defined on C# land
5 // Miguel de Icaza (miguel@novell.com)
6 // Jonathan Pryor (jonpryor@vt.edu)
8 // (C) 2003 Novell, Inc.
9 // (C) 2004 Jonathan Pryor
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:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
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.
33 using System.Collections;
35 using System.Reflection;
36 using System.Runtime.InteropServices;
38 delegate void CreateFileHandler (string assembly_name, string file_prefix);
39 delegate void AssemblyAttributesHandler (Assembly assembly);
40 delegate void TypeHandler (Type t, string ns, string fn);
41 delegate void CloseFileHandler (string file_prefix);
45 public static int Main (string [] args)
47 FileGenerator[] generators = new FileGenerator[]{
48 new HeaderFileGenerator (),
49 new SourceFileGenerator (),
50 new ConvertFileGenerator (),
51 new MphPrototypeFileGenerator (),
54 MakeMap composite = new MakeMap ();
55 foreach (FileGenerator g in generators) {
56 composite.FileCreators += new CreateFileHandler (g.CreateFile);
57 composite.AssemblyAttributesHandler +=
58 new AssemblyAttributesHandler (g.WriteAssemblyAttributes);
59 composite.TypeHandler += new TypeHandler (g.WriteType);
60 composite.FileClosers += new CloseFileHandler (g.CloseFile);
63 return composite.Run (args);
66 event CreateFileHandler FileCreators;
67 event AssemblyAttributesHandler AssemblyAttributesHandler;
68 event TypeHandler TypeHandler;
69 event CloseFileHandler FileClosers;
71 int Run (string[] args)
73 if (args.Length != 2){
74 Console.WriteLine ("Usage is: make-map assembly output");
78 string assembly_name = args[0];
79 string output = args[1];
81 FileCreators (assembly_name, output);
83 Assembly assembly = Assembly.LoadFrom (assembly_name);
84 AssemblyAttributesHandler (assembly);
86 Type [] exported_types = assembly.GetTypes ();
87 Array.Sort (exported_types, new TypeFullNameComparer ());
89 foreach (Type t in exported_types) {
90 string ns = t.Namespace;
91 if (ns == null || !ns.StartsWith ("Mono"))
93 string fn = GetNativeName (t.FullName);
94 ns = GetNativeName (ns);
96 TypeHandler (t, ns, fn);
103 private class TypeFullNameComparer : IComparer {
104 public int Compare (object o1, object o2)
106 Type t1 = o1 as Type;
107 Type t2 = o2 as Type;
114 return Comparer.DefaultInvariant.Compare (t1.FullName, t2.FullName);
118 private class _MemberNameComparer : IComparer {
119 public int Compare (object o1, object o2)
121 MemberInfo m1 = o1 as MemberInfo;
122 MemberInfo m2 = o2 as MemberInfo;
129 return Comparer.DefaultInvariant.Compare (m1.Name, m2.Name);
133 internal static IComparer MemberNameComparer = new _MemberNameComparer ();
135 internal static string GetNativeName (string fn)
137 fn = fn.Replace ('.', '_');
138 if (fn.StartsWith ("Mono_Unix_Native"))
139 return fn.Replace ("Mono_Unix_Native", "Mono_Posix");
140 return fn.Replace ("Mono_Unix", "Mono_Posix");
144 abstract class FileGenerator {
145 public abstract void CreateFile (string assembly_name, string file_prefix);
147 public virtual void WriteAssemblyAttributes (Assembly assembly)
151 public abstract void WriteType (Type t, string ns, string fn);
152 public abstract void CloseFile (string file_prefix);
154 protected static void WriteHeader (StreamWriter s, string assembly)
156 WriteHeader (s, assembly, false);
159 protected static void WriteHeader (StreamWriter s, string assembly, bool noConfig)
163 " * This file was automatically generated by make-map from {0}.\n" +
165 " * DO NOT MODIFY.\n" +
169 s.WriteLine ("#include <config.h>");
174 protected static bool CanMapType (Type t, out bool bits)
176 object [] attributes = t.GetCustomAttributes (false);
180 foreach (object attr in attributes) {
181 if (attr.GetType ().Name == "MapAttribute")
183 if (attr.GetType ().Name == "FlagsAttribute")
189 protected static string GetNativeType (Type t)
193 ut = Enum.GetUnderlyingType (t).Name;
194 Type et = t.GetElementType ();
195 if (et != null && et.IsEnum)
196 ut = Enum.GetUnderlyingType (et).Name;
201 case "Boolean": type = "int"; break;
202 case "Byte": type = "unsigned char"; break;
203 case "SByte": type = "signed char"; break;
204 case "Int16": type = "short"; break;
205 case "UInt16": type = "unsigned short"; break;
206 case "Int32": type = "int"; break;
207 case "UInt32": type = "unsigned int"; break;
208 case "UInt32[]": type = "unsigned int*"; break;
209 case "Int64": type = "gint64"; break;
210 case "UInt64": type = "guint64"; break;
211 case "IntPtr": type = "void*"; break;
212 case "Byte[]": type = "void*"; break;
213 case "String": type = "const char*"; break;
214 case "StringBuilder": type = "char*"; break;
215 case "Void": type = "void"; break;
216 case "HandleRef": type = "void*"; break;
219 return string.Format ("{0}{1}", type,
220 t.IsByRef ? "*" : "");
221 return GetTypeName (t);
224 private static string GetTypeName (Type t)
226 if (t.Namespace.StartsWith ("System"))
227 return "int /* warning: unknown mapping for type: " + t.Name + " */";
228 string ts = "struct " +
229 MakeMap.GetNativeName (t.FullName).Replace ("+", "_").Replace ("&", "*");
234 class HeaderFileGenerator : FileGenerator {
237 public override void CreateFile (string assembly_name, string file_prefix)
239 sh = File.CreateText (file_prefix + ".h");
240 WriteHeader (sh, assembly_name);
241 sh.WriteLine ("#ifndef INC_Mono_Posix_" + file_prefix + "_H");
242 sh.WriteLine ("#define INC_Mono_Posix_" + file_prefix + "_H\n");
243 sh.WriteLine ("#include <glib/gtypes.h>\n");
244 sh.WriteLine ("G_BEGIN_DECLS\n");
247 public override void WriteType (Type t, string ns, string fn)
250 if (!CanMapType (t, out bits))
252 string etype = GetNativeType (t);
254 WriteLiteralValues (sh, t, fn);
255 sh.WriteLine ("int {1}_From{2} ({0} x, {0} *r);", etype, ns, t.Name);
256 sh.WriteLine ("int {1}_To{2} ({0} x, {0} *r);", etype, ns, t.Name);
260 static void WriteLiteralValues (StreamWriter sh, Type t, string n)
262 object inst = Activator.CreateInstance (t);
263 FieldInfo[] fields = t.GetFields ();
264 Array.Sort (fields, MakeMap.MemberNameComparer);
265 foreach (FieldInfo fi in fields) {
268 sh.WriteLine ("#define {0}_{1} 0x{2:x}", n, fi.Name, fi.GetValue (inst));
272 public override void CloseFile (string file_prefix)
274 sh.WriteLine ("G_END_DECLS\n");
275 sh.WriteLine ("#endif /* ndef INC_Mono_Posix_" + file_prefix + "_H */\n");
280 class SourceFileGenerator : FileGenerator {
283 public override void CreateFile (string assembly_name, string file_prefix)
285 sc = File.CreateText (file_prefix + ".c");
286 WriteHeader (sc, assembly_name);
288 if (file_prefix.IndexOf ("/") != -1)
289 file_prefix = file_prefix.Substring (file_prefix.IndexOf ("/") + 1);
290 sc.WriteLine ("#include \"{0}.h\"", file_prefix);
294 public override void WriteAssemblyAttributes (Assembly assembly)
296 object [] x = assembly.GetCustomAttributes (false);
297 Console.WriteLine ("Got: " + x.Length);
298 foreach (object aattr in assembly.GetCustomAttributes (false)) {
299 Console.WriteLine ("Got: " + aattr.GetType ().Name);
300 if (aattr.GetType ().Name == "HeaderAttribute"){
301 WriteDefines (sc, aattr);
302 WriteIncludes (sc, aattr);
307 static void WriteDefines (TextWriter writer, object o)
309 PropertyInfo prop = o.GetType ().GetProperty ("Defines");
311 throw new Exception ("Cannot find 'Defines' property");
313 MethodInfo method = prop.GetGetMethod ();
314 string [] defines = method.Invoke (o, null).ToString ().Split (',');
315 foreach (string def in defines) {
316 writer.WriteLine ("#ifndef {0}", def);
317 writer.WriteLine ("#define {0}", def);
318 writer.WriteLine ("#endif /* ndef {0} */", def);
322 static void WriteIncludes (TextWriter writer, object o)
324 PropertyInfo prop = o.GetType ().GetProperty ("Includes");
326 throw new Exception ("Cannot find 'Includes' property");
328 MethodInfo method = prop.GetGetMethod ();
329 string [] includes = method.Invoke (o, null).ToString ().Split (',');;
330 foreach (string inc in includes){
331 if (inc.Length > 3 &&
332 string.CompareOrdinal (inc, 0, "ah:", 0, 3) == 0) {
333 string i = inc.Substring (3);
334 writer.WriteLine ("#ifdef HAVE_" + (i.ToUpper ().Replace ("/", "_").Replace (".", "_")));
335 writer.WriteLine ("#include <{0}>", i);
336 writer.WriteLine ("#endif");
338 writer.WriteLine ("#include <{0}>", inc);
343 public override void WriteType (Type t, string ns, string fn)
346 if (!CanMapType (t, out bits))
348 string etype = GetNativeType (t);
350 WriteFromManagedType (t, ns, fn, etype, bits);
351 WriteToManagedType (t, ns, fn, etype, bits);
354 private void WriteFromManagedType (Type t, string ns, string fn, string etype, bool bits)
356 sc.WriteLine ("int {1}_From{2} ({0} x, {0} *r)", etype, ns, t.Name);
358 sc.WriteLine ("\t*r = 0;");
359 // For many values, 0 is a valid value, but doesn't have it's own symbol.
360 // Examples: Error (0 means "no error"), WaitOptions (0 means "no options").
361 // Make 0 valid for all conversions.
362 sc.WriteLine ("\tif (x == 0)\n\t\treturn 0;");
363 FieldInfo[] fields = t.GetFields ();
364 Array.Sort (fields, MakeMap.MemberNameComparer);
365 foreach (FieldInfo fi in fields) {
368 if (Attribute.GetCustomAttribute (fi,
369 typeof(ObsoleteAttribute), false) != null) {
370 sc.WriteLine ("\t/* {0}_{1} is obsolete; ignoring */", fn, fi.Name);
374 // properly handle case where [Flags] enumeration has helper
375 // synonyms. e.g. DEFFILEMODE and ACCESSPERMS for mode_t.
376 sc.WriteLine ("\tif ((x & {0}_{1}) == {0}_{1})", fn, fi.Name);
378 sc.WriteLine ("\tif (x == {0}_{1})", fn, fi.Name);
379 sc.WriteLine ("#ifdef {0}", fi.Name);
381 sc.WriteLine ("\t\t*r |= {1};", fn, fi.Name);
383 sc.WriteLine ("\t\t{{*r = {1}; return 0;}}", fn, fi.Name);
384 sc.WriteLine ("#else /* def {0} */\n\t\t{{errno = EINVAL; return -1;}}", fi.Name);
385 sc.WriteLine ("#endif /* ndef {0} */", fi.Name);
388 sc.WriteLine ("\treturn 0;");
390 sc.WriteLine ("\terrno = EINVAL; return -1;"); // return error if not matched
391 sc.WriteLine ("}\n");
394 private void WriteToManagedType (Type t, string ns, string fn, string etype, bool bits)
396 sc.WriteLine ("int {1}_To{2} ({0} x, {0} *r)", etype, ns, t.Name);
398 sc.WriteLine ("\t*r = 0;", etype);
399 // For many values, 0 is a valid value, but doesn't have it's own symbol.
400 // Examples: Error (0 means "no error"), WaitOptions (0 means "no options").
401 // Make 0 valid for all conversions.
402 sc.WriteLine ("\tif (x == 0)\n\t\treturn 0;");
403 FieldInfo[] fields = t.GetFields ();
404 Array.Sort (fields, MakeMap.MemberNameComparer);
405 foreach (FieldInfo fi in fields) {
408 sc.WriteLine ("#ifdef {0}", fi.Name);
410 // properly handle case where [Flags] enumeration has helper
411 // synonyms. e.g. DEFFILEMODE and ACCESSPERMS for mode_t.
412 sc.WriteLine ("\tif ((x & {1}) == {1})\n\t\t*r |= {0}_{1};", fn, fi.Name);
414 sc.WriteLine ("\tif (x == {1})\n\t\t{{*r = {0}_{1}; return 0;}}", fn, fi.Name);
415 sc.WriteLine ("#endif /* ndef {0} */", fi.Name);
418 sc.WriteLine ("\treturn 0;");
420 sc.WriteLine ("\terrno = EINVAL; return -1;");
421 sc.WriteLine ("}\n");
424 public override void CloseFile (string file_prefix)
430 class ConvertFileGenerator : FileGenerator {
433 public override void CreateFile (string assembly_name, string file_prefix)
435 scs = File.CreateText (file_prefix + ".cs");
436 WriteHeader (scs, assembly_name, true);
437 scs.WriteLine ("using System;");
438 scs.WriteLine ("using System.Runtime.InteropServices;");
439 scs.WriteLine ("using Mono.Unix.Native;\n");
440 scs.WriteLine ("namespace Mono.Unix.Native {\n");
441 scs.WriteLine ("\t[CLSCompliant (false)]");
442 scs.WriteLine ("\tpublic sealed /* static */ partial class NativeConvert");
443 scs.WriteLine ("\t{");
444 scs.WriteLine ("\t\tprivate NativeConvert () {}\n");
445 scs.WriteLine ("\t\tprivate const string LIB = \"MonoPosixHelper\";\n");
446 scs.WriteLine ("\t\tprivate static void ThrowArgumentException (object value)");
447 scs.WriteLine ("\t\t{");
448 scs.WriteLine ("\t\t\tthrow new ArgumentOutOfRangeException (\"value\", value,");
449 scs.WriteLine ("\t\t\t\tLocale.GetText (\"Current platform doesn't support this value.\"));");
450 scs.WriteLine ("\t\t}\n");
453 public override void WriteType (Type t, string ns, string fn)
456 if (!CanMapType (t, out bits))
459 string mtype = Enum.GetUnderlyingType(t).Name;
460 ObsoleteAttribute oa = (ObsoleteAttribute) Attribute.GetCustomAttribute (t,
461 typeof(ObsoleteAttribute), false);
462 string obsolete = "";
464 obsolete = "[Obsolete (\"" + oa.Message + "\")]\n\t\t";
466 scs.WriteLine ("\t\t[DllImport (LIB, " +
467 "EntryPoint=\"{0}_From{1}\")]\n" +
468 "\t\tprivate static extern int From{1} ({1} value, out {2} rval);\n",
470 scs.WriteLine ("\t\t{3}public static bool TryFrom{1} ({1} value, out {2} rval)\n" +
472 "\t\t\treturn From{1} (value, out rval) == 0;\n" +
473 "\t\t}}\n", ns, t.Name, mtype, obsolete);
474 scs.WriteLine ("\t\t{2}public static {0} From{1} ({1} value)", mtype, t.Name, obsolete);
475 scs.WriteLine ("\t\t{");
476 scs.WriteLine ("\t\t\t{0} rval;", mtype);
477 scs.WriteLine ("\t\t\tif (From{0} (value, out rval) == -1)\n" +
478 "\t\t\t\tThrowArgumentException (value);", t.Name);
479 scs.WriteLine ("\t\t\treturn rval;");
480 scs.WriteLine ("\t\t}\n");
481 scs.WriteLine ("\t\t[DllImport (LIB, " +
482 "EntryPoint=\"{0}_To{1}\")]\n" +
483 "\t\tprivate static extern int To{1} ({2} value, out {1} rval);\n",
485 scs.WriteLine ("\t\t{2}public static bool TryTo{1} ({0} value, out {1} rval)\n" +
487 "\t\t\treturn To{1} (value, out rval) == 0;\n" +
488 "\t\t}}\n", mtype, t.Name, obsolete);
489 scs.WriteLine ("\t\t{2}public static {1} To{1} ({0} value)", mtype, t.Name, obsolete);
490 scs.WriteLine ("\t\t{");
491 scs.WriteLine ("\t\t\t{0} rval;", t.Name);
492 scs.WriteLine ("\t\t\tif (To{0} (value, out rval) == -1)\n" +
493 "\t\t\t\tThrowArgumentException (value);", t.Name);
494 scs.WriteLine ("\t\t\treturn rval;");
495 scs.WriteLine ("\t\t}\n");
498 public override void CloseFile (string file_prefix)
500 scs.WriteLine ("\t}");
501 scs.WriteLine ("}\n");
506 class MphPrototypeFileGenerator : FileGenerator {
508 Hashtable methods = new Hashtable ();
509 Hashtable structs = new Hashtable ();
511 public override void CreateFile (string assembly_name, string file_prefix)
513 icall = File.CreateText (file_prefix + "-icalls.h");
514 WriteHeader (icall, assembly_name);
515 icall.WriteLine ("#ifndef INC_Mono_Posix_" + file_prefix + "_ICALLS_H");
516 icall.WriteLine ("#define INC_Mono_Posix_" + file_prefix + "_ICALLS_H\n");
517 icall.WriteLine ("#include <glib/gtypes.h>\n");
518 icall.WriteLine ("G_BEGIN_DECLS\n");
520 // Kill warning about unused method
524 public override void WriteType (Type t, string ns, string fn)
526 BindingFlags bf = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
527 foreach (MethodInfo m in t.GetMethods (bf)) {
528 if ((m.Attributes & MethodAttributes.PinvokeImpl) == 0)
530 DllImportAttribute dia = GetDllImportInfo (m);
532 Console.WriteLine ("Unable to emit native prototype for P/Invoke " +
536 // we shouldn't declare prototypes for POSIX, etc. functions.
537 if (dia.Value != "MonoPosixHelper" || IsOnExcludeList (dia.EntryPoint))
539 methods [dia.EntryPoint] = m;
544 private static DllImportAttribute GetDllImportInfo (MethodInfo method)
546 // .NET 2.0 synthesizes pseudo-attributes such as DllImport
547 DllImportAttribute dia = (DllImportAttribute) Attribute.GetCustomAttribute (method,
548 typeof(DllImportAttribute), false);
552 // We're not on .NET 2.0; assume we're on Mono and use some internal
554 Type MonoMethod = Type.GetType ("System.Reflection.MonoMethod", false);
555 if (MonoMethod == null) {
556 Console.WriteLine ("cannot find MonoMethod");
559 MethodInfo GetDllImportAttribute =
560 MonoMethod.GetMethod ("GetDllImportAttribute",
561 BindingFlags.Static | BindingFlags.NonPublic);
562 if (GetDllImportAttribute == null) {
563 Console.WriteLine ("cannot find GetDllImportAttribute");
566 IntPtr mhandle = method.MethodHandle.Value;
567 return (DllImportAttribute) GetDllImportAttribute.Invoke (null,
568 new object[]{mhandle});
571 private static string[] ExcludeList = new string[]{
572 "Mono_Posix_Stdlib_snprintf",
575 private bool IsOnExcludeList (string method)
577 int idx = Array.BinarySearch (ExcludeList, method);
578 return (idx >= 0 && idx < ExcludeList.Length) ? true : false;
581 private void RecordStructs (MethodInfo method)
583 ParameterInfo[] parameters = method.GetParameters ();
584 foreach (ParameterInfo pi in parameters) {
585 string s = GetNativeType (pi.ParameterType);
586 if (s.StartsWith ("struct"))
591 public override void CloseFile (string file_prefix)
593 icall.WriteLine ("/*\n * Structure Declarations\n */");
594 foreach (string s in Sort (structs.Keys))
595 icall.WriteLine ("{0};", s.Replace ("*", ""));
599 icall.WriteLine ("/*\n * Function Declarations\n */");
600 foreach (string method in Sort (methods.Keys)) {
601 WriteMethodDeclaration ((MethodInfo) methods [method], method);
604 icall.WriteLine ("\nG_END_DECLS\n");
605 icall.WriteLine ("#endif /* ndef INC_Mono_Posix_" + file_prefix + "_ICALLS_H */\n");
609 private static IEnumerable Sort (ICollection c)
611 ArrayList al = new ArrayList (c);
616 private void WriteMethodDeclaration (MethodInfo method, string entryPoint)
618 icall.Write ("{0} ", GetNativeType (method.ReturnType));
619 icall.Write ("{0} ", entryPoint);
620 ParameterInfo[] parameters = method.GetParameters();
621 if (parameters.Length == 0) {
622 icall.WriteLine ("(void);");
625 if (parameters.Length > 0) {
627 WriteParameterDeclaration (parameters [0]);
629 for (int i = 1; i < parameters.Length; ++i) {
631 WriteParameterDeclaration (parameters [i]);
633 icall.WriteLine (");");
636 private void DumpTypeInfo (Type t)
641 icall.WriteLine ("\t\t/* Type Info for " + t.FullName + ":");
642 foreach (MemberInfo mi in typeof(Type).GetMembers()) {
643 icall.WriteLine ("\t\t\t{0}={1}", mi.Name, GetMemberValue (mi, t));
645 icall.WriteLine ("\t\t */");
648 private static string GetMemberValue (MemberInfo mi, Type t)
651 switch (mi.MemberType) {
652 case MemberTypes.Constructor:
653 case MemberTypes.Method: {
654 MethodBase b = (MethodBase) mi;
655 if (b.GetParameters().Length == 0)
656 return b.Invoke (t, new object[]{}).ToString();
657 return "<<cannot invoke>>";
659 case MemberTypes.Field:
660 return ((FieldInfo) mi).GetValue (t).ToString ();
661 case MemberTypes.Property: {
662 PropertyInfo pi = (PropertyInfo) mi;
664 return "<<cannot read>>";
665 return pi.GetValue (t, null).ToString ();
668 return "<<unknown value>>";
671 catch (Exception e) {
672 return "<<exception reading member: " + e.Message + ">>";
676 private void WriteParameterDeclaration (ParameterInfo pi)
678 // DumpTypeInfo (pi.ParameterType);
679 icall.Write ("{0} {1}", GetNativeType (pi.ParameterType), pi.Name);