Skip to content

Commit

Permalink
WIP on develop
Browse files Browse the repository at this point in the history
generated state-machine wip
  • Loading branch information
bfarmer67 committed Sep 3, 2024
1 parent 8765275 commit 7582003
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 194 deletions.
39 changes: 35 additions & 4 deletions src/Hyperbee.AsyncExpressions/AsyncBaseExpression.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Diagnostics;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;

namespace Hyperbee.AsyncExpressions;

Expand All @@ -23,25 +25,54 @@ protected AsyncBaseExpression( Expression body )

protected abstract Type GetFinalResultType();

protected abstract Expression BuildStateMachine<TResult>();
protected abstract void ConfigureStateMachine<TResult>( StateMachineBuilder<TResult> builder );

public override Expression Reduce()
{
if ( _isReduced )
return _stateMachineBody;

var finalResultType = GetFinalResultType();

var stateMachineResultType = finalResultType == typeof(void) ? typeof(VoidResult) : finalResultType;

var buildStateMachine = typeof(AsyncBaseExpression)
.GetMethod( nameof(BuildStateMachine), BindingFlags.NonPublic | BindingFlags.Instance )!
.MakeGenericMethod( finalResultType );
.MakeGenericMethod( stateMachineResultType );

_stateMachineBody = (Expression) buildStateMachine.Invoke( this, null );
_isReduced = true;

return _stateMachineBody!;
}

private MethodCallExpression BuildStateMachine<TResult>()
{
// Create a dynamic assembly and module for the state machine
var assemblyName = new AssemblyName( "DynamicStateMachineAssembly" );
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly( assemblyName, AssemblyBuilderAccess.Run );
var moduleBuilder = assemblyBuilder.DefineDynamicModule( "MainModule" );

// Create a state machine builder
var stateMachineBuilder = new StateMachineBuilder<TResult>( moduleBuilder, "DynamicStateMachine" );

// Delegate to the derived class to configure the builder
ConfigureStateMachine( stateMachineBuilder );

// Create the state machine type
var stateMachineType = stateMachineBuilder.CreateStateMachineType();

// Create a proxy expression for handling MoveNext and SetStateMachine calls
var proxyConstructor = typeof(StateMachineProxy).GetConstructor( new[] { typeof(IAsyncStateMachine) } );
var stateMachineInstance = Expression.New( stateMachineType );
var proxyInstance = Expression.New( proxyConstructor!, stateMachineInstance );

// Build an expression that represents invoking the MoveNext method on the proxy
var moveNextMethod = typeof(IAsyncStateMachine).GetMethod( nameof(IAsyncStateMachine.MoveNext) );
var moveNextCall = Expression.Call( proxyInstance, moveNextMethod! );

return moveNextCall;
}

internal static bool IsTask( Type returnType )
{
return returnType == typeof(Task) ||
Expand Down
106 changes: 56 additions & 50 deletions src/Hyperbee.AsyncExpressions/AsyncBlockExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,78 +4,77 @@ namespace Hyperbee.AsyncExpressions;

public class AsyncBlockExpression : AsyncBaseExpression
{
private readonly Expression[] _expressions;
private readonly BlockExpression _reducedBlock;
private readonly Type _finalResultType;

public AsyncBlockExpression( Expression[] expressions) : base(null)
public AsyncBlockExpression( Expression[] expressions ) : base( null )
{
_expressions = expressions;
if ( expressions == null || expressions.Length == 0 )
{
throw new ArgumentException( "AsyncBlockExpression must contain at least one expression.", nameof(expressions) );
}

_reducedBlock = ReduceBlock( expressions, out _finalResultType );
}

protected override Type GetFinalResultType()
{
// Get the final result type from the last block
var (_, finalResultType) = ReduceBlock(_expressions);
return finalResultType;
return _finalResultType;
}

protected override void ConfigureStateMachine<TResult>( StateMachineBuilder<TResult> builder )
{
builder.GenerateMoveNextMethod( _reducedBlock );
}

protected override Expression BuildStateMachine<TResult>()
private static BlockExpression ReduceBlock( Expression[] expressions, out Type finalResultType )
{
var (blocks, finalResultType) = ReduceBlock(_expressions);
var parentBlockExpressions = new List<Expression>();
var currentBlockExpressions = new List<Expression>();
var awaitEncountered = false;

var builder = new StateMachineBuilder<TResult>();
// Collect all variables declared in the block
var variables = new HashSet<ParameterExpression>();
finalResultType = typeof(void); // Default to void, adjust if task found

foreach (var block in blocks)
foreach ( var expr in expressions )
{
var lastExpr = block.Expressions.Last();
if (IsTask(lastExpr.Type))
{
if (lastExpr.Type == typeof(Task))
{
builder.AddTaskBlock(block); // Block with Task
}
else if (lastExpr.Type.IsGenericType && lastExpr.Type.GetGenericTypeDefinition() == typeof(Task<>))
{
builder.AddTaskResultBlock(block); // Block with Task<TResult>
}
}
else
if ( expr is AsyncBlockExpression asyncBlock )
{
builder.AddBlock(block); // Regular code block
// Recursively reduce the inner async block
var reducedInnerBlock = asyncBlock.Reduce();
currentBlockExpressions.Add( reducedInnerBlock );
continue;
}
}

return builder.Build();
}

// ReduceBlock method to split the block into sub-blocks
private (List<BlockExpression> blocks, Type finalResultType) ReduceBlock( Expression[] expressions)
{
var blocks = new List<BlockExpression>();
var currentBlock = new List<Expression>();
Type finalResultType = typeof(void);

foreach (var expr in expressions)
{
currentBlock.Add(expr);
currentBlockExpressions.Add( expr );

if (expr is AwaitExpression)
switch ( expr )
{
// Finalize the current block and add it to the list
blocks.Add(Block(currentBlock));
currentBlock.Clear();
case BinaryExpression binaryExpr when binaryExpr.Left is ParameterExpression varExpr:
variables.Add( varExpr );
break;
case AwaitExpression:
{
awaitEncountered = true;
var currentBlock = Block( currentBlockExpressions );
parentBlockExpressions.Add( currentBlock );
currentBlockExpressions = [];
break;
}
}
}

// Add the last block if it exists
if (currentBlock.Count > 0)
if ( currentBlockExpressions.Count > 0 )
{
blocks.Add(Block(currentBlock));
var lastExpr = currentBlock.Last();
var finalBlock = Block( currentBlockExpressions );
parentBlockExpressions.Add( finalBlock );

// Determine the final result type from the last expression
if (IsTask(lastExpr.Type))
// Update the final result type based on the last expression in the final block
var lastExpr = currentBlockExpressions.Last();
if ( IsTask( lastExpr.Type ) )
{
if (lastExpr.Type.IsGenericType)
if ( lastExpr.Type.IsGenericType )
{
finalResultType = lastExpr.Type.GetGenericArguments()[0];
}
Expand All @@ -86,7 +85,13 @@ protected override Expression BuildStateMachine<TResult>()
}
}

return (blocks, finalResultType);
if ( !awaitEncountered )
{
throw new InvalidOperationException( $"{nameof(AsyncBlockExpression)} must contain at least one {nameof(AwaitExpression)}." );
}

// Combine all child blocks into a single parent block, with variables declared at the parent level
return Block( variables, parentBlockExpressions ); // Declare variables only once at the top level
}
}

Expand All @@ -97,3 +102,4 @@ public static AsyncBaseExpression BlockAsync( params Expression[] expressions )
return new AsyncBlockExpression( expressions );
}
}

27 changes: 7 additions & 20 deletions src/Hyperbee.AsyncExpressions/AsyncInvocationExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,22 @@ protected override Type GetFinalResultType()
{
return typeof(void); // No result to return
}
else if ( _invocationExpression.Type.IsGenericType && _invocationExpression.Type.GetGenericTypeDefinition() == typeof(Task<>) )

if ( _invocationExpression.Type.IsGenericType && _invocationExpression.Type.GetGenericTypeDefinition() == typeof(Task<>) )
{
return _invocationExpression.Type.GetGenericArguments()[0]; // Return T from Task<T>
}
else
{
throw new InvalidOperationException( "Invocation must return Task or Task<T>" );
}

throw new InvalidOperationException( "Invocation must return Task or Task<T>" );
}

protected override Expression BuildStateMachine<TResult>()
protected override void ConfigureStateMachine<TResult>( StateMachineBuilder<TResult> builder )
{
var builder = new StateMachineBuilder<TResult>();
var taskType = _invocationExpression.Type;

if ( taskType == typeof(Task) )
{
builder.AddTaskBlock( _invocationExpression ); // Await the single Task
}
else if ( taskType.IsGenericType && taskType.GetGenericTypeDefinition() == typeof(Task<>) )
{
builder.AddTaskResultBlock( _invocationExpression ); // Await the single Task with a result
}

return builder.Build(); // Return the built state machine
var block = Block( _invocationExpression );
builder.GenerateMoveNextMethod( block );
}
}


public static partial class AsyncExpression
{
public static AsyncBaseExpression InvokeAsync( LambdaExpression lambdaExpression, params Expression[] arguments )
Expand Down
26 changes: 7 additions & 19 deletions src/Hyperbee.AsyncExpressions/AsyncMethodCallExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,19 @@ protected override Type GetFinalResultType()
{
return typeof(void); // No result to return
}
else if ( _methodCallExpression.Type.IsGenericType && _methodCallExpression.Type.GetGenericTypeDefinition() == typeof(Task<>) )

if ( _methodCallExpression.Type.IsGenericType && _methodCallExpression.Type.GetGenericTypeDefinition() == typeof(Task<>) )
{
return _methodCallExpression.Type.GetGenericArguments()[0]; // Return T from Task<T>
}
else
{
throw new InvalidOperationException( "Method call must return Task or Task<T>" );
}

throw new InvalidOperationException( "Method call must return Task or Task<T>" );
}

protected override Expression BuildStateMachine<TResult>()
protected override void ConfigureStateMachine<TResult>( StateMachineBuilder<TResult> builder )
{
var builder = new StateMachineBuilder<TResult>();
var taskType = _methodCallExpression.Type;

if ( taskType == typeof(Task) )
{
builder.AddTaskBlock( _methodCallExpression ); // Await the single Task
}
else if ( taskType.IsGenericType && taskType.GetGenericTypeDefinition() == typeof(Task<>) )
{
builder.AddTaskResultBlock( _methodCallExpression ); // Await the single Task with a result
}

return builder.Build(); // Return the built state machine
var block = Block( _methodCallExpression );
builder.GenerateMoveNextMethod( block );
}
}

Expand Down
Loading

0 comments on commit 7582003

Please sign in to comment.