protected virtual void OnRegisterCategory (ObjCType type, ref List<Exception> exceptions) {}
protected virtual void OnReloadType (ObjCType type) {}
protected virtual void OnRegisterProtocol (ObjCType type) {}
public ObjCMethod (Registrar registrar, ObjCType declaringType, TMethod method) : base (registrar, declaringType) { Method = method; }
protected virtual void OnRegisterType (ObjCType type) {}
public ObjCMember (Registrar registrar, ObjCType declaringType) { Registrar = registrar; DeclaringType = declaringType; }
// This method is not thread-safe wrt 'types', and must be called with // a lock held on 'types'. ObjCType RegisterTypeUnsafe (TType type, ref List<Exception> exceptions) { ObjCType objcType; bool isGenericType = false; bool isProtocol = false; bool isInformalProtocol = false; if (IsGenericType (type)) { type = GetGenericTypeDefinition (type); isGenericType = true; } if (types.TryGetValue (type, out objcType)) { OnReloadType (objcType); return objcType; } var categoryAttribute = GetCategoryAttribute (type); if (categoryAttribute != null) return RegisterCategory (type, categoryAttribute, ref exceptions); if (!IsNSObject (type)) { //Trace ("{0} is not derived from NSObject", GetTypeFullName (type)); if (!IsInterface (type)) return null; if (!HasProtocolAttribute (type)) return null; if (isGenericType) { exceptions.Add (ErrorHelper.CreateError (4148, "The registrar found a generic protocol: '{0}'. Exporting generic protocols is not supported.", GetTypeFullName (type))); return null; } // This is a protocol var pAttr = GetProtocolAttribute (type); isInformalProtocol = pAttr.IsInformal; isProtocol = true; } // make sure the base type is already registered var baseType = GetBaseType (type); ObjCType baseObjCType = null; if (baseType != null) { Trace ("Registering base type {0} of {1}", GetTypeName (baseType), GetTypeName (type)); baseObjCType = RegisterTypeUnsafe (baseType, ref exceptions); } var register_attribute = GetRegisterAttribute (type); if (register_attribute != null && register_attribute.SkipRegistration) return baseObjCType; objcType = new ObjCType () { Registrar = this, RegisterAttribute = GetRegisterAttribute (type), Type = type, IsModel = HasModelAttribute (type), IsProtocol = isProtocol, IsGeneric = isGenericType, }; objcType.VerifyRegisterAttribute (); objcType.Protocols = GetProtocols (objcType, ref exceptions); objcType.BaseType = isProtocol ? null : (baseObjCType ?? objcType); objcType.IsWrapper = (isProtocol && !isInformalProtocol) ? (GetProtocolAttributeWrapperType (objcType.Type) != null) : (objcType.RegisterAttribute != null && objcType.RegisterAttribute.IsWrapper); if (!objcType.IsWrapper && objcType.BaseType != null) VerifyTypeInSDK (ref exceptions, objcType.BaseType.Type, baseTypeOf: objcType.Type); // make sure all the protocols this type implements are registered if (objcType.Protocols != null) { foreach (var p in objcType.Protocols) { Trace ("Registering implemented protocol {0} of {1}", p == null ? "null" : p.ProtocolName, GetTypeName (type)); OnRegisterProtocol (p); } } TType previous_type; if (objcType.IsProtocol) { lock (protocol_map) { if (protocol_map.TryGetValue (objcType.ExportedName, out previous_type)) throw ErrorHelper.CreateError (4126, "Cannot register two managed protocols ('{0}' and '{1}') with the same native name ('{2}').", GetAssemblyQualifiedName (type), GetAssemblyQualifiedName (previous_type), objcType.ExportedName); protocol_map.Add (objcType.ExportedName, type); } } else { lock (type_map) { if (type_map.TryGetValue (objcType.ExportedName, out previous_type)) throw ErrorHelper.CreateError (4118, "Cannot register two managed types ('{0}' and '{1}') with the same native name ('{2}').", GetAssemblyQualifiedName (type), GetAssemblyQualifiedName (previous_type), objcType.ExportedName); type_map.Add (objcType.ExportedName, type); } } types.Add (type, objcType); Trace (" [TYPE] Registering {0} => {1} IsWrapper: {2} BaseType: {3} IsModel: {4} IsProtocol: {5}", type.ToString ().Replace ('+', '/'), objcType.ExportedName, objcType.IsWrapper, objcType.BaseType == null ? "null" : objcType.BaseType.Name, objcType.IsModel, objcType.IsProtocol); // Special methods bool is_first_nonWrapper = false; var methods = new List<TMethod> (CollectMethods (type)); if (!isProtocol) { is_first_nonWrapper = !(objcType.IsWrapper || objcType.IsModel) && (objcType.BaseType.IsWrapper || objcType.BaseType.IsModel); if (is_first_nonWrapper) { bool isCalayerSubclass = IsSubClassOf (objcType.Type, CoreAnimation, "CALayer"); if (!isCalayerSubclass) { objcType.Add (new ObjCMethod (this, objcType, null) { Selector = "release", Trampoline = Trampoline.Release, Signature = "v@:", IsStatic = false, }, ref exceptions); objcType.Add (new ObjCMethod (this, objcType, null) { Selector = "retain", Trampoline = Trampoline.Retain, Signature = "@@:", IsStatic = false, }, ref exceptions); } objcType.Add (new ObjCMethod (this, objcType, null) { Selector = "xamarinGetGCHandle", Trampoline = Trampoline.GetGCHandle, Signature = "i@:", IsStatic = false, }, ref exceptions); objcType.Add (new ObjCMethod (this, objcType, null) { Selector = "xamarinSetGCHandle:", Trampoline = Trampoline.SetGCHandle, Signature = "v@:i", IsStatic = false, }, ref exceptions); } // Find conform_to_protocol if (conforms_to_protocol == null && Is (type, Foundation, "NSObject")) { foreach (var method in methods) { switch (GetMethodName (method)) { case "InvokeConformsToProtocol": invoke_conforms_to_protocol = method; break; case "ConformsToProtocol": conforms_to_protocol = method; break; } if (invoke_conforms_to_protocol != null && conforms_to_protocol != null) break; } } #if MMP || MTOUCH // Special fields if (is_first_nonWrapper) { // static registrar objcType.Add (new ObjCField () { DeclaringType = objcType, FieldType = "XamarinObject",// "^v", // void* Name = "__monoObjectGCHandle", }, ref exceptions); } #endif } var properties = new List<TProperty> (CollectProperties (type)); var hasProtocolMemberAttributes = false; if (isProtocol && !isInformalProtocol) { var attribs = GetProtocolMemberAttributes (type); foreach (var attrib in attribs) { hasProtocolMemberAttributes = true; if (attrib.IsProperty) { if (attrib.IsStatic) { // There is no such things as a static property in ObjC, so export this as just the getter[+setter]. var objcGetter = new ObjCMethod (this, objcType, null) { Name = attrib.Name, Selector = attrib.GetterSelector, Parameters = new TType[] { }, ReturnType = attrib.PropertyType, IsStatic = attrib.IsStatic, IsOptional = !attrib.IsRequired, IsConstructor = false, }; objcType.Add (objcGetter, ref exceptions); if (!string.IsNullOrEmpty (attrib.SetterSelector)) { var objcSetter = new ObjCMethod (this, objcType, null) { Name = attrib.Name, Selector = attrib.SetterSelector, Parameters = new TType[] { attrib.PropertyType }, ReturnType = GetSystemVoidType (), IsStatic = attrib.IsStatic, IsOptional = !attrib.IsRequired, IsConstructor = false, }; objcType.Add (objcSetter, ref exceptions); } } else { var objcProperty = new ObjCProperty () { Registrar = this, DeclaringType = objcType, Property = null, Name = attrib.Name, Selector = attrib.Selector, ArgumentSemantic = attrib.ArgumentSemantic, IsReadOnly = string.IsNullOrEmpty (attrib.SetterSelector), IsStatic = attrib.IsStatic, IsOptional = !attrib.IsRequired, GetterSelector = attrib.GetterSelector, SetterSelector = attrib.SetterSelector, PropertyType = attrib.PropertyType, }; objcType.Add (objcProperty, ref exceptions); } } else { var objcMethod = new ObjCMethod (this, objcType, null) { Name = attrib.Name, Selector = attrib.Selector, ArgumentSemantic = attrib.ArgumentSemantic, IsVariadic = attrib.IsVariadic, ReturnType = attrib.ReturnType ?? GetSystemVoidType (), IsStatic = attrib.IsStatic, IsOptional = !attrib.IsRequired, IsConstructor = false, }; if (attrib.ParameterType != null) { var parameters = new TType [attrib.ParameterType.Length]; for (int i = 0; i < parameters.Length; i++) { if (attrib.ParameterByRef [i]) { parameters [i] = MakeByRef (attrib.ParameterType [i]); } else { parameters [i] = attrib.ParameterType [i]; } } objcMethod.Parameters = parameters; } else { objcMethod.Parameters = new TType[] { }; } objcType.Add (objcMethod, ref exceptions); } } } foreach (TProperty property in properties) { if (hasProtocolMemberAttributes) continue; if (!isProtocol) { var ca = GetConnectAttribute (property); if (ca != null) { if (!IsINativeObject (GetPropertyType (property))) { AddException (ref exceptions, CreateException (4139, property, "The registrar cannot marshal the property type '{0}' of the property '{1}.{2}'. Properties with the [Connect] attribute must have a property type of NSObject (or a subclass of NSObject).", GetTypeFullName (GetPropertyType (property)), GetTypeFullName (type), GetPropertyName (property))); continue; } objcType.Add (new ObjCField () { DeclaringType = objcType, Name = ca.Name ?? GetPropertyName (property), #if !MTOUCH && !MMP Size = Is64Bits ? 8 : 4, Alignment = (byte) (Is64Bits ? 3 : 2), #endif FieldType = "@", IsProperty = true, }, ref exceptions); } } var ea = GetExportAttribute (property); if (ea == null) continue; if (IsStatic (property) && (objcType.IsWrapper || objcType.IsModel)) { // This is useless to export, since the user can't actually do anything with it, // it'll just call back into the base implementation. continue; } if (IsStatic (property) && isGenericType) { AddException (ref exceptions, CreateException (4131, property, "The registrar cannot export static properties in generic classes ('{0}.{1}').", GetTypeFullName (type), GetPropertyName (property))); continue; } TType property_type = null; if (isGenericType && !VerifyIsConstrainedToNSObject (GetPropertyType (property), out property_type)) { AddException (ref exceptions, CreateException (4132, property, "The registrar found an invalid generic return type '{0}' in the property '{1}.{2}'. The return type must have an 'NSObject' constraint.", GetTypeFullName (GetPropertyType (property)), GetTypeFullName (type), GetPropertyName (property))); continue; } if (property_type == null) property_type = GetPropertyType (property); Trace (" [PROPERTY] {0} => {1}", property, ea.Selector); var objcProperty = new ObjCProperty () { Registrar = this, DeclaringType = objcType, Property = property, Name = property.Name, Selector = ea.Selector ?? GetPropertyName (property), ArgumentSemantic = ea.ArgumentSemantic, PropertyType = property_type, }; TMethod getter = GetGetMethod (property); TMethod setter = GetSetMethod (property); if (getter != null && VerifyNonGenericMethod (ref exceptions, type, getter)) { var method = new ObjCMethod (this, objcType, getter) { Selector = ea.Selector ?? GetPropertyName (property), ArgumentSemantic = ea.ArgumentSemantic, ReturnType = property_type, }; List<Exception> excs = null; if (!method.ValidateSignature (ref excs)) { exceptions.Add (CreateException (4138, excs [0], property, "The registrar cannot marshal the property type '{0}' of the property '{1}.{2}'.", GetTypeFullName (property.PropertyType), property.DeclaringType.FullName, property.Name)); continue; } if (!objcType.Add (method, ref exceptions)) continue; Trace (" [GET] {0}", objcType.Methods [objcType.Methods.Count - 1].Name); } if (setter != null && VerifyNonGenericMethod (ref exceptions, type, setter)) { string setterName = ea.Selector ?? GetPropertyName (property); var method = new ObjCMethod (this, objcType, setter) { Selector = CreateSetterSelector (setterName), ArgumentSemantic = ea.ArgumentSemantic, Parameters = new TType[] { property_type }, }; List<Exception> excs = null; if (!method.ValidateSignature (ref excs)) { exceptions.Add (CreateException (4138, excs [0], property, "The registrar cannot marshal the property type '{0}' of the property '{1}.{2}'.", GetTypeFullName (property.PropertyType), property.DeclaringType.FullName, property.Name)); continue; } if (!objcType.Add (method, ref exceptions)) continue; Trace (" [SET] {0}", objcType.Methods [objcType.Methods.Count - 1].Name); } objcType.Add (objcProperty, ref exceptions); } var custom_conforms_to_protocol = !is_first_nonWrapper; // we only have to generate the conformsToProtocol method for the first non-wrapper type. #if MONOMAC ObjCMethod custom_copy_with_zone = null; var isNSCellSubclass = IsSubClassOf (type, AppKit, "NSCell"); #endif Dictionary<TMethod, List<TMethod>> method_map = null; if (!isProtocol) method_map = PrepareMethodMapping (type); foreach (TMethod method in methods) { if (hasProtocolMemberAttributes) continue; var ea = GetExportAttribute (method); if (ea == null) { List<TMethod> impls; if (method_map != null && method_map.TryGetValue (method, out impls)) { if (impls.Count != 1) { AddException (ref exceptions, Shared.GetMT4127 (method, impls)); continue; } ea = GetExportAttribute (impls [0]); } } if (ea == null) continue; if (IsStatic (method) && (objcType.IsWrapper || objcType.IsModel) && !(objcType.IsProtocol && !objcType.IsFakeProtocol)) { // This is useless to export, since the user can't actually do anything with it, // it'll just call back into the base implementation. continue; } if (objcType.IsModel && IsVirtual (method)) continue; Trace (" [METHOD] {0} => {1}", method, ea.Selector); if (!custom_conforms_to_protocol && method.DeclaringType == type && GetBaseMethod (method) == conforms_to_protocol) custom_conforms_to_protocol = true; if (!VerifyNonGenericMethod (ref exceptions, type, method)) continue; var objcMethod = new ObjCMethod (this, objcType, method); if (!objcMethod.SetExportAttribute (ea, ref exceptions)) continue; #if MONOMAC if (objcMethod.Selector == "copyWithZone:") custom_copy_with_zone = objcMethod; #endif if (IsStatic (method) && isGenericType) { AddException (ref exceptions, CreateException (4130, method, "The registrar cannot export static methods in generic classes ('{0}').", GetDescriptiveMethodName (type, method))); continue; } else if (isGenericType && !VerifyIsConstrainedToNSObject (ref exceptions, type, objcMethod)) { continue; } try { objcType.Add (objcMethod, ref exceptions); } catch (Exception ex) { AddException (ref exceptions, ex); } } if (!isProtocol && !custom_conforms_to_protocol) { objcType.Add (new ObjCMethod (this, objcType, invoke_conforms_to_protocol) { Selector = "conformsToProtocol:", Trampoline = Trampoline.Normal, Signature = "B@:^v", IsStatic = false, }, ref exceptions); } #if MONOMAC if (isNSCellSubclass) { if (custom_copy_with_zone != null) { custom_copy_with_zone.Trampoline = Trampoline.CopyWithZone2; } else { objcType.Add (new ObjCMethod (this, objcType, null) { Selector = "copyWithZone:", Trampoline = Trampoline.CopyWithZone1, Signature = "@@:^v", IsStatic = false, }, ref exceptions); } } #endif foreach (TMethod ctor in CollectConstructors (type)) { if (IsStatic (ctor)) continue; var parameters = GetParameters (ctor); if (parameters == null || parameters.Length == 0) { Trace (" [CTOR] {0} default => init", GetTypeName (type)); objcType.Add (new ObjCMethod (this, objcType, ctor) { Selector = "init", Trampoline = Trampoline.Constructor, }, ref exceptions); continue; } var ea = GetExportAttribute (ctor); if (ea == null) continue; Trace (" [CTOR] {2} {0} => {1}", GetMethodName (ctor), ea.Selector, GetTypeName (type)); if (!VerifyNonGenericMethod (ref exceptions, type, ctor)) continue; var method = new ObjCMethod (this, objcType, ctor) { Trampoline = Trampoline.Constructor, }; if (method.SetExportAttribute (ea, ref exceptions)) objcType.Add (method, ref exceptions); } if (objcType.IsProtocol) { OnRegisterProtocol (objcType); } else { OnRegisterType (objcType); } return objcType; }
ObjCType RegisterCategory (TType type, CategoryAttribute attrib, ref List<Exception> exceptions) { if (IsINativeObject (type)) { AddException (ref exceptions, ErrorHelper.CreateError (4152, "Cannot register the type '{0}' as a category because it implements INativeObject or subclasses NSObject.", GetTypeFullName (type))); return null; } if (IsGenericType (type)) { AddException (ref exceptions, ErrorHelper.CreateError (4153, "Cannot register the type '{0}' as a category because it's generic.", GetTypeFullName (type))); return null; } if (attrib.Type == null) { AddException (ref exceptions, ErrorHelper.CreateError (4151, "Cannot register the type '{0}' because the Type property in its Category attribute isn't set.", GetTypeFullName (type))); return null; } var declaringType = RegisterType (attrib.Type, ref exceptions); if (declaringType == null) { AddException (ref exceptions, ErrorHelper.CreateError (4150, "Cannot register the type '{0}' because the category type '{1}' in its Category attribute does not inherit from NSObject.", GetTypeFullName (type), GetTypeFullName (attrib.Type))); return null; } var objcType = new ObjCType () { Registrar = this, Type = type, BaseType = declaringType, CategoryAttribute = attrib, }; lock (categories_map) { TType previous_type; if (categories_map.TryGetValue (objcType.CategoryName, out previous_type)) { AddException (ref exceptions, ErrorHelper.CreateError (4156, "Cannot register two categories ('{0}' and '{1}') with the same native name ('{2}')", GetAssemblyQualifiedName (type), GetAssemblyQualifiedName (previous_type), objcType.CategoryName)); return null; } categories_map.Add (objcType.CategoryName, type); } types.Add (type, objcType); Trace (" [CATEGORY] Registering {0} on {1}", type.ToString ().Replace ('+', '/'), attrib.Type.FullName); foreach (var ctor in CollectConstructors (type)) { var ea = GetExportAttribute (ctor); if (ea == null) continue; AddException (ref exceptions, CreateException (4158, ctor, "Cannot register the constructor {0}.{1} in the category {0} because constructors in categories are not supported.", GetTypeFullName (type), GetDescriptiveMethodName (ctor))); } foreach (var method in CollectMethods (type)) { var ea = GetExportAttribute (method); if (ea == null) continue; if (!IsStatic (method)) { AddException (ref exceptions, CreateException (4159, method, "Cannot register the method '{0}.{1}' as a category method because category methods must be static.", GetTypeFullName (type), GetMethodName (method))); return null; } if (HasThisAttribute (method)) { var parameters = GetParameters (method); if (parameters == null || parameters.Length == 0) { AddException (ref exceptions, CreateException (4157, method, "Cannot register the category method '{0}.{1}' because at least one parameter is required for extension methods (and its type must match the category type '{2}').", GetTypeFullName (type), GetMethodName (method), GetTypeFullName (declaringType.Type))); continue; } else if (GetTypeFullName (parameters [0]) != GetTypeFullName (declaringType.Type)) { AddException (ref exceptions, CreateException (4149, method, "Cannot register the extension method '{0}.{1}' because the type of the first parameter ('{2}') does not match the category type ('{3}').", GetTypeFullName (type), GetMethodName (method), GetTypeFullName (parameters [0]), GetTypeFullName (declaringType.Type))); continue; } } if (IsGenericMethod (method)) { AddException (ref exceptions, CreateException (4154, method, "Cannot register the method '{0}.{1}' as a category method because it's generic.", GetTypeFullName (type), GetMethodName (method))); continue; } Trace (" [METHOD] {0} => {1}", method, ea.Selector); var category_method = new ObjCMethod (this, declaringType, method) { CategoryType = objcType, }; if (category_method.SetExportAttribute (ea, ref exceptions)) { objcType.Add (category_method, ref exceptions); declaringType.Add (category_method, ref exceptions); } } // TODO: properties OnRegisterCategory (objcType, ref exceptions); return objcType; }
ObjCType [] GetProtocols (ObjCType type, ref List<Exception> exceptions) { var interfaces = GetInterfacesImpl (type); if (interfaces == null || interfaces.Length == 0) return null; var protocolList = new List<ObjCType> (interfaces.Length); for (int i = 0; i < interfaces.Length; i++) { if (interfaces [i] == null) continue; var baseP = RegisterTypeUnsafe (interfaces [i], ref exceptions); if (baseP != null) protocolList.Add (baseP); } if (protocolList.Count == 0) return null; return protocolList.ToArray (); }
TType [] GetInterfacesImpl (ObjCType objcType) { // Both the dynamic and static registrars (i.e both Cecil and SRE) // return interfaces from all base classes as well. // We only want the interfaces declared on this type. // This function will return arrays with null entries. var type = objcType.Type; var allI = GetInterfaces (type); if (allI == null || allI.Length == 0) return allI; FlattenInterfaces (allI); var baseType = objcType.SuperType; if (baseType == null || baseType.Type == null) return allI; var baseI = GetInterfaces (baseType.Type); if (baseI == null || baseI.Length == 0) return allI; FlattenInterfaces (baseI); // Remove all interfaces the base type implements. var c = 0; for (int i = 0; i < allI.Length; i++) { if (Array.IndexOf (baseI, allI [i]) < 0) { allI [c++] = allI [i]; } } Array.Resize (ref allI, c); return allI; }