CPF/CPF.Mac/Mac/ObjCRuntime/NativeImplementationBuilder.cs
2023-11-21 23:05:03 +08:00

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++;
}
}
}
}
}