Skip to content

Commit e516a9b

Browse files
Release v1.1.0 (#33)
* Previous version was 'v1.0.0'. Version now 'v1.0.1'. * [FEATURE]: Improve AwaitBinder and Lowering Results (#22) * Create draft PR for #21 [skip ci] ## Updates * Switch StateExpression to StateNode * Added ResultExpression * Return Empty for Jump Tables with no cases * Added local awaiter variable (for FEC) * Added lambda Extern parameters (for FEC) --------- Co-authored-by: @bfarmer67 Co-authored-by: Matt Edwards <matthew.edwards@wagglebee.net> * Updated documentation to remove FEC support until feature #24 is done. * [FEATURE]: Add Support for Fast Expression Compiler (#26) * Create draft PR for #24 [skip ci] * Initial tests and support for FEC * Moving FEC troubleshooting to FastExpressionCompilerTests * Switching to LinkDictionary to track scopes * Removed External Variables * Turning off FAST_COMPILE for tests --------- Co-authored-by: Matt Edwards <matthew.edwards@wagglebee.net> Co-authored-by: Brenton Farmer <brent.farmer@wagglebee.net> * Previous version was 'v1.0.1'. Version now 'v1.0.2'. * Previous version was 'v1.0.2'. Version now 'v1.1.0'. * [FEATURE]: Update to Fast Expression Compiler v5.0.1 (#29) * Create draft PR for #28 * Update FastExpressionCompiler and Test nugets --------- Co-authored-by: Matt Edwards <matthew.edwards@wagglebee.net> Co-authored-by: Brenton Farmer <brent.farmer@wagglebee.net>
1 parent e36ad22 commit e516a9b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1902
-858
lines changed

Directory.Build.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
<!-- Solution version numbers -->
33
<PropertyGroup>
44
<MajorVersion>1</MajorVersion>
5-
<MinorVersion>0</MinorVersion>
6-
<PatchVersion>1</PatchVersion>
5+
<MinorVersion>1</MinorVersion>
6+
<PatchVersion>0</PatchVersion>
77
</PropertyGroup>
88
<!-- Disable automatic package publishing -->
99
<PropertyGroup>

Hyperbee.Expressions.sln.DotSettings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<s:Boolean x:Key="/Default/UserDictionary/Words/=Awaiters/@EntryIndexedValue">True</s:Boolean>
55
<s:Boolean x:Key="/Default/UserDictionary/Words/=Castclass/@EntryIndexedValue">True</s:Boolean>
66
<s:Boolean x:Key="/Default/UserDictionary/Words/=comparand/@EntryIndexedValue">True</s:Boolean>
7+
<s:Boolean x:Key="/Default/UserDictionary/Words/=ctors/@EntryIndexedValue">True</s:Boolean>
78
<s:Boolean x:Key="/Default/UserDictionary/Words/=fingerprinter/@EntryIndexedValue">True</s:Boolean>
89
<s:Boolean x:Key="/Default/UserDictionary/Words/=gotos/@EntryIndexedValue">True</s:Boolean>
910
<s:Boolean x:Key="/Default/UserDictionary/Words/=Hyperbee/@EntryIndexedValue">True</s:Boolean>

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ workflows and other language constructs.
2727

2828
The following example demonstrates how to create an asynchronous expression tree.
2929

30-
When the expression tree is compiled, the `BlockAsyncExpression` will auto-generate a state machine that executes
30+
When the expression tree is compiled, the `AsyncBlockExpression` will auto-generate a state machine that executes
3131
`AwaitExpressions` in the block asynchronously.
3232

3333
```csharp
3434

35-
public class AsyncExample
35+
public class Example
3636
{
3737
public async Task ExampleAsync()
3838
{
@@ -79,15 +79,15 @@ public class AsyncExample
7979
The following example demonstrates how to create a Using expression.
8080

8181
```csharp
82-
public class UsingExample
82+
public class Example
8383
{
8484
private class DisposableResource : IDisposable
8585
{
8686
public bool IsDisposed { get; private set; }
8787
public void Dispose() => IsDisposed = true;
8888
}
8989

90-
public void UsingExpression_ShouldDisposeResource_AfterUse()
90+
public void ExampleUsing()
9191
{
9292
var resource = new TestDisposableResource();
9393

@@ -113,6 +113,7 @@ public class UsingExample
113113
Special thanks to:
114114

115115
- Sergey Tepliakov - [Dissecting the async methods in C#](https://devblogs.microsoft.com/premier-developer/dissecting-the-async-methods-in-c/).
116+
- [Fast Expression Compiler](https://github.com/dadhi/FastExpressionCompiler) for improved performance. :heart:
116117
- [Just The Docs](https://github.com/just-the-docs/just-the-docs) for the documentation theme.
117118

118119
## Contributing

docs/index.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,20 @@ trees to handle asynchronous workflows and other language constructs.
2626
* `StringFormatExpression`: An expression that creates a string using a supplied format string and parameters.
2727
* `DebugExpression`: An expression that helps when debugging expression trees.
2828

29+
* Supports Fast Expression Compiler (FEC) for improved performance.
30+
2931
## Examples
3032

3133
### Asynchronous Expressions
3234

3335
The following example demonstrates how to create an asynchronous expression tree.
3436

35-
When the expression tree is compiled, the `BlockAsyncExpression` will auto-generate a state machine that executes
37+
When the expression tree is compiled, the `AsyncBlockExpression` will auto-generate a state machine that executes
3638
`AwaitExpressions` in the block asynchronously.
3739

3840
```csharp
3941

40-
public class AsyncExample
42+
public class Example
4143
{
4244
public async Task ExampleAsync()
4345
{
@@ -83,15 +85,15 @@ public class AsyncExample
8385
The following example demonstrates how to create a Using expression.
8486

8587
```csharp
86-
public class UsingExample
88+
public class Example
8789
{
8890
private class DisposableResource : IDisposable
8991
{
9092
public bool IsDisposed { get; private set; }
9193
public void Dispose() => IsDisposed = true;
9294
}
9395

94-
public void UsingExpression_ShouldDisposeResource_AfterUse()
96+
public void ExampleUsing()
9597
{
9698
var resource = new TestDisposableResource();
9799

@@ -117,6 +119,7 @@ public class UsingExample
117119
Special thanks to:
118120

119121
- Sergey Tepliakov - [Dissecting the async methods in C#](https://devblogs.microsoft.com/premier-developer/dissecting-the-async-methods-in-c/).
122+
- [Fast Expression Compiler](https://github.com/dadhi/FastExpressionCompiler) for improved performance. :heart:
120123
- [Just The Docs](https://github.com/just-the-docs/just-the-docs) for the documentation theme.
121124

122125
## Contributing

docs/state-machine/lowering-visitor.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ The `LoweringVisitor` is responsible for transforming an expression tree into di
2020
to generate the final state machine.
2121

2222
The purpose of this visitor is to "lower" high-level constructs, such as `await`, `if/else`, `switch`, `try\catch`, and loops,
23-
into individual `NodeExpression` objects that the state machine can later process.
23+
into individual `StateNode` objects that the state machine can later process.
2424

2525

2626
## 1. Handling Control Flow Constructs (Branching and Loops)

docs/state-machine/overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ operations that must suspend and resume execution.
2121
State machine creation occurs in two passes:
2222

2323
### Pass 1: Expression Tree Transformation
24-
The first pass uses a Lowering Technique to transform `BlockAsyncExpression`s into state trees, and handles the lowering of
24+
The first pass uses a Lowering Technique to transform `AsyncBlockExpression`s into state trees, and handles the lowering of
2525
complex flow control constructs (ifs, switches, loops, try/catch, and awaits) into more primitive representations. This step
2626
also identifies variables that persist across states and require hoisting.
2727

src/Hyperbee.Expressions/AsyncBlockExpression.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
using System.Collections.ObjectModel;
22
using System.Diagnostics;
33
using System.Linq.Expressions;
4-
using Hyperbee.Expressions.Transformation;
4+
using Hyperbee.Expressions.CompilerServices;
5+
using Hyperbee.Expressions.CompilerServices.Collections;
56

67
namespace Hyperbee.Expressions;
78

@@ -13,7 +14,8 @@ public class AsyncBlockExpression : Expression
1314

1415
public ReadOnlyCollection<Expression> Expressions { get; }
1516
public ReadOnlyCollection<ParameterExpression> Variables { get; }
16-
internal ReadOnlyCollection<ParameterExpression> ExternVariables { get; set; }
17+
18+
internal LinkedDictionary<ParameterExpression, ParameterExpression> ScopedVariables { get; set; }
1719

1820
public Expression Result => Expressions[^1];
1921

@@ -25,15 +27,15 @@ internal AsyncBlockExpression( ReadOnlyCollection<ParameterExpression> variables
2527
internal AsyncBlockExpression(
2628
ReadOnlyCollection<ParameterExpression> variables,
2729
ReadOnlyCollection<Expression> expressions,
28-
ReadOnlyCollection<ParameterExpression> externVariables
30+
LinkedDictionary<ParameterExpression, ParameterExpression> scopedVariables
2931
)
3032
{
3133
if ( expressions == null || expressions.Count == 0 )
3234
throw new ArgumentException( $"{nameof( AsyncBlockExpression )} must contain at least one expression.", nameof( expressions ) );
3335

3436
Variables = variables;
3537
Expressions = expressions;
36-
ExternVariables = externVariables;
38+
ScopedVariables = scopedVariables;
3739

3840
_taskType = GetTaskType( Result.Type );
3941
}
@@ -59,7 +61,7 @@ private LoweringInfo LoweringTransformer()
5961
Result.Type,
6062
[.. Variables],
6163
[.. Expressions],
62-
ExternVariables != null ? [.. ExternVariables] : []
64+
ScopedVariables ?? []
6365
);
6466
}
6567
catch ( LoweringException ex )
@@ -76,7 +78,7 @@ protected override Expression VisitChildren( ExpressionVisitor visitor )
7678
if ( Compare( newVariables, Variables ) && Compare( newExpressions, Expressions ) )
7779
return this;
7880

79-
return new AsyncBlockExpression( newVariables, newExpressions, ExternVariables );
81+
return new AsyncBlockExpression( newVariables, newExpressions, ScopedVariables );
8082
}
8183

8284
internal static bool Compare<T>( ICollection<T> compare, IReadOnlyList<T> current )

src/Hyperbee.Expressions/AwaitExpression.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System.Diagnostics;
22
using System.Linq.Expressions;
3-
using Hyperbee.Expressions.Transformation;
3+
using Hyperbee.Expressions.CompilerServices;
44

55
namespace Hyperbee.Expressions;
66

@@ -42,8 +42,7 @@ private static Type ResultType( Type awaitableType )
4242

4343
var genericTypeDef = awaitableType.GetGenericTypeDefinition();
4444

45-
if ( genericTypeDef.IsSubclassOf( typeof( Task ) ) ||
46-
genericTypeDef.IsSubclassOf( typeof( ValueTask ) ) )
45+
if ( genericTypeDef.IsSubclassOf( typeof( Task ) ) || genericTypeDef.IsSubclassOf( typeof( ValueTask ) ) )
4746
{
4847
return awaitableType.GetGenericArguments()[0];
4948
}
@@ -67,7 +66,9 @@ protected override Expression VisitChildren( ExpressionVisitor visitor )
6766

6867
internal static bool IsAwaitable( Type type )
6968
{
70-
return typeof( Task ).IsAssignableFrom( type ) || typeof( ValueTask ).IsAssignableFrom( type ) || AwaitBinderFactory.TryGetOrCreate( type, out _ );
69+
return typeof( Task ).IsAssignableFrom( type ) ||
70+
typeof( ValueTask ).IsAssignableFrom( type ) ||
71+
AwaitBinderFactory.TryGetOrCreate( type, out _ );
7172
}
7273

7374
private class AwaitExpressionDebuggerProxy( AwaitExpression node )

src/Hyperbee.Expressions/Transformation/AwaitBinder.cs renamed to src/Hyperbee.Expressions/CompilerServices/AwaitBinder.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System.Reflection;
22
using System.Runtime.CompilerServices;
33

4-
namespace Hyperbee.Expressions.Transformation;
4+
namespace Hyperbee.Expressions.CompilerServices;
55

66
internal delegate TAwaiter AwaitBinderGetAwaiterDelegate<TAwaitable, out TAwaiter>( ref TAwaitable awaitable, bool configureAwait );
77
internal delegate TResult AwaitBinderGetResultDelegate<TAwaiter, out TResult>( ref TAwaiter awaiter );
@@ -30,9 +30,22 @@ internal AwaitBinder(
3030
GetResultMethod = getResultMethod;
3131
GetAwaiterImplDelegate = getAwaiterImplDelegate;
3232
GetResultImplDelegate = getResultImplDelegate;
33+
34+
// Pre-jit methods and delegates
35+
// This saves a little time when the methods are executed for the first time
36+
37+
RuntimeHelpers.PrepareMethod( WaitMethod.MethodHandle );
38+
RuntimeHelpers.PrepareMethod( GetAwaiterMethod.MethodHandle );
39+
RuntimeHelpers.PrepareMethod( GetResultMethod.MethodHandle );
40+
41+
if ( getAwaiterImplDelegate != null )
42+
RuntimeHelpers.PrepareDelegate( getAwaiterImplDelegate );
43+
44+
if ( getResultImplDelegate != null )
45+
RuntimeHelpers.PrepareDelegate( getResultImplDelegate );
3346
}
3447

35-
// Await methods
48+
// Wait methods
3649

3750
[MethodImpl( MethodImplOptions.AggressiveInlining )]
3851
internal void Wait<TAwaitable, TAwaiter>( ref TAwaitable awaitable, bool configureAwait )

src/Hyperbee.Expressions/Transformation/AwaitBinderFactory.cs renamed to src/Hyperbee.Expressions/CompilerServices/AwaitBinderFactory.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Reflection.Emit;
44
using System.Runtime.CompilerServices;
55

6-
namespace Hyperbee.Expressions.Transformation;
6+
namespace Hyperbee.Expressions.CompilerServices;
77

88
internal static class AwaitBinderFactory
99
{
@@ -112,7 +112,8 @@ private static AwaitBinder CreateGenericValueTaskAwaitBinder( Type awaitableType
112112
awaitableType,
113113
WaitResultMethod.MakeGenericMethod( awaitableType, awaiterType, awaiterResultType ),
114114
GetAwaiterValueTaskResultMethod.MakeGenericMethod( awaiterResultType ),
115-
GetResultValueTaskResultMethod.MakeGenericMethod( awaiterResultType ) );
115+
GetResultValueTaskResultMethod.MakeGenericMethod( awaiterResultType )
116+
);
116117
}
117118

118119
private static AwaitBinder CreateTaskAwaitBinder( Type awaitableType )

0 commit comments

Comments
 (0)