private LightLambdaExpression CreateFunctionLambda() {
bool needsWrapperMethod = _parameters.Length > PythonCallTargets.MaxArgs;
Delegate originalDelegate;
Type delegateType = GetDelegateType(_parameters, needsWrapperMethod, out originalDelegate);
MSAst.ParameterExpression localContext = null;
ReadOnlyCollectionBuilder<MSAst.ParameterExpression> locals = new ReadOnlyCollectionBuilder<MSAst.ParameterExpression>();
if (NeedsLocalContext) {
localContext = LocalCodeContextVariable;
locals.Add(localContext);
}
MSAst.ParameterExpression[] parameters = CreateParameters(needsWrapperMethod, locals);
List<MSAst.Expression> init = new List<MSAst.Expression>();
foreach (var param in _parameters) {
IPythonVariableExpression pyVar = GetVariableExpression(param.PythonVariable) as IPythonVariableExpression;
if (pyVar != null) {
var varInit = pyVar.Create();
if (varInit != null) {
init.Add(varInit);
}
}
}
// Transform the parameters.
init.Add(Ast.ClearDebugInfo(GlobalParent.Document));
locals.Add(PythonAst._globalContext);
init.Add(Ast.Assign(PythonAst._globalContext, new GetGlobalContextExpression(_parentContext)));
GlobalParent.PrepareScope(locals, init);
// Create variables and references. Since references refer to
// parameters, do this after parameters have been created.
CreateFunctionVariables(locals, init);
// Initialize parameters - unpack tuples.
// Since tuples unpack into locals, this must be done after locals have been created.
InitializeParameters(init, needsWrapperMethod, parameters);
List<MSAst.Expression> statements = new List<MSAst.Expression>();
// add beginning sequence point
var start = GlobalParent.IndexToLocation(StartIndex);
statements.Add(GlobalParent.AddDebugInfo(
AstUtils.Empty(),
new SourceSpan(new SourceLocation(0, start.Line, start.Column), new SourceLocation(0, start.Line, Int32.MaxValue))));
// For generators, we need to do a check before the first statement for Generator.Throw() / Generator.Close().
// The exception traceback needs to come from the generator's method body, and so we must do the check and throw
// from inside the generator.
if (IsGenerator) {
MSAst.Expression s1 = YieldExpression.CreateCheckThrowExpression(SourceSpan.None);
statements.Add(s1);
}
MSAst.ParameterExpression extracted = null;
if (!IsGenerator && _canSetSysExcInfo) {
// need to allocate the exception here so we don't share w/ exceptions made & freed
// during the body.
extracted = Ast.Parameter(typeof(Exception), "$ex");
locals.Add(extracted);
}
if (_body.CanThrow && !(_body is SuiteStatement) && _body.StartIndex != -1) {
statements.Add(UpdateLineNumber(GlobalParent.IndexToLocation(_body.StartIndex).Line));
}
statements.Add(Body);
MSAst.Expression body = Ast.Block(statements);
// If this function can modify sys.exc_info() (_canSetSysExcInfo), then it must restore the result on finish.
//
// Wrap in
// $temp = PythonOps.SaveCurrentException()
// <body>
// PythonOps.RestoreCurrentException($temp)
// Skip this if we're a generator. For generators, the try finally is handled by the PythonGenerator class
// before it's invoked. This is because the restoration must occur at every place the function returns from
// a yield point. That's different than the finally semantics in a generator.
if (extracted != null) {
MSAst.Expression s = AstUtils.Try(
Ast.Assign(
extracted,
Ast.Call(AstMethods.SaveCurrentException)
),
body
).Finally(
Ast.Call(
AstMethods.RestoreCurrentException, extracted
)
);
body = s;
}
if (_body.CanThrow && GlobalParent.PyContext.PythonOptions.Frames) {
body = AddFrame(LocalContext, Ast.Property(_functionParam, typeof(PythonFunction).GetProperty("__code__")), body);
locals.Add(FunctionStackVariable);
}
body = AddProfiling(body);
body = WrapScopeStatements(body, _body.CanThrow);
body = Ast.Block(body, AstUtils.Empty());
body = AddReturnTarget(body);
MSAst.Expression bodyStmt = body;
if (localContext != null) {
var createLocal = CreateLocalContext(_parentContext);
init.Add(
Ast.Assign(
localContext,
createLocal
)
);
}
init.Add(bodyStmt);
bodyStmt = Ast.Block(init);
// wrap a scope if needed
bodyStmt = Ast.Block(locals.ToReadOnlyCollection(), bodyStmt);
return AstUtils.LightLambda(
typeof(object),
delegateType,
AddDefaultReturn(bodyStmt, typeof(object)),
Name + "$" + Interlocked.Increment(ref _lambdaId),
parameters
);
}