private DelayedTupleExpression LiftVariable(ParameterExpression param) { DelayedTupleExpression res; if (!_vars.TryGetValue(param, out res)) { _vars[param] = res = new DelayedTupleExpression(_vars.Count, _tupleExpr, _tupleType, param.Type); _orderedVars.Add(new KeyValuePair <ParameterExpression, DelayedTupleExpression>(param, res)); } return(res); }
internal Expression Reduce(bool shouldInterpret, bool emitDebugSymbols, int compilationThreshold, IList <ParameterExpression> parameters, Func <Expression <Func <MutableTuple, object> >, Expression <Func <MutableTuple, object> > > bodyConverter) { _state = LiftVariable(Expression.Parameter(typeof(int), "state")); _current = LiftVariable(Expression.Parameter(typeof(object), "current")); // lift the parameters into the tuple foreach (ParameterExpression pe in parameters) { LiftVariable(pe); } DelayedTupleExpression liftedGen = LiftVariable(_generatorParam); // Visit body Expression body = Visit(_body); Debug.Assert(_returnLabels.Count == 1); // Add the switch statement to the body int count = _yields.Count; var cases = new SwitchCase[count + 1]; for (int i = 0; i < count; i++) { cases[i] = Expression.SwitchCase(Expression.Goto(_yields[i].Label), AstUtils.Constant(_yields[i].State)); } cases[count] = Expression.SwitchCase(Expression.Goto(_returnLabels.Peek()), AstUtils.Constant(Finished)); // Create the lambda for the PythonGeneratorNext, hoisting variables // into a tuple outside the lambda Expression[] tupleExprs = new Expression[_vars.Count]; foreach (var variable in _orderedVars) { // first 2 are our state & out var if (variable.Value.Index >= 2 && variable.Value.Index < (parameters.Count + 2)) { tupleExprs[variable.Value.Index] = parameters[variable.Value.Index - 2]; } else { tupleExprs[variable.Value.Index] = Expression.Default(variable.Key.Type); } } Expression newTuple = MutableTuple.Create(tupleExprs); Type tupleType = _tupleType.Value = newTuple.Type; ParameterExpression tupleExpr = _tupleExpr.Value = Expression.Parameter(tupleType, "tuple"); ParameterExpression tupleArg = Expression.Parameter(typeof(MutableTuple), "tupleArg"); _temps.Add(_gotoRouter); _temps.Add(tupleExpr); // temps for the outer lambda ParameterExpression tupleTmp = Expression.Parameter(tupleType, "tuple"); ParameterExpression ret = Expression.Parameter(typeof(PythonGenerator), "ret"); var innerLambda = Expression.Lambda <Func <MutableTuple, object> >( Expression.Block( _temps.ToArray(), Expression.Assign( tupleExpr, Expression.Convert( tupleArg, tupleType ) ), Expression.Switch(Expression.Assign(_gotoRouter, _state), cases), body, MakeAssign(_state, AstUtils.Constant(Finished)), Expression.Label(_returnLabels.Peek()), _current ), _name, new ParameterExpression[] { tupleArg } ); // Generate a call to PythonOps.MakeGeneratorClosure(Tuple data, object generatorCode) return(Expression.Block( new[] { tupleTmp, ret }, Expression.Assign( ret, Expression.Call( typeof(PythonOps).GetMethod("MakeGenerator"), parameters[0], Expression.Assign(tupleTmp, newTuple), emitDebugSymbols ? (Expression)bodyConverter(innerLambda) : (Expression)Expression.Constant( new LazyCode <Func <MutableTuple, object> >( bodyConverter(innerLambda), shouldInterpret, compilationThreshold ), typeof(object) ) ) ), new DelayedTupleAssign( new DelayedTupleExpression(liftedGen.Index, new StrongBox <ParameterExpression>(tupleTmp), _tupleType, typeof(PythonGenerator)), ret ), ret )); }