PageRenderTime 60ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/Languages/IronPython/IronPython/Compiler/GeneratorRewriter.cs

http://github.com/IronLanguages/main
C# | 1096 lines | 820 code | 143 blank | 133 comment | 185 complexity | 6364069b33beec1a8fccfc53b5066f92 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if FEATURE_CORE_DLR
  16. using System.Linq.Expressions;
  17. #endif
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Collections.ObjectModel;
  21. using System.Diagnostics;
  22. using System.Reflection;
  23. using System.Runtime.CompilerServices;
  24. using Microsoft.Scripting;
  25. using Microsoft.Scripting.Ast;
  26. using Microsoft.Scripting.Utils;
  27. using IronPython.Runtime;
  28. using IronPython.Runtime.Operations;
  29. using AstUtils = Microsoft.Scripting.Ast.Utils;
  30. namespace IronPython.Compiler {
  31. /// <summary>
  32. /// When finding a yield return or yield break, this rewriter flattens out
  33. /// containing blocks, scopes, and expressions with stack state. All
  34. /// scopes encountered have their variables promoted to the generator's
  35. /// closure, so they survive yields.
  36. /// </summary>
  37. internal sealed class GeneratorRewriter : DynamicExpressionVisitor {
  38. private readonly Expression _body;
  39. private readonly string _name;
  40. private readonly StrongBox<Type> _tupleType = new StrongBox<Type>(null);
  41. private readonly StrongBox<ParameterExpression> _tupleExpr = new StrongBox<ParameterExpression>(null);
  42. // The one return label, or more than one if we're in a finally
  43. private readonly Stack<LabelTarget> _returnLabels = new Stack<LabelTarget>();
  44. private ParameterExpression _gotoRouter;
  45. private bool _inTryWithFinally;
  46. private readonly List<YieldMarker> _yields = new List<YieldMarker>();
  47. private readonly Dictionary<ParameterExpression, DelayedTupleExpression> _vars = new Dictionary<ParameterExpression, DelayedTupleExpression>();
  48. private readonly List<KeyValuePair<ParameterExpression, DelayedTupleExpression>> _orderedVars = new List<KeyValuePair<ParameterExpression, DelayedTupleExpression>>();
  49. // Possible optimization: reuse temps. Requires scoping them correctly,
  50. // and then storing them back in a free list
  51. private readonly List<ParameterExpression> _temps = new List<ParameterExpression>();
  52. private Expression _state, _current;
  53. // These two constants are used internally. They should not conflict
  54. // with valid yield states.
  55. private const int GotoRouterYielding = 0;
  56. private const int GotoRouterNone = -1;
  57. // The state of the generator before it starts and when it's done
  58. internal const int NotStarted = -1;
  59. internal const int Finished = 0;
  60. internal static ParameterExpression _generatorParam = Expression.Parameter(typeof(PythonGenerator), "$generator");
  61. internal GeneratorRewriter(string name, Expression body) {
  62. _body = body;
  63. _name = name;
  64. _returnLabels.Push(Expression.Label("retLabel"));
  65. _gotoRouter = Expression.Variable(typeof(int), "$gotoRouter");
  66. }
  67. internal Expression Reduce(bool shouldInterpret, bool emitDebugSymbols, int compilationThreshold,
  68. IList<ParameterExpression> parameters, Func<Expression<Func<MutableTuple, object>>,
  69. Expression<Func<MutableTuple, object>>> bodyConverter) {
  70. _state = LiftVariable(Expression.Parameter(typeof(int), "state"));
  71. _current = LiftVariable(Expression.Parameter(typeof(object), "current"));
  72. // lift the parameters into the tuple
  73. foreach (ParameterExpression pe in parameters) {
  74. LiftVariable(pe);
  75. }
  76. DelayedTupleExpression liftedGen = LiftVariable(_generatorParam);
  77. // Visit body
  78. Expression body = Visit(_body);
  79. Debug.Assert(_returnLabels.Count == 1);
  80. // Add the switch statement to the body
  81. int count = _yields.Count;
  82. var cases = new SwitchCase[count + 1];
  83. for (int i = 0; i < count; i++) {
  84. cases[i] = Expression.SwitchCase(Expression.Goto(_yields[i].Label), AstUtils.Constant(_yields[i].State));
  85. }
  86. cases[count] = Expression.SwitchCase(Expression.Goto(_returnLabels.Peek()), AstUtils.Constant(Finished));
  87. // Create the lambda for the PythonGeneratorNext, hoisting variables
  88. // into a tuple outside the lambda
  89. Expression[] tupleExprs = new Expression[_vars.Count];
  90. foreach (var variable in _orderedVars) {
  91. // first 2 are our state & out var
  92. if (variable.Value.Index >= 2 && variable.Value.Index < (parameters.Count + 2)) {
  93. tupleExprs[variable.Value.Index] = parameters[variable.Value.Index - 2];
  94. } else {
  95. tupleExprs[variable.Value.Index] = Expression.Default(variable.Key.Type);
  96. }
  97. }
  98. Expression newTuple = MutableTuple.Create(tupleExprs);
  99. Type tupleType = _tupleType.Value = newTuple.Type;
  100. ParameterExpression tupleExpr = _tupleExpr.Value = Expression.Parameter(tupleType, "tuple");
  101. ParameterExpression tupleArg = Expression.Parameter(typeof(MutableTuple), "tupleArg");
  102. _temps.Add(_gotoRouter);
  103. _temps.Add(tupleExpr);
  104. // temps for the outer lambda
  105. ParameterExpression tupleTmp = Expression.Parameter(tupleType, "tuple");
  106. ParameterExpression ret = Expression.Parameter(typeof(PythonGenerator), "ret");
  107. var innerLambda = Expression.Lambda<Func<MutableTuple, object>>(
  108. Expression.Block(
  109. _temps.ToArray(),
  110. Expression.Assign(
  111. tupleExpr,
  112. Expression.Convert(
  113. tupleArg,
  114. tupleType
  115. )
  116. ),
  117. Expression.Switch(Expression.Assign(_gotoRouter, _state), cases),
  118. body,
  119. MakeAssign(_state, AstUtils.Constant(Finished)),
  120. Expression.Label(_returnLabels.Peek()),
  121. _current
  122. ),
  123. _name,
  124. new ParameterExpression[] { tupleArg }
  125. );
  126. // Generate a call to PythonOps.MakeGeneratorClosure(Tuple data, object generatorCode)
  127. return Expression.Block(
  128. new[] { tupleTmp, ret },
  129. Expression.Assign(
  130. ret,
  131. Expression.Call(
  132. typeof(PythonOps).GetMethod("MakeGenerator"),
  133. parameters[0],
  134. Expression.Assign(tupleTmp, newTuple),
  135. emitDebugSymbols ?
  136. (Expression)bodyConverter(innerLambda) :
  137. (Expression)Expression.Constant(
  138. new LazyCode<Func<MutableTuple, object>>(
  139. bodyConverter(innerLambda),
  140. shouldInterpret,
  141. compilationThreshold
  142. ),
  143. typeof(object)
  144. )
  145. )
  146. ),
  147. new DelayedTupleAssign(
  148. new DelayedTupleExpression(liftedGen.Index, new StrongBox<ParameterExpression>(tupleTmp), _tupleType, typeof(PythonGenerator)),
  149. ret
  150. ),
  151. ret
  152. );
  153. }
  154. private YieldMarker GetYieldMarker(YieldExpression node) {
  155. YieldMarker result = new YieldMarker(_yields.Count + 1);
  156. _yields.Add(result);
  157. Debug.Assert(node.YieldMarker == -1);
  158. return result;
  159. }
  160. /// <summary>
  161. /// Spills the right side into a temp, and replaces it with its temp.
  162. /// Returns the expression that initializes the temp.
  163. /// </summary>
  164. private Expression ToTemp(ref Expression e) {
  165. Debug.Assert(e != null);
  166. var temp = LiftVariable(Expression.Variable(e.Type, "generatorTemp" + _temps.Count));
  167. var result = MakeAssign(temp, e);
  168. e = temp;
  169. return result;
  170. }
  171. /// <summary>
  172. /// Makes an assignment to this variable. Pushes the assignment as far
  173. /// into the right side as possible, to allow jumps into it.
  174. /// </summary>
  175. private Expression MakeAssign(Expression variable, Expression value) {
  176. // TODO: this is not complete.
  177. // It may end up generating a bad tree if any of these nodes
  178. // contain yield and return a value: Switch, Loop, or Goto.
  179. // Those are not supported, but we can't throw here because we may
  180. // end up disallowing valid uses (if some other expression contains
  181. // yield, but not this one).
  182. switch (value.NodeType) {
  183. case ExpressionType.Block:
  184. return MakeAssignBlock(variable, value);
  185. case ExpressionType.Conditional:
  186. return MakeAssignConditional(variable, value);
  187. case ExpressionType.Label:
  188. return MakeAssignLabel(variable, (LabelExpression)value);
  189. }
  190. return DelayedAssign(variable, value);
  191. }
  192. struct GotoRewriteInfo {
  193. public readonly Expression Variable;
  194. public readonly LabelTarget VoidTarget;
  195. public GotoRewriteInfo(Expression variable, LabelTarget voidTarget) {
  196. Variable = variable;
  197. VoidTarget = voidTarget;
  198. }
  199. }
  200. private Expression MakeAssignLabel(Expression variable, LabelExpression value) {
  201. GotoRewriteInfo curVariable = new GotoRewriteInfo(variable, Expression.Label(value.Target.Name + "_voided"));
  202. var defaultValue = new GotoRewriter(this, curVariable, value.Target).Visit(value.DefaultValue);
  203. return MakeAssignLabel(variable, curVariable, value.Target, defaultValue);
  204. }
  205. private Expression MakeAssignLabel(Expression variable, GotoRewriteInfo curVariable, LabelTarget target, Expression defaultValue) {
  206. return Expression.Label(
  207. curVariable.VoidTarget,
  208. MakeAssign(variable, defaultValue)
  209. );
  210. }
  211. class GotoRewriter : ExpressionVisitor {
  212. private readonly GotoRewriteInfo _gotoInfo;
  213. private readonly LabelTarget _target;
  214. private readonly GeneratorRewriter _rewriter;
  215. public GotoRewriter(GeneratorRewriter rewriter, GotoRewriteInfo gotoInfo, LabelTarget target) {
  216. _gotoInfo = gotoInfo;
  217. _target = target;
  218. _rewriter = rewriter;
  219. }
  220. protected override Expression VisitGoto(GotoExpression node) {
  221. if (node.Target == _target) {
  222. return Expression.Goto(
  223. _gotoInfo.VoidTarget,
  224. Expression.Block(
  225. _rewriter.MakeAssign(_gotoInfo.Variable, node.Value),
  226. Expression.Default(typeof(void))
  227. ),
  228. node.Type
  229. );
  230. }
  231. return base.VisitGoto(node);
  232. }
  233. }
  234. private Expression MakeAssignBlock(Expression variable, Expression value) {
  235. var node = (BlockExpression)value;
  236. var newBlock = new ReadOnlyCollectionBuilder<Expression>(node.Expressions);
  237. Expression blockRhs = newBlock[newBlock.Count - 1];
  238. if (blockRhs.NodeType == ExpressionType.Label) {
  239. var label = (LabelExpression)blockRhs;
  240. GotoRewriteInfo curVariable = new GotoRewriteInfo(variable, Expression.Label(label.Target.Name + "_voided"));
  241. var rewriter = new GotoRewriter(this, curVariable, label.Target);
  242. for (int i = 0; i < newBlock.Count - 1; i++) {
  243. newBlock[i] = rewriter.Visit(newBlock[i]);
  244. }
  245. newBlock[newBlock.Count - 1] = MakeAssignLabel(variable, curVariable, label.Target, rewriter.Visit(label.DefaultValue));
  246. } else {
  247. newBlock[newBlock.Count - 1] = MakeAssign(variable, newBlock[newBlock.Count - 1]);
  248. }
  249. return Expression.Block(node.Variables, newBlock);
  250. }
  251. private Expression MakeAssignConditional(Expression variable, Expression value) {
  252. var node = (ConditionalExpression)value;
  253. return Expression.Condition(node.Test, MakeAssign(variable, node.IfTrue), MakeAssign(variable, node.IfFalse));
  254. }
  255. private BlockExpression ToTemp(ref ReadOnlyCollection<Expression> args) {
  256. int count = args.Count;
  257. var block = new Expression[count];
  258. var newArgs = new Expression[count];
  259. args.CopyTo(newArgs, 0);
  260. for (int i = 0; i < count; i++) {
  261. block[i] = ToTemp(ref newArgs[i]);
  262. }
  263. args = new ReadOnlyCollection<Expression>(newArgs);
  264. return Expression.Block(block);
  265. }
  266. #region VisitTry
  267. protected override Expression VisitTry(TryExpression node) {
  268. int startYields = _yields.Count;
  269. bool savedInTryWithFinally = _inTryWithFinally;
  270. if (node.Finally != null || node.Fault != null) {
  271. _inTryWithFinally = true;
  272. }
  273. Expression @try = Visit(node.Body);
  274. int tryYields = _yields.Count;
  275. IList<CatchBlock> handlers = Visit(node.Handlers, VisitCatchBlock);
  276. int catchYields = _yields.Count;
  277. // push a new return label in case the finally block yields
  278. _returnLabels.Push(Expression.Label("tryLabel"));
  279. // only one of these can be non-null
  280. Expression @finally = Visit(node.Finally);
  281. Expression fault = Visit(node.Fault);
  282. LabelTarget finallyReturn = _returnLabels.Pop();
  283. int finallyYields = _yields.Count;
  284. _inTryWithFinally = savedInTryWithFinally;
  285. if (@try == node.Body &&
  286. handlers == node.Handlers &&
  287. @finally == node.Finally &&
  288. fault == node.Fault) {
  289. return node;
  290. }
  291. // No yields, just return
  292. if (startYields == _yields.Count) {
  293. Debug.Assert(@try.Type == node.Type);
  294. Debug.Assert(handlers == null || handlers.Count == 0 || handlers[0].Body.Type == node.Type);
  295. return Expression.MakeTry(null, @try, @finally, fault, handlers);
  296. }
  297. if (fault != null && finallyYields != catchYields) {
  298. // No one needs this yet, and it's not clear how we should get back to
  299. // the fault
  300. throw new NotSupportedException("yield in fault block is not supported");
  301. }
  302. // If try has yields, we need to build a new try body that
  303. // dispatches to the yield labels
  304. var tryStart = Expression.Label("tryStart");
  305. if (tryYields != startYields) {
  306. @try = Expression.Block(MakeYieldRouter(node.Body.Type, startYields, tryYields, tryStart), @try);
  307. Debug.Assert(@try.Type == node.Body.Type);
  308. }
  309. // Transform catches with yield to deferred handlers
  310. if (catchYields != tryYields) {
  311. var block = new List<Expression>();
  312. block.Add(MakeYieldRouter(node.Body.Type, tryYields, catchYields, tryStart));
  313. block.Add(null); // empty slot to fill in later
  314. for (int i = 0, n = handlers.Count; i < n; i++) {
  315. CatchBlock c = handlers[i];
  316. if (c == node.Handlers[i]) {
  317. continue;
  318. }
  319. if (handlers.IsReadOnly) {
  320. handlers = ArrayUtils.ToArray(handlers);
  321. }
  322. // the variable that will be scoped to the catch block
  323. var exceptionVar = Expression.Variable(c.Test, null);
  324. // the variable that the catch block body will use to
  325. // access the exception. We reuse the original variable if
  326. // the catch block had one. It needs to be hoisted because
  327. // the catch might contain yields.
  328. var deferredVar = c.Variable ?? Expression.Variable(c.Test, null);
  329. LiftVariable(deferredVar);
  330. // We need to ensure that filters can access the exception
  331. // variable
  332. Expression filter = c.Filter;
  333. if (filter != null && c.Variable != null) {
  334. filter = Expression.Block(new[] { c.Variable }, Expression.Assign(c.Variable, exceptionVar), filter);
  335. }
  336. // catch (ExceptionType exceptionVar) {
  337. // deferredVar = exceptionVar;
  338. // }
  339. handlers[i] = Expression.Catch(
  340. exceptionVar,
  341. Expression.Block(
  342. DelayedAssign(Visit(deferredVar), exceptionVar),
  343. Expression.Default(node.Body.Type)
  344. ),
  345. filter
  346. );
  347. // We need to rewrite rethrows into "throw deferredVar"
  348. var catchBody = new RethrowRewriter { Exception = deferredVar }.Visit(c.Body);
  349. // if (deferredVar != null) {
  350. // ... catch body ...
  351. // }
  352. block.Add(
  353. Expression.Condition(
  354. Expression.NotEqual(Visit(deferredVar), AstUtils.Constant(null, deferredVar.Type)),
  355. catchBody,
  356. Expression.Default(node.Body.Type)
  357. )
  358. );
  359. }
  360. block[1] = Expression.MakeTry(null, @try, null, null, new ReadOnlyCollection<CatchBlock>(handlers));
  361. @try = Expression.Block(block);
  362. Debug.Assert(@try.Type == node.Body.Type);
  363. handlers = new CatchBlock[0]; // so we don't reuse these
  364. }
  365. if (finallyYields != catchYields) {
  366. // We need to add a catch block to save the exception, so we
  367. // can rethrow in case there is a yield in the finally. Also,
  368. // add logic for returning. It looks like this:
  369. //
  370. // try { ... } catch (Exception all) { saved = all; }
  371. // finally {
  372. // if (_finallyReturnVar) goto finallyReturn;
  373. // ...
  374. // if (saved != null) throw saved;
  375. // finallyReturn:
  376. // }
  377. // if (_finallyReturnVar) goto _return;
  378. // We need to add a catch(Exception), so if we have catches,
  379. // wrap them in a try
  380. if (handlers.Count > 0) {
  381. @try = Expression.MakeTry(null, @try, null, null, handlers);
  382. Debug.Assert(@try.Type == node.Body.Type);
  383. handlers = new CatchBlock[0];
  384. }
  385. // NOTE: the order of these routers is important
  386. // The first call changes the labels to all point at "tryEnd",
  387. // so the second router will jump to "tryEnd"
  388. var tryEnd = Expression.Label("tryEnd");
  389. Expression inFinallyRouter = MakeYieldRouter(node.Body.Type, catchYields, finallyYields, tryEnd);
  390. Expression inTryRouter = MakeYieldRouter(node.Body.Type, catchYields, finallyYields, tryStart);
  391. var all = Expression.Variable(typeof(Exception), "e");
  392. var saved = Expression.Variable(typeof(Exception), "$saved$" + _temps.Count);
  393. LiftVariable(saved);
  394. @try = Expression.Block(
  395. Expression.TryCatchFinally(
  396. Expression.Block(
  397. inTryRouter,
  398. @try,
  399. DelayedAssign(Visit(saved), AstUtils.Constant(null, saved.Type)),
  400. Expression.Label(tryEnd)
  401. ),
  402. Expression.Block(
  403. MakeSkipFinallyBlock(finallyReturn),
  404. inFinallyRouter,
  405. @finally,
  406. Expression.Condition(
  407. Expression.NotEqual(Visit(saved), AstUtils.Constant(null, saved.Type)),
  408. Expression.Throw(Visit(saved)),
  409. Utils.Empty()
  410. ),
  411. Expression.Label(finallyReturn)
  412. ),
  413. Expression.Catch(all, Utils.Void(DelayedAssign(Visit(saved), all)))
  414. ),
  415. Expression.Condition(
  416. Expression.Equal(_gotoRouter, AstUtils.Constant(GotoRouterYielding)),
  417. Expression.Goto(_returnLabels.Peek()),
  418. Utils.Empty()
  419. )
  420. );
  421. @finally = null;
  422. } else if (@finally != null) {
  423. // try or catch had a yield, modify finally so we can skip over it
  424. @finally = Expression.Block(
  425. MakeSkipFinallyBlock(finallyReturn),
  426. @finally,
  427. Expression.Label(finallyReturn)
  428. );
  429. }
  430. // Make the outer try, if needed
  431. if (handlers.Count > 0 || @finally != null || fault != null) {
  432. @try = Expression.MakeTry(null, @try, @finally, fault, handlers);
  433. }
  434. Debug.Assert(@try.Type == node.Body.Type);
  435. return Expression.Block(Expression.Label(tryStart), @try);
  436. }
  437. private class RethrowRewriter : ExpressionVisitor {
  438. internal Expression Exception;
  439. protected override Expression VisitUnary(UnaryExpression node) {
  440. if (node.NodeType == ExpressionType.Throw && node.Operand == null) {
  441. return Expression.Throw(Exception, node.Type);
  442. }
  443. return base.VisitUnary(node);
  444. }
  445. protected override Expression VisitLambda<T>(Expression<T> node) {
  446. return node; // don't recurse into lambdas
  447. }
  448. protected override Expression VisitTry(TryExpression node) {
  449. return node; // don't recurse into other try's
  450. }
  451. protected override Expression VisitExtension(Expression node) {
  452. if (node is DelayedTupleExpression) {
  453. return node;
  454. }
  455. return base.VisitExtension(node);
  456. }
  457. }
  458. // Skip the finally block if we are yielding, but not if we're doing a
  459. // yield break
  460. private Expression MakeSkipFinallyBlock(LabelTarget target) {
  461. return Expression.Condition(
  462. Expression.AndAlso(
  463. Expression.Equal(_gotoRouter, AstUtils.Constant(GotoRouterYielding)),
  464. Expression.NotEqual(_state, AstUtils.Constant(Finished))
  465. ),
  466. Expression.Goto(target),
  467. Utils.Empty()
  468. );
  469. }
  470. // Mostly copied from the base implementation.
  471. // - makes sure we disallow yield in filters
  472. // - lifts exception variable
  473. protected override CatchBlock VisitCatchBlock(CatchBlock node) {
  474. if (node.Variable != null) {
  475. LiftVariable(node.Variable);
  476. }
  477. Expression v = Visit(node.Variable);
  478. int yields = _yields.Count;
  479. Expression f = Visit(node.Filter);
  480. if (yields != _yields.Count) {
  481. // No one needs this yet, and it's not clear what it should even do
  482. throw new NotSupportedException("yield in filter is not allowed");
  483. }
  484. Expression b = Visit(node.Body);
  485. if (v == node.Variable && b == node.Body && f == node.Filter) {
  486. return node;
  487. }
  488. // if we have variable and no yields in the catch block then
  489. // we need to hoist the variable into a closure
  490. if (v != node.Variable && yields == _yields.Count) {
  491. return Expression.MakeCatchBlock(
  492. node.Test,
  493. node.Variable,
  494. Expression.Block(
  495. new DelayedTupleAssign(v, node.Variable),
  496. b
  497. ),
  498. f);
  499. }
  500. return Expression.MakeCatchBlock(node.Test, node.Variable, b, f);
  501. }
  502. #endregion
  503. private SwitchExpression MakeYieldRouter(Type type, int start, int end, LabelTarget newTarget) {
  504. Debug.Assert(end > start);
  505. var cases = new SwitchCase[end - start];
  506. for (int i = start; i < end; i++) {
  507. YieldMarker y = _yields[i];
  508. cases[i - start] = Expression.SwitchCase(Expression.Goto(y.Label, type), AstUtils.Constant(y.State));
  509. // Any jumps from outer switch statements should go to the this
  510. // router, not the original label (which they cannot legally jump to)
  511. y.Label = newTarget;
  512. }
  513. return Expression.Switch(_gotoRouter, Expression.Default(type), cases);
  514. }
  515. protected override Expression VisitExtension(Expression node) {
  516. var yield = node as YieldExpression;
  517. if (yield != null) {
  518. return VisitYield(yield);
  519. }
  520. var ffc = node as FinallyFlowControlExpression;
  521. if (ffc != null) {
  522. return Visit(node.ReduceExtensions());
  523. }
  524. return Visit(node.ReduceExtensions());
  525. }
  526. private Expression VisitYield(YieldExpression node) {
  527. var value = Visit(node.Value);
  528. var block = new List<Expression>();
  529. if (value == null) {
  530. // Yield break
  531. block.Add(MakeAssign(_state, AstUtils.Constant(Finished)));
  532. if (_inTryWithFinally) {
  533. block.Add(Expression.Assign(_gotoRouter, AstUtils.Constant(GotoRouterYielding)));
  534. }
  535. block.Add(Expression.Goto(_returnLabels.Peek()));
  536. return Expression.Block(block);
  537. }
  538. // Yield return
  539. block.Add(MakeAssign(_current, value));
  540. YieldMarker marker = GetYieldMarker(node);
  541. block.Add(MakeAssign(_state, AstUtils.Constant(marker.State)));
  542. if (_inTryWithFinally) {
  543. block.Add(Expression.Assign(_gotoRouter, AstUtils.Constant(GotoRouterYielding)));
  544. }
  545. block.Add(Expression.Goto(_returnLabels.Peek()));
  546. block.Add(Expression.Label(marker.Label));
  547. block.Add(Expression.Assign(_gotoRouter, AstUtils.Constant(GotoRouterNone)));
  548. block.Add(Utils.Empty());
  549. return Expression.Block(block);
  550. }
  551. protected override Expression VisitBlock(BlockExpression node) {
  552. // save the variables for later
  553. // (they'll be hoisted outside of the lambda)
  554. foreach (ParameterExpression param in node.Variables) {
  555. LiftVariable(param);
  556. }
  557. int yields = _yields.Count;
  558. var b = Visit(node.Expressions);
  559. if (b == node.Expressions) {
  560. return node;
  561. }
  562. if (yields == _yields.Count) {
  563. return Expression.Block(node.Type, node.Variables, b);
  564. }
  565. // Return a new block expression with the rewritten body except for that
  566. // all the variables are removed.
  567. return Expression.Block(node.Type, b);
  568. }
  569. private DelayedTupleExpression LiftVariable(ParameterExpression param) {
  570. DelayedTupleExpression res;
  571. if (!_vars.TryGetValue(param, out res)) {
  572. _vars[param] = res = new DelayedTupleExpression(_vars.Count, _tupleExpr, _tupleType, param.Type);
  573. _orderedVars.Add(new KeyValuePair<ParameterExpression, DelayedTupleExpression>(param, res));
  574. }
  575. return res;
  576. }
  577. protected override Expression VisitParameter(ParameterExpression node) {
  578. return _vars[node];
  579. }
  580. protected override Expression VisitLambda<T>(Expression<T> node) {
  581. // don't recurse into nested lambdas
  582. return node;
  583. }
  584. #region stack spilling (to permit yield in the middle of an expression)
  585. private Expression VisitAssign(BinaryExpression node) {
  586. int yields = _yields.Count;
  587. Expression left = Visit(node.Left);
  588. Expression right = Visit(node.Right);
  589. if (left == node.Left && right == node.Right) {
  590. return node;
  591. }
  592. if (yields == _yields.Count) {
  593. if (left is DelayedTupleExpression) {
  594. return new DelayedTupleAssign(left, right);
  595. }
  596. return Expression.Assign(left, right);
  597. }
  598. var block = new List<Expression>();
  599. // If the left hand side did not rewrite itself, we may still need
  600. // to rewrite to ensure proper evaluation order. Essentially, we
  601. // want all of the left side evaluated first, then the value, then
  602. // the assignment
  603. if (left == node.Left) {
  604. switch (left.NodeType) {
  605. case ExpressionType.MemberAccess:
  606. var member = (MemberExpression)node.Left;
  607. Expression e = Visit(member.Expression);
  608. block.Add(ToTemp(ref e));
  609. left = Expression.MakeMemberAccess(e, member.Member);
  610. break;
  611. case ExpressionType.Index:
  612. var index = (IndexExpression)node.Left;
  613. Expression o = Visit(index.Object);
  614. ReadOnlyCollection<Expression> a = Visit(index.Arguments);
  615. if (o == index.Object && a == index.Arguments) {
  616. return index;
  617. }
  618. block.Add(ToTemp(ref o));
  619. block.Add(ToTemp(ref a));
  620. left = Expression.MakeIndex(o, index.Indexer, a);
  621. break;
  622. case ExpressionType.Parameter:
  623. // no action needed
  624. break;
  625. default:
  626. // Extension should've been reduced by Visit above,
  627. // and returned a different node
  628. throw Assert.Unreachable;
  629. }
  630. } else if (left is BlockExpression) {
  631. // Get the last expression of the rewritten left side
  632. var leftBlock = (BlockExpression)left;
  633. left = leftBlock.Expressions[leftBlock.Expressions.Count - 1];
  634. block.AddRange(leftBlock.Expressions);
  635. block.RemoveAt(block.Count - 1);
  636. }
  637. if (right != node.Right) {
  638. block.Add(ToTemp(ref right));
  639. }
  640. if (left is DelayedTupleExpression) {
  641. block.Add(DelayedAssign(left, right));
  642. } else {
  643. block.Add(Expression.Assign(left, right));
  644. }
  645. return Expression.Block(block);
  646. }
  647. protected override Expression VisitDynamic(DynamicExpression node) {
  648. int yields = _yields.Count;
  649. ReadOnlyCollection<Expression> a = Visit(node.Arguments);
  650. if (a == node.Arguments) {
  651. return node;
  652. }
  653. if (yields == _yields.Count) {
  654. return DynamicExpression.MakeDynamic(node.DelegateType, node.Binder, a);
  655. }
  656. return Expression.Block(
  657. ToTemp(ref a),
  658. DynamicExpression.MakeDynamic(node.DelegateType, node.Binder, a)
  659. );
  660. }
  661. protected override Expression VisitIndex(IndexExpression node) {
  662. int yields = _yields.Count;
  663. Expression o = Visit(node.Object);
  664. ReadOnlyCollection<Expression> a = Visit(node.Arguments);
  665. if (o == node.Object && a == node.Arguments) {
  666. return node;
  667. }
  668. if (yields == _yields.Count) {
  669. return Expression.MakeIndex(o, node.Indexer, a);
  670. }
  671. return Expression.Block(
  672. ToTemp(ref o),
  673. ToTemp(ref a),
  674. Expression.MakeIndex(o, node.Indexer, a)
  675. );
  676. }
  677. protected override Expression VisitInvocation(InvocationExpression node) {
  678. int yields = _yields.Count;
  679. Expression e = Visit(node.Expression);
  680. ReadOnlyCollection<Expression> a = Visit(node.Arguments);
  681. if (e == node.Expression && a == node.Arguments) {
  682. return node;
  683. }
  684. if (yields == _yields.Count) {
  685. return Expression.Invoke(e, a);
  686. }
  687. return Expression.Block(
  688. ToTemp(ref e),
  689. ToTemp(ref a),
  690. Expression.Invoke(e, a)
  691. );
  692. }
  693. protected override Expression VisitMethodCall(MethodCallExpression node) {
  694. int yields = _yields.Count;
  695. Expression o = Visit(node.Object);
  696. ReadOnlyCollection<Expression> a = Visit(node.Arguments);
  697. if (o == node.Object && a == node.Arguments) {
  698. return node;
  699. }
  700. if (yields == _yields.Count) {
  701. return Expression.Call(o, node.Method, a);
  702. }
  703. if (o == null) {
  704. return Expression.Block(
  705. ToTemp(ref a),
  706. Expression.Call(null, node.Method, a)
  707. );
  708. }
  709. return Expression.Block(
  710. ToTemp(ref o),
  711. ToTemp(ref a),
  712. Expression.Call(o, node.Method, a)
  713. );
  714. }
  715. protected override Expression VisitNew(NewExpression node) {
  716. int yields = _yields.Count;
  717. ReadOnlyCollection<Expression> a = Visit(node.Arguments);
  718. if (a == node.Arguments) {
  719. return node;
  720. }
  721. if (yields == _yields.Count) {
  722. return (node.Members != null)
  723. ? Expression.New(node.Constructor, a, node.Members)
  724. : Expression.New(node.Constructor, a);
  725. }
  726. return Expression.Block(
  727. ToTemp(ref a),
  728. (node.Members != null)
  729. ? Expression.New(node.Constructor, a, node.Members)
  730. : Expression.New(node.Constructor, a)
  731. );
  732. }
  733. protected override Expression VisitNewArray(NewArrayExpression node) {
  734. int yields = _yields.Count;
  735. ReadOnlyCollection<Expression> e = Visit(node.Expressions);
  736. if (e == node.Expressions) {
  737. return node;
  738. }
  739. if (yields == _yields.Count) {
  740. return (node.NodeType == ExpressionType.NewArrayInit)
  741. ? Expression.NewArrayInit(node.Type.GetElementType(), e)
  742. : Expression.NewArrayBounds(node.Type.GetElementType(), e);
  743. }
  744. return Expression.Block(
  745. ToTemp(ref e),
  746. (node.NodeType == ExpressionType.NewArrayInit)
  747. ? Expression.NewArrayInit(node.Type.GetElementType(), e)
  748. : Expression.NewArrayBounds(node.Type.GetElementType(), e)
  749. );
  750. }
  751. protected override Expression VisitMember(MemberExpression node) {
  752. int yields = _yields.Count;
  753. Expression e = Visit(node.Expression);
  754. if (e == node.Expression) {
  755. return node;
  756. }
  757. if (yields == _yields.Count) {
  758. return Expression.MakeMemberAccess(e, node.Member);
  759. }
  760. return Expression.Block(
  761. ToTemp(ref e),
  762. Expression.MakeMemberAccess(e, node.Member)
  763. );
  764. }
  765. protected override Expression VisitBinary(BinaryExpression node) {
  766. if (node.NodeType == ExpressionType.Assign) {
  767. return VisitAssign(node);
  768. }
  769. // For OpAssign nodes: if has a yield, we need to do the generator
  770. // transformation on the reduced value.
  771. if (node.CanReduce) {
  772. return Visit(node.Reduce());
  773. }
  774. int yields = _yields.Count;
  775. Expression left = Visit(node.Left);
  776. Expression right = Visit(node.Right);
  777. if (left == node.Left && right == node.Right) {
  778. return node;
  779. }
  780. if (yields == _yields.Count) {
  781. return Expression.MakeBinary(node.NodeType, left, right, node.IsLiftedToNull, node.Method, node.Conversion);
  782. }
  783. return Expression.Block(
  784. ToTemp(ref left),
  785. ToTemp(ref right),
  786. Expression.MakeBinary(node.NodeType, left, right, node.IsLiftedToNull, node.Method, node.Conversion)
  787. );
  788. }
  789. protected override Expression VisitTypeBinary(TypeBinaryExpression node) {
  790. int yields = _yields.Count;
  791. Expression e = Visit(node.Expression);
  792. if (e == node.Expression) {
  793. return node;
  794. }
  795. if (yields == _yields.Count) {
  796. return (node.NodeType == ExpressionType.TypeIs)
  797. ? Expression.TypeIs(e, node.TypeOperand)
  798. : Expression.TypeEqual(e, node.TypeOperand);
  799. }
  800. return Expression.Block(
  801. ToTemp(ref e),
  802. (node.NodeType == ExpressionType.TypeIs)
  803. ? Expression.TypeIs(e, node.TypeOperand)
  804. : Expression.TypeEqual(e, node.TypeOperand)
  805. );
  806. }
  807. protected override Expression VisitUnary(UnaryExpression node) {
  808. // For OpAssign nodes: if has a yield, we need to do the generator
  809. // transformation on the reduced value.
  810. if (node.CanReduce) {
  811. return Visit(node.Reduce());
  812. }
  813. int yields = _yields.Count;
  814. Expression o = Visit(node.Operand);
  815. if (o == node.Operand) {
  816. return node;
  817. }
  818. // Void convert can be jumped into, no need to spill
  819. // TODO: remove when that feature goes away.
  820. if (yields == _yields.Count ||
  821. (node.NodeType == ExpressionType.Convert && node.Type == typeof(void))) {
  822. return Expression.MakeUnary(node.NodeType, o, node.Type, node.Method);
  823. }
  824. return Expression.Block(
  825. ToTemp(ref o),
  826. Expression.MakeUnary(node.NodeType, o, node.Type, node.Method)
  827. );
  828. }
  829. protected override Expression VisitMemberInit(MemberInitExpression node) {
  830. // See if anything changed
  831. int yields = _yields.Count;
  832. Expression e = base.VisitMemberInit(node);
  833. if (yields == _yields.Count) {
  834. return e;
  835. }
  836. // It has a yield. Reduce to basic nodes so we can jump in
  837. return e.Reduce();
  838. }
  839. protected override Expression VisitListInit(ListInitExpression node) {
  840. // See if anything changed
  841. int yields = _yields.Count;
  842. Expression e = base.VisitListInit(node);
  843. if (yields == _yields.Count) {
  844. return e;
  845. }
  846. // It has a yield. Reduce to basic nodes so we can jump in
  847. return e.Reduce();
  848. }
  849. private static Expression DelayedAssign(Expression lhs, Expression rhs) {
  850. return new DelayedTupleAssign(lhs, rhs);
  851. }
  852. #endregion
  853. private sealed class YieldMarker {
  854. // Note: Label can be mutated as we generate try blocks
  855. internal LabelTarget Label = Expression.Label("yieldMarker");
  856. internal readonly int State;
  857. internal YieldMarker(int state) {
  858. State = state;
  859. }
  860. }
  861. }
  862. /// <summary>
  863. /// Accesses the property of a tuple. The node can be created first and then the tuple and index
  864. /// type can be filled in before the tree is actually generated. This enables creation of these
  865. /// nodes before the tuple type is actually known.
  866. /// </summary>
  867. sealed class DelayedTupleExpression : Expression {
  868. public readonly int Index;
  869. private readonly StrongBox<Type> _tupleType;
  870. private readonly StrongBox<ParameterExpression> _tupleExpr;
  871. private readonly Type _type;
  872. public DelayedTupleExpression(int index, StrongBox<ParameterExpression> tupleExpr, StrongBox<Type> tupleType, Type type) {
  873. Index = index;
  874. _tupleType = tupleType;
  875. _tupleExpr = tupleExpr;
  876. _type = type;
  877. }
  878. public override Expression Reduce() {
  879. Expression res = _tupleExpr.Value;
  880. foreach (PropertyInfo pi in MutableTuple.GetAccessPath(_tupleType.Value, Index)) {
  881. res = Expression.Property(res, pi);
  882. }
  883. return res;
  884. }
  885. public sealed override ExpressionType NodeType {
  886. get { return ExpressionType.Extension; }
  887. }
  888. public sealed override Type/*!*/ Type {
  889. get { return _type; }
  890. }
  891. public override bool CanReduce {
  892. get {
  893. return true;
  894. }
  895. }
  896. protected override Expression VisitChildren(ExpressionVisitor visitor) {
  897. return this;
  898. }
  899. }
  900. sealed class DelayedTupleAssign : Expression {
  901. private readonly Expression _lhs, _rhs;
  902. public DelayedTupleAssign(Expression lhs, Expression rhs) {
  903. _lhs = lhs;
  904. _rhs = rhs;
  905. }
  906. public override Expression Reduce() {
  907. // we assign to a temporary and then assign that to the tuple
  908. // because there may be branches in the RHS which can cause
  909. // us to not have the tuple instance
  910. return Expression.Assign(_lhs.Reduce(), _rhs);
  911. }
  912. public sealed override ExpressionType NodeType {
  913. get { return ExpressionType.Extension; }
  914. }
  915. public sealed override Type/*!*/ Type {
  916. get { return _lhs.Type; }
  917. }
  918. public override bool CanReduce {
  919. get {
  920. return true;
  921. }
  922. }
  923. protected override Expression VisitChildren(ExpressionVisitor visitor) {
  924. Expression rhs = visitor.Visit(_rhs);
  925. if (rhs != _rhs) {
  926. return new DelayedTupleAssign(_lhs, rhs);
  927. }
  928. return this;
  929. }
  930. }
  931. internal sealed class PythonGeneratorExpression : Expression {
  932. private readonly LightLambdaExpression _lambda;
  933. private readonly int _compilationThreshold;
  934. public PythonGeneratorExpression(LightLambdaExpression lambda, int compilationThreshold) {
  935. _lambda = lambda;
  936. _compilationThreshold = compilationThreshold;
  937. }
  938. public override Expression Reduce() {
  939. return _lambda.ToGenerator(false, true, _compilationThreshold);
  940. }
  941. public sealed override ExpressionType NodeType {
  942. get { return ExpressionType.Extension; }
  943. }
  944. public sealed override Type/*!*/ Type {
  945. get { return _lambda.Type; }
  946. }
  947. public override bool CanReduce {
  948. get {
  949. return true;
  950. }
  951. }
  952. }
  953. }