Skip to content

Commit

Permalink
Release v1.1.0 (#33)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people authored Dec 23, 2024
1 parent e36ad22 commit e516a9b
Show file tree
Hide file tree
Showing 53 changed files with 1,902 additions and 858 deletions.
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<!-- Solution version numbers -->
<PropertyGroup>
<MajorVersion>1</MajorVersion>
<MinorVersion>0</MinorVersion>
<PatchVersion>1</PatchVersion>
<MinorVersion>1</MinorVersion>
<PatchVersion>0</PatchVersion>
</PropertyGroup>
<!-- Disable automatic package publishing -->
<PropertyGroup>
Expand Down
1 change: 1 addition & 0 deletions Hyperbee.Expressions.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Awaiters/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Castclass/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=comparand/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ctors/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=fingerprinter/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=gotos/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Hyperbee/@EntryIndexedValue">True</s:Boolean>
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ workflows and other language constructs.

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

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

```csharp

public class AsyncExample
public class Example
{
public async Task ExampleAsync()
{
Expand Down Expand Up @@ -79,15 +79,15 @@ public class AsyncExample
The following example demonstrates how to create a Using expression.

```csharp
public class UsingExample
public class Example
{
private class DisposableResource : IDisposable
{
public bool IsDisposed { get; private set; }
public void Dispose() => IsDisposed = true;
}

public void UsingExpression_ShouldDisposeResource_AfterUse()
public void ExampleUsing()
{
var resource = new TestDisposableResource();

Expand All @@ -113,6 +113,7 @@ public class UsingExample
Special thanks to:

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

## Contributing
Expand Down
11 changes: 7 additions & 4 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,20 @@ trees to handle asynchronous workflows and other language constructs.
* `StringFormatExpression`: An expression that creates a string using a supplied format string and parameters.
* `DebugExpression`: An expression that helps when debugging expression trees.

* Supports Fast Expression Compiler (FEC) for improved performance.

## Examples

### Asynchronous Expressions

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

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

```csharp

public class AsyncExample
public class Example
{
public async Task ExampleAsync()
{
Expand Down Expand Up @@ -83,15 +85,15 @@ public class AsyncExample
The following example demonstrates how to create a Using expression.

```csharp
public class UsingExample
public class Example
{
private class DisposableResource : IDisposable
{
public bool IsDisposed { get; private set; }
public void Dispose() => IsDisposed = true;
}

public void UsingExpression_ShouldDisposeResource_AfterUse()
public void ExampleUsing()
{
var resource = new TestDisposableResource();

Expand All @@ -117,6 +119,7 @@ public class UsingExample
Special thanks to:

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

## Contributing
Expand Down
2 changes: 1 addition & 1 deletion docs/state-machine/lowering-visitor.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The `LoweringVisitor` is responsible for transforming an expression tree into di
to generate the final state machine.

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


## 1. Handling Control Flow Constructs (Branching and Loops)
Expand Down
2 changes: 1 addition & 1 deletion docs/state-machine/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ operations that must suspend and resume execution.
State machine creation occurs in two passes:

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

Expand Down
14 changes: 8 additions & 6 deletions src/Hyperbee.Expressions/AsyncBlockExpression.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq.Expressions;
using Hyperbee.Expressions.Transformation;
using Hyperbee.Expressions.CompilerServices;
using Hyperbee.Expressions.CompilerServices.Collections;

namespace Hyperbee.Expressions;

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

public ReadOnlyCollection<Expression> Expressions { get; }
public ReadOnlyCollection<ParameterExpression> Variables { get; }
internal ReadOnlyCollection<ParameterExpression> ExternVariables { get; set; }

internal LinkedDictionary<ParameterExpression, ParameterExpression> ScopedVariables { get; set; }

public Expression Result => Expressions[^1];

Expand All @@ -25,15 +27,15 @@ internal AsyncBlockExpression( ReadOnlyCollection<ParameterExpression> variables
internal AsyncBlockExpression(
ReadOnlyCollection<ParameterExpression> variables,
ReadOnlyCollection<Expression> expressions,
ReadOnlyCollection<ParameterExpression> externVariables
LinkedDictionary<ParameterExpression, ParameterExpression> scopedVariables
)
{
if ( expressions == null || expressions.Count == 0 )
throw new ArgumentException( $"{nameof( AsyncBlockExpression )} must contain at least one expression.", nameof( expressions ) );

Variables = variables;
Expressions = expressions;
ExternVariables = externVariables;
ScopedVariables = scopedVariables;

_taskType = GetTaskType( Result.Type );
}
Expand All @@ -59,7 +61,7 @@ private LoweringInfo LoweringTransformer()
Result.Type,
[.. Variables],
[.. Expressions],
ExternVariables != null ? [.. ExternVariables] : []
ScopedVariables ?? []
);
}
catch ( LoweringException ex )
Expand All @@ -76,7 +78,7 @@ protected override Expression VisitChildren( ExpressionVisitor visitor )
if ( Compare( newVariables, Variables ) && Compare( newExpressions, Expressions ) )
return this;

return new AsyncBlockExpression( newVariables, newExpressions, ExternVariables );
return new AsyncBlockExpression( newVariables, newExpressions, ScopedVariables );
}

internal static bool Compare<T>( ICollection<T> compare, IReadOnlyList<T> current )
Expand Down
9 changes: 5 additions & 4 deletions src/Hyperbee.Expressions/AwaitExpression.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Diagnostics;
using System.Linq.Expressions;
using Hyperbee.Expressions.Transformation;
using Hyperbee.Expressions.CompilerServices;

namespace Hyperbee.Expressions;

Expand Down Expand Up @@ -42,8 +42,7 @@ private static Type ResultType( Type awaitableType )

var genericTypeDef = awaitableType.GetGenericTypeDefinition();

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

internal static bool IsAwaitable( Type type )
{
return typeof( Task ).IsAssignableFrom( type ) || typeof( ValueTask ).IsAssignableFrom( type ) || AwaitBinderFactory.TryGetOrCreate( type, out _ );
return typeof( Task ).IsAssignableFrom( type ) ||
typeof( ValueTask ).IsAssignableFrom( type ) ||
AwaitBinderFactory.TryGetOrCreate( type, out _ );
}

private class AwaitExpressionDebuggerProxy( AwaitExpression node )
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Reflection;
using System.Runtime.CompilerServices;

namespace Hyperbee.Expressions.Transformation;
namespace Hyperbee.Expressions.CompilerServices;

internal delegate TAwaiter AwaitBinderGetAwaiterDelegate<TAwaitable, out TAwaiter>( ref TAwaitable awaitable, bool configureAwait );
internal delegate TResult AwaitBinderGetResultDelegate<TAwaiter, out TResult>( ref TAwaiter awaiter );
Expand Down Expand Up @@ -30,9 +30,22 @@ internal AwaitBinder(
GetResultMethod = getResultMethod;
GetAwaiterImplDelegate = getAwaiterImplDelegate;
GetResultImplDelegate = getResultImplDelegate;

// Pre-jit methods and delegates
// This saves a little time when the methods are executed for the first time

RuntimeHelpers.PrepareMethod( WaitMethod.MethodHandle );
RuntimeHelpers.PrepareMethod( GetAwaiterMethod.MethodHandle );
RuntimeHelpers.PrepareMethod( GetResultMethod.MethodHandle );

if ( getAwaiterImplDelegate != null )
RuntimeHelpers.PrepareDelegate( getAwaiterImplDelegate );

if ( getResultImplDelegate != null )
RuntimeHelpers.PrepareDelegate( getResultImplDelegate );
}

// Await methods
// Wait methods

[MethodImpl( MethodImplOptions.AggressiveInlining )]
internal void Wait<TAwaitable, TAwaiter>( ref TAwaitable awaitable, bool configureAwait )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using System.Reflection.Emit;
using System.Runtime.CompilerServices;

namespace Hyperbee.Expressions.Transformation;
namespace Hyperbee.Expressions.CompilerServices;

internal static class AwaitBinderFactory
{
Expand Down Expand Up @@ -112,7 +112,8 @@ private static AwaitBinder CreateGenericValueTaskAwaitBinder( Type awaitableType
awaitableType,
WaitResultMethod.MakeGenericMethod( awaitableType, awaiterType, awaiterResultType ),
GetAwaiterValueTaskResultMethod.MakeGenericMethod( awaiterResultType ),
GetResultValueTaskResultMethod.MakeGenericMethod( awaiterResultType ) );
GetResultValueTaskResultMethod.MakeGenericMethod( awaiterResultType )
);
}

private static AwaitBinder CreateTaskAwaitBinder( Type awaitableType )
Expand Down
Loading

0 comments on commit e516a9b

Please sign in to comment.