Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

189 lines
6.2 KiB
C#

//
// Mono.Unix/CdeclFunction.cs
//
// Authors:
// Jonathan Pryor (jonpryor@vt.edu)
//
// (C) 2004 Jonathan Pryor
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Text;
namespace Mono.Unix.Native {
// This class represents a single unmanaged function with "cdecl" calling
// convention -- that is, it can accept a variable number of arguments which
// are passed on the runtime stack.
//
// To use, create an instance:
//
// CdeclFunction printf = new CdeclFunction ("the library",
// "the function name", /* optional */ typeof (ReturnType));
//
// Then call the Invoke method with the appropriate number of arguments:
//
// printf.Invoke (new object[]{"hello, %s\n", "world!"});
//
// In the background a P/Invoke definition for the method with the
// requested argument types will be generated and invoked, invoking the
// unmanaged function. The generated methods are cached, so that subsequent
// calls with the same argument list do not generate new code, speeding up
// the call sequence.
//
// Invoking Cdecl functions is not guaranteed to be portable across all
// platforms. For example, AMD64 requires that the caller set EAX to the
// number of floating point arguments passed in the SSE registers. This
// is only required for variable argument/cdecl functions; consequently,
// the overload technique used by this class wouldn't normally work.
// Mono's AMD64 JIT works around this by always setting EAX on P/Invoke
// invocations, allowing CdeclFunction to work properly, but it will not
// necessarily always work. See also:
//
// http://lwn.net/Articles/5201/?format=printable
//
// Due to potential portability issues, cdecl functions should be avoided
// on most platforms.
//
// This class is intended to be thread-safe.
public sealed class CdeclFunction
{
// The readonly fields (1) shouldn't be modified, and (2) should only be
// used when `overloads' is locked.
private readonly string library;
private readonly string method;
private readonly Type returnType;
private readonly AssemblyName assemblyName;
private readonly AssemblyBuilder assemblyBuilder;
private readonly ModuleBuilder moduleBuilder;
private Hashtable overloads;
public CdeclFunction (string library, string method)
: this (library, method, typeof(void))
{
}
public CdeclFunction (string library, string method, Type returnType)
{
this.library = library;
this.method = method;
this.returnType = returnType;
this.overloads = new Hashtable ();
this.assemblyName = new AssemblyName ();
this.assemblyName.Name = "Mono.Posix.Imports." + library;
this.assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly (
assemblyName, AssemblyBuilderAccess.Run);
this.moduleBuilder = assemblyBuilder.DefineDynamicModule (assemblyName.Name);
}
public object Invoke (object[] parameters)
{
Type[] parameterTypes = GetParameterTypes (parameters);
MethodInfo m = CreateMethod (parameterTypes);
return m.Invoke (null, parameters);
}
private MethodInfo CreateMethod (Type[] parameterTypes)
{
string typeName = GetTypeName (parameterTypes);
lock (overloads) {
MethodInfo mi = (MethodInfo) overloads [typeName];
if (mi != null) {
return mi;
}
TypeBuilder tb = CreateType (typeName);
/* MethodBuilder mb = */ tb.DefinePInvokeMethod (
method,
library,
MethodAttributes.PinvokeImpl | MethodAttributes.Static | MethodAttributes.Public,
CallingConventions.Standard,
returnType,
parameterTypes,
CallingConvention.Cdecl,
CharSet.Ansi);
mi = tb.CreateType ().GetMethod (method);
overloads.Add (typeName, mi);
return mi;
}
}
private TypeBuilder CreateType (string typeName)
{
return moduleBuilder.DefineType (typeName, TypeAttributes.Public);
}
private static Type GetMarshalType (Type t)
{
switch (Type.GetTypeCode (t)) {
// types < sizeof(int) are marshaled as ints
case TypeCode.Boolean: case TypeCode.Char: case TypeCode.SByte:
case TypeCode.Int16: case TypeCode.Int32:
return typeof(int);
case TypeCode.Byte: case TypeCode.UInt16: case TypeCode.UInt32:
return typeof(uint);
case TypeCode.Int64:
return typeof(long);
case TypeCode.UInt64:
return typeof(ulong);
case TypeCode.Single: case TypeCode.Double:
return typeof(double);
default:
return t;
}
}
private string GetTypeName (Type[] parameterTypes)
{
StringBuilder sb = new StringBuilder ();
sb.Append ("[").Append (library).Append ("] ").Append (method);
sb.Append ("(");
if (parameterTypes.Length > 0)
sb.Append (parameterTypes [0]);
for (int i = 1; i < parameterTypes.Length; ++i)
sb.Append (",").Append (parameterTypes [i]);
sb.Append (") : ").Append (returnType.FullName);
return sb.ToString ();
}
private static Type[] GetParameterTypes (object[] parameters)
{
Type[] parameterTypes = new Type [parameters.Length];
for (int i = 0; i < parameters.Length; ++i)
parameterTypes [i] = GetMarshalType (parameters [i].GetType ());
return parameterTypes;
}
}
}