a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
189 lines
6.2 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
|