2 // Mono.Unix/CdeclFunction.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
7 // (C) 2004 Jonathan Pryor
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
30 using System.Collections;
31 using System.Reflection;
32 using System.Reflection.Emit;
33 using System.Runtime.InteropServices;
36 namespace Mono.Unix.Native {
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.
42 // To use, create an instance:
44 // CdeclFunction printf = new CdeclFunction ("the library",
45 // "the function name", /* optional */ typeof (ReturnType));
47 // Then call the Invoke method with the appropriate number of arguments:
49 // printf.Invoke (new object[]{"hello, %s\n", "world!"});
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
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:
66 // http://lwn.net/Articles/5201/?format=printable
68 // Due to potential portability issues, cdecl functions should be avoided
71 // This class is intended to be thread-safe.
72 public sealed class CdeclFunction
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;
83 private Hashtable overloads;
85 public CdeclFunction (string library, string method)
86 : this (library, method, typeof(void))
90 public CdeclFunction (string library, string method, Type returnType)
92 this.library = library;
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);
103 public object Invoke (object[] parameters)
105 Type[] parameterTypes = GetParameterTypes (parameters);
106 MethodInfo m = CreateMethod (parameterTypes);
107 return m.Invoke (null, parameters);
110 private MethodInfo CreateMethod (Type[] parameterTypes)
112 string typeName = GetTypeName (parameterTypes);
115 MethodInfo mi = (MethodInfo) overloads [typeName];
121 TypeBuilder tb = CreateType (typeName);
122 /* MethodBuilder mb = */ tb.DefinePInvokeMethod (
125 MethodAttributes.PinvokeImpl | MethodAttributes.Static | MethodAttributes.Public,
126 CallingConventions.Standard,
129 CallingConvention.Cdecl,
131 mi = tb.CreateType ().GetMethod (method);
132 overloads.Add (typeName, mi);
137 private TypeBuilder CreateType (string typeName)
139 return moduleBuilder.DefineType (typeName, TypeAttributes.Public);
142 private static Type GetMarshalType (Type t)
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:
149 case TypeCode.Byte: case TypeCode.UInt16: case TypeCode.UInt32:
153 case TypeCode.UInt64:
154 return typeof(ulong);
155 case TypeCode.Single: case TypeCode.Double:
156 return typeof(double);
162 private string GetTypeName (Type[] parameterTypes)
164 StringBuilder sb = new StringBuilder ();
166 sb.Append ("[").Append (library).Append ("] ").Append (method);
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]);
174 sb.Append (") : ").Append (returnType.FullName);
176 return sb.ToString ();
179 private static Type[] GetParameterTypes (object[] parameters)
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;