374 lines
11 KiB
C#
374 lines
11 KiB
C#
using CPF.Mac.Foundation;
|
|
using System;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace CPF.Mac.ObjCRuntime
|
|
{
|
|
internal abstract class NativeImplementationBuilder
|
|
{
|
|
internal static AssemblyBuilder builder;
|
|
|
|
internal static ModuleBuilder module;
|
|
|
|
private static MethodInfo convertarray;
|
|
|
|
private static MethodInfo convertsarray;
|
|
|
|
private static MethodInfo convertstring;
|
|
|
|
private static MethodInfo getobject;
|
|
|
|
private static MethodInfo gethandle;
|
|
|
|
private static FieldInfo intptrzero;
|
|
|
|
private Delegate del;
|
|
|
|
internal int ArgumentOffset
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
internal IntPtr Selector
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
internal Type[] ParameterTypes
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
internal ParameterInfo[] Parameters
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
internal Delegate Delegate
|
|
{
|
|
get
|
|
{
|
|
if ((object)del == null)
|
|
{
|
|
del = CreateDelegate();
|
|
}
|
|
return del;
|
|
}
|
|
}
|
|
|
|
internal Type DelegateType
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
internal string Signature
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
static NativeImplementationBuilder()
|
|
{
|
|
convertarray = typeof(NSArray).GetMethod("ArrayFromHandle", new Type[1]
|
|
{
|
|
typeof(IntPtr)
|
|
});
|
|
convertsarray = typeof(NSArray).GetMethod("StringArrayFromHandle", new Type[1]
|
|
{
|
|
typeof(IntPtr)
|
|
});
|
|
convertstring = typeof(NSString).GetMethod("ToString", Type.EmptyTypes);
|
|
getobject = typeof(Runtime).GetMethod("GetNSObject", BindingFlags.Static | BindingFlags.Public);
|
|
gethandle = typeof(NSObject).GetMethod("get_Handle", BindingFlags.Instance | BindingFlags.Public);
|
|
intptrzero = typeof(IntPtr).GetField("Zero", BindingFlags.Static | BindingFlags.Public);
|
|
builder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()), AssemblyBuilderAccess.Run);
|
|
module = builder.DefineDynamicModule("Implementations");
|
|
}
|
|
|
|
internal abstract Delegate CreateDelegate();
|
|
|
|
protected Type CreateDelegateType(Type return_type, Type[] argument_types)
|
|
{
|
|
TypeBuilder typeBuilder = module.DefineType(Guid.NewGuid().ToString(), TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AutoClass, typeof(MulticastDelegate));
|
|
typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[2]
|
|
{
|
|
typeof(object),
|
|
typeof(int)
|
|
}).SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
|
|
MethodBuilder methodBuilder = null;
|
|
methodBuilder = typeBuilder.DefineMethod("Invoke", MethodAttributes.FamANDAssem | MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.VtableLayoutMask, return_type, argument_types);
|
|
if (NeedsCustomMarshaler(return_type))
|
|
{
|
|
SetupParameter(methodBuilder, 0, return_type);
|
|
}
|
|
for (int i = 1; i <= argument_types.Length; i++)
|
|
{
|
|
if (NeedsCustomMarshaler(argument_types[i - 1]))
|
|
{
|
|
SetupParameter(methodBuilder, i, argument_types[i - 1]);
|
|
}
|
|
}
|
|
methodBuilder.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
|
|
return typeBuilder.CreateTypeInfo().AsType();
|
|
}
|
|
|
|
private bool NeedsCustomMarshaler(Type t)
|
|
{
|
|
if (t == typeof(NSObject) || t.IsSubclassOf(typeof(NSObject)))
|
|
{
|
|
return true;
|
|
}
|
|
if (t == typeof(Selector))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private Type MarshalerForType(Type t)
|
|
{
|
|
if (t == typeof(NSObject) || t.IsSubclassOf(typeof(NSObject)))
|
|
{
|
|
return typeof(NSObjectMarshaler<>).MakeGenericType(t);
|
|
}
|
|
if (t == typeof(Selector))
|
|
{
|
|
return typeof(SelectorMarshaler);
|
|
}
|
|
throw new ArgumentException("Cannot determine marshaler type for: " + t);
|
|
}
|
|
|
|
private void SetupParameter(MethodBuilder builder, int index, Type t)
|
|
{
|
|
ParameterBuilder parameterBuilder = builder.DefineParameter(index, ParameterAttributes.HasFieldMarshal, $"arg{index}");
|
|
ConstructorInfo constructor = typeof(MarshalAsAttribute).GetConstructor(new Type[1]
|
|
{
|
|
typeof(UnmanagedType)
|
|
});
|
|
FieldInfo field = typeof(MarshalAsAttribute).GetField("MarshalTypeRef");
|
|
CustomAttributeBuilder customAttribute = new CustomAttributeBuilder(constructor, new object[1]
|
|
{
|
|
UnmanagedType.CustomMarshaler
|
|
}, new FieldInfo[1]
|
|
{
|
|
field
|
|
}, new object[1]
|
|
{
|
|
MarshalerForType(t)
|
|
});
|
|
parameterBuilder.SetCustomAttribute(customAttribute);
|
|
}
|
|
|
|
protected bool IsWrappedType(Type type)
|
|
{
|
|
if (type == typeof(NSObject) || type.IsSubclassOf(typeof(NSObject)) || type == typeof(string))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected void ConvertParameters(ParameterInfo[] parms, bool isstatic, bool isstret)
|
|
{
|
|
if (isstret)
|
|
{
|
|
ArgumentOffset = 3;
|
|
ParameterTypes = new Type[ArgumentOffset + parms.Length];
|
|
ParameterTypes[0] = typeof(IntPtr);
|
|
ParameterTypes[1] = (isstatic ? typeof(IntPtr) : typeof(NSObject));
|
|
ParameterTypes[2] = typeof(Selector);
|
|
}
|
|
else
|
|
{
|
|
ArgumentOffset = 2;
|
|
ParameterTypes = new Type[ArgumentOffset + parms.Length];
|
|
ParameterTypes[0] = (isstatic ? typeof(IntPtr) : typeof(NSObject));
|
|
ParameterTypes[1] = typeof(Selector);
|
|
}
|
|
for (int i = 0; i < Parameters.Length; i++)
|
|
{
|
|
if (Parameters[i].ParameterType.IsByRef && IsWrappedType(Parameters[i].ParameterType.GetElementType()))
|
|
{
|
|
ParameterTypes[i + ArgumentOffset] = typeof(IntPtr).MakeByRefType();
|
|
}
|
|
else if (Parameters[i].ParameterType.IsArray && IsWrappedType(Parameters[i].ParameterType.GetElementType()))
|
|
{
|
|
ParameterTypes[i + ArgumentOffset] = typeof(IntPtr);
|
|
}
|
|
else if (typeof(INativeObject).IsAssignableFrom(Parameters[i].ParameterType) && !IsWrappedType(Parameters[i].ParameterType))
|
|
{
|
|
ParameterTypes[i + ArgumentOffset] = typeof(IntPtr);
|
|
}
|
|
else if (Parameters[i].ParameterType == typeof(string))
|
|
{
|
|
ParameterTypes[i + ArgumentOffset] = typeof(NSString);
|
|
}
|
|
else
|
|
{
|
|
ParameterTypes[i + ArgumentOffset] = Parameters[i].ParameterType;
|
|
}
|
|
Signature += TypeConverter.ToNative(Parameters[i].ParameterType);
|
|
}
|
|
}
|
|
|
|
protected void DeclareLocals(ILGenerator il)
|
|
{
|
|
for (int i = 0; i < Parameters.Length; i++)
|
|
{
|
|
if (Parameters[i].ParameterType.IsByRef && IsWrappedType(Parameters[i].ParameterType.GetElementType()))
|
|
{
|
|
il.DeclareLocal(Parameters[i].ParameterType.GetElementType());
|
|
}
|
|
else if (Parameters[i].ParameterType.IsArray && IsWrappedType(Parameters[i].ParameterType.GetElementType()))
|
|
{
|
|
il.DeclareLocal(Parameters[i].ParameterType);
|
|
}
|
|
else if (Parameters[i].ParameterType == typeof(string))
|
|
{
|
|
il.DeclareLocal(typeof(string));
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void ConvertArguments(ILGenerator il, int locoffset)
|
|
{
|
|
int i = ArgumentOffset;
|
|
int num = 0;
|
|
for (; i < ParameterTypes.Length; i++)
|
|
{
|
|
if (Parameters[i - ArgumentOffset].ParameterType.IsByRef && Attribute.GetCustomAttribute(Parameters[i - ArgumentOffset], typeof(OutAttribute)) == null && IsWrappedType(Parameters[i - ArgumentOffset].ParameterType.GetElementType()))
|
|
{
|
|
Label label = il.DefineLabel();
|
|
Label label2 = il.DefineLabel();
|
|
il.Emit(OpCodes.Ldarg, i);
|
|
il.Emit(OpCodes.Brfalse, label);
|
|
il.Emit(OpCodes.Ldarg, i);
|
|
il.Emit(OpCodes.Ldind_I);
|
|
il.Emit(OpCodes.Call, getobject);
|
|
il.Emit(OpCodes.Br, label2);
|
|
il.MarkLabel(label);
|
|
il.Emit(OpCodes.Ldnull);
|
|
il.MarkLabel(label2);
|
|
il.Emit(OpCodes.Stloc, num + locoffset);
|
|
num++;
|
|
}
|
|
else if (Parameters[i - ArgumentOffset].ParameterType.IsArray && IsWrappedType(Parameters[i - ArgumentOffset].ParameterType.GetElementType()))
|
|
{
|
|
Label label3 = il.DefineLabel();
|
|
Label label4 = il.DefineLabel();
|
|
il.Emit(OpCodes.Ldarg, i);
|
|
il.Emit(OpCodes.Brfalse, label3);
|
|
il.Emit(OpCodes.Ldarg, i);
|
|
if (Parameters[i - ArgumentOffset].ParameterType.GetElementType() == typeof(string))
|
|
{
|
|
il.Emit(OpCodes.Call, convertsarray);
|
|
}
|
|
else
|
|
{
|
|
il.Emit(OpCodes.Call, convertarray.MakeGenericMethod(Parameters[i - ArgumentOffset].ParameterType.GetElementType()));
|
|
}
|
|
il.Emit(OpCodes.Br, label4);
|
|
il.MarkLabel(label3);
|
|
il.Emit(OpCodes.Ldnull);
|
|
il.MarkLabel(label4);
|
|
il.Emit(OpCodes.Stloc, num + locoffset);
|
|
num++;
|
|
}
|
|
else if (Parameters[i - ArgumentOffset].ParameterType == typeof(string))
|
|
{
|
|
Label label5 = il.DefineLabel();
|
|
Label label6 = il.DefineLabel();
|
|
il.Emit(OpCodes.Ldarg, i);
|
|
il.Emit(OpCodes.Brfalse, label5);
|
|
il.Emit(OpCodes.Ldarg, i);
|
|
il.Emit(OpCodes.Call, convertstring);
|
|
il.Emit(OpCodes.Br, label6);
|
|
il.MarkLabel(label5);
|
|
il.Emit(OpCodes.Ldnull);
|
|
il.MarkLabel(label6);
|
|
il.Emit(OpCodes.Stloc, num + locoffset);
|
|
num++;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void LoadArguments(ILGenerator il, int locoffset)
|
|
{
|
|
int i = ArgumentOffset;
|
|
int num = 0;
|
|
for (; i < ParameterTypes.Length; i++)
|
|
{
|
|
if (Parameters[i - ArgumentOffset].ParameterType.IsByRef && IsWrappedType(Parameters[i - ArgumentOffset].ParameterType.GetElementType()))
|
|
{
|
|
il.Emit(OpCodes.Ldloca_S, num + locoffset);
|
|
num++;
|
|
}
|
|
else if (Parameters[i - ArgumentOffset].ParameterType.IsArray && IsWrappedType(Parameters[i - ArgumentOffset].ParameterType.GetElementType()))
|
|
{
|
|
il.Emit(OpCodes.Ldloc, num + locoffset);
|
|
num++;
|
|
}
|
|
else if (typeof(INativeObject).IsAssignableFrom(Parameters[i - ArgumentOffset].ParameterType) && !IsWrappedType(Parameters[i - ArgumentOffset].ParameterType))
|
|
{
|
|
il.Emit(OpCodes.Ldarg, i);
|
|
il.Emit(OpCodes.Newobj, Parameters[i - ArgumentOffset].ParameterType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1]
|
|
{
|
|
typeof(IntPtr)
|
|
}, null));
|
|
}
|
|
else if (Parameters[i - ArgumentOffset].ParameterType == typeof(string))
|
|
{
|
|
il.Emit(OpCodes.Ldloc, num + locoffset);
|
|
num++;
|
|
}
|
|
else
|
|
{
|
|
il.Emit(OpCodes.Ldarg, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void UpdateByRefArguments(ILGenerator il, int locoffset)
|
|
{
|
|
int i = ArgumentOffset;
|
|
int num = 0;
|
|
for (; i < ParameterTypes.Length; i++)
|
|
{
|
|
if (Parameters[i - ArgumentOffset].ParameterType.IsByRef && IsWrappedType(Parameters[i - ArgumentOffset].ParameterType.GetElementType()))
|
|
{
|
|
Label label = il.DefineLabel();
|
|
Label label2 = il.DefineLabel();
|
|
il.Emit(OpCodes.Ldloc, num + locoffset);
|
|
il.Emit(OpCodes.Brfalse, label);
|
|
il.Emit(OpCodes.Ldarg, i);
|
|
il.Emit(OpCodes.Ldloc, num + locoffset);
|
|
il.Emit(OpCodes.Call, gethandle);
|
|
il.Emit(OpCodes.Stind_I);
|
|
il.Emit(OpCodes.Br, label2);
|
|
il.MarkLabel(label);
|
|
il.Emit(OpCodes.Ldarg, i);
|
|
il.Emit(OpCodes.Ldsfld, intptrzero);
|
|
il.Emit(OpCodes.Stind_I);
|
|
il.MarkLabel(label2);
|
|
num++;
|
|
}
|
|
else if (Parameters[i - ArgumentOffset].ParameterType.IsArray && IsWrappedType(Parameters[i - ArgumentOffset].ParameterType.GetElementType()))
|
|
{
|
|
num++;
|
|
}
|
|
else if (Parameters[i - ArgumentOffset].ParameterType == typeof(string))
|
|
{
|
|
num++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|