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

NetFabric / NetFabric.CodeAnalysis / 6130084901

09 Sep 2023 09:14AM UTC coverage: 83.158% (+0.2%) from 82.932%
6130084901

push

github

web-flow
Fixes (#27)

227 of 266 branches covered (0.0%)

Branch coverage included in aggregate %.

661 of 661 new or added lines in 31 files covered. (100.0%)

642 of 779 relevant lines covered (82.41%)

18.46 hits per line

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

82.35
/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 errors))
54!
31
            return ForEach(enumerableInfo, enumerable, body);
54✔
32
        
33
        if (errors.HasFlag(Errors.MissingGetEnumerator))
×
34
            throw new Exception($"'{enumerableType}' does not contain a public definition for 'GetEnumerator'");
×
35

36
        if (errors.HasFlag(Errors.MissingCurrent))
×
37
            throw new Exception(
×
38
                $"'{enumerableInfo!.GetEnumerator.ReturnType.Name}' does not contain a public definition for 'Current'");
×
39

40
        if (errors.HasFlag(Errors.MissingMoveNext))
×
41
            throw new Exception(
×
42
                $"'{enumerableInfo!.GetEnumerator.ReturnType.Name}' does not contain a public definition for 'MoveNext'");
×
43

44
        throw new Exception("Unhandled error!");
×
45
    }
46

47
    internal static int localVariableCounter = 0;
48

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

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

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

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

123
            static Expression Disposable(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
124
                => Using(enumerator, 
24✔
125
                    EnumerationLoop(enumeratorInfo, enumerator, body)
24✔
126
                );
24✔
127

128
            static Expression NotDisposable(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
129
            {
130
                return enumeratorInfo switch
24✔
131
                {
24✔
132
                    {IsValueType: true} => NotDisposableValueType(enumeratorInfo, enumerator, body),
12✔
133
                    _ => NotDisposableReferenceType(enumeratorInfo, enumerator, body)
12✔
134
                };
24✔
135

136
                static Expression NotDisposableValueType(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
137
                    => EnumerationLoop(enumeratorInfo, enumerator, body);
12✔
138

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

158
            static Expression EnumerationLoop(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
159
                => While(Call(enumerator, enumeratorInfo.MoveNext),
48✔
160
                    body(Property(enumerator, enumeratorInfo.Current))
48✔
161
                );
48✔
162
        }
163
    }
164
}
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