/// <summary>
/// Takes current result and wraps it into try-filter(MethodUnwinder)-finally block that ensures correct "break" behavior for
/// library method calls with block given in bfcVariable (BlockParam).
/// </summary>
public static void RuleControlFlowBuilder(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args)
{
if (metaBuilder.Error)
{
return;
}
var metaBlock = args.GetMetaBlock();
Debug.Assert(metaBlock != null, "RuleControlFlowBuilder should only be used if the signature has a block");
// We construct CF only for non-nil blocks thus we need a test for it:
if (metaBlock.Value == null)
{
metaBuilder.AddRestriction(Ast.Equal(metaBlock.Expression, AstUtils.Constant(null)));
return;
}
// don't need to test the exact type of the Proc since the code is subclass agnostic:
metaBuilder.AddRestriction(Ast.NotEqual(metaBlock.Expression, AstUtils.Constant(null)));
Expression bfcVariable = metaBuilder.BfcVariable;
Debug.Assert(bfcVariable != null);
// Method call with proc can invoke control flow that returns an arbitrary value from the call, so we need to type result to Object.
// Otherwise, the result could only be result of targetExpression unless its return type is void.
Expression resultVariable = metaBuilder.GetTemporary(typeof(object), "#result");
ParameterExpression unwinder;
metaBuilder.Result = Ast.Block(
Ast.Assign(bfcVariable, Methods.CreateBfcForLibraryMethod.OpCall(AstUtils.Convert(args.GetBlockExpression(), typeof(Proc)))),
AstUtils.Try(
Ast.Assign(resultVariable, AstUtils.Convert(metaBuilder.Result, typeof(object)))
).Filter(unwinder = Ast.Parameter(typeof(MethodUnwinder), "#unwinder"),
Methods.IsProcConverterTarget.OpCall(bfcVariable, unwinder),
Ast.Assign(resultVariable, Ast.Field(unwinder, MethodUnwinder.ReturnValueField)),
AstUtils.Default(typeof(object))
).Finally(
Methods.LeaveProcConverter.OpCall(bfcVariable)
),
resultVariable
);
}