• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NetFabric / NetFabric.CodeAnalysis / 6253798736

20 Sep 2023 08:33PM UTC coverage: 75.653% (-7.5%) from 83.158%
6253798736

Pull #28

github

aalmada
Fixes
Pull Request #28: Add IsIndexable

292 of 404 branches covered (0.0%)

Branch coverage included in aggregate %.

259 of 259 new or added lines in 15 files covered. (100.0%)

693 of 898 relevant lines covered (77.17%)

17.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.71
/NetFabric.Reflection/Expressions/ExpressionEx.ForEach.cs
1
using System;
2
using System.Collections;
3
using System.Linq.Expressions;
4
using NetFabric.Reflection;
5
using static System.Linq.Expressions.Expression;
6

7
namespace NetFabric.Expressions;
8

9
public static partial class ExpressionEx
10
{
11
    /// <summary>
12
    /// Creates a <see cref="System.Linq.Expressions.Expression"/> with behaviour similar to the <c>foreach</c> statement in C#.
13
    /// </summary>
14
    /// <param name="enumerable">An <see cref="System.Linq.Expressions.Expression"/> that defines the enumerator.</param>
15
    /// <param name="body">
16
    /// A <see cref="System.Func&lt;Expression, Expression&gt;"/> that returns the body, given an
17
    /// <see cref="System.Linq.Expressions.Expression"/> that defines the item on each loop iteration.
18
    /// </param>
19
    /// <returns>The created <see cref="System.Linq.Expressions.Expression"/>.</returns>
20
    /// <exception cref="Exception">Object is not enumerable.</exception>
21
    /// <remarks>
22
    /// <p>
23
    /// The created <see cref="System.Linq.Expressions.Expression"/> depends on if the object defined in <paramref name="enumerable"/>
24
    /// is an <c>interface</c>, <c>class</c>, <c>struct</c>, <c>ref struct</c>, and if is disposable.
25
    /// </p>
26
    /// </remarks>
27
    public static Expression ForEach(Expression enumerable, Func<Expression, Expression> body)
28
    {
29
        var enumerableType = enumerable.Type;
54✔
30
        if (enumerableType.IsEnumerable(out var enumerableInfo, out var error))
54!
31
            return ForEach(enumerableInfo, enumerable, body);
54✔
32

33
        return error switch
×
34
        {
×
35
            IsEnumerableError.MissingGetEnumerator => throw new Exception($"'{enumerableType}' does not contain a public definition for 'GetEnumerator'"),
×
36
            IsEnumerableError.MissingCurrent => throw new Exception($"'{enumerableInfo!.GetEnumerator.ReturnType}' does not contain a public definition for 'Current'"),
×
37
            IsEnumerableError.MissingMoveNext => throw new Exception($"'{enumerableInfo!.GetEnumerator.ReturnType}' does not contain a public definition for 'MoveNext'"),
×
38
            _ => throw new Exception("Unhandled error!"),
×
39
        };
×
40
    }
41

42
    internal static int localVariableCounter = 0;
43

44
    /// <summary>
45
    /// Creates a <see cref="System.Linq.Expressions.Expression"/> with behaviour similar to the <c>foreach</c> statement in C#.
46
    /// </summary>
47
    /// <param name="enumerableInfo">The information returned by a call to <see cref="NetFabric.Reflection.TypeExtensions.IsEnumerable(Type, out EnumerableInfo)"/>.</param>
48
    /// <param name="enumerable">An <see cref="System.Linq.Expressions.Expression"/> that defines the enumerator.</param>
49
    /// <param name="body">
50
    /// A <see cref="System.Func&lt;Expression, Expression&gt;"/> that returns the body, given an
51
    /// <see cref="System.Linq.Expressions.Expression"/> that defines the item on each loop iteration.
52
    /// </param>
53
    /// <returns>The created <see cref="System.Linq.Expressions.Expression"/>.</returns>
54
    /// <remarks>
55
    /// <p>
56
    /// The created <see cref="System.Linq.Expressions.Expression"/> depends on if the object defined in <paramref name="enumerable"/>
57
    /// is an <c>interface</c>, <c>class</c>, <c>struct</c>, <c>ref struct</c>, and if is disposable.
58
    /// </p>
59
    /// </remarks>
60
    public static Expression ForEach(EnumerableInfo enumerableInfo, Expression enumerable, Func<Expression, Expression> body)
61
    {
62
        if(enumerableInfo.ForEachUsesIndexer)
54✔
63
        {
64
            if (enumerable.Type.IsSpanOrReadOnlySpan())
6!
65
            {                
66
                // throws exception because the indexer returns a reference to the item and 
67
                // references are not yet supported by expression trees
68
                throw new NotSupportedException("ForEach Expression creation for Span<> or ReadOnlySpan<> is not supported.");
×
69
            }
70

71
            return UseIndexer(enumerable, body);
6✔
72
        }
73
        
74
        return UseEnumerable(enumerableInfo, enumerable, body);
48✔
75
        
76
        static Expression UseIndexer(Expression array, Func<Expression, Expression> body)
77
        {
78
            var indexVariable = Variable(typeof(int), "index");
6✔
79
            var arrayVariable = Variable(array.Type, $"__local_array_{localVariableCounter++}__");
6✔
80
            return Block(
6✔
81
                new[] { indexVariable, arrayVariable },
6✔
82
                Assign(arrayVariable, array), // local array for JIT compiler to remove bounds check
6✔
83
                For(
6✔
84
                    Assign(indexVariable, Constant(0)),
6✔
85
                    LessThan(indexVariable, Property(arrayVariable, typeof(Array).GetReadProperty(TypeExtensions.PublicInstanceDeclaredOnly, NameOf.Length)!)),
6✔
86
                    PostIncrementAssign(indexVariable),
6✔
87
                    body(ArrayIndex(arrayVariable, indexVariable))
6✔
88
                )
6✔
89
            );
6✔
90
        }
91

92
        static Expression UseEnumerable(EnumerableInfo enumerableInfo, Expression enumerable, Func<Expression, Expression> body)
93
        {
94
            var enumeratorType = enumerableInfo.GetEnumerator.ReturnType;
48✔
95
            var enumeratorInfo = enumerableInfo.EnumeratorInfo;
48✔
96
            var enumeratorVariable = Variable(enumeratorType, "enumerator");
48✔
97
            return Block(
48✔
98
                new[] { enumeratorVariable },
48✔
99
                Assign(enumeratorVariable, CallGetEnumerator(enumerableInfo, enumerable)),
48✔
100
                enumeratorInfo switch
48✔
101
                {
48✔
102
                    {Dispose: null} => NotDisposable(enumeratorInfo, enumeratorVariable, body),
24✔
103
                    _ => Disposable(enumeratorInfo, enumeratorVariable, body)
24✔
104
                });
48✔
105

106
            static Expression CallGetEnumerator(EnumerableInfo enumerableInfo, Expression enumerable)
107
                => enumerableInfo.EnumeratorInfo switch
48✔
108
                {
48✔
109
                    {IsGenericsEnumeratorInterface: true}
48✔
110
                        => Call(Convert(enumerable, enumerableInfo.GetEnumerator.DeclaringType!), enumerableInfo.GetEnumerator),
6✔
111
                    
48✔
112
                    {IsEnumeratorInterface: true}
48✔
113
                        => Call(Convert(enumerable, typeof(IEnumerable)), enumerableInfo.GetEnumerator),
6✔
114
                    
48✔
115
                    _ => Call(enumerable, enumerableInfo.GetEnumerator)
36✔
116
                };
48✔
117

118
            static Expression Disposable(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
119
                => Using(enumerator, 
24✔
120
                    EnumerationLoop(enumeratorInfo, enumerator, body)
24✔
121
                );
24✔
122

123
            static Expression NotDisposable(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
124
            {
125
                return enumeratorInfo switch
24✔
126
                {
24✔
127
                    {IsValueType: true} => NotDisposableValueType(enumeratorInfo, enumerator, body),
12✔
128
                    _ => NotDisposableReferenceType(enumeratorInfo, enumerator, body)
12✔
129
                };
24✔
130

131
                static Expression NotDisposableValueType(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
132
                    => EnumerationLoop(enumeratorInfo, enumerator, body);
12✔
133

134
                static Expression NotDisposableReferenceType(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
135
                {
136
                    var disposable = Variable(typeof(IDisposable), "disposable");
12✔
137
                    return TryFinally(
12✔
138
                        EnumerationLoop(enumeratorInfo, enumerator, body),
12✔
139
                        Block(
12✔
140
                            new[] {disposable},
12✔
141
                            Assign(disposable, TypeAs(enumerator, typeof(IDisposable))),
12✔
142
                            IfThen(
12✔
143
                                NotEqual(disposable, Constant(null)),
12✔
144
                                Call(disposable,
12✔
145
                                    typeof(IDisposable).GetMethod(TypeExtensions.PublicInstanceDeclaredOnly,
12✔
146
                                        NameOf.Dispose, Type.EmptyTypes)!)
12✔
147
                            )
12✔
148
                        )
12✔
149
                    );
12✔
150
                }
151
            }
152

153
            static Expression EnumerationLoop(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
154
                => While(Call(enumerator, enumeratorInfo.MoveNext),
48✔
155
                    body(Property(enumerator, enumeratorInfo.Current))
48✔
156
                );
48✔
157
        }
158
    }
159
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc