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

89 lines
3.6 KiB
C#

using CPF.Mac.Foundation;
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Serialization;
namespace CPF.Mac.ObjCRuntime
{
internal class NativeConstructorBuilder : NativeImplementationBuilder
{
private static MethodInfo trygetnsobject = typeof(Runtime).GetMethod("TryGetNSObject", BindingFlags.Static | BindingFlags.Public);
private static MethodInfo newobject = typeof(FormatterServices).GetMethod("GetUninitializedObject", BindingFlags.Static | BindingFlags.Public);
private static MethodInfo gettype = typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public);
private static MethodInfo retain = typeof(NSObject).GetMethod("Retain", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
private static FieldInfo handlefld = typeof(NSObject).GetField("handle", BindingFlags.Instance | BindingFlags.NonPublic);
private static FieldInfo valuefld = typeof(RuntimeTypeHandle).GetField("value", BindingFlags.Instance | BindingFlags.NonPublic);
private static IntPtr selInit = CPF.Mac.ObjCRuntime.Selector.GetHandle("init");
private ConstructorInfo cinfo;
internal NativeConstructorBuilder(ConstructorInfo cinfo)
{
ExportAttribute exportAttribute = (ExportAttribute)Attribute.GetCustomAttribute(cinfo, typeof(ExportAttribute));
base.Parameters = cinfo.GetParameters();
if (exportAttribute == null && base.Parameters.Length != 0)
{
throw new ArgumentException("ConstructorInfo does not have a export attribute");
}
if (exportAttribute == null)
{
base.Selector = selInit;
}
else
{
base.Selector = new Selector(exportAttribute.Selector, alloc: true).Handle;
}
base.Signature = "@@:";
ConvertParameters(base.Parameters, isstatic: true, isstret: false);
base.DelegateType = CreateDelegateType(typeof(IntPtr), base.ParameterTypes);
this.cinfo = cinfo;
}
internal override Delegate CreateDelegate()
{
DynamicMethod dynamicMethod = new DynamicMethod(Guid.NewGuid().ToString(), typeof(IntPtr), base.ParameterTypes, NativeImplementationBuilder.module, skipVisibility: true);
ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
Label label = iLGenerator.DefineLabel();
iLGenerator.DeclareLocal(typeof(object));
DeclareLocals(iLGenerator);
for (int i = 0; i < base.Parameters.Length; i++)
{
if (base.Parameters[i].ParameterType.IsByRef && (base.Parameters[i].ParameterType.GetElementType().IsSubclassOf(typeof(NSObject)) || base.Parameters[i].ParameterType.GetElementType() == typeof(NSObject)))
{
iLGenerator.DeclareLocal(base.Parameters[i].ParameterType.GetElementType());
}
}
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Call, trygetnsobject);
iLGenerator.Emit(OpCodes.Brtrue, label);
iLGenerator.Emit(OpCodes.Ldtoken, cinfo.DeclaringType);
iLGenerator.Emit(OpCodes.Call, gettype);
iLGenerator.Emit(OpCodes.Call, newobject);
iLGenerator.Emit(OpCodes.Stloc_0);
iLGenerator.Emit(OpCodes.Ldloc_0);
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Stfld, handlefld);
ConvertArguments(iLGenerator, 1);
iLGenerator.Emit(OpCodes.Ldloc_0);
iLGenerator.Emit(OpCodes.Castclass, cinfo.DeclaringType);
LoadArguments(iLGenerator, 1);
iLGenerator.Emit(OpCodes.Call, cinfo);
UpdateByRefArguments(iLGenerator, 1);
iLGenerator.Emit(OpCodes.Ldloc_0);
iLGenerator.Emit(OpCodes.Call, retain);
iLGenerator.Emit(OpCodes.Pop);
iLGenerator.MarkLabel(label);
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ret);
return dynamicMethod.CreateDelegate(base.DelegateType);
}
}
}