Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Specializing layouts of LambdaExpression nodes #13133

Merged
merged 9 commits into from
Nov 23, 2016
3 changes: 1 addition & 2 deletions src/Common/src/System/Dynamic/Utils/ExpressionUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace System.Dynamic.Utils
{
internal static class ExpressionUtils
internal static partial class ExpressionUtils
{
public static ReadOnlyCollection<T> ReturnReadOnly<T>(ref IReadOnlyList<T> collection)
{
Expand Down Expand Up @@ -67,7 +67,6 @@ public static ReadOnlyCollection<Expression> ReturnReadOnly(IArgumentProvider pr
return (ReadOnlyCollection<Expression>)collection;
}


/// <summary>
/// Helper which is used for specialized subtypes which use ReturnReadOnly(ref object, ...).
/// This is the reverse version of ReturnReadOnly which takes an IArgumentProvider.
Expand Down
96 changes: 54 additions & 42 deletions src/Common/src/System/Dynamic/Utils/ListArgumentProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,25 @@

namespace System.Dynamic.Utils
{
/// <summary>
/// Provides a wrapper around an IArgumentProvider which exposes the argument providers
/// members out as an IList of Expression. This is used to avoid allocating an array
/// which needs to be stored inside of a ReadOnlyCollection. Instead this type has
/// the same amount of overhead as an array without duplicating the storage of the
/// elements. This ensures that internally we can avoid creating and copying arrays
/// while users of the Expression trees also don't pay a size penalty for this internal
/// optimization. See IArgumentProvider for more general information on the Expression
/// tree optimizations being used here.
/// </summary>
internal sealed class ListArgumentProvider : IList<Expression>
internal abstract class ListProvider<T> : IList<T>
where T : class
{
private readonly IArgumentProvider _provider;
private readonly Expression _arg0;

internal ListArgumentProvider(IArgumentProvider provider, Expression arg0)
{
_provider = provider;
_arg0 = arg0;
}
protected abstract T First { get; }
protected abstract int ElementCount { get; }
protected abstract T GetElement(int index);

#region IList<Expression> Members
#region IList<T> Members

public int IndexOf(Expression item)
public int IndexOf(T item)
{
if (_arg0 == item)
if (First == item)
{
return 0;
}

for (int i = 1, n = _provider.ArgumentCount; i < n; i++)
for (int i = 1, n = ElementCount; i < n; i++)
{
if (_provider.GetArgument(i) == item)
if (GetElement(i) == item)
{
return i;
}
Expand All @@ -49,7 +35,7 @@ public int IndexOf(Expression item)
return -1;
}

public void Insert(int index, Expression item)
public void Insert(int index, T item)
{
throw ContractUtils.Unreachable;
}
Expand All @@ -59,16 +45,16 @@ public void RemoveAt(int index)
throw ContractUtils.Unreachable;
}

public Expression this[int index]
public T this[int index]
{
get
{
if (index == 0)
{
return _arg0;
return First;
}

return _provider.GetArgument(index);
return GetElement(index);
}
set
{
Expand All @@ -78,9 +64,9 @@ public Expression this[int index]

#endregion

#region ICollection<Expression> Members
#region ICollection<T> Members

public void Add(Expression item)
public void Add(T item)
{
throw ContractUtils.Unreachable;
}
Expand All @@ -90,37 +76,37 @@ public void Clear()
throw ContractUtils.Unreachable;
}

public bool Contains(Expression item) => IndexOf(item) != -1;
public bool Contains(T item) => IndexOf(item) != -1;

public void CopyTo(Expression[] array, int arrayIndex)
public void CopyTo(T[] array, int arrayIndex)
{
array[arrayIndex++] = _arg0;
for (int i = 1, n = _provider.ArgumentCount; i < n; i++)
array[arrayIndex++] = First;
for (int i = 1, n = ElementCount; i < n; i++)
{
array[arrayIndex++] = _provider.GetArgument(i);
array[arrayIndex++] = GetElement(i);
}
}

public int Count => _provider.ArgumentCount;
public int Count => ElementCount;

public bool IsReadOnly => true;

public bool Remove(Expression item)
public bool Remove(T item)
{
throw ContractUtils.Unreachable;
}

#endregion

#region IEnumerable<Expression> Members
#region IEnumerable<T> Members

public IEnumerator<Expression> GetEnumerator()
public IEnumerator<T> GetEnumerator()
{
yield return _arg0;
yield return First;

for (int i = 1, n = _provider.ArgumentCount; i < n; i++)
for (int i = 1, n = ElementCount; i < n; i++)
{
yield return _provider.GetArgument(i);
yield return GetElement(i);
}
}

Expand All @@ -132,4 +118,30 @@ public IEnumerator<Expression> GetEnumerator()

#endregion
}

/// <summary>
/// Provides a wrapper around an IArgumentProvider which exposes the argument providers
/// members out as an IList of Expression. This is used to avoid allocating an array
/// which needs to be stored inside of a ReadOnlyCollection. Instead this type has
/// the same amount of overhead as an array without duplicating the storage of the
/// elements. This ensures that internally we can avoid creating and copying arrays
/// while users of the Expression trees also don't pay a size penalty for this internal
/// optimization. See IArgumentProvider for more general information on the Expression
/// tree optimizations being used here.
/// </summary>
internal sealed class ListArgumentProvider : ListProvider<Expression>
{
private readonly IArgumentProvider _provider;
private readonly Expression _arg0;

internal ListArgumentProvider(IArgumentProvider provider, Expression arg0)
{
_provider = provider;
_arg0 = arg0;
}

protected override Expression First => _arg0;
protected override int ElementCount => _provider.ArgumentCount;
protected override Expression GetElement(int index) => _provider.GetArgument(index);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@
<Compile Include="$(CommonPath)\System\Runtime\CompilerServices\TrueReadOnlyCollection.cs">
<Link>Common\System\Runtime\CompilerServices\TrueReadOnlyCollection.cs</Link>
</Compile>
<Compile Include="System\Dynamic\Utils\ExpressionUtils.cs" />
<Compile Include="System\Dynamic\Utils\ExpressionVisitorUtils.cs" />
<Compile Include="System\Dynamic\Utils\ListParameterProvider.cs" />
<Compile Include="System\Dynamic\Utils\TypeExtensions.cs" />
<Compile Include="System\Dynamic\Utils\TypeUtils.cs" />
<Compile Include="System\Linq\Expressions\Common\ArrayBuilderExtensions.cs" />
Expand All @@ -126,6 +128,7 @@
<Compile Include="System\Linq\Expressions\IDynamicExpression.cs" />
<Compile Include="System\Linq\Expressions\IndexExpression.cs" />
<Compile Include="System\Linq\Expressions\InvocationExpression.cs" />
<Compile Include="System\Linq\Expressions\IParameterProvider.cs" />
<Compile Include="System\Linq\Expressions\LabelExpression.cs" />
<Compile Include="System\Linq\Expressions\LabelTarget.cs" />
<Compile Include="System\Linq\Expressions\LambdaExpression.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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.ObjectModel;
using System.Linq.Expressions;
using System.Threading;

namespace System.Dynamic.Utils
{
internal static partial class ExpressionUtils
{
/// <summary>
/// See overload with <see cref="IArgumentProvider"/> for more information.
/// </summary>
public static ReadOnlyCollection<ParameterExpression> ReturnReadOnly(IParameterProvider provider, ref object collection)
{
ParameterExpression tObj = collection as ParameterExpression;
if (tObj != null)
{
// otherwise make sure only one readonly collection ever gets exposed
Interlocked.CompareExchange(
ref collection,
new ReadOnlyCollection<ParameterExpression>(new ListParameterProvider(provider, tObj)),
tObj
);
}

// and return what is not guaranteed to be a readonly collection
return (ReadOnlyCollection<ParameterExpression>)collection;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,30 @@ public static Expression[] VisitBlockExpressions(ExpressionVisitor visitor, Bloc
}
return newNodes;
}

public static ParameterExpression[] VisitParameters(ExpressionVisitor visitor, IParameterProvider nodes, string callerName)
{
ParameterExpression[] newNodes = null;
for (int i = 0, n = nodes.ParameterCount; i < n; i++)
{
ParameterExpression curNode = nodes.GetParameter(i);
ParameterExpression node = visitor.VisitAndConvert(curNode, callerName);

if (newNodes != null)
{
newNodes[i] = node;
}
else if (!object.ReferenceEquals(node, curNode))
{
newNodes = new ParameterExpression[n];
for (int j = 0; j < i; j++)
{
newNodes[j] = nodes.GetParameter(j);
}
newNodes[i] = node;
}
}
return newNodes;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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.Linq.Expressions;

namespace System.Dynamic.Utils
{
/// <summary>
/// See <see cref="ListArgumentProvider"/> for design considerations.
/// </summary>
internal sealed class ListParameterProvider : ListProvider<ParameterExpression>
{
private readonly IParameterProvider _provider;
private readonly ParameterExpression _arg0;

internal ListParameterProvider(IParameterProvider provider, ParameterExpression arg0)
{
_provider = provider;
_arg0 = arg0;
}

protected override ParameterExpression First => _arg0;
protected override int ElementCount => _provider.ParameterCount;
protected override ParameterExpression GetElement(int index) => _provider.GetParameter(index);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1407,7 +1407,7 @@ public static BinaryExpression Coalesce(Expression left, Expression right, Lambd
throw Error.UserDefinedOperatorMustNotBeVoid(conversion, nameof(conversion));
}
ParameterInfo[] pms = method.GetParametersCached();
Debug.Assert(pms.Length == conversion.Parameters.Count);
Debug.Assert(pms.Length == conversion.ParameterCount);
if (pms.Length != 1)
{
throw Error.IncorrectNumberOfMethodCallArguments(conversion, nameof(conversion));
Expand Down Expand Up @@ -1559,7 +1559,7 @@ private static void ValidateOpAssignConversionLambda(LambdaExpression conversion
Debug.Assert(typeof(System.MulticastDelegate).IsAssignableFrom(delegateType) && delegateType != typeof(System.MulticastDelegate));
MethodInfo mi = delegateType.GetMethod("Invoke");
ParameterInfo[] pms = mi.GetParametersCached();
Debug.Assert(pms.Length == conversion.Parameters.Count);
Debug.Assert(pms.Length == conversion.ParameterCount);
if (pms.Length != 1)
{
throw Error.IncorrectNumberOfMethodCallArguments(conversion, nameof(conversion));
Expand Down
Loading