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