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

140 lines
3.9 KiB
C#

using CPF.Mac.Foundation;
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
namespace CPF.Mac.ObjCRuntime
{
internal class NativeMethodBuilder : NativeImplementationBuilder
{
private static MethodInfo creatensstring = typeof(NSString).GetMethod("op_Explicit", new Type[1]
{
typeof(string)
});
private static MethodInfo convertstruct = typeof(Marshal).GetMethod("StructureToPtr", new Type[3]
{
typeof(object),
typeof(IntPtr),
typeof(bool)
});
private static MethodInfo buildarray = typeof(NSArray).GetMethod("FromNSObjects", new Type[1]
{
typeof(NSObject[])
});
private static MethodInfo buildsarray = typeof(NSArray).GetMethod("FromStrings", new Type[1]
{
typeof(string[])
});
private MethodInfo minfo;
private Type type;
private Type rettype;
private bool isstret;
internal NativeMethodBuilder(MethodInfo minfo)
: this(minfo, minfo.DeclaringType, (ExportAttribute)Attribute.GetCustomAttribute(minfo.GetBaseDefinition(), typeof(ExportAttribute)))
{
}
internal NativeMethodBuilder(MethodInfo minfo, Type type, ExportAttribute ea)
{
if (ea == null)
{
throw new ArgumentException("MethodInfo does not have a export attribute");
}
if (minfo.DeclaringType.IsGenericType)
{
throw new ArgumentException("MethodInfo cannot be in a generic type");
}
base.Parameters = minfo.GetParameters();
rettype = ConvertReturnType(minfo.ReturnType);
base.Selector = new Selector(ea.Selector ?? minfo.Name, alloc: true).Handle;
base.Signature = $"{TypeConverter.ToNative(minfo.ReturnType)}@:";
ConvertParameters(base.Parameters, minfo.IsStatic, isstret);
base.DelegateType = CreateDelegateType(rettype, base.ParameterTypes);
this.minfo = minfo;
this.type = type;
}
internal override Delegate CreateDelegate()
{
DynamicMethod dynamicMethod = new DynamicMethod($"[{minfo.DeclaringType}:{minfo}]", rettype, base.ParameterTypes, NativeImplementationBuilder.module, skipVisibility: true);
ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
DeclareLocals(iLGenerator);
ConvertArguments(iLGenerator, 0);
if (!minfo.IsStatic)
{
iLGenerator.Emit(OpCodes.Ldarg, isstret ? 1 : 0);
iLGenerator.Emit(OpCodes.Castclass, type);
}
LoadArguments(iLGenerator, 0);
if (minfo.IsVirtual)
{
iLGenerator.Emit(OpCodes.Callvirt, minfo);
}
else
{
iLGenerator.Emit(OpCodes.Call, minfo);
}
UpdateByRefArguments(iLGenerator, 0);
if (minfo.ReturnType == typeof(string))
{
iLGenerator.Emit(OpCodes.Call, creatensstring);
}
else if (minfo.ReturnType.IsArray && IsWrappedType(minfo.ReturnType.GetElementType()))
{
if (minfo.ReturnType.GetElementType() == typeof(string))
{
iLGenerator.Emit(OpCodes.Call, buildsarray);
}
else
{
iLGenerator.Emit(OpCodes.Call, buildarray);
}
}
else if (typeof(INativeObject).IsAssignableFrom(minfo.ReturnType) && !IsWrappedType(minfo.ReturnType))
{
iLGenerator.Emit(OpCodes.Call, minfo.ReturnType.GetProperty("Handle").GetGetMethod());
}
else if (isstret)
{
iLGenerator.Emit(OpCodes.Box, minfo.ReturnType);
iLGenerator.Emit(OpCodes.Ldarg, 0);
iLGenerator.Emit(OpCodes.Ldc_I4, 0);
iLGenerator.Emit(OpCodes.Call, convertstruct);
}
iLGenerator.Emit(OpCodes.Ret);
return dynamicMethod.CreateDelegate(base.DelegateType);
}
private Type ConvertReturnType(Type type)
{
if (type.IsValueType && !type.IsEnum && type.Assembly != typeof(object).Assembly && Marshal.SizeOf(type) > IntPtr.Size * 2)
{
isstret = true;
return typeof(void);
}
if (type == typeof(string))
{
return typeof(NSString);
}
if (type.IsArray && IsWrappedType(type.GetElementType()))
{
return typeof(NSArray);
}
if (typeof(INativeObject).IsAssignableFrom(type) && !IsWrappedType(type))
{
return typeof(IntPtr);
}
return type;
}
}
}