Skip to content

Commit

Permalink
debugging MoveNextMethod code
Browse files Browse the repository at this point in the history
  • Loading branch information
bfarmer67 committed Sep 3, 2024
1 parent 7582003 commit f4c1335
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 48 deletions.
6 changes: 3 additions & 3 deletions src/Hyperbee.AsyncExpressions/AsyncBaseExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ namespace Hyperbee.AsyncExpressions;
[DebuggerTypeProxy( typeof(AsyncBaseExpressionDebuggerProxy) )]
public abstract class AsyncBaseExpression : Expression
{
protected Expression _body;
private readonly Expression[] _body;
protected bool _isReduced;
protected Expression _stateMachineBody;

protected AsyncBaseExpression( Expression body )
protected AsyncBaseExpression( Expression[] body )
{
_body = body;
}
Expand Down Expand Up @@ -89,7 +89,7 @@ public AsyncBaseExpressionDebuggerProxy( AsyncBaseExpression node )
_node = node;
}

public Expression Body => _node._body;
public Expression[] Body => _node._body;
public Expression StateMachineBody => _node._stateMachineBody;
public bool IsReduced => _node._isReduced;
public Type ReturnType => _node.Type;
Expand Down
4 changes: 2 additions & 2 deletions src/Hyperbee.AsyncExpressions/AsyncBlockExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class AsyncBlockExpression : AsyncBaseExpression
private readonly BlockExpression _reducedBlock;
private readonly Type _finalResultType;

public AsyncBlockExpression( Expression[] expressions ) : base( null )
public AsyncBlockExpression( Expression[] expressions ) : base( expressions )
{
if ( expressions == null || expressions.Length == 0 )
{
Expand Down Expand Up @@ -97,7 +97,7 @@ private static BlockExpression ReduceBlock( Expression[] expressions, out Type f

public static partial class AsyncExpression
{
public static AsyncBaseExpression BlockAsync( params Expression[] expressions )
public static AsyncBlockExpression BlockAsync( params Expression[] expressions )
{
return new AsyncBlockExpression( expressions );
}
Expand Down
2 changes: 1 addition & 1 deletion src/Hyperbee.AsyncExpressions/AsyncInvocationExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public class AsyncInvocationExpression : AsyncBaseExpression
{
private readonly InvocationExpression _invocationExpression;

public AsyncInvocationExpression( InvocationExpression invocationExpression ) : base( null )
public AsyncInvocationExpression( InvocationExpression invocationExpression ) : base( [invocationExpression] )
{
_invocationExpression = invocationExpression;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Hyperbee.AsyncExpressions/AsyncMethodCallExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class AsyncMethodCallExpression : AsyncBaseExpression
{
private readonly MethodCallExpression _methodCallExpression;

public AsyncMethodCallExpression( MethodCallExpression methodCallExpression ) : base( null )
public AsyncMethodCallExpression( MethodCallExpression methodCallExpression ) : base( [methodCallExpression] )
{
_methodCallExpression = methodCallExpression;
}
Expand Down
69 changes: 28 additions & 41 deletions src/Hyperbee.AsyncExpressions/StateMachineBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,88 +41,75 @@ public StateMachineBuilder( ModuleBuilder moduleBuilder, string typeName )

public void GenerateMoveNextMethod( BlockExpression reducedBlock )
{
// Section: Initialization
// Use variables from reducedBlock to ensure variable scope is managed correctly.
// Use variables from reducedBlock
var variables = reducedBlock.Variables;

// List to hold all expressions that make up the state machine's body
// Define a parameter to represent the instance of the state machine class
var stateMachineInstance = Expression.Parameter( _typeBuilder, "stateMachine" );

var bodyExpressions = new List<Expression>
{
// Initialize the AsyncTaskMethodBuilder field
Expression.Assign(Expression.Field(Expression.Constant(this), _builderField),
Expression.Call(typeof(AsyncTaskMethodBuilder<TResult>), nameof(AsyncTaskMethodBuilder<TResult>.Create), null))
Expression.Assign( Expression.Field( stateMachineInstance, _builderField ),
Expression.Call( typeof(AsyncTaskMethodBuilder<TResult>), nameof(AsyncTaskMethodBuilder<TResult>.Create), null ) )
};

// Section: State Handling
// Iterate through each block to define state transitions
var blocks = reducedBlock.Expressions;
for ( var i = 0; i < blocks.Count; i++ )
{
var blockExpr = blocks[i];

// Define the types for the ConfiguredTaskAwaitable and its awaiter
var configuredTaskAwaitableType = typeof( ConfiguredTaskAwaitable<> ).MakeGenericType( typeof( TResult ) );
var configuredTaskAwaitableType = typeof(ConfiguredTaskAwaitable<>).MakeGenericType( typeof(TResult) );
var configuredTaskAwaiterType = configuredTaskAwaitableType.GetNestedType( "ConfiguredTaskAwaiter" );

// Define a field to hold the awaiter for this state
var awaiterField = _typeBuilder.DefineField( $"_awaiter_{i}", configuredTaskAwaiterType!, FieldAttributes.Private );

// Check if the current state matches
var stateCheck = Expression.Equal( Expression.Field( Expression.Constant( this ), _stateField ), Expression.Constant( i ) );

// Assign the awaiter
var stateCheck = Expression.Equal( Expression.Field( stateMachineInstance, _stateField ), Expression.Constant( i ) );
var assignAwaiter = Expression.Assign(
Expression.Field( Expression.Constant( this ), awaiterField ),
Expression.Field( stateMachineInstance, awaiterField ),
Expression.Call(
Expression.Call( blockExpr, nameof( Task.ConfigureAwait ), null, Expression.Constant( false ) ),
nameof( ConfiguredTaskAwaitable<TResult>.GetAwaiter ), null )
Expression.Call( blockExpr, nameof(Task.ConfigureAwait), null, Expression.Constant( false ) ),
nameof(ConfiguredTaskAwaitable<TResult>.GetAwaiter), null )
);

// Create the StateMachineProxy instance and assign it
var stateMachineProxy = Expression.New( typeof( StateMachineProxy ).GetConstructor( [typeof( IAsyncStateMachine )] )!, Expression.Constant( this ) );
var assignProxy = Expression.Assign( Expression.Field( Expression.Constant( this ), _proxyField ), stateMachineProxy );
var stateMachineProxy = Expression.New( typeof(StateMachineProxy).GetConstructor( new[] { typeof(IAsyncStateMachine) } )!, stateMachineInstance );
var assignProxy = Expression.Assign( Expression.Field( stateMachineInstance, _proxyField ), stateMachineProxy );

// Setup continuation with the builder, using the awaiter and the state machine proxy
var setupContinuation = Expression.Call(
Expression.Field( Expression.Constant( this ), _builderField ),
nameof( AsyncTaskMethodBuilder<TResult>.AwaitUnsafeOnCompleted ),
[configuredTaskAwaiterType, typeof( IAsyncStateMachine )],
Expression.Field( Expression.Constant( this ), awaiterField ),
Expression.Field( Expression.Constant( this ), _proxyField )
Expression.Field( stateMachineInstance, _builderField ),
nameof(AsyncTaskMethodBuilder<TResult>.AwaitUnsafeOnCompleted),
new Type[] { configuredTaskAwaiterType, typeof(IAsyncStateMachine) },
Expression.Field( stateMachineInstance, awaiterField ),
Expression.Field( stateMachineInstance, _proxyField )
);

// Move to the next state
var moveToNextState = Expression.Assign( Expression.Field( Expression.Constant( this ), _stateField ), Expression.Constant( i + 1 ) );
var moveToNextState = Expression.Assign( Expression.Field( stateMachineInstance, _stateField ), Expression.Constant( i + 1 ) );

// Section: State Execution Logic
// Check if the task is completed or needs to await
var ifNotCompleted = Expression.IfThenElse(
Expression.IsFalse( Expression.Property( Expression.Field( Expression.Constant( this ), awaiterField ), nameof( TaskAwaiter.IsCompleted ) ) ),
Expression.Block( assignAwaiter, assignProxy, setupContinuation, Expression.Return( Expression.Label( typeof( void ) ) ) ),
Expression.IsFalse( Expression.Property( Expression.Field( stateMachineInstance, awaiterField ), nameof(TaskAwaiter.IsCompleted) ) ),
Expression.Block( assignAwaiter, assignProxy, setupContinuation, Expression.Return( Expression.Label( typeof(void) ) ) ),
Expression.Block( assignAwaiter, moveToNextState )
);

// Add the state check and logic to the body expressions
bodyExpressions.Add( Expression.IfThen( stateCheck, ifNotCompleted ) );
}

// Section: Final State
// Set the final result of the async operation
var setResult = Expression.Call(
Expression.Field( Expression.Constant( this ), _builderField ),
nameof( AsyncTaskMethodBuilder<TResult>.SetResult ),
Expression.Field( stateMachineInstance, _builderField ),
nameof(AsyncTaskMethodBuilder<TResult>.SetResult),
null,
Expression.Field( Expression.Constant( this ), _finalResultField )
Expression.Field( stateMachineInstance, _finalResultField )
);

bodyExpressions.Add( setResult );

// Section: Emit and Compile
// Include the variables in the block to maintain their scope and compile the MoveNext method
// Include the variables in the block
var stateMachineBody = Expression.Block( variables, bodyExpressions );

// Emit the state machine body to the MoveNext method
EmitCompileToMethod( stateMachineBody, _moveNextMethod );
}


private void EmitCompileToMethod( Expression stateMachineBody, MethodBuilder methodBuilder )
{
// compile the generated state machine into a method
Expand Down

0 comments on commit f4c1335

Please sign in to comment.