* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / Mono.Posix / Mono.Unix.Native / CdeclFunction.cs
1 //
2 // Mono.Unix/CdeclFunction.cs
3 //
4 // Authors:
5 //   Jonathan Pryor (jonpryor@vt.edu)
6 //
7 // (C) 2004 Jonathan Pryor
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections;
31 using System.Reflection;
32 using System.Reflection.Emit;
33 using System.Runtime.InteropServices;
34 using System.Text;
35
36 namespace Mono.Unix.Native {
37
38         // This class represents a single unmanaged function with "cdecl" calling
39         // convention -- that is, it can accept a variable number of arguments which
40         // are passed on the runtime stack.
41         //
42         // To use, create an instance:
43         //
44         //    CdeclFunction printf = new CdeclFunction ("the library", 
45         //        "the function name", /* optional */ typeof (ReturnType));
46         //
47         // Then call the Invoke method with the appropriate number of arguments:
48         //
49         //    printf.Invoke (new object[]{"hello, %s\n", "world!"});
50         //
51         // In the background a P/Invoke definition for the method with the
52         // requested argument types will be generated and invoked, invoking the
53         // unmanaged function.  The generated methods are cached, so that subsequent
54         // calls with the same argument list do not generate new code, speeding up
55         // the call sequence.
56         //
57         // Invoking Cdecl functions is not guaranteed to be portable across all 
58         // platforms.  For example, AMD64 requires that the caller set EAX to the 
59         // number of floating point arguments passed in the SSE registers.  This 
60         // is only required for variable argument/cdecl functions; consequently, 
61         // the overload technique used by this class wouldn't normally work.  
62         // Mono's AMD64 JIT works around this by always setting EAX on P/Invoke
63         // invocations, allowing CdeclFunction to work properly, but it will not
64         // necessarily always work.  See also: 
65         //
66         //     http://lwn.net/Articles/5201/?format=printable
67         //
68         // Due to potential portability issues, cdecl functions should be avoided 
69         // on most platforms.
70         //
71         // This class is intended to be thread-safe.
72         public sealed class CdeclFunction
73         {
74                 // The readonly fields (1) shouldn't be modified, and (2) should only be
75                 // used when `overloads' is locked.
76                 private readonly string library;
77                 private readonly string method;
78                 private readonly Type returnType;
79                 private readonly AssemblyName assemblyName;
80                 private readonly AssemblyBuilder assemblyBuilder;
81                 private readonly ModuleBuilder moduleBuilder;
82
83                 private Hashtable overloads;
84
85                 public CdeclFunction (string library, string method)
86                         : this (library, method, typeof(void))
87                 {
88                 }
89
90                 public CdeclFunction (string library, string method, Type returnType)
91                 {
92                         this.library = library;
93                         this.method = method;
94                         this.returnType = returnType;
95                         this.overloads = new Hashtable ();
96                         this.assemblyName = new AssemblyName ();
97                         this.assemblyName.Name = "Mono.Posix.Imports." + library;
98                         this.assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly (
99                                         assemblyName, AssemblyBuilderAccess.Run);
100                         this.moduleBuilder = assemblyBuilder.DefineDynamicModule (assemblyName.Name);
101                 }
102
103                 public object Invoke (object[] parameters)
104                 {
105                         Type[] parameterTypes = GetParameterTypes (parameters);
106                         MethodInfo m = CreateMethod (parameterTypes);
107                         return m.Invoke (null, parameters);
108                 }
109
110                 private MethodInfo CreateMethod (Type[] parameterTypes)
111                 {
112                         string typeName = GetTypeName (parameterTypes);
113
114                         lock (overloads) {
115                                 MethodInfo mi = (MethodInfo) overloads [typeName];
116
117                                 if (mi != null) {
118                                         return mi;
119                                 }
120
121                                 TypeBuilder tb = CreateType (typeName);
122                                 /* MethodBuilder mb = */ tb.DefinePInvokeMethod (
123                                                 method, 
124                                                 library, 
125                                                 MethodAttributes.PinvokeImpl | MethodAttributes.Static | MethodAttributes.Public,
126                                                 CallingConventions.Standard, 
127                                                 returnType, 
128                                                 parameterTypes, 
129                                                 CallingConvention.Cdecl,
130                                                 CharSet.Ansi);
131                                 mi = tb.CreateType ().GetMethod (method);
132                                 overloads.Add (typeName, mi);
133                                 return mi;
134                         }
135                 }
136
137                 private TypeBuilder CreateType (string typeName)
138                 {
139                         return moduleBuilder.DefineType (typeName, TypeAttributes.Public);
140                 }
141
142                 private static Type GetMarshalType (Type t)
143                 {
144                         switch (Type.GetTypeCode (t)) {
145                                 // types < sizeof(int) are marshaled as ints
146                                 case TypeCode.Boolean: case TypeCode.Char: case TypeCode.SByte: 
147                                 case TypeCode.Int16: case TypeCode.Int32: 
148                                         return typeof(int);
149                                 case TypeCode.Byte: case TypeCode.UInt16: case TypeCode.UInt32:
150                                         return typeof(uint);
151                                 case TypeCode.Int64:
152                                         return typeof(long);
153                                 case TypeCode.UInt64:
154                                         return typeof(ulong);
155                                 case TypeCode.Single: case TypeCode.Double:
156                                         return typeof(double);
157                                 default:
158                                         return t;
159                         }
160                 }
161
162                 private string GetTypeName (Type[] parameterTypes)
163                 {
164                         StringBuilder sb = new StringBuilder ();
165
166                         sb.Append ("[").Append (library).Append ("] ").Append (method);
167                         sb.Append ("(");
168
169                         if (parameterTypes.Length > 0)
170                                 sb.Append (parameterTypes [0]);
171                         for (int i = 1; i < parameterTypes.Length; ++i)
172                                 sb.Append (",").Append (parameterTypes [i]);
173
174                         sb.Append (") : ").Append (returnType.FullName);
175
176                         return sb.ToString ();
177                 }
178
179                 private static Type[] GetParameterTypes (object[] parameters)
180                 {
181                         Type[] parameterTypes = new Type [parameters.Length];
182                         for (int i = 0; i < parameters.Length; ++i)
183                                 parameterTypes [i] = GetMarshalType (parameters [i].GetType ());
184                         return parameterTypes;
185                 }
186         }
187 }
188