mirror of
https://gitee.com/csharpui/CPF.git
synced 2025-04-05 17:37:51 +08:00
591 lines
22 KiB
C#
591 lines
22 KiB
C#
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<IntPtr, Type> type_map = new Dictionary<IntPtr, Type>();
|
||
|
||
private static Dictionary<Type, Type> custom_types = new Dictionary<Type, Type>();
|
||
|
||
private static List<Delegate> method_wrappers = new List<Delegate>();
|
||
|
||
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<ProtocolAttribute>() != 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
|
||
}
|
||
}
|
||
}
|