// Returns a new instance of a proxy the derives from 'baseType' and implements 'interfaceType'
        internal static object CreateProxyInstance(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type baseType,
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type interfaceType)
        {
            Debug.Assert(baseType != null);
            Debug.Assert(interfaceType != null);

            GeneratedTypeInfo proxiedType = GetProxyType(baseType, interfaceType);

            return(Activator.CreateInstance(proxiedType.GeneratedType, new object[] { proxiedType.MethodInfos }) !);
        }
        private static GeneratedTypeInfo GenerateProxyType(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type baseType,
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type interfaceType)
        {
            // Parameter validation is deferred until the point we need to create the proxy.
            // This prevents unnecessary overhead revalidating cached proxy types.

            // The interface type must be an interface, not a class
            if (!interfaceType.IsInterface)
            {
                // "T" is the generic parameter seen via the public contract
                throw new ArgumentException(SR.Format(SR.InterfaceType_Must_Be_Interface, interfaceType.FullName), "T");
            }

            // The base type cannot be sealed because the proxy needs to subclass it.
            if (baseType.IsSealed)
            {
                // "TProxy" is the generic parameter seen via the public contract
                throw new ArgumentException(SR.Format(SR.BaseType_Cannot_Be_Sealed, baseType.FullName), "TProxy");
            }

            // The base type cannot be abstract
            if (baseType.IsAbstract)
            {
                throw new ArgumentException(SR.Format(SR.BaseType_Cannot_Be_Abstract, baseType.FullName), "TProxy");
            }

            // The base type must have a public default ctor
            if (baseType.GetConstructor(Type.EmptyTypes) == null)
            {
                throw new ArgumentException(SR.Format(SR.BaseType_Must_Have_Default_Ctor, baseType.FullName), "TProxy");
            }

            // Create a type that derives from 'baseType' provided by caller
            ProxyBuilder pb = s_proxyAssembly.CreateProxy("generatedProxy", baseType);

            foreach (Type t in interfaceType.GetInterfaces())
            {
                pb.AddInterfaceImpl(t);
            }

            pb.AddInterfaceImpl(interfaceType);

            GeneratedTypeInfo generatedProxyType = pb.CreateType();

            return(generatedProxyType);
        }
        private void GenerateType(IndentedStringBuilder writer, INamedTypeSymbol ownerType)
        {
            if (_typeMap.ContainsKey(ownerType))
            {
                return;
            }

            var ownerTypeName = GetGlobalQualifier(ownerType) + ownerType.ToString();

            var flattenedProperties =
                from property in ownerType.GetAllProperties()
                where !property.IsStatic &&
                !IsIgnoredType(property.ContainingSymbol as INamedTypeSymbol) &&
                !IsNonBindable(property) &&
                !IsOverride(property.GetMethod)
                select property;

            var properties =
                from property in ownerType.GetProperties()
                where !property.IsStatic &&
                !IsNonBindable(property) &&
                HasPublicGetter(property) &&
                !IsOverride(property.GetMethod)
                select property;

            var propertyDependencyProperties =
                from property in ownerType.GetProperties()
                where property.IsStatic &&
                Equals(property.Type, _dependencyPropertySymbol)
                select property.Name;

            var fieldDependencyProperties =
                from field in ownerType.GetFields()
                where field.IsStatic &&
                Equals(field.Type, _dependencyPropertySymbol)
                select field.Name;

            var dependencyProperties = fieldDependencyProperties
                                       .Concat(propertyDependencyProperties)
                                       .ToArray();

            properties = from prop in properties.Distinct(new PropertyNameEqualityComparer())
                         where !dependencyProperties.Contains(prop.Name + "Property")
                         select prop;

            properties = properties
                         .ToArray();

            var typeInfo = new GeneratedTypeInfo(
                index: _typeMap.Count,
                hasProperties: properties.Any() || dependencyProperties.Any()
                );

            _typeMap.Add(ownerType, typeInfo);

            var baseType = GetBaseType(ownerType);

            // Call the builders for the base types
            if (baseType != null)
            {
                GenerateType(writer, baseType);
            }

            writer.AppendLineInvariant("/// <summary>");
            writer.AppendLineInvariant("/// Builder for {0}", ownerType.GetFullName());
            writer.AppendLineInvariant("/// </summary>");
            writer.AppendLineInvariant("[System.Runtime.CompilerServices.CompilerGeneratedAttribute]");
            writer.AppendLineInvariant("[System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Maintainability\", \"CA1502:AvoidExcessiveComplexity\", Justification=\"Must be ignored even if generated code is checked.\")]");
            writer.AppendLineInvariant("[System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Maintainability\", \"CA1506:AvoidExcessiveClassCoupling\", Justification = \"Must be ignored even if generated code is checked.\")]");

            AnalyzerSuppressionsGenerator.Generate(writer, AnalyzerSuppressions);
            using (writer.BlockInvariant("static class MetadataBuilder_{0:000}", typeInfo.Index))
            {
                var postWriter = new IndentedStringBuilder();
                postWriter.Indent(writer.CurrentLevel);

                // Generate a parameter-less build to avoid generating a lambda during registration (avoids creating a caching backing field)
                writer.AppendLineInvariant("[System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Maintainability\", \"CA1502:AvoidExcessiveComplexity\", Justification=\"Must be ignored even if generated code is checked.\")]");
                writer.AppendLineInvariant("[System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Maintainability\", \"CA1506:AvoidExcessiveClassCoupling\", Justification = \"Must be ignored even if generated code is checked.\")]");
                writer.AppendLineInvariant("[System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Maintainability\", \"CA1505:AvoidUnmaintainableCode\", Justification = \"Must be ignored even if generated code is checked.\")]");
                using (writer.BlockInvariant("internal static global::Uno.UI.DataBinding.IBindableType Build()"))
                {
                    writer.AppendLineInvariant("return Build(null);");
                }

                writer.AppendLineInvariant("[System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Maintainability\", \"CA1502:AvoidExcessiveComplexity\", Justification=\"Must be ignored even if generated code is checked.\")]");
                writer.AppendLineInvariant("[System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Maintainability\", \"CA1506:AvoidExcessiveClassCoupling\", Justification = \"Must be ignored even if generated code is checked.\")]");
                writer.AppendLineInvariant("[System.Diagnostics.CodeAnalysis.SuppressMessage(\"Microsoft.Maintainability\", \"CA1505:AvoidUnmaintainableCode\", Justification = \"Must be ignored even if generated code is checked.\")]");
                using (writer.BlockInvariant("internal static global::Uno.UI.DataBinding.IBindableType Build(global::Uno.UI.DataBinding.BindableType parent)"))
                {
                    writer.AppendLineInvariant(
                        @"var bindableType = parent ?? new global::Uno.UI.DataBinding.BindableType({0}, typeof({1}));",
                        flattenedProperties
                        .Where(p => !IsStringIndexer(p) && HasPublicGetter(p))
                        .Count()
                        , ExpandType(ownerType)
                        );

                    // Call the builders for the base types
                    if (baseType != null)
                    {
                        var baseTypeMapped = _typeMap.UnoGetValueOrDefault(baseType);

                        writer.AppendLineInvariant(@"MetadataBuilder_{0:000}.Build(bindableType); // {1}", baseTypeMapped.Index, ExpandType(baseType));
                    }

                    var ctor = ownerType.GetMethods().FirstOrDefault(m => m.MethodKind == MethodKind.Constructor && !m.Parameters.Any() && m.IsLocallyPublic(_currentModule));

                    if (ctor != null && IsCreateable(ownerType))
                    {
                        using (writer.BlockInvariant("if(parent == null)"))
                        {
                            writer.AppendLineInvariant(@"bindableType.AddActivator(CreateInstance);");
                            postWriter.AppendLineInvariant($@"private static object CreateInstance() => new {ownerTypeName}();");
                        }
                    }

                    foreach (var property in properties)
                    {
                        var propertyTypeName = property.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
                        var propertyName     = property.Name;

                        if (IsStringIndexer(property))
                        {
                            writer.AppendLineInvariant("bindableType.AddIndexer(GetIndexer, SetIndexer);");

                            postWriter.AppendLineInvariant($@"private static object GetIndexer(object instance, string name) => (({ownerTypeName})instance)[name];");

                            if (property.SetMethod != null)
                            {
                                postWriter.AppendLineInvariant($@"private static void SetIndexer(object instance, string name, object value) => (({ownerTypeName})instance)[name] = ({propertyTypeName})value;");
                            }
                            else
                            {
                                postWriter.AppendLineInvariant("private static void SetIndexer(object instance, string name, object value) {{}}");
                            }

                            continue;
                        }

                        if (property.IsIndexer)
                        {
                            // Other types of indexers are currently not supported.
                            continue;
                        }

                        if (
                            property.SetMethod != null &&
                            property.SetMethod != null &&
                            property.SetMethod.IsLocallyPublic(_currentModule)
                            )
                        {
                            if (property.Type.IsValueType)
                            {
                                writer.AppendLineInvariant($@"bindableType.AddProperty(""{propertyName}"", typeof({propertyTypeName}), Get{propertyName}, Set{propertyName});");

                                postWriter.AppendLineInvariant($@"private static object Get{propertyName}(object instance, Windows.UI.Xaml.DependencyPropertyValuePrecedences? precedence) => (({ownerTypeName})instance).{propertyName};");

                                using (postWriter.BlockInvariant($@"private static void Set{propertyName}(object instance, object value, Windows.UI.Xaml.DependencyPropertyValuePrecedences? precedence)"))
                                {
                                    using (postWriter.BlockInvariant($"if(value != null)"))
                                    {
                                        postWriter.AppendLineInvariant($"(({ownerTypeName})instance).{propertyName} = ({propertyTypeName})value;");
                                    }
                                }
                            }
                            else
                            {
                                writer.AppendLineInvariant($@"bindableType.AddProperty(""{propertyName}"", typeof({propertyTypeName}), Get{propertyName}, Set{propertyName});");

                                postWriter.AppendLineInvariant($@"private static object Get{propertyName}(object instance,  Windows.UI.Xaml.DependencyPropertyValuePrecedences? precedence) => (({ownerTypeName})instance).{propertyName};");
                                postWriter.AppendLineInvariant($@"private static void Set{propertyName}(object instance, object value, Windows.UI.Xaml.DependencyPropertyValuePrecedences? precedence) => (({ownerTypeName})instance).{propertyName} = ({propertyTypeName})value;");
                            }
                        }
                        else if (HasPublicGetter(property))
                        {
                            writer.AppendLineInvariant($@"bindableType.AddProperty(""{propertyName}"", typeof({propertyTypeName}), Get{propertyName});");

                            postWriter.AppendLineInvariant($@"private static object Get{propertyName}(object instance, Windows.UI.Xaml.DependencyPropertyValuePrecedences? precedence) => (({ownerTypeName})instance).{propertyName};");
                        }
                    }

                    foreach (var dependencyProperty in dependencyProperties)
                    {
                        var propertyName = dependencyProperty.TrimEnd("Property");

                        var getMethod = ownerType.GetMethods().FirstOrDefault(m => m.Name == "Get" + propertyName && m.Parameters.Length == 1 && m.IsLocallyPublic(_currentModule));
                        var setMethod = ownerType.GetMethods().FirstOrDefault(m => m.Name == "Set" + propertyName && m.Parameters.Length == 2 && m.IsLocallyPublic(_currentModule));

                        if (getMethod == null)
                        {
                            getMethod = ownerType
                                        .GetProperties()
                                        .FirstOrDefault(p => p.Name == propertyName && (p.GetMethod?.IsLocallyPublic(_currentModule) ?? false))
                                        ?.GetMethod;
                        }

                        if (getMethod != null)
                        {
                            var getter = $"{XamlConstants.Types.DependencyObjectExtensions}.GetValue(instance, {ownerTypeName}.{propertyName}Property, precedence)";
                            var setter = $"{XamlConstants.Types.DependencyObjectExtensions}.SetValue(instance, {ownerTypeName}.{propertyName}Property, value, precedence)";

                            var propertyType = GetGlobalQualifier(getMethod.ReturnType) + SanitizeTypeName(getMethod.ReturnType.ToString());

                            writer.AppendLineInvariant($@"bindableType.AddProperty(""{propertyName}"", typeof({propertyType}),  Get{propertyName}, Set{propertyName});");

                            postWriter.AppendLineInvariant($@"private static object Get{propertyName}(object instance,  Windows.UI.Xaml.DependencyPropertyValuePrecedences? precedence) => {getter};");
                            postWriter.AppendLineInvariant($@"private static void Set{propertyName}(object instance, object value, Windows.UI.Xaml.DependencyPropertyValuePrecedences? precedence) => {setter};");
                        }
                    }

                    writer.AppendLineInvariant(@"return bindableType;");
                }

                writer.Append(postWriter.ToString());
            }

            writer.AppendLine();
        }