internal MSA.Expression/*!*/ Transform(AstGenerator/*!*/ gen, bool isLambda) {
ScopeBuilder scope = DefineLocals(gen.CurrentScope);
// define hidden parameters and RHS-placeholders (#1..#n will be used as RHS of a parallel assignment):
MSA.ParameterExpression blockParameter, selfParameter;
var parameters = DefineParameters(out selfParameter, out blockParameter);
MSA.ParameterExpression scopeVariable = scope.DefineHiddenVariable("#scope", typeof(RubyBlockScope));
MSA.LabelTarget redoLabel = Ast.Label();
gen.EnterBlockDefinition(
scope,
blockParameter,
selfParameter,
scopeVariable,
redoLabel
);
MSA.Expression paramInit = MakeParametersInitialization(gen, parameters);
MSA.ParameterExpression blockUnwinder, filterVariable;
MSA.Expression traceCall, traceReturn;
if (gen.TraceEnabled) {
int firstStatementLine = _body.Count > 0 ? _body.First.Location.Start.Line : Location.End.Line;
int lastStatementLine = _body.Count > 0 ? _body.Last.Location.End.Line : Location.End.Line;
traceCall = Methods.TraceBlockCall.OpCall(scopeVariable, blockParameter, gen.SourcePathConstant, AstUtils.Constant(firstStatementLine));
traceReturn = Methods.TraceBlockReturn.OpCall(scopeVariable, blockParameter, gen.SourcePathConstant, AstUtils.Constant(lastStatementLine));
} else {
traceCall = traceReturn = Ast.Empty();
}
MSA.Expression body = AstUtils.Try(
paramInit,
traceCall,
Ast.Label(redoLabel),
AstUtils.Try(
gen.TransformStatements(_body, ResultOperation.Return)
).Catch(blockUnwinder = Ast.Parameter(typeof(BlockUnwinder), "#u"),
// redo:
AstUtils.IfThen(Ast.Field(blockUnwinder, BlockUnwinder.IsRedoField), Ast.Goto(redoLabel)),
// next:
gen.Return(Ast.Field(blockUnwinder, BlockUnwinder.ReturnValueField))
)
).Filter(filterVariable = Ast.Parameter(typeof(Exception), "#e"),
Methods.FilterBlockException.OpCall(scopeVariable, filterVariable)
).Finally(
traceReturn,
Ast.Empty()
);
body = gen.AddReturnTarget(
scope.CreateScope(
scopeVariable,
Methods.CreateBlockScope.OpCall(new AstExpressions {
scope.MakeLocalsStorage(),
scope.GetVariableNamesExpression(),
blockParameter,
selfParameter,
EnterInterpretedFrameExpression.Instance
}),
body
)
);
gen.LeaveBlockDefinition();
int parameterCount = ParameterCount;
var attributes = _parameters.GetBlockSignatureAttributes();
var dispatcher = Ast.Constant(
BlockDispatcher.Create(parameterCount, attributes, gen.SourcePath, Location.Start.Line), typeof(BlockDispatcher)
);
return Ast.Coalesce(
(isLambda ? Methods.InstantiateLambda : Methods.InstantiateBlock).OpCall(gen.CurrentScopeVariable, gen.CurrentSelfVariable, dispatcher),
(isLambda ? Methods.DefineLambda : Methods.DefineBlock).OpCall(gen.CurrentScopeVariable, gen.CurrentSelfVariable, dispatcher,
BlockDispatcher.CreateLambda(
body,
RubyStackTraceBuilder.EncodeMethodName(gen.CurrentMethod.MethodName, gen.SourcePath, Location, gen.DebugMode),
parameters,
parameterCount,
attributes
)
)
);
}