Dictionary <IntPtr, MethodDescription> RegisterMethods (Type type) { // Caller must hold the obj_lock. Dictionary <IntPtr, MethodDescription> methods = new Dictionary <IntPtr, MethodDescription> (Runtime.IntPtrEqualityComparer); foreach (PropertyInfo pinfo in type.GetProperties (BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)) { ExportAttribute ea = (ExportAttribute) Attribute.GetCustomAttribute (pinfo, typeof (ExportAttribute)); if (ea == null) continue; MethodInfo g = pinfo.GetGetMethod (true); if (g != null) { IntPtr selector = Selector.GetHandle (ea.ToGetter (pinfo).Selector); #if DEBUG_POPULATE Console.WriteLine ("[GETTER] Registering {0}[0x{1:x}|{2}] on {3} -> ({4})", ea.Selector, (int) selector, Method.Signature (g), type, pinfo); #endif methods [selector] = new MethodDescription (g, ea.ArgumentSemantic); } MethodInfo s = pinfo.GetSetMethod (true); if (s != null) { IntPtr selector = Selector.GetHandle (ea.ToSetter (pinfo).Selector); #if DEBUG_POPULATE Console.WriteLine ("[SETTER] Registering {0}[0x{1:x}|{2}] on {3} -> ({4})", ea.Selector, (int) selector, Method.Signature (s), type, pinfo); #endif methods [selector] = new MethodDescription (s, ea.ArgumentSemantic); } } var method_interface_map = SharedDynamic.PrepareInterfaceMethodMapping (type); foreach (MethodInfo minfo in type.GetMethods (BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)) { ExportAttribute ea = (ExportAttribute) Attribute.GetCustomAttribute (minfo.GetBaseDefinition (), typeof (ExportAttribute)); if (ea == null) ea = GetMappedExportAttribute (method_interface_map, minfo); if (ea == null) continue; IntPtr selector = Selector.GetHandle (ea.Selector ?? minfo.Name); MethodDescription md; if (!methods.TryGetValue (selector, out md)) { #if DEBUG_POPULATE Console.WriteLine ("[METHOD] Registering {0}[0x{1:x}|{2}] from {3} -> ({4} on {5})", ea.Selector, (int) selector, Method.Signature (minfo), type, minfo, minfo.DeclaringType); #endif methods.Add (selector, new MethodDescription (minfo, ea.ArgumentSemantic)); continue; } // // More than one method can exist for hidden methods. Choose one closest to // the caller type // if (minfo.DeclaringType.IsSubclassOf (md.method.DeclaringType)) { #if DEBUG_POPULATE Console.WriteLine ("[METHOD] Re-registering {0}[0x{1:x}|{2}] from {3} -> ({4} on {5})", ea.Selector, (int) selector, Method.Signature (minfo), type, minfo, minfo.DeclaringType); #endif methods [selector] = new MethodDescription (minfo, ea.ArgumentSemantic); } } bool default_ctor_found = false; foreach (ConstructorInfo cinfo in type.GetConstructors (BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)) { if (!default_ctor_found && !cinfo.IsStatic && cinfo.GetParameters ().Length == 0) { default_ctor_found = true; methods [Selector.GetHandle ("init")] = new MethodDescription (cinfo, ArgumentSemantic.Assign); } ExportAttribute ea = (ExportAttribute) Attribute.GetCustomAttribute (cinfo, typeof (ExportAttribute)); if (ea == null) continue; if (ea.Selector == null) throw new Exception ("Constructor's must have a Export attribute with the selector specified"); IntPtr selector = Selector.GetHandle (ea.Selector); #if DEBUG_POPULATE Console.WriteLine ("[CTOR] Registering {0}[0x{1:x}|{2}] on {3} -> ({4})", ea.Selector, (int) selector, Method.Signature (cinfo), type, cinfo); #endif methods [selector] = new MethodDescription (cinfo, ea.ArgumentSemantic); } method_map [type] = methods; return methods; }
public void RegisterMethod (MethodInfo minfo, ExportAttribute ea, Type type, IntPtr handle, bool update_map) { IntPtr reg_handle = IntPtr.Zero; IntPtr tramp = IntPtr.Zero; IntPtr sel = Selector.GetHandle (ea.Selector ?? minfo.Name); string signature = Method.Signature (minfo); Type return_type = minfo.ReturnType; reg_handle = minfo.IsStatic ? Class.object_getClass (handle) : handle; if (return_type.IsValueType && !return_type.IsEnum && return_type.Assembly != typeof (object).Assembly && (Runtime.Arch == Arch.DEVICE || Marshal.SizeOf (return_type) > 8)) { if (Runtime.Arch == Arch.SIMULATOR) { if (TypeContainsDouble (return_type)) tramp = minfo.IsStatic ? Method.X86_DoubleABI_StaticStretTrampoline : Method.X86_DoubleABI_StretTrampoline; else tramp = minfo.IsStatic ? Method.StaticStretTrampoline : Method.StretTrampoline; } else { tramp = minfo.IsStatic ? Method.StaticStretTrampoline : Method.StretTrampoline; } } else if (return_type.IsValueType && !return_type.IsEnum && return_type.Assembly != typeof (object).Assembly && Runtime.Arch == Arch.SIMULATOR && Marshal.SizeOf (return_type) > 4) { // for instance CGSize... tramp = minfo.IsStatic ? Method.StaticLongTrampoline : Method.LongTrampoline; } else { switch (signature [0]) { case 'Q': case 'q': tramp = minfo.IsStatic ? Method.StaticLongTrampoline : Method.LongTrampoline; break; case 'f': tramp = minfo.IsStatic ? Method.StaticSingleTrampoline : Method.SingleTrampoline; break; case 'd': tramp = minfo.IsStatic ? Method.StaticDoubleTrampoline : Method.DoubleTrampoline; break; default: tramp = minfo.IsStatic ? Method.StaticTrampoline : Method.Trampoline; break; } } #if DEBUG_REGISTER Console.WriteLine ("[METHOD] Registering {0}[0x{1:x}|{2}] on {3} -> ({4}) tramp: 0x{5}", ea.Selector, (int) sel, Method.Signature (minfo), type, minfo, tramp.ToString ("x")); #endif Class.class_addMethod (reg_handle, sel, tramp, Method.Signature (minfo)); if (update_map) { Dictionary<IntPtr, MethodDescription> methods; lock (lock_obj) { if (!method_map.TryGetValue (type, out methods)) { methods = new Dictionary<IntPtr, MethodDescription> (Runtime.IntPtrEqualityComparer); method_map.Add (type, methods); } methods[sel] = new MethodDescription (minfo, ea.ArgumentSemantic); } } }
unsafe IntPtr Register (Type type, string name, bool is_wrapper) { IntPtr parent = IntPtr.Zero; IntPtr handle = IntPtr.Zero; lock (lock_obj) { handle = Class.objc_getClass (name); if (handle != IntPtr.Zero) { if (!type_map.ContainsKey (handle)) { type_map [handle] = type; } return handle; } /*FIXME pick a more suitable exception type */ /*FIXME try to guess the name of the missing library - quite trivial for monotouch.dll*/ if (is_wrapper) { if (Runtime.Arch == Arch.DEVICE) { // types decorated with [Model] attribute are not registered (see registrar.cs and regression from #769) // a missing [Model] attribute will cause this error on devices (e.g. bug #4864) if (!Attribute.IsDefined (type, typeof (ModelAttribute), false)) throw new Exception (string.Format ("Wrapper type '{0}' is missing its native ObjectiveC class '{1}'.", type.FullName, name)); } else { /*On simulator this is a common issue since we eagerly register all types. This is an issue with unlinked monotouch.dll since we don't link all frameworks most of the time. */ return IntPtr.Zero; } } Dictionary <IntPtr, MethodDescription> methods = new Dictionary <IntPtr, MethodDescription> (Runtime.IntPtrEqualityComparer); Type parent_type = type.BaseType; string parent_name = null; while (Attribute.IsDefined (parent_type, typeof (ModelAttribute), false)) parent_type = parent_type.BaseType; RegisterAttribute parent_attr = (RegisterAttribute) Attribute.GetCustomAttribute (parent_type, typeof (RegisterAttribute), false); parent_name = parent_attr == null ? parent_type.FullName : parent_attr.Name ?? parent_type.FullName; parent = Class.objc_getClass (parent_name); if (parent == IntPtr.Zero && parent_type.Assembly != NSObject.MonoTouchAssembly) { bool parent_is_wrapper = parent_attr == null ? false : parent_attr.IsWrapper; // Its possible as we scan that we might be derived from a type that isn't reigstered yet. Register (parent_type, parent_name, parent_is_wrapper); parent = Class.objc_getClass (parent_name); } if (parent == IntPtr.Zero) { // This spams mtouch, we need a way to differentiate from mtouch's (ab)use // Console.WriteLine ("CRITICAL WARNING: Falling back to NSObject for type {0} reported as {1}", type, parent_type); parent = Class.objc_getClass ("NSObject"); } handle = Class.objc_allocateClassPair (parent, name, IntPtr.Zero); Class.class_addIvar (handle, "__monoObjectGCHandle", (IntPtr) Marshal.SizeOf (typeof (Int32)), (byte) 4, "i"); foreach (PropertyInfo prop in type.GetProperties (BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) { ConnectAttribute cattr = (ConnectAttribute) Attribute.GetCustomAttribute (prop, typeof (ConnectAttribute)); if (cattr != null) { string ivar_name = cattr.Name ?? prop.Name; Class.class_addIvar (handle, ivar_name, (IntPtr) Marshal.SizeOf (typeof (IntPtr)), (byte) Math.Log (Marshal.SizeOf (typeof (IntPtr)), 2), "@"); } var exportAtt = (ExportAttribute) Attribute.GetCustomAttribute (prop, typeof (ExportAttribute)); if (exportAtt != null) { var m = prop.GetGetMethod (true); if (m != null) { var ea = exportAtt.ToGetter (prop); RegisterMethod (m, ea, type, handle, false); var sel = Selector.GetHandle (ea.Selector); methods [sel] = new MethodDescription (m, ea.ArgumentSemantic); } m = prop.GetSetMethod (true); if (m != null) { var ea = exportAtt.ToSetter (prop); RegisterMethod (m, ea, type, handle, false); var sel = Selector.GetHandle (ea.Selector); methods [sel] = new MethodDescription (m, ea.ArgumentSemantic); } // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html int count = 0; var props = new Class.objc_attribute_prop [3]; props [count++] = new Class.objc_attribute_prop { name = "T", value = TypeConverter.ToNative (prop.PropertyType) }; switch (exportAtt.ArgumentSemantic) { case ArgumentSemantic.Copy: props [count++] = new Class.objc_attribute_prop { name = "C", value = "" }; break; case ArgumentSemantic.Retain: props [count++] = new Class.objc_attribute_prop { name = "&", value = "" }; break; } props [count++] = new Class.objc_attribute_prop { name = "V", value = exportAtt.Selector }; Class.class_addProperty (handle, exportAtt.Selector, props, count); #if DEBUG_REGISTER Console.WriteLine ("[PROPERTY] Registering {0} of type {2} ({3}) on {1}", exportAtt.Selector, type, prop.PropertyType, TypeConverter.ToNative (prop.PropertyType)); #endif } } Class.class_addMethod (handle, Selector.GetHandle (Selector.Release), Method.ReleaseTrampoline, "v@:"); Class.class_addMethod (handle, Selector.GetHandle (Selector.Retain), Method.RetainTrampoline, "@@:"); Class.class_addMethod (handle, Selector.GetHandle ("xamarinGetGCHandle"), Method.GetGCHandleTrampoline, "i@:"); Class.class_addMethod (handle, Selector.GetHandle ("xamarinSetGCHandle:"), Method.SetGCHandleTrampoline, "v@:i"); var method_interface_map = SharedDynamic.PrepareInterfaceMethodMapping (type); foreach (MethodInfo minfo in type.GetMethods (BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)) { ExportAttribute ea = (ExportAttribute) Attribute.GetCustomAttribute (minfo.GetBaseDefinition (), typeof (ExportAttribute)); if (ea == null) ea = GetMappedExportAttribute (method_interface_map, minfo); if (ea == null) continue; if (minfo.IsGenericMethod || minfo.IsGenericMethodDefinition) { Console.WriteLine ("The registrar found an exported generic method: '{0}.{1}'. Exporting generic methods is not supported.", minfo.DeclaringType.FullName, minfo.Name); continue; } bool is_conforms_to_protocol; bool is_model = false; is_conforms_to_protocol = minfo.DeclaringType.Assembly == NSObject.MonoTouchAssembly && minfo.DeclaringType.Name == "NSObject" && minfo.Name == "ConformsToProtocol"; if (!is_conforms_to_protocol) is_model = minfo.IsVirtual && ((minfo.DeclaringType != type && minfo.DeclaringType.Assembly == NSObject.MonoTouchAssembly) || (Attribute.IsDefined (minfo.DeclaringType, typeof (ModelAttribute), false))); if (is_model) continue; RegisterMethod (minfo, ea, type, handle, false); var sel = Selector.GetHandle (ea.Selector ?? minfo.Name); methods [sel] = new MethodDescription (minfo, ea.ArgumentSemantic); } bool default_ctor_found = false; foreach (ConstructorInfo cinfo in type.GetConstructors (BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)) { if (!default_ctor_found && !cinfo.IsStatic && cinfo.GetParameters ().Length == 0) { #if DEBUG_REGISTER Console.WriteLine ("[CTOR] Registering {0}[0x{1:x}|{2}] on {3} -> ({4})", "init", (int) Selector.Init, Method.Signature (cinfo), type, cinfo); #endif default_ctor_found = true; Class.class_addMethod (handle, Selector.GetHandle ("init"), Method.ConstructorTrampoline, Method.Signature (cinfo)); methods [Selector.GetHandle ("init")] = new MethodDescription (cinfo, ArgumentSemantic.Assign); } ExportAttribute ea = (ExportAttribute) Attribute.GetCustomAttribute (cinfo, typeof (ExportAttribute)); if (ea == null) continue; IntPtr sel = Selector.GetHandle (ea.Selector); Class.class_addMethod (handle, sel, Method.ConstructorTrampoline, Method.Signature (cinfo)); #if DEBUG_REGISTER Console.WriteLine ("[CTOR] Registering {0}[0x{1:x}|{2}] on {3} -> ({4})", ea.Selector, (int) sel, Method.Signature (cinfo), type, cinfo); #endif methods [sel] = new MethodDescription (cinfo, ea.ArgumentSemantic); } Class.objc_registerClassPair (handle); type_map [handle] = type; method_map [type] = methods; AddCustomType (type); return handle; } }