/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs
933 строки
40 KB
Copilot
Implement runtime async support for dynamic (#83713)
04 июн 2026, 03:14
Не верифицирован
04 июн 2026, 03:14
563c933
Код
Авторство
О чём код?
// 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 System.Linq; 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 string? _localFunctionOrdinalOrSuffix; private NamedTypeSymbol? _currentDynamicCallSiteContainer; private int _callSiteIdDispenser; internal LoweredDynamicOperationFactory(SyntheticBoundNodeFactory factory, int methodOrdinal, string? localFunctionOrdinalOrSuffix = null) { Debug.Assert(factory != null); _factory = factory; _methodOrdinal = methodOrdinal; _localFunctionOrdinalOrSuffix = localFunctionOrdinalOrSuffix; } 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, _localFunctionOrdinalOrSuffix); } 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, string? localFunctionOrdinalOrSuffix) { 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, localFunctionOrdinalOrSuffix, 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, DeclarationModifiers.Public, 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; } } }