File: Compiler\AnonymousTypeMethodBodySynthesizer.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    internal sealed partial class AnonymousTypeManager
    {
        private sealed partial class AnonymousTypeConstructorSymbol : SynthesizedMethodBase
        {
            internal override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics)
            {
                //  Method body:
                //
                //  {
                //      Object..ctor();
                //      this.backingField_1 = arg1;
                //      ...
                //      this.backingField_N = argN;
                //  }
                SyntheticBoundNodeFactory F = this.CreateBoundNodeFactory(compilationState, diagnostics);
 
                int paramCount = this.ParameterCount;
 
                // List of statements
                BoundStatement[] statements = new BoundStatement[paramCount + 2];
                int statementIndex = 0;
 
                //  explicit base constructor call
                Debug.Assert(ContainingType.BaseTypeNoUseSiteDiagnostics.SpecialType == SpecialType.System_Object);
                BoundExpression call = Binder.GenerateBaseParameterlessConstructorInitializer(this, diagnostics);
                if (call == null)
                {
                    // This may happen if Object..ctor is not found or is inaccessible
                    return;
                }
                statements[statementIndex++] = F.ExpressionStatement(call);
 
                if (paramCount > 0)
                {
                    AnonymousTypeTemplateSymbol anonymousType = (AnonymousTypeTemplateSymbol)this.ContainingType;
                    Debug.Assert(anonymousType.Properties.Length == paramCount);
 
                    // Assign fields
                    for (int index = 0; index < this.ParameterCount; index++)
                    {
                        // Generate 'field' = 'parameter' statement
                        statements[statementIndex++] =
                            F.Assignment(F.Field(F.This(), anonymousType.Properties[index].BackingField), F.Parameter(_parameters[index]));
                    }
                }
 
                // Final return statement
                statements[statementIndex++] = F.Return();
 
                // Create a bound block 
                F.CloseMethod(F.Block(statements));
            }
 
            internal override bool HasSpecialName
            {
                get { return true; }
            }
 
            protected override bool HasSetsRequiredMembersImpl => false;
        }
 
        private sealed partial class AnonymousTypePropertyGetAccessorSymbol : SynthesizedMethodBase
        {
            internal override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics)
            {
                //  Method body:
                //
                //  {
                //      return this.backingField;
                //  }
 
                SyntheticBoundNodeFactory F = this.CreateBoundNodeFactory(compilationState, diagnostics);
                F.CloseMethod(F.Block(F.Return(F.Field(F.This(), _property.BackingField))));
            }
 
            internal override bool HasSpecialName
            {
                get { return true; }
            }
        }
 
        private sealed partial class AnonymousTypeEqualsMethodSymbol : SynthesizedMethodBase
        {
            internal override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics)
            {
                AnonymousTypeManager manager = ((AnonymousTypeTemplateSymbol)this.ContainingType).Manager;
                SyntheticBoundNodeFactory F = this.CreateBoundNodeFactory(compilationState, diagnostics);
 
                //  Method body:
                //
                //  {
                //      $anonymous$ local = value as $anonymous$;
                //      return (object)local == this || (local != null 
                //             && System.Collections.Generic.EqualityComparer<T_1>.Default.Equals(this.backingFld_1, local.backingFld_1)
                //             ...
                //             && System.Collections.Generic.EqualityComparer<T_N>.Default.Equals(this.backingFld_N, local.backingFld_N));
                //  }
 
                // Type and type expression
                AnonymousTypeTemplateSymbol anonymousType = (AnonymousTypeTemplateSymbol)this.ContainingType;
 
                //  local
                BoundAssignmentOperator assignmentToTemp;
                BoundLocal boundLocal = F.StoreToTemp(F.As(F.Parameter(_parameters[0]), anonymousType), out assignmentToTemp);
 
                //  Generate: statement <= 'local = value as $anonymous$'
                BoundStatement assignment = F.ExpressionStatement(assignmentToTemp);
 
                //  Generate expression for return statement
                //      retExpression <= 'local != null'
                BoundExpression retExpression = F.Binary(BinaryOperatorKind.ObjectNotEqual,
                                                         manager.System_Boolean,
                                                         F.Convert(manager.System_Object, boundLocal),
                                                         F.Null(manager.System_Object));
 
                // Compare fields
                if (anonymousType.Properties.Length > 0)
                {
                    var fields = ArrayBuilder<FieldSymbol>.GetInstance(anonymousType.Properties.Length);
                    foreach (var prop in anonymousType.Properties)
                    {
                        fields.Add(prop.BackingField);
                    }
                    retExpression = MethodBodySynthesizer.GenerateFieldEquals(retExpression, boundLocal, fields, F);
                    fields.Free();
                }
 
                // Compare references
                retExpression = F.LogicalOr(F.ObjectEqual(F.This(), boundLocal), retExpression);
 
                // Final return statement
                BoundStatement retStatement = F.Return(retExpression);
 
                // Create a bound block 
                F.CloseMethod(F.Block(ImmutableArray.Create<LocalSymbol>(boundLocal.LocalSymbol), assignment, retStatement));
            }
 
            internal override bool HasSpecialName
            {
                get { return false; }
            }
        }
 
        private sealed partial class AnonymousTypeGetHashCodeMethodSymbol : SynthesizedMethodBase
        {
            internal override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics)
            {
                AnonymousTypeManager manager = ((AnonymousTypeTemplateSymbol)this.ContainingType).Manager;
                SyntheticBoundNodeFactory F = this.CreateBoundNodeFactory(compilationState, diagnostics);
 
                //  Method body:
                //
                //  HASH_FACTOR = 0xa5555529;
                //  INIT_HASH = (...((0 * HASH_FACTOR) + GetFNVHashCode(backingFld_1.Name)) * HASH_FACTOR
                //                                     + GetFNVHashCode(backingFld_2.Name)) * HASH_FACTOR
                //                                     + ...
                //                                     + GetFNVHashCode(backingFld_N.Name)
                //
                //  {
                //      return (...((INITIAL_HASH * HASH_FACTOR) + EqualityComparer<T_1>.Default.GetHashCode(this.backingFld_1)) * HASH_FACTOR
                //                                               + EqualityComparer<T_2>.Default.GetHashCode(this.backingFld_2)) * HASH_FACTOR
                //                                               ...
                //                                               + EqualityComparer<T_N>.Default.GetHashCode(this.backingFld_N)
                //  }
                //
                // Where GetFNVHashCode is the FNV-1a hash code.
 
                // Type expression
                AnonymousTypeTemplateSymbol anonymousType = (AnonymousTypeTemplateSymbol)this.ContainingType;
 
                //  INIT_HASH
                int initHash = 0;
                foreach (var property in anonymousType.Properties)
                {
                    initHash = unchecked(initHash * MethodBodySynthesizer.HASH_FACTOR + Hash.GetFNVHashCode(property.BackingField.Name));
                }
 
                //  Generate expression for return statement
                //      retExpression <= 'INITIAL_HASH'
                BoundExpression retExpression = F.Literal(initHash);
 
                //  prepare symbols
                MethodSymbol equalityComparer_GetHashCode = manager.System_Collections_Generic_EqualityComparer_T__GetHashCode;
                MethodSymbol equalityComparer_get_Default = manager.System_Collections_Generic_EqualityComparer_T__get_Default;
 
                //  bound HASH_FACTOR
                BoundLiteral boundHashFactor = null;
 
                // Process fields
                for (int index = 0; index < anonymousType.Properties.Length; index++)
                {
                    retExpression = MethodBodySynthesizer.GenerateHashCombine(retExpression, equalityComparer_GetHashCode, equalityComparer_get_Default, ref boundHashFactor,
                                                                              F.Field(F.This(), anonymousType.Properties[index].BackingField),
                                                                              F);
                }
 
                // Create a bound block 
                F.CloseMethod(F.Block(F.Return(retExpression)));
            }
 
            internal override bool HasSpecialName
            {
                get { return false; }
            }
        }
 
        private sealed partial class AnonymousTypeToStringMethodSymbol : SynthesizedMethodBase
        {
            internal override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics)
            {
                AnonymousTypeManager manager = ((AnonymousTypeTemplateSymbol)this.ContainingType).Manager;
                SyntheticBoundNodeFactory F = this.CreateBoundNodeFactory(compilationState, diagnostics);
 
                //  Method body:
                //
                //  {
                //      return String.Format(
                //          "{ <name1> = {0}", <name2> = {1}", ... <nameN> = {N-1}",
                //          this.backingFld_1, 
                //          this.backingFld_2, 
                //          ...
                //          this.backingFld_N
                //  }
 
                // Type expression
                AnonymousTypeTemplateSymbol anonymousType = (AnonymousTypeTemplateSymbol)this.ContainingType;
 
                //  build arguments
                int fieldCount = anonymousType.Properties.Length;
                BoundExpression retExpression = null;
 
                if (fieldCount > 0)
                {
                    //  we do have fields, so have to use String.Format(...)
                    BoundExpression[] arguments = new BoundExpression[fieldCount];
 
                    //  process properties
                    PooledStringBuilder formatString = PooledStringBuilder.GetInstance();
                    for (int i = 0; i < fieldCount; i++)
                    {
                        AnonymousTypePropertySymbol property = anonymousType.Properties[i];
 
                        // build format string
                        formatString.Builder.AppendFormat(i == 0 ? "{{{{ {0} = {{{1}}}" : ", {0} = {{{1}}}", property.Name, i);
 
                        // build argument
                        arguments[i] = F.Convert(manager.System_Object,
                                                 new BoundLoweredConditionalAccess(F.Syntax,
                                                                            F.Field(F.This(), property.BackingField),
                                                                            null,
                                                                            F.Call(new BoundConditionalReceiver(
                                                                                F.Syntax,
                                                                                id: i,
                                                                                type: property.BackingField.Type), manager.System_Object__ToString),
                                                                            null,
                                                                            id: i,
                                                                            forceCopyOfNullableValueType: true,
                                                                            type: manager.System_String),
                                                 Conversion.ImplicitReference);
                    }
                    formatString.Builder.Append(" }}");
 
                    //  add format string argument
                    BoundExpression format = F.Literal(formatString.ToStringAndFree());
 
                    //  Generate expression for return statement
                    //      retExpression <= System.String.Format(args)
                    var formatMethod = manager.System_String__Format_IFormatProvider;
                    retExpression = F.StaticCall(manager.System_String, formatMethod, F.Null(formatMethod.Parameters[0].Type), format, F.ArrayOrEmpty(manager.System_Object, arguments));
                }
                else
                {
                    //  this is an empty anonymous type, just return "{ }"
                    retExpression = F.Literal("{ }");
                }
 
                F.CloseMethod(F.Block(F.Return(retExpression)));
            }
 
            internal override bool HasSpecialName
            {
                get { return false; }
            }
        }
    }
}