|
// 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.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
internal sealed partial class LocalRewriter
{
public override BoundNode VisitDynamicObjectCreationExpression(BoundDynamicObjectCreationExpression node)
{
// There are no target types for dynamic object creation scenarios, so there should be no implicit handler conversions
AssertNoImplicitInterpolatedStringHandlerConversions(node.Arguments);
var loweredArguments = VisitList(node.Arguments);
var constructorInvocation = _dynamicFactory.MakeDynamicConstructorInvocation(node.Syntax, node.Type, loweredArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt).ToExpression();
if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors)
{
return constructorInvocation;
}
return MakeExpressionWithInitializer(node.Syntax, constructorInvocation, node.InitializerExpressionOpt, node.Type);
}
public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node)
{
Debug.Assert(node != null);
var constructor = node.Constructor;
// Rewrite the arguments.
// NOTE: We may need additional argument rewriting such as
// re-ordering arguments based on argsToParamsOpt map, etc.
// NOTE: This is done later by MakeArguments, for now we just lower each argument.
BoundExpression? receiverDiscard = null;
ImmutableArray<RefKind> argumentRefKindsOpt = node.ArgumentRefKindsOpt;
ArrayBuilder<LocalSymbol>? tempsBuilder = null;
ImmutableArray<BoundExpression> rewrittenArguments = VisitArgumentsAndCaptureReceiverIfNeeded(
ref receiverDiscard,
captureReceiverMode: ReceiverCaptureMode.Default,
node.Arguments,
constructor,
node.ArgsToParamsOpt,
argumentRefKindsOpt,
storesOpt: null,
ref tempsBuilder);
Debug.Assert(receiverDiscard is null);
// We have already lowered each argument, but we may need some additional rewriting for the arguments,
// such as re-ordering arguments based on argsToParamsOpt map, etc.
rewrittenArguments = MakeArguments(
rewrittenArguments,
constructor,
node.Expanded,
node.ArgsToParamsOpt,
ref argumentRefKindsOpt,
ref tempsBuilder);
BoundExpression rewrittenObjectCreation;
var temps = tempsBuilder.ToImmutableAndFree();
if (_inExpressionLambda)
{
if (!temps.IsDefaultOrEmpty)
{
throw ExceptionUtilities.UnexpectedValue(temps.Length);
}
rewrittenObjectCreation = node.Update(constructor, rewrittenArguments, argumentRefKindsOpt, MakeObjectCreationInitializerForExpressionTree(node.InitializerExpressionOpt), changeTypeOpt: constructor.ContainingType);
if (node.Type.IsInterfaceType())
{
Debug.Assert(TypeSymbol.Equals(rewrittenObjectCreation.Type, ((NamedTypeSymbol)node.Type).ComImportCoClass, TypeCompareKind.ConsiderEverything2));
rewrittenObjectCreation = MakeConversionNode(rewrittenObjectCreation, node.Type, false, false);
}
return rewrittenObjectCreation;
}
if (Instrument)
{
BoundExpression? receiver = null;
Instrumenter.InterceptCallAndAdjustArguments(ref constructor, ref receiver, ref rewrittenArguments, ref argumentRefKindsOpt);
}
rewrittenObjectCreation = node.Update(constructor, rewrittenArguments, argumentRefKindsOpt, newInitializerExpression: null, changeTypeOpt: constructor.ContainingType);
// replace "new S()" with a default struct ctor with "default(S)"
if (constructor.IsDefaultValueTypeConstructor())
{
rewrittenObjectCreation = new BoundDefaultExpression(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type!);
}
if (!temps.IsDefaultOrEmpty)
{
rewrittenObjectCreation = new BoundSequence(
node.Syntax,
temps,
ImmutableArray<BoundExpression>.Empty,
rewrittenObjectCreation,
node.Type);
}
if (node.Type.IsInterfaceType())
{
Debug.Assert(TypeSymbol.Equals(rewrittenObjectCreation.Type, ((NamedTypeSymbol)node.Type).ComImportCoClass, TypeCompareKind.ConsiderEverything2));
rewrittenObjectCreation = MakeConversionNode(rewrittenObjectCreation, node.Type, false, false);
}
if (Instrument)
{
rewrittenObjectCreation = Instrumenter.InstrumentObjectCreationExpression(node, rewrittenObjectCreation);
}
if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors)
{
return rewrittenObjectCreation;
}
return MakeExpressionWithInitializer(node.Syntax, rewrittenObjectCreation, node.InitializerExpressionOpt, node.Type);
}
public override BoundNode VisitWithExpression(BoundWithExpression withExpr)
{
TypeSymbol type = withExpr.Type;
BoundExpression receiver = withExpr.Receiver;
Debug.Assert(receiver.Type!.Equals(type, TypeCompareKind.ConsiderEverything));
// for a with expression of the form
//
// receiver with { P1 = e1, P2 = e2 } // P3 is copied implicitly
//
// if the receiver is a struct, duplicate the value, then set the given struct properties:
//
// var tmp = receiver;
// tmp.P1 = e1;
// tmp.P2 = e2;
// tmp
//
// if the receiver is an anonymous type, then invoke its constructor:
//
// new Type(e1, e2, receiver.P3);
//
// otherwise the receiver is a record class and we want to lower it to a call to its `Clone` method, then
// set the given record properties. i.e.
//
// var tmp = (ReceiverType)receiver.Clone();
// tmp.P1 = e1;
// tmp.P2 = e2;
// tmp
BoundExpression rewrittenReceiver = VisitExpression(receiver);
if (type.IsAnonymousType)
{
var anonymousType = (AnonymousTypeManager.AnonymousTypePublicSymbol)type;
var sideEffects = ArrayBuilder<BoundExpression>.GetInstance();
var temps = ArrayBuilder<LocalSymbol>.GetInstance();
BoundLocal oldValue = _factory.StoreToTemp(rewrittenReceiver, out BoundAssignmentOperator boundAssignmentToTemp);
temps.Add(oldValue.LocalSymbol);
sideEffects.Add(boundAssignmentToTemp);
BoundExpression value = _factory.New(anonymousType, getAnonymousTypeValues(withExpr, oldValue, anonymousType, sideEffects, temps));
return new BoundSequence(withExpr.Syntax, temps.ToImmutableAndFree(), sideEffects.ToImmutableAndFree(), value, type);
}
BoundExpression expression;
if (type.IsValueType)
{
expression = rewrittenReceiver;
}
else
{
Debug.Assert(withExpr.CloneMethod is not null);
Debug.Assert(withExpr.CloneMethod.ParameterCount == 0);
expression = _factory.Convert(
type,
_factory.Call(
rewrittenReceiver,
withExpr.CloneMethod));
}
return MakeExpressionWithInitializer(
withExpr.Syntax,
expression,
withExpr.InitializerExpression,
type);
ImmutableArray<BoundExpression> getAnonymousTypeValues(BoundWithExpression withExpr, BoundExpression oldValue, AnonymousTypeManager.AnonymousTypePublicSymbol anonymousType,
ArrayBuilder<BoundExpression> sideEffects, ArrayBuilder<LocalSymbol> temps)
{
// map: [propertyIndex] -> valueTemp
var valueTemps = ArrayBuilder<BoundExpression?>.GetInstance(anonymousType.Properties.Length, fillWithValue: null);
foreach (BoundExpression initializer in withExpr.InitializerExpression.Initializers)
{
var assignment = (BoundAssignmentOperator)initializer;
var left = (BoundObjectInitializerMember)assignment.Left;
Debug.Assert(left.MemberSymbol is not null);
// We evaluate the values provided in source first
var rewrittenRight = VisitExpression(assignment.Right);
BoundLocal valueTemp = _factory.StoreToTemp(rewrittenRight, out BoundAssignmentOperator boundAssignmentToTemp);
temps.Add(valueTemp.LocalSymbol);
sideEffects.Add(boundAssignmentToTemp);
var property = left.MemberSymbol;
Debug.Assert(property.MemberIndexOpt!.Value >= 0 && property.MemberIndexOpt.Value < anonymousType.Properties.Length);
valueTemps[property.MemberIndexOpt.Value] = valueTemp;
}
var builder = ArrayBuilder<BoundExpression>.GetInstance(anonymousType.Properties.Length);
foreach (var property in anonymousType.Properties)
{
if (valueTemps[property.MemberIndexOpt!.Value] is BoundExpression initializerValue)
{
builder.Add(initializerValue);
}
else
{
// The values that are implicitly copied over will get evaluated afterwards, in the order they are needed
builder.Add(_factory.Property(oldValue, property));
}
}
valueTemps.Free();
return builder.ToImmutableAndFree();
}
}
[return: NotNullIfNotNull(nameof(initializerExpressionOpt))]
private BoundObjectInitializerExpressionBase? MakeObjectCreationInitializerForExpressionTree(BoundObjectInitializerExpressionBase? initializerExpressionOpt)
{
if (initializerExpressionOpt != null && !initializerExpressionOpt.HasErrors)
{
// We may need to MakeArguments for collection initializer add method call if the method has a param array parameter.
var rewrittenInitializers = MakeObjectOrCollectionInitializersForExpressionTree(initializerExpressionOpt);
return UpdateInitializers(initializerExpressionOpt, rewrittenInitializers);
}
return null;
}
// Shared helper for VisitWithExpression, MakeObjectCreationWithInitializer and MakeNewT
private BoundExpression MakeExpressionWithInitializer(
SyntaxNode syntax,
BoundExpression rewrittenExpression,
BoundExpression initializerExpression,
TypeSymbol type)
{
Debug.Assert(!_inExpressionLambda);
Debug.Assert(initializerExpression != null && !initializerExpression.HasErrors);
// Create a temp and assign it with the object creation expression.
BoundAssignmentOperator boundAssignmentToTemp;
BoundLocal value = _factory.StoreToTemp(rewrittenExpression, out boundAssignmentToTemp, isKnownToReferToTempIfReferenceType: true);
// Rewrite object/collection initializer expressions
ArrayBuilder<BoundExpression>? dynamicSiteInitializers = null;
ArrayBuilder<LocalSymbol>? temps = null;
ArrayBuilder<BoundExpression>? loweredInitializers = ArrayBuilder<BoundExpression>.GetInstance();
AddObjectOrCollectionInitializers(ref dynamicSiteInitializers, ref temps, loweredInitializers, value, initializerExpression);
int dynamicSiteCount = dynamicSiteInitializers?.Count ?? 0;
var sideEffects = ArrayBuilder<BoundExpression>.GetInstance(1 + dynamicSiteCount + loweredInitializers.Count);
sideEffects.Add(boundAssignmentToTemp);
if (dynamicSiteCount > 0)
{
sideEffects.AddRange(dynamicSiteInitializers!);
dynamicSiteInitializers!.Free();
}
sideEffects.AddRange(loweredInitializers);
loweredInitializers.Free();
ImmutableArray<LocalSymbol> locals;
if (temps == null)
{
locals = ImmutableArray.Create(value.LocalSymbol);
}
else
{
temps.Insert(0, value.LocalSymbol);
locals = temps.ToImmutableAndFree();
}
return new BoundSequence(
syntax,
locals,
sideEffects.ToImmutableAndFree(),
value,
type);
}
public override BoundNode VisitNewT(BoundNewT node)
{
if (_inExpressionLambda)
{
return node.Update(MakeObjectCreationInitializerForExpressionTree(node.InitializerExpressionOpt), node.WasTargetTyped, node.Type);
}
var rewrittenNewT = MakeNewT(node.Syntax, (TypeParameterSymbol)node.Type);
if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors)
{
return rewrittenNewT;
}
return MakeExpressionWithInitializer(node.Syntax, rewrittenNewT, node.InitializerExpressionOpt, rewrittenNewT.Type!);
}
private BoundExpression MakeNewT(SyntaxNode syntax, TypeParameterSymbol typeParameter)
{
// "new T()" is rewritten as: "Activator.CreateInstance<T>()".
// NOTE: DIFFERENCE FROM DEV12
// Dev12 tried to statically optimize this and would emit default(T) if T happens to be struct
// However semantics of "new" in C# requires that parameterless constructor be called
// if struct defines one.
// Since we cannot know if T has a parameterless constructor statically,
// we must call Activator.CreateInstance unconditionally.
MethodSymbol? method;
if (!this.TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Activator__CreateInstance_T, out method))
{
return new BoundDefaultExpression(syntax, type: typeParameter, hasErrors: true);
}
Debug.Assert((object)method != null);
method = method.Construct(ImmutableArray.Create<TypeSymbol>(typeParameter));
method.CheckConstraints(new ConstraintsHelper.CheckConstraintsArgs(_compilation, _compilation.Conversions, syntax.GetLocation(), _diagnostics));
var createInstanceCall = new BoundCall(
syntax,
receiverOpt: null,
initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown,
method,
ImmutableArray<BoundExpression>.Empty,
default(ImmutableArray<string?>),
default(ImmutableArray<RefKind>),
isDelegateCall: false,
expanded: false,
invokedAsExtensionMethod: false,
argsToParamsOpt: default(ImmutableArray<int>),
defaultArguments: default(BitVector),
resultKind: LookupResultKind.Viable,
type: typeParameter);
return createInstanceCall;
}
public override BoundNode VisitNoPiaObjectCreationExpression(BoundNoPiaObjectCreationExpression node)
{
// For the NoPIA feature, we need to gather the GUID from the coclass, and
// generate the following:
//
// (IPiaType)System.Activator.CreateInstance(System.Runtime.InteropServices.Marshal.GetTypeFromCLSID(new Guid(GUID)))
//
// If System.Runtime.InteropServices.Marshal.GetTypeFromCLSID is not available (older framework),
// System.Type.GetTypeFromCLSID() is used to get the type for the CLSID:
//
// (IPiaType)System.Activator.CreateInstance(System.Type.GetTypeFromCLSID(new Guid(GUID)))
SyntaxNode oldSyntax = _factory.Syntax;
_factory.Syntax = node.Syntax;
var ctor = _factory.WellKnownMethod(WellKnownMember.System_Guid__ctor);
BoundExpression newGuid;
if (ctor is { })
{
Debug.Assert(node.GuidString is { });
newGuid = _factory.New(ctor, _factory.Literal(node.GuidString));
}
else
{
newGuid = new BoundBadExpression(node.Syntax, LookupResultKind.NotCreatable, ImmutableArray<Symbol?>.Empty, ImmutableArray<BoundExpression>.Empty, ErrorTypeSymbol.UnknownResultType);
}
var getTypeFromCLSID = _factory.WellKnownMethod(WellKnownMember.System_Runtime_InteropServices_Marshal__GetTypeFromCLSID, isOptional: true);
if (getTypeFromCLSID is null)
{
getTypeFromCLSID = _factory.WellKnownMethod(WellKnownMember.System_Type__GetTypeFromCLSID);
}
BoundExpression callGetTypeFromCLSID;
if (getTypeFromCLSID is { })
{
callGetTypeFromCLSID = _factory.Call(null, getTypeFromCLSID, newGuid);
}
else
{
callGetTypeFromCLSID = new BoundBadExpression(node.Syntax, LookupResultKind.OverloadResolutionFailure, ImmutableArray<Symbol?>.Empty, ImmutableArray<BoundExpression>.Empty, ErrorTypeSymbol.UnknownResultType);
}
var createInstance = _factory.WellKnownMethod(WellKnownMember.System_Activator__CreateInstance);
BoundExpression rewrittenObjectCreation;
if ((object)createInstance != null)
{
rewrittenObjectCreation = _factory.Convert(node.Type, _factory.Call(null, createInstance, callGetTypeFromCLSID));
}
else
{
rewrittenObjectCreation = new BoundBadExpression(node.Syntax, LookupResultKind.OverloadResolutionFailure, ImmutableArray<Symbol?>.Empty, ImmutableArray<BoundExpression>.Empty, node.Type);
}
_factory.Syntax = oldSyntax;
if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors)
{
return rewrittenObjectCreation;
}
return MakeExpressionWithInitializer(node.Syntax, rewrittenObjectCreation, node.InitializerExpressionOpt, node.Type);
}
}
}
|