/// <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>
internal static void ApplyBlockFlowHandlingInternal(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args)
{
// 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.
if (metaBuilder.Error || !args.Signature.HasBlock)
{
return;
}
Expression rfcVariable = metaBuilder.GetTemporary(typeof(RuntimeFlowControl), "#rfc");
ParameterExpression methodUnwinder = metaBuilder.GetTemporary(typeof(MethodUnwinder), "#unwinder");
Expression resultVariable = metaBuilder.GetTemporary(typeof(object), "#result");
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(methodUnwinder, Ast.Equal(Ast.Field(methodUnwinder, MethodUnwinder.TargetFrameField), rfcVariable),
// return unwinder.ReturnValue;
Ast.Assign(resultVariable, Ast.Field(methodUnwinder, MethodUnwinder.ReturnValueField))
).Finally(
// we need to mark the RFC dead snce the block might escape and break later:
Ast.Assign(Ast.Field(rfcVariable, RuntimeFlowControl.IsActiveMethodField), Ast.Constant(false))
),
resultVariable
);
}