File: Lowering\LocalRewriter\LoweredDynamicOperationFactory.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.
 
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    internal sealed class LoweredDynamicOperationFactory
    {
        private readonly SyntheticBoundNodeFactory _factory;
        private readonly int _methodOrdinal;
        private readonly int _localFunctionOrdinal;
        private NamedTypeSymbol? _currentDynamicCallSiteContainer;
        private int _callSiteIdDispenser;
 
        internal LoweredDynamicOperationFactory(SyntheticBoundNodeFactory factory, int methodOrdinal, int localFunctionOrdinal = -1)
        {
            Debug.Assert(factory != null);
            _factory = factory;
            _methodOrdinal = methodOrdinal;
            _localFunctionOrdinal = localFunctionOrdinal;
        }
 
        public int MethodOrdinal => _methodOrdinal;
 
        // We could read the values of the following enums from metadata instead of hardcoding them here but 
        // - they can never change since existing programs have the values inlined and would be broken if the values changed their meaning,
        // - if any new flags are added to the runtime binder the compiler will change as well to produce them.
 
        // The only scenario that is not supported by hardcoding the values is when a completely new Framework is created 
        // that redefines these constants and is not supposed to run existing programs.
 
        /// <summary>
        /// Corresponds to Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags.
        /// </summary>
        [Flags]
        private enum CSharpBinderFlags
        {
            None = 0,
            CheckedContext = 1,
            InvokeSimpleName = 2,
            InvokeSpecialName = 4,
            BinaryOperationLogical = 8,
            ConvertExplicit = 16,
            ConvertArrayIndex = 32,
            ResultIndexed = 64,
            ValueFromCompoundAssignment = 128,
            ResultDiscarded = 256,
        }
 
        /// <summary>
        /// Corresponds to Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags.
        /// </summary>
        [Flags]
        private enum CSharpArgumentInfoFlags
        {
            None = 0,
            UseCompileTimeType = 1,
            Constant = 2,
            NamedArgument = 4,
            IsRef = 8,
            IsOut = 16,
            IsStaticType = 32,
        }
 
        internal LoweredDynamicOperation MakeDynamicConversion(
            BoundExpression loweredOperand,
            bool isExplicit,
            bool isArrayIndex,
            bool isChecked,
            TypeSymbol resultType)
        {
            _factory.Syntax = loweredOperand.Syntax;
 
            CSharpBinderFlags binderFlags = 0;
            Debug.Assert(!isExplicit || !isArrayIndex);
 
            if (isChecked)
            {
                binderFlags |= CSharpBinderFlags.CheckedContext;
            }
            if (isExplicit)
            {
                binderFlags |= CSharpBinderFlags.ConvertExplicit;
            }
            if (isArrayIndex)
            {
                binderFlags |= CSharpBinderFlags.ConvertArrayIndex;
            }
 
            var loweredArguments = ImmutableArray.Create(loweredOperand);
 
            var binderConstruction = MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__Convert, new[]
            {
                // flags:
                _factory.Literal((int)binderFlags),
 
                // target type:
                _factory.Typeof(resultType, _factory.WellKnownType(WellKnownType.System_Type)),
 
                // context:
                _factory.TypeofDynamicOperationContextType()
            });
 
            return MakeDynamicOperation(binderConstruction, null, RefKind.None, loweredArguments, default(ImmutableArray<RefKind>), null, resultType);
        }
 
        internal LoweredDynamicOperation MakeDynamicUnaryOperator(
            UnaryOperatorKind operatorKind,
            BoundExpression loweredOperand,
            TypeSymbol resultType)
        {
            Debug.Assert(operatorKind.IsDynamic());
 
            _factory.Syntax = loweredOperand.Syntax;
 
            CSharpBinderFlags binderFlags = 0;
            if (operatorKind.IsChecked())
            {
                binderFlags |= CSharpBinderFlags.CheckedContext;
            }
 
            var loweredArguments = ImmutableArray.Create(loweredOperand);
 
            MethodSymbol argumentInfoFactory = GetArgumentInfoFactory();
            var binderConstruction = ((object)argumentInfoFactory != null) ? MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__UnaryOperation, new[]
            {
                // flags:
                _factory.Literal((int)binderFlags),
 
                // expression type:
                _factory.Literal((int)operatorKind.ToExpressionType()),
 
                // context:
                _factory.TypeofDynamicOperationContextType(),
 
                // argument infos:
                MakeCallSiteArgumentInfos(argumentInfoFactory, loweredArguments)
            }) : null;
 
            return MakeDynamicOperation(binderConstruction, null, RefKind.None, loweredArguments, default(ImmutableArray<RefKind>), null, resultType);
        }
 
        internal LoweredDynamicOperation MakeDynamicBinaryOperator(
            BinaryOperatorKind operatorKind,
            BoundExpression loweredLeft,
            BoundExpression loweredRight,
            bool isCompoundAssignment,
            TypeSymbol resultType)
        {
            Debug.Assert(operatorKind.IsDynamic());
 
            _factory.Syntax = loweredLeft.Syntax;
 
            CSharpBinderFlags binderFlags = 0;
            if (operatorKind.IsChecked())
            {
                binderFlags |= CSharpBinderFlags.CheckedContext;
            }
 
            if (operatorKind.IsLogical())
            {
                binderFlags |= CSharpBinderFlags.BinaryOperationLogical;
            }
 
            var loweredArguments = ImmutableArray.Create<BoundExpression>(loweredLeft, loweredRight);
 
            MethodSymbol argumentInfoFactory = GetArgumentInfoFactory();
            var binderConstruction = ((object)argumentInfoFactory != null) ? MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__BinaryOperation, new[]
            {
                // flags:
                _factory.Literal((int)binderFlags),
 
                // expression type:
                _factory.Literal((int)operatorKind.ToExpressionType(isCompoundAssignment)),
 
                // context:
                _factory.TypeofDynamicOperationContextType(),
 
                // argument infos:
                MakeCallSiteArgumentInfos(argumentInfoFactory, loweredArguments)
            }) : null;
 
            return MakeDynamicOperation(binderConstruction, null, RefKind.None, loweredArguments, default(ImmutableArray<RefKind>), null, resultType);
        }
 
        internal LoweredDynamicOperation MakeDynamicMemberInvocation(
            string name,
            BoundExpression loweredReceiver,
            ImmutableArray<TypeWithAnnotations> typeArgumentsWithAnnotations,
            ImmutableArray<BoundExpression> loweredArguments,
            ImmutableArray<string?> argumentNames,
            ImmutableArray<RefKind> refKinds,
            bool hasImplicitReceiver,
            bool resultDiscarded)
        {
            _factory.Syntax = loweredReceiver.Syntax;
            Debug.Assert(_factory.TopLevelMethod is { });
 
            CSharpBinderFlags binderFlags = 0;
            if (hasImplicitReceiver && _factory.TopLevelMethod.RequiresInstanceReceiver)
            {
                binderFlags |= CSharpBinderFlags.InvokeSimpleName;
            }
 
            TypeSymbol resultType;
            if (resultDiscarded)
            {
                binderFlags |= CSharpBinderFlags.ResultDiscarded;
                resultType = _factory.SpecialType(SpecialType.System_Void);
            }
            else
            {
                resultType = AssemblySymbol.DynamicType;
            }
 
            RefKind receiverRefKind;
            bool receiverIsStaticType;
            if (loweredReceiver.Kind == BoundKind.TypeExpression)
            {
                loweredReceiver = _factory.Typeof(((BoundTypeExpression)loweredReceiver).Type, _factory.WellKnownType(WellKnownType.System_Type));
                receiverRefKind = RefKind.None;
                receiverIsStaticType = true;
            }
            else
            {
                receiverRefKind = GetReceiverRefKind(loweredReceiver);
                receiverIsStaticType = false;
            }
 
            MethodSymbol argumentInfoFactory = GetArgumentInfoFactory();
            var binderConstruction = ((object)argumentInfoFactory != null) ? MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__InvokeMember, new[]
            {
                // flags:
                _factory.Literal((int)binderFlags),
 
                // member name:
                _factory.Literal(name),
 
                // type arguments:
                typeArgumentsWithAnnotations.IsDefaultOrEmpty ?
                    _factory.Null(_factory.WellKnownArrayType(WellKnownType.System_Type)) :
                    _factory.ArrayOrEmpty(_factory.WellKnownType(WellKnownType.System_Type), _factory.TypeOfs(typeArgumentsWithAnnotations, _factory.WellKnownType(WellKnownType.System_Type))),
 
                // context:
                _factory.TypeofDynamicOperationContextType(),
 
                // argument infos:
                MakeCallSiteArgumentInfos(argumentInfoFactory, loweredArguments, argumentNames, refKinds, loweredReceiver, receiverRefKind, receiverIsStaticType)
            }) : null;
 
            return MakeDynamicOperation(binderConstruction, loweredReceiver, receiverRefKind, loweredArguments, refKinds, null, resultType);
        }
 
        internal LoweredDynamicOperation MakeDynamicEventAccessorInvocation(
            string accessorName,
            BoundExpression loweredReceiver,
            BoundExpression loweredHandler)
        {
            _factory.Syntax = loweredReceiver.Syntax;
 
            CSharpBinderFlags binderFlags = CSharpBinderFlags.InvokeSpecialName | CSharpBinderFlags.ResultDiscarded;
 
            var loweredArguments = ImmutableArray<BoundExpression>.Empty;
            var resultType = AssemblySymbol.DynamicType;
 
            MethodSymbol argumentInfoFactory = GetArgumentInfoFactory();
            var binderConstruction = ((object)argumentInfoFactory != null) ? MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__InvokeMember, new[]
            {
                // flags:
                _factory.Literal((int)binderFlags),
 
                // member name:
                _factory.Literal(accessorName),
 
                // type arguments:
                _factory.Null(_factory.WellKnownArrayType(WellKnownType.System_Type)),
 
                // context:
                _factory.TypeofDynamicOperationContextType(),
 
                // argument infos:
                MakeCallSiteArgumentInfos(argumentInfoFactory, loweredArguments, loweredReceiver: loweredReceiver, loweredRight: loweredHandler)
            }) : null;
 
            return MakeDynamicOperation(binderConstruction, loweredReceiver, RefKind.None, loweredArguments, default(ImmutableArray<RefKind>), loweredHandler, resultType);
        }
 
        internal LoweredDynamicOperation MakeDynamicInvocation(
            BoundExpression loweredReceiver,
            ImmutableArray<BoundExpression> loweredArguments,
            ImmutableArray<string?> argumentNames,
            ImmutableArray<RefKind> refKinds,
            bool resultDiscarded)
        {
            _factory.Syntax = loweredReceiver.Syntax;
 
            TypeSymbol resultType;
            CSharpBinderFlags binderFlags = 0;
            if (resultDiscarded)
            {
                binderFlags |= CSharpBinderFlags.ResultDiscarded;
                resultType = _factory.SpecialType(SpecialType.System_Void);
            }
            else
            {
                resultType = AssemblySymbol.DynamicType;
            }
 
            MethodSymbol argumentInfoFactory = GetArgumentInfoFactory();
            var binderConstruction = ((object)argumentInfoFactory != null) ? MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__Invoke, new[]
            {
                // flags:
                _factory.Literal((int)binderFlags),
 
                // context:
                _factory.TypeofDynamicOperationContextType(),
 
                // argument infos:
                MakeCallSiteArgumentInfos(argumentInfoFactory, loweredArguments, argumentNames, refKinds, loweredReceiver)
            }) : null;
 
            return MakeDynamicOperation(binderConstruction, loweredReceiver, RefKind.None, loweredArguments, refKinds, null, resultType);
        }
 
        internal LoweredDynamicOperation MakeDynamicConstructorInvocation(
            SyntaxNode syntax,
            TypeSymbol type,
            ImmutableArray<BoundExpression> loweredArguments,
            ImmutableArray<string?> argumentNames,
            ImmutableArray<RefKind> refKinds)
        {
            _factory.Syntax = syntax;
 
            var loweredReceiver = _factory.Typeof(type, _factory.WellKnownType(WellKnownType.System_Type));
 
            MethodSymbol argumentInfoFactory = GetArgumentInfoFactory();
            var binderConstruction = ((object)argumentInfoFactory != null) ? MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__InvokeConstructor, new[]
            {
                // flags:
                _factory.Literal(0),
 
                // context:
                _factory.TypeofDynamicOperationContextType(),
 
                // argument infos:
                MakeCallSiteArgumentInfos(argumentInfoFactory, loweredArguments, argumentNames, refKinds, loweredReceiver, receiverIsStaticType: true)
            }) : null;
 
            return MakeDynamicOperation(binderConstruction, loweredReceiver, RefKind.None, loweredArguments, refKinds, null, type);
        }
 
        internal LoweredDynamicOperation MakeDynamicGetMember(
            BoundExpression loweredReceiver,
            string name,
            bool resultIndexed)
        {
            _factory.Syntax = loweredReceiver.Syntax;
 
            CSharpBinderFlags binderFlags = 0;
            if (resultIndexed)
            {
                binderFlags |= CSharpBinderFlags.ResultIndexed;
            }
 
            var loweredArguments = ImmutableArray<BoundExpression>.Empty;
            var resultType = DynamicTypeSymbol.Instance;
 
            MethodSymbol argumentInfoFactory = GetArgumentInfoFactory();
            var binderConstruction = ((object)argumentInfoFactory != null) ? MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__GetMember, new[]
            {
                // flags:
                _factory.Literal((int)binderFlags),
 
                // name:
                _factory.Literal(name),
 
                // context:
                _factory.TypeofDynamicOperationContextType(),
 
                // argument infos:
                MakeCallSiteArgumentInfos(argumentInfoFactory, loweredArguments, loweredReceiver: loweredReceiver)
            }) : null;
 
            return MakeDynamicOperation(binderConstruction, loweredReceiver, RefKind.None, loweredArguments, default(ImmutableArray<RefKind>), null, resultType);
        }
 
        internal LoweredDynamicOperation MakeDynamicSetMember(
            BoundExpression loweredReceiver,
            string name,
            BoundExpression loweredRight,
            bool isCompoundAssignment = false,
            bool isChecked = false)
        {
            _factory.Syntax = loweredReceiver.Syntax;
 
            CSharpBinderFlags binderFlags = 0;
            if (isCompoundAssignment)
            {
                binderFlags |= CSharpBinderFlags.ValueFromCompoundAssignment;
 
                if (isChecked)
                {
                    binderFlags |= CSharpBinderFlags.CheckedContext;
                }
            }
 
            var loweredArguments = ImmutableArray<BoundExpression>.Empty;
 
            MethodSymbol argumentInfoFactory = GetArgumentInfoFactory();
            var binderConstruction = ((object)argumentInfoFactory != null) ? MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__SetMember, new[]
            {
                // flags:
                _factory.Literal((int)binderFlags),
 
                // name:
                _factory.Literal(name),
 
                // context:
                _factory.TypeofDynamicOperationContextType(),
 
                // argument infos:
                MakeCallSiteArgumentInfos(argumentInfoFactory, loweredArguments, loweredReceiver: loweredReceiver, loweredRight: loweredRight)
            }) : null;
 
            return MakeDynamicOperation(binderConstruction, loweredReceiver, RefKind.None, loweredArguments, default(ImmutableArray<RefKind>), loweredRight, AssemblySymbol.DynamicType);
        }
 
        internal LoweredDynamicOperation MakeDynamicGetIndex(
            BoundExpression loweredReceiver,
            ImmutableArray<BoundExpression> loweredArguments,
            ImmutableArray<string?> argumentNames,
            ImmutableArray<RefKind> refKinds)
        {
            _factory.Syntax = loweredReceiver.Syntax;
 
            var resultType = DynamicTypeSymbol.Instance;
 
            MethodSymbol argumentInfoFactory = GetArgumentInfoFactory();
            var binderConstruction = ((object)argumentInfoFactory != null) ? MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__GetIndex, new[]
            {
                // flags (unused):
                _factory.Literal((int)CSharpBinderFlags.None),
 
                // context:
                _factory.TypeofDynamicOperationContextType(),
 
                // argument infos:
                MakeCallSiteArgumentInfos(argumentInfoFactory, loweredArguments, argumentNames, refKinds, loweredReceiver: loweredReceiver)
            }) : null;
 
            return MakeDynamicOperation(binderConstruction, loweredReceiver, RefKind.None, loweredArguments, refKinds, null, resultType);
        }
 
        internal LoweredDynamicOperation MakeDynamicSetIndex(
            BoundExpression loweredReceiver,
            ImmutableArray<BoundExpression> loweredArguments,
            ImmutableArray<string?> argumentNames,
            ImmutableArray<RefKind> refKinds,
            BoundExpression loweredRight,
            bool isCompoundAssignment = false,
            bool isChecked = false)
        {
            CSharpBinderFlags binderFlags = 0;
            if (isCompoundAssignment)
            {
                binderFlags |= CSharpBinderFlags.ValueFromCompoundAssignment;
 
                if (isChecked)
                {
                    binderFlags |= CSharpBinderFlags.CheckedContext;
                }
            }
 
            var loweredReceiverRefKind = GetReceiverRefKind(loweredReceiver);
            var resultType = DynamicTypeSymbol.Instance;
 
            MethodSymbol argumentInfoFactory = GetArgumentInfoFactory();
            var binderConstruction = ((object)argumentInfoFactory != null) ? MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__SetIndex, new[]
            {
                // flags (unused):
                _factory.Literal((int)binderFlags),
 
                // context:
                _factory.TypeofDynamicOperationContextType(),
 
                // argument infos:
                MakeCallSiteArgumentInfos(argumentInfoFactory, loweredArguments, argumentNames, refKinds, loweredReceiver, loweredReceiverRefKind, loweredRight: loweredRight)
            }) : null;
 
            return MakeDynamicOperation(binderConstruction, loweredReceiver, loweredReceiverRefKind, loweredArguments, refKinds, loweredRight, resultType);
        }
 
        internal LoweredDynamicOperation MakeDynamicIsEventTest(string name, BoundExpression loweredReceiver)
        {
            _factory.Syntax = loweredReceiver.Syntax;
            var resultType = _factory.SpecialType(SpecialType.System_Boolean);
            var binderConstruction = MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__IsEvent, new[]
            {
                // flags (unused):
                _factory.Literal((int)0),
 
                // member name:
                _factory.Literal(name),
 
                // context:
                _factory.TypeofDynamicOperationContextType()
            });
 
            return MakeDynamicOperation(binderConstruction, loweredReceiver, RefKind.None, ImmutableArray<BoundExpression>.Empty, default(ImmutableArray<RefKind>), null, resultType);
        }
 
        private MethodSymbol GetArgumentInfoFactory()
        {
            return _factory.WellKnownMethod(WellKnownMember.Microsoft_CSharp_RuntimeBinder_CSharpArgumentInfo__Create);
        }
 
        private BoundExpression? MakeBinderConstruction(WellKnownMember factoryMethod, BoundExpression[] args)
        {
            var binderFactory = _factory.WellKnownMember(factoryMethod);
            if (binderFactory is null)
            {
                return null;
            }
 
            return _factory.Call(null, (MethodSymbol)binderFactory, args.AsImmutableOrNull());
        }
 
        // If we have a struct calling object, then we need to pass it by ref, provided
        // that it was an Lvalue. For instance,
        //     Struct s = ...; dynamic d = ...;
        //     s.M(d); // becomes Site(ref s, d)
        // however
        //     dynamic d = ...;
        //     GetS().M(d); // becomes Site(GetS(), d) without ref on the target obj arg
        internal RefKind GetReceiverRefKind(BoundExpression loweredReceiver)
        {
            Debug.Assert(loweredReceiver.Type is { });
            if (!loweredReceiver.Type.IsValueType)
            {
                return RefKind.None;
            }
 
            var hasHome = CodeGenerator.HasHome(loweredReceiver,
                CodeGenerator.AddressKind.Writeable,
                _factory.CurrentFunction,
                peVerifyCompatEnabled: false,
                stackLocalsOpt: null);
            return hasHome ? RefKind.Ref : RefKind.None;
        }
 
        internal BoundExpression MakeCallSiteArgumentInfos(
            MethodSymbol argumentInfoFactory,
            ImmutableArray<BoundExpression> loweredArguments,
            ImmutableArray<string?> argumentNames = default(ImmutableArray<string?>),
            ImmutableArray<RefKind> refKinds = default(ImmutableArray<RefKind>),
            BoundExpression? loweredReceiver = null,
            RefKind receiverRefKind = RefKind.None,
            bool receiverIsStaticType = false,
            BoundExpression? loweredRight = null)
        {
            const string? NoName = null;
            Debug.Assert(argumentNames.IsDefaultOrEmpty || loweredArguments.Length == argumentNames.Length);
            Debug.Assert(refKinds.IsDefault || loweredArguments.Length == refKinds.Length);
            Debug.Assert(!receiverIsStaticType || receiverRefKind == RefKind.None);
 
            var infos = new BoundExpression[(loweredReceiver != null ? 1 : 0) + loweredArguments.Length + (loweredRight != null ? 1 : 0)];
            int j = 0;
            if (loweredReceiver != null)
            {
                infos[j++] = GetArgumentInfo(argumentInfoFactory, loweredReceiver, NoName, receiverRefKind, receiverIsStaticType);
            }
 
            for (int i = 0; i < loweredArguments.Length; i++)
            {
                infos[j++] = GetArgumentInfo(
                argumentInfoFactory,
                    loweredArguments[i],
                    argumentNames.IsDefaultOrEmpty ? NoName : argumentNames[i],
                    refKinds.IsDefault ? RefKind.None : refKinds[i],
                    isStaticType: false);
            }
 
            if (loweredRight != null)
            {
                infos[j++] = GetArgumentInfo(argumentInfoFactory, loweredRight, NoName, RefKind.None, isStaticType: false);
            }
 
            return _factory.ArrayOrEmpty(argumentInfoFactory.ContainingType, infos);
        }
 
        internal LoweredDynamicOperation MakeDynamicOperation(
            BoundExpression? binderConstruction,
            BoundExpression? loweredReceiver,
            RefKind receiverRefKind,
            ImmutableArray<BoundExpression> loweredArguments,
            ImmutableArray<RefKind> refKinds,
            BoundExpression? loweredRight,
            TypeSymbol resultType)
        {
            Debug.Assert(!loweredArguments.IsDefault);
 
            // get well-known types and members we need:
            NamedTypeSymbol? delegateTypeOverMethodTypeParameters = GetDelegateType(loweredReceiver, receiverRefKind, loweredArguments, refKinds, loweredRight, resultType);
            NamedTypeSymbol callSiteTypeGeneric = _factory.WellKnownType(WellKnownType.System_Runtime_CompilerServices_CallSite_T);
            MethodSymbol callSiteFactoryGeneric = _factory.WellKnownMethod(WellKnownMember.System_Runtime_CompilerServices_CallSite_T__Create);
            FieldSymbol callSiteTargetFieldGeneric = (FieldSymbol)_factory.WellKnownMember(WellKnownMember.System_Runtime_CompilerServices_CallSite_T__Target);
            MethodSymbol? delegateInvoke;
 
            if (binderConstruction == null ||
                delegateTypeOverMethodTypeParameters is null ||
                delegateTypeOverMethodTypeParameters.IsErrorType() ||
                (delegateInvoke = delegateTypeOverMethodTypeParameters.DelegateInvokeMethod) is null ||
                callSiteTypeGeneric.IsErrorType() ||
                callSiteFactoryGeneric is null ||
                callSiteTargetFieldGeneric is null)
            {
                // CS1969: One or more types required to compile a dynamic expression cannot be found.
                // Dev11 reports it with source location for each dynamic operation, which results in many error messages.
                // The diagnostic that names the specific missing type or member has already been reported.
                _factory.Diagnostics.Add(ErrorCode.ERR_DynamicRequiredTypesMissing, NoLocation.Singleton);
 
                return LoweredDynamicOperation.Bad(loweredReceiver, loweredArguments, loweredRight, resultType);
            }
 
            if (_currentDynamicCallSiteContainer is null)
            {
                _currentDynamicCallSiteContainer = CreateCallSiteContainer(_factory, _methodOrdinal, _localFunctionOrdinal);
            }
 
            var containerDef = (SynthesizedContainer)_currentDynamicCallSiteContainer.OriginalDefinition;
            var methodToContainerTypeParametersMap = containerDef.TypeMap;
 
            ImmutableArray<LocalSymbol> temps = MakeTempsForDiscardArguments(ref loweredArguments);
 
            var callSiteType = callSiteTypeGeneric.Construct(new[] { delegateTypeOverMethodTypeParameters });
            var callSiteFactoryMethod = callSiteFactoryGeneric.AsMember(callSiteType);
            var callSiteTargetField = callSiteTargetFieldGeneric.AsMember(callSiteType);
            var callSiteField = DefineCallSiteStorageSymbol(containerDef, delegateTypeOverMethodTypeParameters, methodToContainerTypeParametersMap);
            var callSiteFieldAccess = _factory.Field(null, callSiteField);
            var callSiteArguments = GetCallSiteArguments(callSiteFieldAccess, loweredReceiver, loweredArguments, loweredRight);
 
            var nullCallSite = _factory.Null(callSiteField.Type);
 
            var siteInitialization = _factory.Conditional(
                _factory.ObjectEqual(callSiteFieldAccess, nullCallSite),
                _factory.AssignmentExpression(callSiteFieldAccess, _factory.Call(null, callSiteFactoryMethod, binderConstruction)),
                nullCallSite,
                callSiteField.Type);
 
            var siteInvocation = _factory.Call(
                _factory.Field(callSiteFieldAccess, callSiteTargetField),
                delegateInvoke,
                callSiteArguments);
 
            return new LoweredDynamicOperation(_factory, siteInitialization, siteInvocation, resultType, temps);
        }
 
        /// <summary>
        /// If there are any discards in the arguments, create locals for each, updates the arguments and
        /// returns the symbols that were created.
        /// Returns default if no discards found.
        /// </summary>
        private ImmutableArray<LocalSymbol> MakeTempsForDiscardArguments(ref ImmutableArray<BoundExpression> loweredArguments)
        {
            int discardCount = loweredArguments.Count(a => a.Kind == BoundKind.DiscardExpression);
 
            if (discardCount == 0)
            {
                return ImmutableArray<LocalSymbol>.Empty;
            }
 
            ArrayBuilder<LocalSymbol> temporariesBuilder = ArrayBuilder<LocalSymbol>.GetInstance(discardCount);
            loweredArguments = _factory.MakeTempsForDiscardArguments(loweredArguments, temporariesBuilder);
            return temporariesBuilder.ToImmutableAndFree();
        }
 
        private static NamedTypeSymbol CreateCallSiteContainer(SyntheticBoundNodeFactory factory, int methodOrdinal, int localFunctionOrdinal)
        {
            Debug.Assert(factory.CompilationState.ModuleBuilderOpt is { });
            Debug.Assert(factory.TopLevelMethod is { });
            Debug.Assert(factory.CurrentFunction is { });
 
            // We don't reuse call-sites during EnC. Each edit creates a new container and sites.
            int generation = factory.CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal;
            var containerName = GeneratedNames.MakeDynamicCallSiteContainerName(methodOrdinal, localFunctionOrdinal, generation);
 
            var synthesizedContainer = new DynamicSiteContainer(containerName, factory.TopLevelMethod, factory.CurrentFunction);
            factory.AddNestedType(synthesizedContainer);
 
            if (!synthesizedContainer.TypeParameters.IsEmpty)
            {
                return synthesizedContainer.Construct(synthesizedContainer.ConstructedFromTypeParameters.Cast<TypeParameterSymbol, TypeSymbol>());
            }
 
            return synthesizedContainer;
        }
 
        internal FieldSymbol DefineCallSiteStorageSymbol(NamedTypeSymbol containerDefinition, NamedTypeSymbol delegateTypeOverMethodTypeParameters, TypeMap methodToContainerTypeParametersMap)
        {
            var fieldName = GeneratedNames.MakeDynamicCallSiteFieldName(_callSiteIdDispenser++);
            var delegateTypeOverContainerTypeParameters = methodToContainerTypeParametersMap.SubstituteNamedType(delegateTypeOverMethodTypeParameters);
            var callSiteType = _factory.Compilation.GetWellKnownType(WellKnownType.System_Runtime_CompilerServices_CallSite_T);
            _factory.Diagnostics.ReportUseSite(callSiteType, _factory.Syntax);
            callSiteType = callSiteType.Construct(new[] { delegateTypeOverContainerTypeParameters });
            var field = new SynthesizedFieldSymbol(containerDefinition, callSiteType, fieldName, isPublic: true, isStatic: true);
            _factory.AddField(containerDefinition, field);
            Debug.Assert(_currentDynamicCallSiteContainer is { });
            return _currentDynamicCallSiteContainer.IsGenericType ? field.AsMember(_currentDynamicCallSiteContainer) : field;
        }
 
        internal NamedTypeSymbol? GetDelegateType(
            BoundExpression? loweredReceiver,
            RefKind receiverRefKind,
            ImmutableArray<BoundExpression> loweredArguments,
            ImmutableArray<RefKind> refKinds,
            BoundExpression? loweredRight,
            TypeSymbol resultType)
        {
            Debug.Assert(refKinds.IsDefaultOrEmpty || refKinds.Length == loweredArguments.Length);
 
            var callSiteType = _factory.WellKnownType(WellKnownType.System_Runtime_CompilerServices_CallSite);
            if (callSiteType.IsErrorType())
            {
                return null;
            }
 
            var delegateSignature = MakeCallSiteDelegateSignature(callSiteType, loweredReceiver, loweredArguments, loweredRight, resultType);
            bool returnsVoid = resultType.IsVoidType();
            bool hasByRefs = receiverRefKind != RefKind.None || !refKinds.IsDefaultOrEmpty;
 
            if (!hasByRefs)
            {
                var wkDelegateType = returnsVoid ?
                    WellKnownTypes.GetWellKnownActionDelegate(invokeArgumentCount: delegateSignature.Length) :
                    WellKnownTypes.GetWellKnownFunctionDelegate(invokeArgumentCount: delegateSignature.Length - 1);
 
                if (wkDelegateType != WellKnownType.Unknown)
                {
                    var delegateType = _factory.Compilation.GetWellKnownType(wkDelegateType);
                    if (!delegateType.HasUseSiteError)
                    {
                        _factory.Diagnostics.AddDependencies(delegateType);
                        return delegateType.Construct(delegateSignature);
                    }
                }
            }
 
            RefKindVector byRefs;
            if (hasByRefs)
            {
                byRefs = RefKindVector.Create(1 + (loweredReceiver != null ? 1 : 0) + loweredArguments.Length + (loweredRight != null ? 1 : 0) + (returnsVoid ? 0 : 1));
 
                int j = 1;
                if (loweredReceiver != null)
                {
                    byRefs[j++] = getRefKind(receiverRefKind);
                }
 
                if (!refKinds.IsDefault)
                {
                    for (int i = 0; i < refKinds.Length; i++, j++)
                    {
                        byRefs[j] = getRefKind(refKinds[i]);
                    }
                }
 
                if (!returnsVoid)
                {
                    byRefs[j++] = RefKind.None;
                }
            }
            else
            {
                byRefs = default(RefKindVector);
            }
 
            int parameterCount = delegateSignature.Length - (returnsVoid ? 0 : 1);
            Debug.Assert(_factory.CompilationState.ModuleBuilderOpt is { });
            int generation = _factory.CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal;
            var synthesizedType = _factory.Compilation.AnonymousTypeManager.SynthesizeDelegate(parameterCount, byRefs, returnsVoid, generation);
            return synthesizedType.Construct(delegateSignature);
 
            // The distinction between by-ref kinds is ignored for dynamic call-sites.
            static RefKind getRefKind(RefKind refKind)
            {
                Debug.Assert(refKind != RefKind.RefReadOnlyParameter);
                return refKind == RefKind.None ? RefKind.None : RefKind.Ref;
            }
        }
 
        private BoundExpression GetArgumentInfo(
            MethodSymbol argumentInfoFactory,
            BoundExpression boundArgument,
            string? name,
            RefKind refKind,
            bool isStaticType)
        {
            CSharpArgumentInfoFlags flags = 0;
 
            if (isStaticType)
            {
                flags |= CSharpArgumentInfoFlags.IsStaticType;
            }
 
            if (name != null)
            {
                flags |= CSharpArgumentInfoFlags.NamedArgument;
            }
 
            Debug.Assert(refKind == RefKind.None || refKind == RefKind.Ref || refKind == RefKind.Out, "unexpected refKind in dynamic");
 
            // by-ref type doesn't trigger dynamic dispatch and it can't be a null literal => set UseCompileTimeType
            if (refKind == RefKind.Out)
            {
                flags |= CSharpArgumentInfoFlags.IsOut | CSharpArgumentInfoFlags.UseCompileTimeType;
            }
            else if (refKind == RefKind.Ref)
            {
                flags |= CSharpArgumentInfoFlags.IsRef | CSharpArgumentInfoFlags.UseCompileTimeType;
            }
 
            var argType = boundArgument.Type;
 
            // Check "literal" constant.
 
            // What the runtime binder does with this LiteralConstant flag is just to create a constant,
            // which is a compelling enough reason to make sure that on the production end of the binder
            // data, we do the inverse (i.e., use the LiteralConstant flag whenever we encounter a constant
            // argument.
 
            // And in fact, the bug being fixed with this change is that the compiler will consider constants
            // for numeric and enum conversions even if they are not literals (such as, (1-1) --> enum), but
            // the runtime binder didn't. So we do need to set this flag whenever we see a constant.
 
            // But the complication is that null values lose their type when they get to the runtime binder,
            // and so we need a way to distinguish a null constant of any given type from the null literal.
            // The design is simple! We use UseCompileTimeType to determine whether we care about the type of
            // a null constant argument, so that the null literal gets "LiteralConstant" whereas every other
            // constant gets "LiteralConstant | UseCompileTimeType". Because obviously UseCompileTimeType is
            // wrong for the null literal.
 
            // We care, because we want to prevent this from working:
            // 
            //    const C x = null;
            //    class C { public void M(SomeUnrelatedReferenceType x) { } }
            //    ...
            //    dynamic d = new C(); d.M(x); // This will pass a null constant and the type is gone!
            //
            // as well as the alternative where x is a const null of type object.
 
            if (boundArgument.ConstantValueOpt != null)
            {
                flags |= CSharpArgumentInfoFlags.Constant;
            }
 
            // Check compile time type.
            // See also DynamicRewriter::GenerateCallingObjectFlags.
            if (argType is { } && !argType.IsDynamic())
            {
                flags |= CSharpArgumentInfoFlags.UseCompileTimeType;
            }
 
            return _factory.Call(null, argumentInfoFactory, _factory.Literal((int)flags), _factory.Literal(name));
        }
 
        private static ImmutableArray<BoundExpression> GetCallSiteArguments(BoundExpression callSiteFieldAccess, BoundExpression? receiver, ImmutableArray<BoundExpression> arguments, BoundExpression? right)
        {
            var result = new BoundExpression[1 + (receiver != null ? 1 : 0) + arguments.Length + (right != null ? 1 : 0)];
            int j = 0;
 
            result[j++] = callSiteFieldAccess;
 
            if (receiver != null)
            {
                result[j++] = receiver;
            }
 
            arguments.CopyTo(result, j);
            j += arguments.Length;
 
            if (right != null)
            {
                result[j++] = right;
            }
 
            return result.AsImmutableOrNull();
        }
 
        private TypeSymbol[] MakeCallSiteDelegateSignature(TypeSymbol callSiteType, BoundExpression? receiver, ImmutableArray<BoundExpression> arguments, BoundExpression? right, TypeSymbol resultType)
        {
            var systemObjectType = _factory.SpecialType(SpecialType.System_Object);
            var result = new TypeSymbol[1 + (receiver != null ? 1 : 0) + arguments.Length + (right != null ? 1 : 0) + (resultType.IsVoidType() ? 0 : 1)];
            int j = 0;
 
            // CallSite:
            result[j++] = callSiteType;
 
            // receiver:
            if (receiver != null)
            {
                result[j++] = receiver.Type ?? systemObjectType;
            }
 
            // argument types:
            for (int i = 0; i < arguments.Length; i++)
            {
                result[j++] = arguments[i].Type ?? systemObjectType;
            }
 
            // right hand side of an assignment:
            if (right != null)
            {
                result[j++] = right.Type ?? systemObjectType;
            }
 
            // return type:
            if (j < result.Length)
            {
                result[j++] = resultType ?? systemObjectType;
            }
 
            return result;
        }
    }
}