|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.ObjectModel;
using System.Dynamic.Utils;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace System.Linq.Expressions.Compiler
{
internal sealed partial class StackSpiller
{
private abstract class BindingRewriter
{
protected readonly MemberBinding _binding;
protected readonly StackSpiller _spiller;
protected RewriteAction _action;
internal BindingRewriter(MemberBinding binding, StackSpiller spiller)
{
_binding = binding;
_spiller = spiller;
}
internal RewriteAction Action => _action;
internal abstract MemberBinding AsBinding();
internal abstract Expression AsExpression(Expression target);
internal static BindingRewriter Create(MemberBinding binding, StackSpiller spiller, Stack stack)
{
switch (binding.BindingType)
{
case MemberBindingType.Assignment:
MemberAssignment assign = (MemberAssignment)binding;
return new MemberAssignmentRewriter(assign, spiller, stack);
case MemberBindingType.ListBinding:
MemberListBinding list = (MemberListBinding)binding;
return new ListBindingRewriter(list, spiller, stack);
case MemberBindingType.MemberBinding:
MemberMemberBinding member = (MemberMemberBinding)binding;
return new MemberMemberBindingRewriter(member, spiller, stack);
}
throw Error.UnhandledBinding();
}
protected void RequireNoValueProperty()
{
if (_binding.Member is PropertyInfo property && property.PropertyType.IsValueType)
{
throw Error.CannotAutoInitializeValueTypeMemberThroughProperty(property);
}
}
}
private sealed class MemberMemberBindingRewriter : BindingRewriter
{
private readonly ReadOnlyCollection<MemberBinding> _bindings;
private readonly BindingRewriter[] _bindingRewriters;
internal MemberMemberBindingRewriter(MemberMemberBinding binding, StackSpiller spiller, Stack stack) :
base(binding, spiller)
{
_bindings = binding.Bindings;
int count = _bindings.Count;
_bindingRewriters = new BindingRewriter[count];
for (int i = 0; i < count; i++)
{
BindingRewriter br = BindingRewriter.Create(_bindings[i], spiller, stack);
_action |= br.Action;
_bindingRewriters[i] = br;
}
}
internal override MemberBinding AsBinding()
{
switch (_action)
{
case RewriteAction.None:
return _binding;
case RewriteAction.Copy:
int count = _bindings.Count;
MemberBinding[] newBindings = new MemberBinding[count];
for (int i = 0; i < count; i++)
{
newBindings[i] = _bindingRewriters[i].AsBinding();
}
return new MemberMemberBinding(_binding.Member, new TrueReadOnlyCollection<MemberBinding>(newBindings));
}
throw ContractUtils.Unreachable;
}
internal override Expression AsExpression(Expression target)
{
RequireNoValueProperty();
Expression member = MemberExpression.Make(target, _binding.Member);
Expression memberTemp = _spiller.MakeTemp(member.Type);
int count = _bindings.Count;
Expression[] block = new Expression[count + 2];
block[0] = new AssignBinaryExpression(memberTemp, member);
for (int i = 0; i < count; i++)
{
BindingRewriter br = _bindingRewriters[i];
block[i + 1] = br.AsExpression(memberTemp);
}
// We need to copy back value types.
if (memberTemp.Type.IsValueType)
{
block[count + 1] = Expression.Block(
typeof(void),
new AssignBinaryExpression(MemberExpression.Make(target, _binding.Member), memberTemp)
);
}
else
{
block[count + 1] = Utils.Empty;
}
return MakeBlock(block);
}
}
private sealed class ListBindingRewriter : BindingRewriter
{
private readonly ReadOnlyCollection<ElementInit> _inits;
private readonly ChildRewriter[] _childRewriters;
internal ListBindingRewriter(MemberListBinding binding, StackSpiller spiller, Stack stack) :
base(binding, spiller)
{
_inits = binding.Initializers;
int count = _inits.Count;
_childRewriters = new ChildRewriter[count];
for (int i = 0; i < count; i++)
{
ElementInit init = _inits[i];
ChildRewriter cr = new ChildRewriter(spiller, stack, init.Arguments.Count);
cr.Add(init.Arguments);
_action |= cr.Action;
_childRewriters[i] = cr;
}
}
internal override MemberBinding AsBinding()
{
switch (_action)
{
case RewriteAction.None:
return _binding;
case RewriteAction.Copy:
int count = _inits.Count;
ElementInit[] newInits = new ElementInit[count];
for (int i = 0; i < count; i++)
{
ChildRewriter cr = _childRewriters[i];
if (cr.Action == RewriteAction.None)
{
newInits[i] = _inits[i];
}
else
{
newInits[i] = new ElementInit(_inits[i].AddMethod, new TrueReadOnlyCollection<Expression>(cr[0, -1]!));
}
}
return new MemberListBinding(_binding.Member, new TrueReadOnlyCollection<ElementInit>(newInits));
}
throw ContractUtils.Unreachable;
}
internal override Expression AsExpression(Expression target)
{
RequireNoValueProperty();
Expression member = MemberExpression.Make(target, _binding.Member);
Expression memberTemp = _spiller.MakeTemp(member.Type);
int count = _inits.Count;
Expression[] block = new Expression[count + 2];
block[0] = new AssignBinaryExpression(memberTemp, member);
for (int i = 0; i < count; i++)
{
ChildRewriter cr = _childRewriters[i];
Result add = cr.Finish(new InstanceMethodCallExpressionN(_inits[i].AddMethod, memberTemp, cr[0, -1]!));
block[i + 1] = add.Node;
}
// We need to copy back value types
if (memberTemp.Type.IsValueType)
{
block[count + 1] = Expression.Block(
typeof(void),
new AssignBinaryExpression(MemberExpression.Make(target, _binding.Member), memberTemp)
);
}
else
{
block[count + 1] = Utils.Empty;
}
return MakeBlock(block);
}
}
private sealed class MemberAssignmentRewriter : BindingRewriter
{
private readonly Expression _rhs;
internal MemberAssignmentRewriter(MemberAssignment binding, StackSpiller spiller, Stack stack) :
base(binding, spiller)
{
Result result = spiller.RewriteExpression(binding.Expression, stack);
_action = result.Action;
_rhs = result.Node;
}
internal override MemberBinding AsBinding() =>
_action switch
{
RewriteAction.None => _binding,
RewriteAction.Copy => new MemberAssignment(_binding.Member, _rhs),
_ => throw ContractUtils.Unreachable,
};
internal override Expression AsExpression(Expression target)
{
Expression member = MemberExpression.Make(target, _binding.Member);
Expression memberTemp = _spiller.MakeTemp(member.Type);
return MakeBlock(
new AssignBinaryExpression(memberTemp, _rhs),
new AssignBinaryExpression(member, memberTemp),
Utils.Empty
);
}
}
}
}
|