/// <summary>
/// Takes current result and wraps it into try-filter(MethodUnwinder)-finally block that ensures correct "break" behavior for
/// Ruby method calls with a block given in arguments.
///
/// Sets up a RFC frame similarly to MethodDeclaration.
/// </summary>
public static void RuleControlFlowBuilder(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args)
{
Debug.Assert(args.Signature.HasBlock);
if (metaBuilder.Error)
{
return;
}
// TODO (improvement):
// We don't special case null block here, although we could (we would need a test for that then).
// We could also statically know (via call-site flag) that the current method is not a proc-converter (passed by ref),
// which would make such calls faster.
var rfcVariable = metaBuilder.GetTemporary(typeof(RuntimeFlowControl), "#rfc");
var resultVariable = metaBuilder.GetTemporary(typeof(object), "#result");
MSA.ParameterExpression unwinder;
metaBuilder.Result = Ast.Block(
// initialize frame (RFC):
Ast.Assign(rfcVariable, Methods.CreateRfcForMethod.OpCall(AstUtils.Convert(args.GetBlockExpression(), typeof(Proc)))),
AstUtils.Try(
Ast.Assign(resultVariable, metaBuilder.Result)
).Filter(unwinder = Ast.Parameter(typeof(MethodUnwinder), "#unwinder"),
Ast.Equal(Ast.Field(unwinder, MethodUnwinder.TargetFrameField), rfcVariable),
// return unwinder.ReturnValue;
Ast.Assign(resultVariable, Ast.Field(unwinder, MethodUnwinder.ReturnValueField))
).Finally(
// we need to mark the RFC dead snce the block might escape and break later:
Methods.LeaveMethodFrame.OpCall(rfcVariable)
),
resultVariable
);
}