using CPF.Mac.Foundation; using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.InteropServices; namespace CPF.Mac.ObjCRuntime { public class Class : INativeObject { [MonoNativeFunctionWrapper] private delegate int getFrameLengthDelegate(IntPtr @this, IntPtr sel); [MonoNativeFunctionWrapper] private delegate IntPtr addPropertyDelegate(IntPtr cls, string name, objc_attribute_prop[] attributes, int count); private struct objc_attribute_prop { [MarshalAs(UnmanagedType.LPStr)] internal string name; [MarshalAs(UnmanagedType.LPStr)] internal string value; } public static bool ThrowOnInitFailure = true; private static Dictionary type_map = new Dictionary(); private static Dictionary custom_types = new Dictionary(); private static List method_wrappers = new List(); private static object lock_obj = new object(); internal IntPtr handle; private static IntPtr memory; private static int size_left; private static getFrameLengthDelegate getFrameLength = Selector.GetFrameLength; private static IntPtr getFrameLengthPtr = Marshal.GetFunctionPointerForDelegate(getFrameLength); private static addPropertyDelegate addProperty; private static bool addPropertyInitialized; public IntPtr Handle => handle; public IntPtr SuperClass => class_getSuperclass(handle); public string Name => Messaging.StringFromNativeUtf8(class_getName(handle)); public Class(string name) { handle = objc_getClass(name); if (handle == IntPtr.Zero) { throw new ArgumentException($"name {name} is an unknown class", "name"); } } public Class(Type type) { handle = Register(type); } public Class(IntPtr handle) { this.handle = handle; } internal static Class Construct(IntPtr handle) { return new Class(handle); } internal static string GetName(IntPtr @class) { return Messaging.StringFromNativeUtf8(class_getName(@class)); } public static IntPtr GetHandle(string name) { return objc_getClass(name); } public static IntPtr GetHandle(Type type) { RegisterAttribute registerAttribute = (RegisterAttribute)Attribute.GetCustomAttribute(type, typeof(RegisterAttribute), inherit: false); string name = (registerAttribute == null) ? type.FullName : (registerAttribute.Name ?? type.FullName); bool is_wrapper = registerAttribute?.IsWrapper ?? false; IntPtr intPtr = objc_getClass(name); if (intPtr == IntPtr.Zero) { intPtr = Register(type, name, is_wrapper); } return intPtr; } public static bool IsCustomType(Type type) { lock (lock_obj) { return custom_types.ContainsKey(type); } } internal static Type Lookup(IntPtr klass) { return Lookup(klass, throw_on_error: true); } internal static Type Lookup(IntPtr klass, bool throw_on_error) { lock (lock_obj) { if (type_map.TryGetValue(klass, out Type value)) { return value; } IntPtr key = klass; while (true) { IntPtr intPtr = class_getSuperclass(klass); if (type_map.TryGetValue(intPtr, out value)) { type_map[key] = value; return value; } if (intPtr == IntPtr.Zero) { break; } klass = intPtr; } if (throw_on_error) { throw new ArgumentException("Could not find a valid superclass for type " + new Class(key).Name + ". Did you forget to register the bindings at " + typeof(Class).FullName + ".Register() or call NSApplication.Init()?"); } return null; } } internal static IntPtr Register(Type type) { RegisterAttribute registerAttribute = (RegisterAttribute)Attribute.GetCustomAttribute(type, typeof(RegisterAttribute), inherit: false); string name = (registerAttribute == null) ? type.FullName : (registerAttribute.Name ?? type.FullName); bool is_wrapper = registerAttribute?.IsWrapper ?? false; return Register(type, name, is_wrapper); } private static IntPtr Register(Type type, string name, bool is_wrapper) { IntPtr zero = IntPtr.Zero; IntPtr zero2 = IntPtr.Zero; zero2 = objc_getClass(name); lock (lock_obj) { if (zero2 != IntPtr.Zero) { if (!type_map.ContainsKey(zero2)) { type_map[zero2] = type; } return zero2; } if (objc_getProtocol(name) != IntPtr.Zero) { throw new ArgumentException("Attempting to register a class named: " + name + " which is a valid protocol"); } if (is_wrapper) { return IntPtr.Zero; } Type baseType = type.BaseType; string text = null; while (Attribute.IsDefined(baseType, typeof(ModelAttribute), inherit: false)) { baseType = baseType.BaseType; } RegisterAttribute registerAttribute = (RegisterAttribute)Attribute.GetCustomAttribute(baseType, typeof(RegisterAttribute), inherit: false); text = ((registerAttribute == null) ? baseType.FullName : (registerAttribute.Name ?? baseType.FullName)); zero = objc_getClass(text); if (zero == IntPtr.Zero && (baseType.Assembly != NSObject.MonoMacAssembly || typeof(NotMonoMac).IsAssignableFrom(baseType))) { bool is_wrapper2 = registerAttribute?.IsWrapper ?? false; Register(baseType, text, is_wrapper2); zero = objc_getClass(text); } if (zero == IntPtr.Zero) { zero = objc_getClass("NSObject"); } zero2 = objc_allocateClassPair(zero, name, IntPtr.Zero); PropertyInfo[] properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { ConnectAttribute connectAttribute = (ConnectAttribute)Attribute.GetCustomAttribute(propertyInfo, typeof(ConnectAttribute)); if (connectAttribute != null) { string name2 = connectAttribute.Name ?? propertyInfo.Name; class_addIvar(zero2, name2, (IntPtr)Marshal.SizeOf(typeof(IntPtr)), (ushort)Math.Log(Marshal.SizeOf(typeof(IntPtr)), 2.0), "@"); } RegisterProperty(propertyInfo, type, zero2); } NSObject.OverrideRetainAndRelease(zero2); MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < methods.Length; i++) { RegisterMethod(methods[i], type, zero2); } ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes); if (constructor != null) { NativeConstructorBuilder nativeConstructorBuilder = new NativeConstructorBuilder(constructor); class_addMethod(zero2, nativeConstructorBuilder.Selector, nativeConstructorBuilder.Delegate, nativeConstructorBuilder.Signature); method_wrappers.Add(nativeConstructorBuilder.Delegate); } ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (ConstructorInfo constructorInfo in constructors) { if ((ExportAttribute)Attribute.GetCustomAttribute(constructorInfo, typeof(ExportAttribute)) != null) { NativeConstructorBuilder nativeConstructorBuilder2 = new NativeConstructorBuilder(constructorInfo); class_addMethod(zero2, nativeConstructorBuilder2.Selector, nativeConstructorBuilder2.Delegate, nativeConstructorBuilder2.Signature); method_wrappers.Add(nativeConstructorBuilder2.Delegate); } } var inter = type.GetInterfaces(); if (inter != null) { foreach (var item in inter) { if (item.GetCustomAttribute() != null) { var prot = objc_getProtocol(item.Name); if (prot == null) { throw new Exception("未找到Protocol:" + item.Name); } class_addProtocol(zero2, prot); } } } objc_registerClassPair(zero2); type_map[zero2] = type; custom_types.Add(type, type); return zero2; } } internal static void RegisterProperty(PropertyInfo prop, Type type, IntPtr handle) { ExportAttribute exportAttribute = (ExportAttribute)Attribute.GetCustomAttribute(prop, typeof(ExportAttribute)); if (exportAttribute != null) { if (prop.PropertyType.IsGenericType || prop.PropertyType.IsGenericTypeDefinition) { throw new ArgumentException($"Cannot export the property '{prop.DeclaringType.FullName}.{prop.Name}': it is generic."); } MethodInfo getMethod = prop.GetGetMethod(nonPublic: true); if (getMethod != null) { RegisterMethod(getMethod, exportAttribute.ToGetter(prop), type, handle); } getMethod = prop.GetSetMethod(nonPublic: true); if (getMethod != null) { RegisterMethod(getMethod, exportAttribute.ToSetter(prop), type, handle); } int count = 0; objc_attribute_prop[] array = new objc_attribute_prop[3]; objc_attribute_prop objc_attribute_prop = array[count++] = new objc_attribute_prop { name = "T", value = TypeConverter.ToNative(prop.PropertyType) }; switch (exportAttribute.ArgumentSemantic) { case ArgumentSemantic.Copy: { int num2 = count++; objc_attribute_prop = new objc_attribute_prop { name = "C", value = "" }; array[num2] = objc_attribute_prop; break; } case ArgumentSemantic.Retain: { int num = count++; objc_attribute_prop = new objc_attribute_prop { name = "&", value = "" }; array[num] = objc_attribute_prop; break; } } objc_attribute_prop = (array[count++] = new objc_attribute_prop { name = "V", value = exportAttribute.Selector }); class_addProperty(handle, exportAttribute.Selector, array, count); } } internal static void RegisterMethod(MethodInfo minfo, Type type, IntPtr handle) { ExportAttribute exportAttribute = (ExportAttribute)Attribute.GetCustomAttribute(minfo.GetBaseDefinition(), typeof(ExportAttribute)); if (exportAttribute != null && (!minfo.IsVirtual || !(minfo.DeclaringType != type) || (minfo.DeclaringType.Assembly != NSObject.MonoMacAssembly || typeof(NotMonoMac).IsAssignableFrom(minfo.DeclaringType)))) { RegisterMethod(minfo, exportAttribute, type, handle); } } private static IntPtr AllocExecMemory(int size) { if (size_left < size) { size_left = 4096; memory = Marshal.AllocHGlobal(size_left); if (memory == IntPtr.Zero) { throw new Exception($"Could not allocate memory for specialized x86 floating point stret delegate thunk: {Marshal.GetLastWin32Error()}"); } if (mprotect(memory, size_left, 7) != 0) { throw new Exception($"Could not make allocated memory for specialized x86 floating point stret delegate thunk code executable: {Marshal.GetLastWin32Error()}"); } } IntPtr result = memory; size_left -= size; memory = new IntPtr(memory.ToInt32() + size); return result; } private static bool TypeRequiresFloatingPointTrampoline(Type t) { if (IntPtr.Size != 4) { return false; } if (typeof(float) == t || typeof(double) == t) { return false; } if (!t.IsValueType || t.IsEnum) { return false; } if (Marshal.SizeOf(t) <= 8) { return false; } return TypeContainsFloatingPoint(t); } private static bool TypeContainsFloatingPoint(Type t) { if (!t.IsValueType || t.IsEnum || t.IsPrimitive) { return false; } FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(double) || fieldInfo.FieldType == typeof(float)) { return true; } if (!(fieldInfo.FieldType == t) && TypeContainsFloatingPoint(fieldInfo.FieldType)) { return true; } } return false; } private static IntPtr GetFunctionPointer(MethodInfo minfo, Delegate @delegate) { IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate(@delegate); if (!TypeRequiresFloatingPointTrampoline(minfo.ReturnType)) { return functionPointerForDelegate; } IntPtr intPtr = AllocExecMemory(83); IntPtr intPtr2 = new IntPtr(functionPointerForDelegate.ToInt32() - intPtr.ToInt32() - 70); IntPtr intPtr3 = new IntPtr(getFrameLengthPtr.ToInt32() - intPtr.ToInt32() - 27); byte[] bytes = BitConverter.GetBytes(intPtr2.ToInt32()); byte[] bytes2 = BitConverter.GetBytes(intPtr3.ToInt32()); byte[] obj = new byte[83] { 85, 137, 229, 86, 87, 83, 131, 236, 60, 139, 69, 16, 137, 68, 36, 4, 139, 69, 12, 137, 4, 36, 232, 0, 0, 0, 0, 137, 69, 240, 131, 192, 15, 193, 232, 4, 193, 224, 4, 41, 196, 139, 77, 240, 141, 117, 8, 137, 231, 131, 249, 0, 116, 11, 131, 233, 4, 139, 4, 14, 137, 4, 15, 235, 240, 232, 0, 0, 0, 0, 139, 93, 244, 139, 125, 248, 139, 117, 252, 201, 194, 4, 0 }; obj[23] = bytes2[0]; obj[24] = bytes2[1]; obj[25] = bytes2[2]; obj[26] = bytes2[3]; obj[66] = bytes[0]; obj[67] = bytes[1]; obj[68] = bytes[2]; obj[69] = bytes[3]; byte[] array = obj; Marshal.Copy(array, 0, intPtr, array.Length); return intPtr; } internal static void RegisterMethod(MethodInfo minfo, ExportAttribute ea, Type type, IntPtr handle) { NativeMethodBuilder nativeMethodBuilder = new NativeMethodBuilder(minfo, type, ea); class_addMethod(minfo.IsStatic ? object_getClass(handle) : handle, nativeMethodBuilder.Selector, GetFunctionPointer(minfo, nativeMethodBuilder.Delegate), nativeMethodBuilder.Signature); lock (lock_obj) { method_wrappers.Add(nativeMethodBuilder.Delegate); } } [DllImport("libc", SetLastError = true)] private static extern int mprotect(IntPtr addr, int len, int prot); [DllImport("libc", SetLastError = true)] private static extern IntPtr mmap(IntPtr start, ulong length, int prot, int flags, int fd, long offset); [DllImport("/usr/lib/libobjc.dylib")] internal static extern IntPtr objc_allocateClassPair(IntPtr superclass, string name, IntPtr extraBytes); [DllImport("/usr/lib/libobjc.dylib")] internal static extern IntPtr objc_getClass(string name); [DllImport("/usr/lib/libobjc.dylib")] internal static extern IntPtr objc_getProtocol(string name); [DllImport("/usr/lib/libobjc.dylib")] internal static extern bool class_addProtocol(IntPtr cls, IntPtr protocol); [DllImport("/usr/lib/libobjc.dylib")] private static extern void objc_registerClassPair(IntPtr cls); [DllImport("/usr/lib/libobjc.dylib")] private static extern bool class_addIvar(IntPtr cls, string name, IntPtr size, ushort alignment, string types); [DllImport("/usr/lib/libobjc.dylib")] internal static extern bool class_addMethod(IntPtr cls, IntPtr name, Delegate imp, string types); [DllImport("/usr/lib/libobjc.dylib")] internal static extern bool class_addMethod(IntPtr cls, IntPtr name, IntPtr imp, string types); [DllImport("/usr/lib/libobjc.dylib")] private static extern IntPtr class_getName(IntPtr cls); [DllImport("/usr/lib/libobjc.dylib")] internal static extern IntPtr class_getSuperclass(IntPtr cls); [DllImport("/usr/lib/libobjc.dylib")] internal static extern IntPtr object_getClass(IntPtr obj); [DllImport("/usr/lib/libobjc.dylib")] internal static extern IntPtr class_getMethodImplementation(IntPtr cls, IntPtr sel); [DllImport("/usr/lib/libobjc.dylib")] internal static extern IntPtr class_getInstanceVariable(IntPtr cls, string name); private static IntPtr class_addProperty(IntPtr cls, string name, objc_attribute_prop[] attributes, int count) { if (!addPropertyInitialized) { IntPtr intPtr = Dlfcn.dlopen("/usr/lib/libobjc.dylib", 0); try { IntPtr intPtr2 = Dlfcn.dlsym(intPtr, "class_addProperty"); if (intPtr2 != IntPtr.Zero) { addProperty = (addPropertyDelegate)Marshal.GetDelegateForFunctionPointer(intPtr2, typeof(addPropertyDelegate)); } } finally { Dlfcn.dlclose(intPtr); } addPropertyInitialized = true; } if (addProperty == null) { return IntPtr.Zero; } return addProperty(cls, name, attributes, count); } [DllImport("/usr/lib/libobjc.dylib")] internal static extern IntPtr objc_setAssociatedObject(IntPtr obj, IntPtr name, IntPtr value, long policy); internal enum objc_AssociationPolicy { OBJC_ASSOCIATION_ASSIGN = 0, OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, OBJC_ASSOCIATION_COPY_NONATOMIC = 3, OBJC_ASSOCIATION_RETAIN = 01401, OBJC_ASSOCIATION_COPY = 01403 } } }