public AutoIndentStringBuilder WriteLine(AutoIndentStringBuilder isb)
 {
     return AppendLine (isb);
 }
        public string Generate()
        {
            using (var sb = new AutoIndentStringBuilder ()) {
                using (var hdr = new AutoIndentStringBuilder ()) {
                    using (var decls = new AutoIndentStringBuilder ()) {
                        using (var mthds = new AutoIndentStringBuilder ()) {
                            hdr.WriteLine ("#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"");
                            hdr.WriteLine ("#pragma clang diagnostic ignored \"-Wtypedef-redefinition\""); // temporary hack until we can stop including glib.h
                            hdr.WriteLine ("#pragma clang diagnostic ignored \"-Wobjc-designated-initializers\"");

                            if (Driver.EnableDebug)
                                hdr.WriteLine ("#define DEBUG 1");

                            hdr.WriteLine ("#include <stdarg.h>");
                            hdr.WriteLine ("#include <xamarin/xamarin.h>");
                            hdr.WriteLine ("#include <objc/objc.h>");
                            hdr.WriteLine ("#include <objc/runtime.h>");

                            header = hdr;
                            declarations = decls;
                            methods = mthds;

                            Specialize (sb);

                            header = null;
                            declarations = null;
                            methods = null;

                            FlushTrace ();

                            return hdr.ToString () + "\n" + decls.ToString () + "\n" + mthds.ToString () + "\n" + sb.ToString ();
                        }
                    }
                }
            }
        }
        void Specialize(AutoIndentStringBuilder sb, ObjCMethod method, List<Exception> exceptions)
        {
            var isGeneric = method.DeclaringType.IsGeneric;

            switch (method.CurrentTrampoline) {
            case Trampoline.Retain:
                sb.WriteLine ("-(id) retain");
                sb.WriteLine ("{");
                sb.WriteLine ("return xamarin_retain_trampoline (self, _cmd);");
                sb.WriteLine ("}");
                sb.WriteLine ();
                return;
            case Trampoline.Release:
                sb.WriteLine ("-(void) release");
                sb.WriteLine ("{");
                sb.WriteLine ("xamarin_release_trampoline (self, _cmd);");
                sb.WriteLine ("}");
                sb.WriteLine ();
                return;
            case Trampoline.GetGCHandle:
                sb.WriteLine ("-(int) xamarinGetGCHandle");
                sb.WriteLine ("{");
                sb.WriteLine ("return __monoObjectGCHandle.gc_handle;");
                sb.WriteLine ("}");
                sb.WriteLine ();
                return;
            case Trampoline.SetGCHandle:
                sb.WriteLine ("-(void) xamarinSetGCHandle: (int) gc_handle");
                sb.WriteLine ("{");
                sb.WriteLine ("__monoObjectGCHandle.gc_handle = gc_handle;");
                sb.WriteLine ("__monoObjectGCHandle.native_object = self;");
                sb.WriteLine ("}");
                sb.WriteLine ();
                return;
            case Trampoline.Constructor:
                if (isGeneric) {
                    sb.WriteLine (GetObjCSignature (method, exceptions));
                    sb.WriteLine ("{");
                    sb.WriteLine ("xamarin_throw_product_exception (4126, \"Cannot construct an instance of the type '{0}' from Objective-C because the type is generic.\");\n", method.DeclaringType.Type.FullName.Replace ("/", "+"));
                    sb.WriteLine ("return self;");
                    sb.WriteLine ("}");
                    return;
                }
                break;
            #if MONOMAC
            case Trampoline.CopyWithZone1:
                sb.AppendLine ("-(id) copyWithZone: (NSZone *) zone");
                sb.AppendLine ("{");
                sb.AppendLine ("id rv;");
                sb.AppendLine ("int gchandle;");
                sb.AppendLine ();
                sb.AppendLine ("gchandle = xamarin_get_gchandle_with_flags (self);");
                sb.AppendLine ("if (gchandle != 0)");
                sb.Indent ().AppendLine ("xamarin_set_gchandle (self, 0);").Unindent ();
                // Call the base class implementation
                sb.AppendLine ("rv = [super copyWithZone: zone];");
                sb.AppendLine ();
                sb.AppendLine ("if (gchandle != 0)");
                sb.Indent ().AppendLine ("xamarin_set_gchandle (self, gchandle);").Unindent ();
                sb.AppendLine ();
                sb.AppendLine ("return rv;");
                sb.AppendLine ("}");
                return;
            case Trampoline.CopyWithZone2:
                sb.AppendLine ("-(id) copyWithZone: (NSZone *) zone");
                sb.AppendLine ("{");
                sb.AppendLine ("return xamarin_copyWithZone_trampoline2 (self, _cmd, zone);");
                sb.AppendLine ("}");
                return;
            #endif
            }

            var rettype = string.Empty;
            var returntype = method.Method.ReturnType;
            var isStatic = method.IsStatic;
            var isInstanceCategory = method.IsCategoryInstance;
            var isCtor = false;
            var num_arg = method.Method.Parameters.Count;
            var descriptiveMethodName = method.DescriptiveMethodName;
            var name = GetUniqueTrampolineName ("native_to_managed_trampoline_" + descriptiveMethodName);
            var isVoid = returntype.FullName == "System.Void";
            var arguments = new List<string> ();
            var merge_bodies = true;

            switch (method.CurrentTrampoline) {
            case Trampoline.None:
            case Trampoline.Normal:
            case Trampoline.Static:
            case Trampoline.Single:
            case Trampoline.Double:
            case Trampoline.Long:
            case Trampoline.StaticLong:
            case Trampoline.StaticDouble:
            case Trampoline.StaticSingle:
            case Trampoline.X86_DoubleABI_StaticStretTrampoline:
            case Trampoline.X86_DoubleABI_StretTrampoline:
            case Trampoline.StaticStret:
            case Trampoline.Stret:
                switch (returntype.FullName) {
                case "System.Int64":
                    rettype = "long long";
                    break;
                case "System.UInt64":
                    rettype = "unsigned long long";
                    break;
                case "System.Single":
                    rettype = "float";
                    break;
                case "System.Double":
                    rettype = "double";
                    break;
                default:
                    rettype = ToObjCParameterType (returntype, descriptiveMethodName, exceptions, method.Method);
                    break;
                }
                break;
            case Trampoline.Constructor:
                rettype = "id";
                isCtor = true;
                break;
            default:
                return;
            }

            comment.Clear ();
            nslog_start.Clear ();
            nslog_end.Clear ();
            copyback.Clear ();
            invoke.Clear ();
            setup_call_stack.Clear ();
            body.Clear ();
            setup_return.Clear ();

            counter++;

            body.WriteLine ("{");

            var indent = merge_bodies ? sb.Indentation : sb.Indentation + 1;
            body.Indentation = indent;
            copyback.Indentation = indent;
            invoke.Indentation = indent;
            setup_call_stack.Indentation = indent;
            setup_return.Indentation = indent;

            // A comment describing the managed signature
            if (trace) {
                nslog_start.Indentation = sb.Indentation;
                comment.Indentation = sb.Indentation;
                nslog_end.Indentation = sb.Indentation;

                comment.AppendFormat ("// {2} {0}.{1} (", method.Method.DeclaringType.FullName, method.Method.Name, method.Method.ReturnType.FullName);
                for (int i = 0; i < num_arg; i++) {
                    var param = method.Method.Parameters [i];
                    if (i > 0)
                        comment.Append (", ");
                    comment.AppendFormat ("{0} {1}", param.ParameterType.FullName, param.Name);
                }
                comment.AppendLine (")");
                comment.AppendLine ("// ArgumentSemantic: {0} IsStatic: {1} Selector: '{2}' Signature: '{3}'", method.ArgumentSemantic, method.IsStatic, method.Selector, method.Signature);
            }

            // a couple of debug printfs
            if (trace) {
                StringBuilder args = new StringBuilder ();
                nslog_start.AppendFormat ("NSLog (@\"{0} (this: %@, sel: %@", name);
                for (int i = 0; i < num_arg; i++) {
                    var type = method.Method.Parameters [i].ParameterType;
                    bool isRef = type.IsByReference;
                    if (isRef)
                        type = type.GetElementType ();
                    var td = type.Resolve ();

                    nslog_start.AppendFormat (", {0}: ", method.Method.Parameters [i].Name);
                    args.Append (", ");
                    switch (type.FullName) {
                    case "System.Drawing.RectangleF":
                        if (isRef) {
                            nslog_start.Append ("%p : %@");
            #if MMP
                            args.AppendFormat ("p{0}, p{0} ? NSStringFromRect (*p{0}) : @\"NULL\"", i);
            #else
                            args.AppendFormat ("p{0}, p{0} ? NSStringFromCGRect (*p{0}) : @\"NULL\"", i);
            #endif
                        } else {
                            nslog_start.Append ("%@");
            #if MMP
                            args.AppendFormat ("NSStringFromRect (p{0})", i);
            #else
                            args.AppendFormat ("NSStringFromCGRect (p{0})", i);
            #endif
                        }
                        break;
                    case "System.Drawing.PointF":
                        if (isRef) {
                            nslog_start.Append ("%p: %@");
            #if MMP
                            args.AppendFormat ("p{0}, p{0} ? NSStringFromPoint (*p{0}) : @\"NULL\"", i);
            #else
                            args.AppendFormat ("p{0}, p{0} ? NSStringFromCGPoint (*p{0}) : @\"NULL\"", i);
            #endif
                        } else {
                            nslog_start.Append ("%@");
            #if MMP
                            args.AppendFormat ("NSStringFromPoint (p{0})", i);
            #else
                            args.AppendFormat ("NSStringFromCGPoint (p{0})", i);
            #endif
                        }
                        break;
                    default:
                        bool unknown;
                        var spec = GetPrintfFormatSpecifier (td, out unknown);
                        if (unknown) {
                            nslog_start.AppendFormat ("%{0}", spec);
                            args.AppendFormat ("&p{0}", i);
                        } else if (isRef) {
                            nslog_start.AppendFormat ("%p *= %{0}", spec);
                            args.AppendFormat ("p{0}, *p{0}", i);
                        } else {
                            nslog_start.AppendFormat ("%{0}", spec);
                            args.AppendFormat ("p{0}", i);
                        }
                        break;
                    }
                }

                string ret_arg = string.Empty;
                nslog_end.Append (nslog_start.ToString ());
                if (!isVoid) {
                    bool unknown;
                    var spec = GetPrintfFormatSpecifier (method.Method.ReturnType.Resolve (), out unknown);
                    if (!unknown) {
                        nslog_end.Append (" ret: %");
                        nslog_end.Append (spec);
                        ret_arg = ", res";
                    }
                }
                nslog_end.Append (") END\", self, NSStringFromSelector (_cmd)");
                nslog_end.Append (args.ToString ());
                nslog_end.Append (ret_arg);
                nslog_end.AppendLine (");");

                nslog_start.Append (") START\", self, NSStringFromSelector (_cmd)");
                nslog_start.Append (args.ToString ());
                nslog_start.AppendLine (");");
            }

            // prepare the parameters
            var baseMethod = GetBaseMethodInTypeHierarchy (method.Method);
            for (int i = 0; i < num_arg; i++) {
                var param = method.Method.Parameters [i];
                var paramBase = baseMethod.Parameters [i];
                var type = method.Parameters [i];
                var objctype = ToObjCParameterType (type, descriptiveMethodName, exceptions, method.Method);
                var original_objctype = objctype;
                var isRef = type.IsByReference;
                var isOut = param.IsOut || paramBase.IsOut;
                var isArray = type is ArrayType;
                var isNativeEnum = false;
                var td = type.Resolve ();
                var isVariadic = i + 1 == num_arg && method.IsVariadic;

                if (isRef) {
                    type = type.GetElementType ();
                    td = type.Resolve ();
                    original_objctype = ToObjCParameterType (type, descriptiveMethodName, exceptions,  method.Method);
                    objctype = ToObjCParameterType (type, descriptiveMethodName, exceptions, method.Method) + "*";
                } else if (td.IsEnum) {
                    type = SharedStatic.GetEnumUnderlyingType (td);
                    isNativeEnum = IsDualBuild && SharedStatic.HasAttribute (td, ObjCRuntime, StringConstants.NativeAttribute);
                    td = type.Resolve ();
                }

                switch (type.FullName) {
                case "System.Int64":
                case "System.UInt64":
                    // We already show MT4145 if the underlying enum type isn't a long or ulong
                    if (isNativeEnum) {
                        string tp;
                        string ntp;
                        if (type.FullName == "System.UInt64") {
                            tp = "unsigned long long";
                            ntp = "NSUInteger";
                        } else {
                            tp = "long long";
                            ntp = "NSInteger";
                        }

                        if (isRef || isOut) {
                            setup_call_stack.AppendLine ("{1} nativeEnum{0} = 0;", i, tp);
                            setup_call_stack.AppendLine ("arg_ptrs [{0}] = &nativeEnum{0};", i);
                            copyback.AppendLine ("*p{0} = ({1}) nativeEnum{0};", ntp);
                        } else {
                            setup_call_stack.AppendLine ("{1} nativeEnum{0} = p{0};", i, tp);
                            setup_call_stack.AppendLine ("arg_ptrs [{0}] = &nativeEnum{0};", i);
                        }
                        break;
                    }
                    goto case "System.SByte";
                case "System.SByte":
                case "System.Byte":
                case "System.Char":
                case "System.Int16":
                case "System.UInt16":
                case "System.Int32":
                case "System.UInt32":
                case "System.Single":
                case "System.Double":
                case "System.Boolean":
                    if (isRef || isOut) {
                        // The isOut semantics isn't quite correct here: we pass the actual input value to managed code.
                        // In theory we should create a temp location and then use a writeback when done instead.
                        // This should be safe though, since managed code (at least C#) can't actually observe the value.
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = p{0};", i);
                    } else {
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = &p{0};", i);
                    }
                    break;
                case "System.IntPtr":
                    if (isVariadic) {
                        setup_call_stack.AppendLine ("va_list a{0};", i);
                        setup_call_stack.AppendLine ("va_start (a{0}, p{1});", i, i - 1);
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = &a{0};", i);
                        copyback.AppendLine ("va_end (a{0});", i);
                    } else if (isOut) {
                        setup_call_stack.AppendLine ("{1} a{0} = 0;", i, objctype);
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = &a{0};", i);
                        copyback.AppendLine ("*p{0} = a{0};", i);
                    } else if (isRef) {
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = p{0};", i);
                    } else {
                        setup_call_stack.AppendLine ("{1} a{0} = p{0};", i, original_objctype);
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = &a{0};", i);
                    }
                    break;
                case "ObjCRuntime.Selector":
                case CompatNamespace + ".ObjCRuntime.Selector":
                    if (isRef) {
                        if (isOut) {
                            setup_call_stack.AppendLine ("void *a{0} = NULL;", i);
                        } else {
                            setup_call_stack.AppendLine ("void *a{0} = *p{0} ? xamarin_get_selector (*p{0}) : NULL;", i);
                        }
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = &a{0};", i);
                        copyback.AppendLine ("*p{0} = a{0};", i);
                    } else {
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = p{0} ? xamarin_get_selector (p{0}) : NULL;", i);
                    }
                    break;
                case "ObjCRuntime.Class":
                case CompatNamespace + ".ObjCRuntime.Class":
                    if (isRef) {
                        if (isOut) {
                            setup_call_stack.AppendLine ("void *a{0} = NULL;", i);
                        } else {
                            setup_call_stack.AppendLine ("void *a{0} = *p{0} ? xamarin_get_class (*p{0}) : NULL;", i);
                        }
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = &a{0};", i);
                        copyback.AppendLine ("*p{0} = a{0};", i);
                    } else {
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = p{0} ?  xamarin_get_class (p{0}) : NULL;", i);
                    }
                    break;
                case "System.String":
                    // This should always be an NSString and never char*
                    if (isRef) {
                        if (isOut) {
                            setup_call_stack.AppendLine ("MonoString *a{0} = NULL;", i);
                        } else {
                            setup_call_stack.AppendLine ("MonoString *a{0} = *p{0} ? mono_string_new (mono_domain_get (), [(*p{0}) UTF8String]) : NULL;", i);
                        }
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = &a{0};", i);
                        copyback.AppendLine ("char *str{0} = mono_string_to_utf8 (a{0});", i);
                        copyback.AppendLine ("*p{0} = [[NSString alloc] initWithUTF8String:str{0}];", i);
                        copyback.AppendLine ("[*p{0} autorelease];", i);
                        copyback.AppendLine ("mono_free (str{0});", i);
                    } else {
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = p{0} ? mono_string_new (mono_domain_get (), [p{0} UTF8String]) : NULL;", i);
                    }
                    break;
                default:
                    if (isArray) {
                        var elementType = ((ArrayType)type).ElementType;
                        var isNativeObject = false;

                        setup_call_stack.AppendLine ("if (p{0}) {{", i);
                        setup_call_stack.AppendLine ("NSArray *arr = (NSArray *) p{0};", i);
                        if (Driver.EnableDebug)
                            setup_call_stack.AppendLine ("xamarin_check_objc_type (p{0}, [NSArray class], _cmd, self, {0}, managed_method);", i);
                        setup_call_stack.AppendLine ("MonoClass *e_class;");
                        setup_call_stack.AppendLine ("MonoArray *marr;");
                        setup_call_stack.AppendLine ("MonoType *p;");
                        setup_call_stack.AppendLine ("int j;", i);
                        setup_call_stack.AppendLine ("p = xamarin_get_parameter_type (managed_method, {0});", i);
                        setup_call_stack.AppendLine ("e_class = mono_class_get_element_class (mono_class_from_mono_type (p));");
                        setup_call_stack.AppendLine ("marr = mono_array_new (mono_domain_get (), e_class, [arr count]);", i);
                        setup_call_stack.AppendLine ("for (j = 0; j < [arr count]; j++) {{", i);
                        if (elementType.FullName == "System.String") {
                            setup_call_stack.AppendLine ("NSString *sv = (NSString *) [arr objectAtIndex: j];", i);
                            setup_call_stack.AppendLine ("mono_array_set (marr, MonoString *, j, mono_string_new (mono_domain_get (), [sv UTF8String]));", i);
                        } else if (IsNSObject (elementType) || (elementType.Namespace == "System" && elementType.Name == "Object") || (isNativeObject = SharedStatic.IsNativeObject (elementType))) {
                            setup_call_stack.AppendLine ("NSObject *nobj = [arr objectAtIndex: j];");
                            setup_call_stack.AppendLine ("MonoObject *mobj{0} = NULL;", i);
                            setup_call_stack.AppendLine ("if (nobj) {");
                            if (isNativeObject) {
                                TypeDefinition nativeObjType = elementType.Resolve ();

                                if (nativeObjType.IsInterface) {
                                    var wrapper_type = GetProtocolAttributeWrapperType (nativeObjType);
                                    if (wrapper_type == null)
                                        throw ErrorHelper.CreateError (4125, "The registrar found an invalid type '{0}' in signature for method '{1}': " +
                                            "The interface must have a Protocol attribute specifying its wrapper type.",
                                            td.FullName, descriptiveMethodName);

                                    nativeObjType = wrapper_type.Resolve ();
                                }

                                // verify that the type has a ctor with two parameters
                                if (!HasIntPtrBoolCtor (nativeObjType))
                                    throw ErrorHelper.CreateError (4103,
                                        "The registrar found an invalid type `{0}` in signature for method `{1}`: " +
                                        "The type implements INativeObject, but does not have a constructor that takes " +
                                        "two (IntPtr, bool) arguments.", nativeObjType.FullName, descriptiveMethodName);

                                if (nativeObjType.IsInterface) {
                                    setup_call_stack.AppendLine ("mobj{0} = xamarin_get_inative_object_static (nobj, false, \"{1}\", \"{2}\");", i, GetAssemblyQualifiedName (nativeObjType), GetAssemblyQualifiedName (elementType));
                                } else {
                                    // find the MonoClass for this parameter
                                    setup_call_stack.AppendLine ("MonoType *type{0};", i);
                                    setup_call_stack.AppendLine ("type{0} = xamarin_get_parameter_type (managed_method, {0});", i);
                                    setup_call_stack.AppendLine ("mobj{0} = xamarin_get_inative_object_dynamic (nobj, false, mono_type_get_object (mono_domain_get (), mono_class_get_type (e_class)));", i);
                                }
                            } else {
                                setup_call_stack.AppendLine ("mobj{0} = xamarin_get_managed_object_for_ptr_fast (nobj);", i);
                            }
                            if (Driver.EnableDebug) {
                                setup_call_stack.AppendLine ("xamarin_verify_parameter (mobj{0}, _cmd, self, nobj, {0}, e_class, managed_method);", i);
                            }
                            setup_call_stack.AppendLine ("}");
                            setup_call_stack.AppendLine ("mono_array_set (marr, MonoObject *, j, mobj{0});", i);
                        } else {
                            throw ErrorHelper.CreateError (4111, method.Method, "The registrar cannot build a signature for type `{0}' in method `{1}`.", type.FullName, descriptiveMethodName);
                        }
                        setup_call_stack.AppendLine ("}");
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = marr;", i);
                        setup_call_stack.AppendLine ("} else {");
                        setup_call_stack.AppendLine ("arg_ptrs [{0}] = NULL;", i);
                        setup_call_stack.AppendLine ("}");
                    } else if (IsNSObject (type)) {
                        if (isRef) {
                            setup_call_stack.AppendLine ("MonoObject *mobj{0} = NULL;", i);
                            if (!isOut) {
                                setup_call_stack.AppendLine ("NSObject *nsobj{0} = *(NSObject **) p{0};", i);
                                setup_call_stack.AppendLine ("if (nsobj{0}) {{", i);
                                setup_call_stack.AppendLine ("MonoType *paramtype{0} = xamarin_get_parameter_type (managed_method, {0});", i);
                                setup_call_stack.AppendLine ("mobj{0} = xamarin_get_nsobject_with_type_for_ptr (nsobj{0}, false, paramtype{0});", i);
                                if (Driver.EnableDebug) {
                                    setup_call_stack.AppendLine ("xamarin_verify_parameter (mobj{0}, _cmd, self, nsobj{0}, {0}, mono_class_from_mono_type (paramtype{0}), managed_method);", i);
                                }
                                setup_call_stack.AppendLine ("}");
                            }

                            // argument semantics?
                            setup_call_stack.AppendLine ("arg_ptrs [{0}] = (int *) &mobj{0};", i);
                            copyback.AppendLine ("void * handle{0} = NULL;", i);
                            copyback.AppendLine ("if (mobj{0} != NULL)", i);
                            copyback.AppendLine ("handle{0} = xamarin_get_nsobject_handle (mobj{0});", i);
                            copyback.AppendLine ("*p{0} = (id) handle{0};", i);
                        } else {
                            setup_call_stack.AppendLine ("NSObject *nsobj{0} = (NSObject *) p{0};", i);
                            if (method.ArgumentSemantic == ArgumentSemantic.Copy) {
                                setup_call_stack.AppendLine ("nsobj{0} = [nsobj{0} copy];", i);
                                setup_call_stack.AppendLine ("[nsobj{0} autorelease];", i);
                            }
                            setup_call_stack.AppendLine ("MonoObject *mobj{0} = NULL;", i);
                            setup_call_stack.AppendLine ("int32_t created{0} = false;", i);
                            setup_call_stack.AppendLine ("if (nsobj{0}) {{", i);
                            setup_call_stack.AppendLine ("MonoType *paramtype{0} = xamarin_get_parameter_type (managed_method, {0});", i);
                            setup_call_stack.AppendLine ("mobj{0} = xamarin_get_nsobject_with_type_for_ptr_created (nsobj{0}, false, paramtype{0}, &created{0});", i);
                            if (Driver.EnableDebug) {
                                setup_call_stack.AppendLine ("xamarin_verify_parameter (mobj{0}, _cmd, self, nsobj{0}, {0}, mono_class_from_mono_type (paramtype{0}), managed_method);", i);
                            }
                            setup_call_stack.AppendLine ("}");
                            setup_call_stack.AppendLine ("arg_ptrs [{0}] = mobj{0};", i);

                            if (SharedStatic.HasAttribute (paramBase, ObjCRuntime, StringConstants.TransientAttribute)) {
                                copyback.AppendLine ("if (created{0})", i);
                                copyback.Indentation++;
                                copyback.AppendLine ("xamarin_dispose (mobj{0});", i);
                                copyback.Indentation--;

                            }
                        }
                    } else if (SharedStatic.IsNativeObject (td)) {
                        TypeDefinition nativeObjType = td;

                        if (td.IsInterface) {
                            var wrapper_type = GetProtocolAttributeWrapperType (td);
                            if (wrapper_type == null)
                                throw ErrorHelper.CreateError (4125, "The registrar found an invalid type '{0}' in signature for method '{1}': " +
                                                              "The interface must have a Protocol attribute specifying its wrapper type.",
                                                              td.FullName, descriptiveMethodName);

                            nativeObjType = wrapper_type.Resolve ();
                        }

                        // verify that the type has a ctor with two parameters
                        if (!HasIntPtrBoolCtor (nativeObjType))
                            throw ErrorHelper.CreateError (4103,
                                                          "The registrar found an invalid type `{0}` in signature for method `{1}`: " +
                                "The type implements INativeObject, but does not have a constructor that takes " +
                                "two (IntPtr, bool) arguments.", nativeObjType.FullName, descriptiveMethodName);

                        if (!td.IsInterface) {
                            // find the MonoClass for this parameter
                            setup_call_stack.AppendLine ("MonoType *type{0};", i);
                            setup_call_stack.AppendLine ("type{0} = xamarin_get_parameter_type (managed_method, {0});", i);
                        }
                        if (isRef) {
                            setup_call_stack.AppendLine ("MonoObject *inobj{0};", i);
                            if (isOut) {
                                setup_call_stack.AppendLine ("inobj{0} = NULL;", i);
                            } else if (td.IsInterface) {
                                setup_call_stack.AppendLine ("inobj{0} = xamarin_get_inative_object_static (*p{0}, false, \"{1}\", \"{2}\");", i, GetAssemblyQualifiedName (nativeObjType), GetAssemblyQualifiedName (td));
                            } else {
                                setup_call_stack.AppendLine ("inobj{0} = xamarin_get_inative_object_dynamic (*p{0}, false, mono_type_get_object (mono_domain_get (), type{0}));", i);
                            }
                            setup_call_stack.AppendLine ("arg_ptrs [{0}] = &inobj{0};", i);
                            copyback.AppendLine ("id handle{0} = nil;", i);
                            copyback.AppendLine ("if (inobj{0} != NULL)", i);
                            copyback.AppendLine ("handle{0} = xamarin_get_handle_for_inativeobject (inobj{0});", i);
                            copyback.AppendLine ("*p{0} = (id) handle{0};", i);
                        } else {
                            if (td.IsInterface) {
                                setup_call_stack.AppendLine ("arg_ptrs [{0}] = xamarin_get_inative_object_static (p{0}, false, \"{1}\", \"{2}\");", i, GetAssemblyQualifiedName (nativeObjType), GetAssemblyQualifiedName (td));
                            } else {
                                setup_call_stack.AppendLine ("arg_ptrs [{0}] = xamarin_get_inative_object_dynamic (p{0}, false, mono_type_get_object (mono_domain_get (), type{0}));", i);
                            }
                        }
                    } else if (type.IsValueType) {
                        if (isRef || isOut) {
                            // The isOut semantics isn't quite correct here: we pass the actual input value to managed code.
                            // In theory we should create a temp location and then use a writeback when done instead.
                            // This should be safe though, since managed code (at least C#) can't actually observe the value.
                            setup_call_stack.AppendLine ("arg_ptrs [{0}] = p{0};", i);
                        } else {
                            setup_call_stack.AppendLine ("arg_ptrs [{0}] = &p{0};", i);
                        }
                    } else if (td.BaseType.FullName == "System.MulticastDelegate") {
                        if (isRef) {
                            throw ErrorHelper.CreateError (4110,
                                                          "The registrar cannot marshal the out parameter of type `{0}` in signature for method `{1}`.",
                                                          type.FullName, descriptiveMethodName);
                        } else {
                            // Bug #4858 (also related: #4718)
                            setup_call_stack.AppendLine ("if (p{0}) {{", i);
                            setup_call_stack.AppendLine ("arg_ptrs [{0}] = (void *) xamarin_get_delegate_for_block_parameter (managed_method, {0}, p{0});", i);
                            setup_call_stack.AppendLine ("} else {");
                            setup_call_stack.AppendLine ("arg_ptrs [{0}] = NULL;", i);
                            setup_call_stack.AppendLine ("}");
                        }
                    } else {
                        throw ErrorHelper.CreateError (4105,
                                                      "The registrar cannot marshal the parameter of type `{0}` in signature for method `{1}`.",
                                                      type.FullName, descriptiveMethodName);
                    }
                    break;
                }
            }

            // the actual invoke
            if (isCtor) {
                invoke.AppendLine ("mthis = mono_object_new (mono_domain_get (), mono_method_get_class (managed_method));", counter);
                invoke.AppendLine ("uint8_t flags = NSObjectFlagsNativeRef;");
                invoke.AppendLine ("xamarin_set_nsobject_handle (mthis, self);");
                invoke.AppendLine ("xamarin_set_nsobject_flags (mthis, flags);");
            }

            if (!isVoid)
                invoke.AppendFormat ("{0} retval = ", "MonoObject *");

            invoke.AppendLine ("mono_runtime_invoke (managed_method, {0}, arg_ptrs, NULL);", isStatic ? "NULL" : "mthis");

            if (isCtor)
                invoke.AppendLine ("xamarin_create_managed_ref (self, mthis, true);");

            // prepare the return value
            if (!isVoid) {
                setup_return.AppendLine ("{0} res;", rettype);
                var isArray = returntype is ArrayType;
                var type = returntype.Resolve () ?? returntype;
                var retain = method.RetainReturnValue;

                if (returntype.IsValueType) {
                    setup_return.AppendLine ("res = *({0} *) mono_object_unbox ((MonoObject *) retval);", rettype);
                } else if (isArray) {
                    var elementType = ((ArrayType) returntype).ElementType;

                    setup_return.AppendLine ("if (retval) {");
                    setup_return.AppendLine ("int length = mono_array_length ((MonoArray *) retval);");
                    setup_return.AppendLine ("int i;");
                    setup_return.AppendLine ("id *buf = (id *) malloc (sizeof (void *) * length);");
                    setup_return.AppendLine ("for (i = 0; i < length; i++) {");
                    setup_return.AppendLine ("MonoObject *value = mono_array_get ((MonoArray *) retval, MonoObject *, i);");

                    if (elementType.FullName == "System.String") {
                        setup_return.AppendLine ("char *str = mono_string_to_utf8 ((MonoString *) value);");
                        setup_return.AppendLine ("NSString *sv = [[NSString alloc] initWithUTF8String:str];");
                        setup_return.AppendLine ("[sv autorelease];");
                        setup_return.AppendLine ("mono_free (str);");
                        setup_return.AppendLine ("buf [i] = sv;");
                    } else if (IsNSObject (elementType)) {
                        setup_return.AppendLine ("buf [i] = xamarin_get_nsobject_handle ((MonoObject *) value);");
                    } else if (IsINativeObject (elementType)) {
                        setup_return.AppendLine ("buf [i] = xamarin_get_handle_for_inativeobject ((MonoObject *) value);");
                    } else {
                        throw ErrorHelper.CreateError (4111, method.Method, "The registrar cannot build a signature for type `{0}' in method `{1}`.", returntype.FullName, descriptiveMethodName);
                    }

                    setup_return.AppendLine ("}");

                    setup_return.AppendLine ("NSArray *arr = [[NSArray alloc] initWithObjects: buf count: length];");
                    setup_return.AppendLine ("free (buf);");
                    if (!retain)
                        setup_return.AppendLine ("[arr autorelease];");
                    setup_return.AppendLine ("res = arr;");
                    setup_return.AppendLine ("} else {");
                    setup_return.AppendLine ("res = NULL;");
                    setup_return.AppendLine ("}");
                    setup_return.AppendLine ("xamarin_framework_peer_lock ();");
                    setup_return.AppendLine ("mt_dummy_use (retval);");
                    setup_return.AppendLine ("xamarin_framework_peer_unlock ();");
                } else {
                    setup_return.AppendLine ("if (!retval) {");
                    setup_return.AppendLine ("res = NULL;");
                    setup_return.AppendLine ("} else {");

                    if (IsNSObject (type)) {
                        setup_return.AppendLine ("id retobj;");
                        setup_return.AppendLine ("retobj = xamarin_get_nsobject_handle (retval);");
                        setup_return.AppendLine ("xamarin_framework_peer_lock ();");
                        setup_return.AppendLine ("[retobj retain];");
                        setup_return.AppendLine ("xamarin_framework_peer_unlock ();");
                        if (!retain)
                            setup_return.AppendLine ("[retobj autorelease];");
                        setup_return.AppendLine ("mt_dummy_use (retval);");
                        setup_return.AppendLine ("res = retobj;");
                    } else if (type.IsPlatformType ("ObjCRuntime", "Selector")) {
                        setup_return.AppendLine ("res = xamarin_get_selector_handle (retval);");
                    } else if (type.IsPlatformType ("ObjCRuntime", "Class")) {
                        setup_return.AppendLine ("res = xamarin_get_class_handle (retval);");
                    } else if (SharedStatic.IsNativeObject (type)) {
                        setup_return.AppendLine ("{0} retobj;", rettype);
                        setup_return.AppendLine ("retobj = xamarin_get_handle_for_inativeobject ((MonoObject *) retval);");
                        setup_return.AppendLine ("xamarin_framework_peer_lock ();");
                        setup_return.AppendLine ("[retobj retain];");
                        setup_return.AppendLine ("xamarin_framework_peer_unlock ();");
                        if (!retain)
                            setup_return.AppendLine ("[retobj autorelease];");
                        setup_return.AppendLine ("mt_dummy_use (retval);");
                        setup_return.AppendLine ("res = retobj;");
                    } else if (type.FullName == "System.String") {
                        // This should always be an NSString and never char*
                        setup_return.AppendLine ("char *str = mono_string_to_utf8 ((MonoString *) retval);");
                        setup_return.AppendLine ("NSString *nsstr = [[NSString alloc] initWithUTF8String:str];");
                        if (!retain)
                            setup_return.AppendLine ("[nsstr autorelease];");
                        setup_return.AppendLine ("mono_free (str);");
                        setup_return.AppendLine ("res = nsstr;");
                    } else if (SharedStatic.IsDelegate (type.Resolve ())) {
                        setup_return.AppendLine ("res = xamarin_get_block_for_delegate (managed_method, retval);");
                    } else {
                        throw ErrorHelper.CreateError (4104,
                            "The registrar cannot marshal the return value for type `{0}` in signature for method `{1}`.",
                            returntype.FullName, descriptiveMethodName);
                    }

                    setup_return.AppendLine ("}");
                }
            }

            // Write out everything
            if (merge_bodies) {
                body.WriteLine ("MonoMethod *managed_method = *managed_method_ptr;");
            } else {
                if (!isGeneric)
                    body.Write ("static ");
                body.WriteLine ("MonoMethod *managed_method = NULL;");
            }

            if (comment.Length > 0)
                body.WriteLine (comment.ToString ());
            if (isInstanceCategory)
                body.WriteLine ("id p0 = self;");
            body.WriteLine ("void *arg_ptrs [{0}];", num_arg);
            if (!isStatic || isInstanceCategory)
                body.WriteLine ("MonoObject *mthis;");

            body.WriteLine ("if (mono_domain_get () == NULL)");
            body.Indent ();
            body.WriteLine ("mono_jit_thread_attach (NULL);");
            body.Unindent ();

            if (isCtor) {
                body.WriteLine ("if (xamarin_try_get_nsobject (self)) {");
                body.WriteLine ("*call_super = true;");
                body.WriteLine ("return self;");
                body.WriteLine ("}");
            }

            if ((!isStatic || isInstanceCategory) && !isCtor) {
                body.WriteLine ("mthis = NULL;");
                body.WriteLine ("if (self) {");
                body.WriteLine ("mthis = xamarin_get_managed_object_for_ptr_fast (self);");
                body.WriteLine ("}");
            }

            // no locking should be required here, it doesn't matter if we overwrite the field (it'll be the same value).
            body.WriteLine ("if (!managed_method) {");
            if (num_arg > 0) {
                body.Write ("const char *paramptr[{0}] = {{ ", num_arg);
                for (int i = 0; i < num_arg; i++) {
                    string paramtype;
                    if (isGeneric) {
                        paramtype = GetAssemblyQualifiedName (method.Method.Parameters [i].ParameterType);
                    } else {
                        paramtype = GetAssemblyQualifiedName (method.Parameters [i]);
                    }

                    if (merge_bodies) {
                        body.Write ("r{0}", arguments.Count);
                        arguments.Add (paramtype);
                    } else {
                        body.Write ("\"{0}\"", paramtype);
                    }
                    if (i < num_arg - 1)
                        body.Write (", ");
                }
                body.WriteLine (" };");
            }

            body.Write ("managed_method = ");
            if (isGeneric)
                body.Write ("xamarin_get_reflection_method_method (xamarin_get_generic_method_direct (mthis, ");
            else
                body.Write ("xamarin_get_reflection_method_method (xamarin_get_method_direct(");

            if (merge_bodies) {
                body.WriteLine ("r{2}, r{3}, {0}, {1}));",
                    num_arg, num_arg > 0 ? "paramptr" : "NULL",
                    arguments.Count, arguments.Count + 1);
                arguments.Add (GetAssemblyQualifiedName (method.Method.DeclaringType));
                arguments.Add (method.Method.Name);
            } else {
                body.WriteLine ("\"{0}\", \"{1}\", {2}, {3}));",
                    GetAssemblyQualifiedName (method.Method.DeclaringType),
                    method.Method.Name, num_arg, num_arg > 0 ? "paramptr" : "NULL");
            }
            if (merge_bodies)
                body.WriteLine ("*managed_method_ptr = managed_method;");
            body.WriteLine ("}");

            if (!isStatic && !isInstanceCategory && !isCtor)
                body.WriteLine ("xamarin_check_for_gced_object (mthis, _cmd, self, managed_method);");

            if (trace)
                body.AppendLine (nslog_start);

            body.AppendLine (setup_call_stack);
            body.AppendLine (invoke);
            body.AppendLine (copyback);
            body.AppendLine (setup_return);

            if (trace )
                body.AppendLine (nslog_end);

            if (isCtor) {
                body.WriteLine ("return self;");
            } else if (isVoid) {
                body.WriteLine ("return;");
            } else {
                body.WriteLine ("return res;");
            }
            body.WriteLine ("}");

            /* We merge duplicated bodies (based on the signature of the method and the entire body) */

            var objc_signature = new StringBuilder ().Append (rettype).Append (":");
            for (int i = 0; i < method.Method.Parameters.Count; i++)
                objc_signature.Append (ToObjCParameterType (method.Method.Parameters [i].ParameterType, descriptiveMethodName, exceptions, method.Method)).Append (":");

            Body existing;
            Body b = new Body () {
                Code = body.ToString (),
                Signature = objc_signature.ToString (),
            };

            if (merge_bodies && bodies.TryGetValue (b, out existing)) {
                /* We already have an identical trampoline, use it instead */
                b = existing;
            } else {
                /* Need to create a new trampoline */
                if (merge_bodies)
                    bodies [b] = b;
                b.Name = "native_to_managed_trampoline_" + bodies.Count.ToString ();

                if (merge_bodies) {
                    methods.Append ("static ");
                    methods.Append (rettype).Append (" ").Append (b.Name).Append (" (id self, SEL _cmd, MonoMethod **managed_method_ptr");
                    for (int i = (isInstanceCategory ? 1 : 0); i < method.Method.Parameters.Count; i++) {
                        methods.Append (", ").Append (ToObjCParameterType (method.Method.Parameters [i].ParameterType, descriptiveMethodName, exceptions, method.Method));
                        methods.Append (" ").Append ("p").Append (i.ToString ());
                    }
                    for (int i = 0; i < arguments.Count; i++)
                        methods.Append (", const char *").Append ("r").Append (i.ToString ());
                    if (isCtor)
                        methods.Append (", bool* call_super");
                    methods.AppendLine (")");
                    methods.AppendLine (body);
                    methods.AppendLine ();
                }
            }
            b.Count++;

            sb.WriteLine ();
            sb.WriteLine (GetObjCSignature (method, exceptions));
            if (merge_bodies) {
                sb.WriteLine ("{");
                if (!isGeneric)
                    sb.Write ("static ");
                sb.WriteLine ("MonoMethod *managed_method = NULL;");
                if (isCtor) {
                    sb.WriteLine ("bool call_super = false;");
                    sb.Write ("id rv = ");
                } else if (!isVoid) {
                    sb.Write ("return ");
                }
                sb.Write (b.Name);
                sb.Write (" (self, _cmd, &managed_method");
                var paramCount = method.Method.Parameters.Count;
                if (isInstanceCategory)
                    paramCount--;
                for (int i = 0; i < paramCount; i++)
                    sb.Write (", p{0}", i);
                for (int i = 0; i < arguments.Count; i++)
                    sb.Write (", \"").Write (arguments [i]).Write ("\"");
                if (isCtor)
                    sb.Write (", &call_super");
                sb.WriteLine (");");
                if (isCtor) {
                    sb.WriteLine ("if (call_super)");
                    sb.Indent ();
                    sb.Write ("rv = [super");
                    var split = method.Selector.Split (':');
                    if (split.Length == 1) {
                        sb.Append (" ");
                        sb.Append (split [0]);
                    } else {
                        for (int i = 0; i < split.Length - 1; i++) {
                            sb.Append (" ");
                            sb.Append (split [i]);
                            sb.Append (":");
                            sb.AppendFormat ("p{0}", i);
                        }
                    }
                    sb.WriteLine ("];");
                    sb.Unindent ();
                    sb.WriteLine ("return rv;");
                }
                sb.WriteLine ("}");
            } else {
                sb.WriteLine (body);
            }
        }
        void Specialize(AutoIndentStringBuilder sb)
        {
            List<Exception> exceptions = new List<Exception> ();
            List<ObjCMember> skip = new List<ObjCMember> ();

            var map = new AutoIndentStringBuilder (1);
            var map_init = new AutoIndentStringBuilder ();

            var i = 0;

            map.AppendLine ("static MTClassMap __xamarin_class_map [] = {");
            if (string.IsNullOrEmpty (single_assembly)) {
                map_init.AppendLine ("void xamarin_create_classes () {");
            } else {
                map_init.AppendLine ("void xamarin_create_classes_{0} () {{", single_assembly.Replace ('.', '_').Replace ('-', '_'));
            }

            // Select the types that needs to be registered.
            var allTypes = new List<ObjCType> ();
            foreach (var @class in Types.Values) {
                if (!string.IsNullOrEmpty (single_assembly) && single_assembly != @class.Type.Module.Assembly.Name.Name)
                    continue;

            #if !MONOMAC
                var isPlatformType = IsPlatformType (@class.Type);

                if (isPlatformType && IsSimulatorOrDesktop && IsMetalType (@class))
                    continue; // Metal isn't supported in the simulator.
            #endif

                if (@class.IsFakeProtocol)
                    continue;

                allTypes.Add (@class);
            }

            // Move all the custom types to the end of the list, respecting
            // existing order (so that a derived type always comes after
            // its base type; the Types.Values has that property, and we
            // need to keep it that way).

            var mappedEnd = allTypes.Count;
            var counter = 0;
            while (counter < mappedEnd) {
                if (!IsPlatformType (allTypes [counter].Type)) {
                    var t = allTypes [counter];
                    allTypes.RemoveAt (counter);
                    allTypes.Add (t);
                    mappedEnd--;
                } else {
                    counter++;
                }
            }

            var customTypeCount = 0;
            foreach (var @class in allTypes) {
                var isPlatformType = IsPlatformType (@class.Type);

                skip.Clear ();

                if ([email protected] && [email protected] && [email protected]) {
                    if (!isPlatformType)
                        customTypeCount++;

                    CheckNamespace (@class, exceptions);
                    map.AppendLine ("{{\"{0}\", \"{1}\", NULL }},", @class.ExportedName, GetAssemblyQualifiedName (@class.Type));

                    bool use_dynamic;

                    if (@class.Type.Resolve ().Module.Assembly.Name.Name == PlatformAssembly) {
                        // we don't need to use the static ref to prevent the linker from removing (otherwise unreferenced) code for monotouch.dll types.
                        use_dynamic = true;
                        // be smarter: we don't need to use dynamic refs for types available in the lowest version (target deployment) we building for.
                        // We do need to use dynamic class lookup when the following conditions are all true:
                        // * The class is not available in the target deployment version.
                        // * The class is not in a weakly linked framework (for instance if an existing framework introduces a new class, we don't
                        //   weakly link the framework because it already exists in the target deployment version - but since the class doesn't, we
                        //   must use dynamic class lookup to determine if it's available or not.
                    } else {
                        use_dynamic = false;
                    }

                    switch (@class.ExportedName) {
                    case "EKObject":
                        // EKObject's class is a private symbol, so we can't link with it...
                        use_dynamic = true;
                        break;
                    }

                    string get_class;
                    if (use_dynamic) {
                        get_class = string.Format ("objc_getClass (\"{0}\")", @class.ExportedName);
                    } else {
                        get_class = string.Format ("[{0} class]", EncodeNonAsciiCharacters (@class.ExportedName));
                    }

                    map_init.AppendLine ("__xamarin_class_map [{1}].handle = {0};", get_class, i++);
                }

                if (@class.IsWrapper && isPlatformType)
                    continue;

                if (@class.Methods == null && isPlatformType && [email protected] && [email protected])
                    continue;

                if (@class.IsModel)
                    continue;

                CheckNamespace (@class, exceptions);
                if (@class.BaseType != null)
                    CheckNamespace (@class.BaseType, exceptions);

                var class_name = EncodeNonAsciiCharacters (@class.ExportedName);
                var is_protocol = @class.IsProtocol;
                if (@class.IsCategory) {
                    sb.Write ("@interface {0} ({1})", EncodeNonAsciiCharacters (@class.BaseType.ExportedName), @class.CategoryName);
                } else if (is_protocol) {
                    sb.Write ("@protocol ").Write (EncodeNonAsciiCharacters (@class.ProtocolName));
                } else {
                    sb.Write ("@interface {0} : {1}", class_name, EncodeNonAsciiCharacters (@class.SuperType.ExportedName));
                }
                bool any_protocols = false;
                ObjCType tp = @class;
                while (tp != null && tp != tp.BaseType) {
                    if (tp.IsWrapper)
                        break; // no need to declare protocols for wrapper types, they do it already in their headers.
                    if (tp.Protocols != null) {
                        for (int p = 0; p < tp.Protocols.Length; p++) {
                            if (tp.Protocols [p].ProtocolName == "UIAppearance")
                                continue;
                            sb.Append (any_protocols ? ", " : "<");
                            any_protocols = true;
                            sb.Append (tp.Protocols [p].ProtocolName);
                            CheckNamespace (tp.Protocols [p], exceptions);
                        }
                    }
                    tp = tp.BaseType;
                }
                if (any_protocols)
                    sb.Append (">");

                if (is_protocol) {
                    sb.WriteLine ();
                } else {
                    sb.WriteLine (" {");

                    if (@class.Fields != null) {
                        foreach (var field in @class.Fields.Values) {
                            try {
                                switch (field.FieldType) {
                                case "@":
                                    sb.Write ("id ");
                                    break;
                                case "^v":
                                    sb.Write ("void *");
                                    break;
                                case "XamarinObject":
                                    sb.Write ("XamarinObject ");
                                    break;
                                default:
                                    throw ErrorHelper.CreateError (4120, "The registrar found an unknown field type '{0}' in field '{1}.{2}'. Please file a bug report at http://bugzilla.xamarin.com",
                                        field.FieldType, field.DeclaringType.Type.FullName, field.Name);
                                }
                                sb.Write (field.Name);
                                sb.WriteLine (";");
                            } catch (Exception ex) {
                                exceptions.Add (ex);
                            }
                        }
                    }
                    sb.WriteLine ("}");
                }

                sb.Indent ();
                if (@class.Properties != null) {
                    foreach (var property in @class.Properties) {
                        try {
                            if (is_protocol)
                                sb.Write (property.IsOptional ? "@optional " : "@required ");
                            sb.Write ("@property (nonatomic");
                            switch (property.ArgumentSemantic) {
                            case ArgumentSemantic.Copy:
                                sb.Write (", copy");
                                break;
                            case ArgumentSemantic.Retain:
                                sb.Write (", retain");
                                break;
                            case ArgumentSemantic.Assign:
                            case ArgumentSemantic.None:
                            default:
                                sb.Write (", assign");
                                break;
                            }
                            if (property.IsReadOnly)
                                sb.Write (", readonly");

                            if (property.Selector != null) {
                                if (property.GetterSelector != null && property.Selector != property.GetterSelector)
                                    sb.Write (", getter = ").Write (property.GetterSelector);
                                if (property.SetterSelector != null) {
                                    var setterSel = string.Format ("set{0}{1}:", char.ToUpperInvariant (property.Selector [0]), property.Selector.Substring (1));
                                    if (setterSel != property.SetterSelector)
                                        sb.Write (", setter = ").Write (property.SetterSelector);
                                }
                            }

                            sb.Write (") ");
                            try {
                                sb.Write (ToObjCParameterType (property.PropertyType, property.DeclaringType.Type.FullName, exceptions, property.Property));
                            } catch (ProductException mte) {
                                exceptions.Add (CreateException (4138, mte, property.Property, "The registrar cannot marshal the property type '{0}' of the property '{1}.{2}'.",
                                    GetTypeFullName (property.PropertyType), property.DeclaringType.Type.FullName, property.Name));
                            }
                            sb.Write (" ").Write (property.Selector);
                            sb.WriteLine (";");
                        } catch (Exception ex) {
                            exceptions.Add (ex);
                        }
                    }
                }

                if (@class.Methods != null) {
                    foreach (var method in @class.Methods) {
                        try {
                            if (is_protocol)
                                sb.Write (method.IsOptional ? "@optional " : "@required ");
                            sb.WriteLine ("{0};", GetObjCSignature (method, exceptions));
                        } catch (ProductException ex) {
                            skip.Add (method);
                            exceptions.Add (ex);
                        } catch (Exception ex) {
                            skip.Add (method);
                            exceptions.Add (ErrorHelper.CreateError (4114, ex, "Unexpected error in the registrar for the method '{0}.{1}' - Please file a bug report at http://bugzilla.xamarin.com", method.DeclaringType.Type.FullName, method.Method.Name));
                        }
                    }
                }
                sb.Unindent ();
                sb.WriteLine ("@end");

                if (!is_protocol && [email protected]) {
                    if (@class.IsCategory) {
                        sb.WriteLine ("@implementation {0} ({1})", EncodeNonAsciiCharacters (@class.BaseType.ExportedName), @class.CategoryName);
                    } else {
                        sb.WriteLine ("@implementation {0} {{ }} ", class_name);
                    }
                    sb.Indent ();
                    if (@class.Methods != null) {
                        foreach (var method in @class.Methods) {
                            if (skip.Contains (method))
                                continue;

                            try {
                                Specialize (sb, method, exceptions);
                            } catch (Exception ex) {
                                exceptions.Add (ex);
                            }
                        }
                    }
                    sb.Unindent ();
                    sb.WriteLine ("@end");
                }
                sb.WriteLine ();
            }

            map.AppendLine ("{ NULL, NULL, NULL },");
            map.AppendLine ("};");
            map.AppendLine ();

            map.AppendLine ("static const char *__xamarin_registration_assemblies []= {");
            int count = 0;
            var registered_assemblies = new List<string> ();
            if (string.IsNullOrEmpty (single_assembly)) {
                foreach (var assembly in GetAssemblies ())
                    registered_assemblies.Add (GetAssemblyName (assembly));
            } else {
                registered_assemblies.Add (single_assembly);
            }
            foreach (var assembly in registered_assemblies) {
                count++;
                if (count > 1)
                    map.AppendLine (", ");
                map.Append ("\"");
                map.Append (assembly);
                map.Append ("\"");
            }
            map.AppendLine ();
            map.AppendLine ("};");
            map.AppendLine ();

            map.AppendLine ("static struct MTRegistrationMap __xamarin_registration_map = {");
            map.AppendLine ("NULL,");
            map.AppendLine ("__xamarin_registration_assemblies,");
            map.AppendLine ("__xamarin_class_map,");
            map.AppendLine ("{0},", count);
            map.AppendLine ("{0},", i);
            map.AppendLine ("{0}", customTypeCount);
            map.AppendLine ("};");

            map_init.AppendLine ("xamarin_add_registration_map (&__xamarin_registration_map);");
            map_init.AppendLine ("}");

            sb.WriteLine (map.ToString ());
            sb.WriteLine (map_init.ToString ());

            if (exceptions.Count > 0)
                throw new AggregateException (exceptions);
        }
        string CheckStructure(TypeDefinition structure, string descriptiveMethodName, MemberReference inMember)
        {
            string n;
            StringBuilder name = new StringBuilder ();
            var body = new AutoIndentStringBuilder (1);
            int size = 0;

            ProcessStructure (name, body, structure, ref size, descriptiveMethodName, structure, inMember);

            n = "struct trampoline_struct_" + name.ToString ();
            if (!structures.Contains (n)) {
                structures.Add (n);
                declarations.WriteLine ("{0} {{\n{1}}};", n, body.ToString ());
            }

            return n;
        }
        public AutoIndentStringBuilder AppendLine(AutoIndentStringBuilder isb)
        {
            if (isb.Length > 0) {
                sb.Append (isb.ToString ());
                AppendLine ();
            }

            return this;
        }
 static void ProcessStructure(StringBuilder name, AutoIndentStringBuilder body, TypeDefinition structure, ref int size, string descriptiveMethodName, TypeDefinition root_structure, MemberReference inMember)
 {
     switch (structure.FullName) {
     case "System.Char":
         name.Append ('c');
         body.AppendLine ("char v{0};", size);
         size += 1;
         break;
     case "System.Boolean": // map managed 'bool' to ObjC BOOL
         name.Append ('B');
         body.AppendLine ("BOOL v{0};", size);
         size += 1;
         break;
     case "System.Byte":
     case "System.SByte":
         name.Append ('b');
         body.AppendLine ("char v{0};", size);
         size += 1;
         break;
     case "System.UInt16":
     case "System.Int16":
         name.Append ('s');
         body.AppendLine ("short v{0};", size);
         size += 2;
         break;
     case "System.UInt32":
     case "System.Int32":
         name.Append ('i');
         body.AppendLine ("int v{0};", size);
         size += 4;
         break;
     case "System.Int64":
     case "System.UInt64":
         name.Append ('l');
         body.AppendLine ("long long v{0};", size);
         size += 8;
         break;
     case "System.Single":
         name.Append ('f');
         body.AppendLine ("float v{0};", size);
         size += 4;
         break;
     case "System.Double":
         name.Append ('d');
         body.AppendLine ("double v{0};", size);
         size += 8;
         break;
     case "System.IntPtr":
         name.Append ('p');
         body.AppendLine ("void *v{0};", size);
         size += 4; // for now at least...
         break;
     default:
         bool found = false;
         foreach (FieldDefinition field in structure.Fields) {
             if (field.IsStatic)
                 continue;
             var fieldType = field.FieldType.Resolve ();
             if (fieldType == null)
                 throw ErrorHelper.CreateError (4111, inMember, "The registrar cannot build a signature for type `{0}' in method `{1}`.", structure.FullName, descriptiveMethodName);
             if (!fieldType.IsValueType)
                 throw ErrorHelper.CreateError (4161, inMember, "The registrar found an unsupported structure '{0}': All fields in a structure must also be structures (field '{1}' with type '{2}' is not a structure).", root_structure.FullName, field.Name, fieldType.FullName);
             found = true;
             ProcessStructure (name, body, fieldType, ref size, descriptiveMethodName, root_structure, inMember);
         }
         if (!found)
             throw ErrorHelper.CreateError (4111, inMember, "The registrar cannot build a signature for type `{0}' in method `{1}`.", structure.FullName, descriptiveMethodName);
         break;
     }
 }