public bool Add (ObjCMethod method, ref List<Exception> exceptions) { bool rv; if (Methods == null) Methods = new List<ObjCMethod> (); VerifySelector (method, ref exceptions); method.ValidateSignature (ref exceptions); if (!method.IsPropertyAccessor) Registrar.VerifyInSdk (ref exceptions, method); rv = AddToMap (method, ref exceptions); Methods.Add (method); return rv; }
// 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; }