PageRenderTime 48ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/Microsoft.Scripting/Ast/GeneratorRewriter.cs

https://bitbucket.org/stefanrusek/xronos
C# | 843 lines | 619 code | 98 blank | 126 comment | 167 complexity | de3153a2d44532468b7e6c96c4228928 MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if CODEPLEX_40
  16. using System;
  17. #else
  18. using System; using Microsoft;
  19. #endif
  20. using System.Collections.Generic;
  21. using System.Collections.ObjectModel;
  22. using System.Diagnostics;
  23. #if CODEPLEX_40
  24. using System.Linq.Expressions;
  25. #else
  26. using Microsoft.Linq.Expressions;
  27. #endif
  28. using System.Runtime.CompilerServices;
  29. #if !CODEPLEX_40
  30. using Microsoft.Runtime.CompilerServices;
  31. #endif
  32. using Microsoft.Scripting.Runtime;
  33. using Microsoft.Scripting.Utils;
  34. using AstUtils = Microsoft.Scripting.Ast.Utils;
  35. namespace Microsoft.Scripting.Ast {
  36. /// <summary>
  37. /// When finding a yield return or yield break, this rewriter flattens out
  38. /// containing blocks, scopes, and expressions with stack state. All
  39. /// scopes encountered have their variables promoted to the generator's
  40. /// closure, so they survive yields.
  41. /// </summary>
  42. internal sealed class GeneratorRewriter : ExpressionVisitor {
  43. // These two constants are used internally. They should not conflict
  44. // with valid yield states.
  45. private const int GotoRouterYielding = 0;
  46. private const int GotoRouterNone = -1;
  47. // The state of the generator before it starts and when it's done
  48. internal const int NotStarted = -1;
  49. internal const int Finished = 0;
  50. private sealed class YieldMarker {
  51. // Note: Label can be mutated as we generate try blocks
  52. internal LabelTarget Label = Expression.Label();
  53. internal readonly int State;
  54. internal YieldMarker(int state) {
  55. State = state;
  56. }
  57. }
  58. private readonly GeneratorExpression _generator;
  59. private readonly ParameterExpression _current;
  60. private readonly ParameterExpression _state;
  61. // The one return label, or more than one if we're in a finally
  62. private readonly Stack<LabelTarget> _returnLabels = new Stack<LabelTarget>();
  63. private ParameterExpression _gotoRouter;
  64. private bool _inTryWithFinally;
  65. private readonly List<YieldMarker> _yields = new List<YieldMarker>();
  66. private List<int> _debugCookies;
  67. private readonly Set<ParameterExpression> _vars = new Set<ParameterExpression>();
  68. // Possible optimization: reuse temps. Requires scoping them correctly,
  69. // and then storing them back in a free list
  70. private readonly List<ParameterExpression> _temps = new List<ParameterExpression>();
  71. internal GeneratorRewriter(GeneratorExpression generator) {
  72. _generator = generator;
  73. _state = Expression.Parameter(typeof(int).MakeByRefType(), "state");
  74. _current = Expression.Parameter(_generator.Target.Type.MakeByRefType(), "current");
  75. _returnLabels.Push(Expression.Label());
  76. _gotoRouter = Expression.Variable(typeof(int), "$gotoRouter");
  77. }
  78. internal Expression Reduce() {
  79. // Visit body
  80. Expression body = Visit(_generator.Body);
  81. Debug.Assert(_returnLabels.Count == 1);
  82. // Add the switch statement to the body
  83. int count = _yields.Count;
  84. var cases = new SwitchCase[count + 1];
  85. for (int i = 0; i < count; i++) {
  86. cases[i] = Expression.SwitchCase(Expression.Goto(_yields[i].Label), AstUtils.Constant(_yields[i].State));
  87. }
  88. cases[count] = Expression.SwitchCase(Expression.Goto(_returnLabels.Peek()), AstUtils.Constant(Finished));
  89. Type generatorNextOfT = typeof(GeneratorNext<>).MakeGenericType(_generator.Target.Type);
  90. // Create the lambda for the GeneratorNext<T>, hoisting variables
  91. // into a scope outside the lambda
  92. var allVars = new List<ParameterExpression>(_vars);
  93. allVars.AddRange(_temps);
  94. body = Expression.Block(
  95. allVars,
  96. Expression.Lambda(
  97. generatorNextOfT,
  98. Expression.Block(
  99. new ParameterExpression[] { _gotoRouter },
  100. Expression.Switch(Expression.Assign(_gotoRouter, _state), cases),
  101. body,
  102. Expression.Assign(_state, AstUtils.Constant(Finished)),
  103. Expression.Label(_returnLabels.Peek())
  104. ),
  105. _generator.Name,
  106. new ParameterExpression[] { _state, _current }
  107. )
  108. );
  109. // Enumerable factory takes Func<GeneratorNext<T>> instead of GeneratorNext<T>
  110. if (_generator.IsEnumerable) {
  111. body = Expression.Lambda(body);
  112. }
  113. // We can't create a ConstantExpression of _debugCookies array here because we walk the tree
  114. // after constants have already been rewritten. Instead we create a NewArrayExpression node
  115. // which initializes the array with contents from _debugCookies
  116. Expression debugCookiesArray = null;
  117. if (_debugCookies != null) {
  118. Expression[] debugCookies = new Expression[_debugCookies.Count];
  119. for(int i=0; i < _debugCookies.Count; i++)
  120. debugCookies[i] = AstUtils.Constant(_debugCookies[i]);
  121. debugCookiesArray = Expression.NewArrayInit(
  122. typeof(int),
  123. debugCookies);
  124. }
  125. // Generate a call to ScriptingRuntimeHelpers.MakeGenerator<T>(args)
  126. return Expression.Call(
  127. typeof(ScriptingRuntimeHelpers),
  128. "MakeGenerator",
  129. new[] { _generator.Target.Type },
  130. (debugCookiesArray != null)
  131. ? new[] { body, debugCookiesArray }
  132. : new[] { body }
  133. );
  134. }
  135. private YieldMarker GetYieldMarker(YieldExpression node) {
  136. YieldMarker result = new YieldMarker(_yields.Count + 1);
  137. _yields.Add(result);
  138. if (node.YieldMarker != -1) {
  139. if (_debugCookies == null) {
  140. _debugCookies = new List<int>(1);
  141. _debugCookies.Add(Int32.MaxValue);
  142. }
  143. _debugCookies.Insert(result.State, node.YieldMarker);
  144. } else if (_debugCookies != null) {
  145. _debugCookies.Insert(result.State, Int32.MaxValue);
  146. }
  147. return result;
  148. }
  149. /// <summary>
  150. /// Spills the right side into a temp, and replaces it with its temp.
  151. /// Returns the expression that initializes the temp.
  152. /// </summary>
  153. private Expression ToTemp(ref Expression e) {
  154. Debug.Assert(e != null);
  155. var temp = Expression.Variable(e.Type, "generatorTemp" + _temps.Count);
  156. _temps.Add(temp);
  157. var result = MakeAssign(temp, e);
  158. e = temp;
  159. return result;
  160. }
  161. /// <summary>
  162. /// Makes an assignment to this variable. Pushes the assignment as far
  163. /// into the right side as possible, to allow jumps into it.
  164. /// </summary>
  165. private Expression MakeAssign(ParameterExpression variable, Expression value) {
  166. // TODO: this is not complete.
  167. // It may end up generating a bad tree if any of these nodes
  168. // contain yield and return a value: Switch, Loop, Goto, or Label.
  169. // Those are not supported, but we can't throw here because we may
  170. // end up disallowing valid uses (if some other expression contains
  171. // yield, but not this one).
  172. switch (value.NodeType) {
  173. case ExpressionType.Block:
  174. return MakeAssignBlock(variable, value);
  175. case ExpressionType.Conditional:
  176. return MakeAssignConditional(variable, value);
  177. }
  178. return Expression.Assign(variable, value);
  179. }
  180. private Expression MakeAssignBlock(ParameterExpression variable, Expression value) {
  181. var node = (BlockExpression)value;
  182. var newBlock = new ReadOnlyCollectionBuilder<Expression>(node.Expressions);
  183. newBlock[newBlock.Count - 1] = MakeAssign(variable, newBlock[newBlock.Count - 1]);
  184. return Expression.Block(node.Variables, newBlock);
  185. }
  186. private Expression MakeAssignConditional(ParameterExpression variable, Expression value) {
  187. var node = (ConditionalExpression)value;
  188. return Expression.Condition(node.Test, MakeAssign(variable, node.IfTrue), MakeAssign(variable, node.IfFalse));
  189. }
  190. private BlockExpression ToTemp(ref ReadOnlyCollection<Expression> args) {
  191. int count = args.Count;
  192. var block = new Expression[count];
  193. var newArgs = new Expression[count];
  194. args.CopyTo(newArgs, 0);
  195. for (int i = 0; i < count; i++) {
  196. block[i] = ToTemp(ref newArgs[i]);
  197. }
  198. args = new ReadOnlyCollection<Expression>(newArgs);
  199. return Expression.Block(block);
  200. }
  201. #region VisitTry
  202. protected override Expression VisitTry(TryExpression node) {
  203. int startYields = _yields.Count;
  204. bool savedInTryWithFinally = _inTryWithFinally;
  205. if (node.Finally != null || node.Fault != null) {
  206. _inTryWithFinally = true;
  207. }
  208. Expression @try = Visit(node.Body);
  209. int tryYields = _yields.Count;
  210. IList<CatchBlock> handlers = Visit(node.Handlers, VisitCatchBlock);
  211. int catchYields = _yields.Count;
  212. // push a new return label in case the finally block yields
  213. _returnLabels.Push(Expression.Label());
  214. // only one of these can be non-null
  215. Expression @finally = Visit(node.Finally);
  216. Expression fault = Visit(node.Fault);
  217. LabelTarget finallyReturn = _returnLabels.Pop();
  218. int finallyYields = _yields.Count;
  219. _inTryWithFinally = savedInTryWithFinally;
  220. if (@try == node.Body &&
  221. handlers == node.Handlers &&
  222. @finally == node.Finally &&
  223. fault == node.Fault) {
  224. return node;
  225. }
  226. // No yields, just return
  227. if (startYields == _yields.Count) {
  228. return Expression.MakeTry(null, @try, @finally, fault, handlers);
  229. }
  230. if (fault != null && finallyYields != catchYields) {
  231. // No one needs this yet, and it's not clear how we should get back to
  232. // the fault
  233. throw new NotSupportedException("yield in fault block is not supported");
  234. }
  235. // If try has yields, we need to build a new try body that
  236. // dispatches to the yield labels
  237. var tryStart = Expression.Label();
  238. if (tryYields != startYields) {
  239. @try = Expression.Block(MakeYieldRouter(startYields, tryYields, tryStart), @try);
  240. }
  241. // Transform catches with yield to deferred handlers
  242. if (catchYields != tryYields) {
  243. var block = new List<Expression>();
  244. block.Add(MakeYieldRouter(tryYields, catchYields, tryStart));
  245. block.Add(null); // empty slot to fill in later
  246. for (int i = 0, n = handlers.Count; i < n; i++) {
  247. CatchBlock c = handlers[i];
  248. if (c == node.Handlers[i]) {
  249. continue;
  250. }
  251. if (handlers.IsReadOnly) {
  252. handlers = handlers.ToArray();
  253. }
  254. // the variable that will be scoped to the catch block
  255. var exceptionVar = Expression.Variable(c.Test, null);
  256. // the variable that the catch block body will use to
  257. // access the exception. We reuse the original variable if
  258. // the catch block had one. It needs to be hoisted because
  259. // the catch might contain yields.
  260. var deferredVar = c.Variable ?? Expression.Variable(c.Test, null);
  261. _vars.Add(deferredVar);
  262. // We need to ensure that filters can access the exception
  263. // variable
  264. Expression filter = c.Filter;
  265. if (filter != null && c.Variable != null) {
  266. filter = Expression.Block(new[] { c.Variable }, Expression.Assign(c.Variable, exceptionVar), filter);
  267. }
  268. // catch (ExceptionType exceptionVar) {
  269. // deferredVar = exceptionVar;
  270. // }
  271. handlers[i] = Expression.Catch(
  272. exceptionVar,
  273. Utils.Void(Expression.Assign(deferredVar, exceptionVar)),
  274. filter
  275. );
  276. // We need to rewrite rethrows into "throw deferredVar"
  277. var catchBody = new RethrowRewriter { Exception = deferredVar }.Visit(c.Body);
  278. // if (deferredVar != null) {
  279. // ... catch body ...
  280. // }
  281. block.Add(
  282. Expression.IfThen(
  283. Expression.NotEqual(deferredVar, AstUtils.Constant(null, deferredVar.Type)),
  284. catchBody
  285. )
  286. );
  287. }
  288. block[1] = Expression.MakeTry(null, @try, null, null, new ReadOnlyCollection<CatchBlock>(handlers));
  289. @try = Expression.Block(block);
  290. handlers = new CatchBlock[0]; // so we don't reuse these
  291. }
  292. if (finallyYields != catchYields) {
  293. // We need to add a catch block to save the exception, so we
  294. // can rethrow in case there is a yield in the finally. Also,
  295. // add logic for returning. It looks like this:
  296. //
  297. // try { ... } catch (Exception all) { saved = all; }
  298. // finally {
  299. // if (_finallyReturnVar) goto finallyReturn;
  300. // ...
  301. // if (saved != null) throw saved;
  302. // finallyReturn:
  303. // }
  304. // if (_finallyReturnVar) goto _return;
  305. // We need to add a catch(Exception), so if we have catches,
  306. // wrap them in a try
  307. if (handlers.Count > 0) {
  308. @try = Expression.MakeTry(null, @try, null, null, handlers);
  309. handlers = new CatchBlock[0];
  310. }
  311. // NOTE: the order of these routers is important
  312. // The first call changes the labels to all point at "tryEnd",
  313. // so the second router will jump to "tryEnd"
  314. var tryEnd = Expression.Label();
  315. Expression inFinallyRouter = MakeYieldRouter(catchYields, finallyYields, tryEnd);
  316. Expression inTryRouter = MakeYieldRouter(catchYields, finallyYields, tryStart);
  317. var all = Expression.Variable(typeof(Exception), "e");
  318. var saved = Expression.Variable(typeof(Exception), "$saved$" + _temps.Count);
  319. _temps.Add(saved);
  320. @try = Expression.Block(
  321. Expression.TryCatchFinally(
  322. Expression.Block(
  323. inTryRouter,
  324. @try,
  325. Expression.Assign(saved, AstUtils.Constant(null, saved.Type)),
  326. Expression.Label(tryEnd)
  327. ),
  328. Expression.Block(
  329. MakeSkipFinallyBlock(finallyReturn),
  330. inFinallyRouter,
  331. @finally,
  332. Expression.Condition(
  333. Expression.NotEqual(saved, AstUtils.Constant(null, saved.Type)),
  334. Expression.Throw(saved),
  335. Utils.Empty()
  336. ),
  337. Expression.Label(finallyReturn)
  338. ),
  339. Expression.Catch(all, Utils.Void(Expression.Assign(saved, all)))
  340. ),
  341. Expression.Condition(
  342. Expression.Equal(_gotoRouter, AstUtils.Constant(GotoRouterYielding)),
  343. Expression.Goto(_returnLabels.Peek()),
  344. Utils.Empty()
  345. )
  346. );
  347. @finally = null;
  348. } else if (@finally != null) {
  349. // try or catch had a yield, modify finally so we can skip over it
  350. @finally = Expression.Block(
  351. MakeSkipFinallyBlock(finallyReturn),
  352. @finally,
  353. Expression.Label(finallyReturn)
  354. );
  355. }
  356. // Make the outer try, if needed
  357. if (handlers.Count > 0 || @finally != null || fault != null) {
  358. @try = Expression.MakeTry(null, @try, @finally, fault, handlers);
  359. }
  360. return Expression.Block(Expression.Label(tryStart), @try);
  361. }
  362. private class RethrowRewriter : ExpressionVisitor {
  363. internal ParameterExpression Exception;
  364. protected override Expression VisitUnary(UnaryExpression node) {
  365. if (node.NodeType == ExpressionType.Throw && node.Operand == null) {
  366. return Expression.Throw(Exception, node.Type);
  367. }
  368. return base.VisitUnary(node);
  369. }
  370. protected override Expression VisitLambda<T>(Expression<T> node) {
  371. return node; // don't recurse into lambdas
  372. }
  373. protected override Expression VisitTry(TryExpression node) {
  374. return node; // don't recurse into other try's
  375. }
  376. }
  377. // Skip the finally block if we are yielding, but not if we're doing a
  378. // yield break
  379. private Expression MakeSkipFinallyBlock(LabelTarget target) {
  380. return Expression.Condition(
  381. Expression.AndAlso(
  382. Expression.Equal(_gotoRouter, AstUtils.Constant(GotoRouterYielding)),
  383. Expression.NotEqual(_state, AstUtils.Constant(Finished))
  384. ),
  385. Expression.Goto(target),
  386. Utils.Empty()
  387. );
  388. }
  389. // This is copied from the base implementation.
  390. // Just want to make sure we disallow yield in filters
  391. protected override CatchBlock VisitCatchBlock(CatchBlock node) {
  392. ParameterExpression v = VisitAndConvert(node.Variable, "VisitCatchBlock");
  393. int yields = _yields.Count;
  394. Expression f = Visit(node.Filter);
  395. if (yields != _yields.Count) {
  396. // No one needs this yet, and it's not clear what it should even do
  397. throw new NotSupportedException("yield in filter is not allowed");
  398. }
  399. Expression b = Visit(node.Body);
  400. if (v == node.Variable && b == node.Body && f == node.Filter) {
  401. return node;
  402. }
  403. return Expression.MakeCatchBlock(node.Test, v, b, f);
  404. }
  405. #endregion
  406. private SwitchExpression MakeYieldRouter(int start, int end, LabelTarget newTarget) {
  407. Debug.Assert(end > start);
  408. var cases = new SwitchCase[end - start];
  409. for (int i = start; i < end; i++) {
  410. YieldMarker y = _yields[i];
  411. cases[i - start] = Expression.SwitchCase(Expression.Goto(y.Label), AstUtils.Constant(y.State));
  412. // Any jumps from outer switch statements should go to the this
  413. // router, not the original label (which they cannot legally jump to)
  414. y.Label = newTarget;
  415. }
  416. return Expression.Switch(_gotoRouter, cases);
  417. }
  418. protected override Expression VisitExtension(Expression node) {
  419. var yield = node as YieldExpression;
  420. if (yield != null) {
  421. return VisitYield(yield);
  422. }
  423. var ffc = node as FinallyFlowControlExpression;
  424. if (ffc != null) {
  425. return Visit(node.ReduceExtensions());
  426. }
  427. // Visit the child expression. It may not contain a yield, in which
  428. // case we can just return the (possibly rewritten) node.
  429. int yields = _yields.Count;
  430. Expression result = base.VisitExtension(node);
  431. if (yields == _yields.Count) {
  432. return result;
  433. }
  434. // Otherwise, we have to reduce to ensure proper stack spilling.
  435. return Visit(node.ReduceExtensions());
  436. }
  437. private Expression VisitYield(YieldExpression node) {
  438. if (node.Target != _generator.Target) {
  439. throw new InvalidOperationException("yield and generator must have the same LabelTarget object");
  440. }
  441. var value = Visit(node.Value);
  442. var block = new List<Expression>();
  443. if (value == null) {
  444. // Yield break
  445. block.Add(Expression.Assign(_state, AstUtils.Constant(Finished)));
  446. if (_inTryWithFinally) {
  447. block.Add(Expression.Assign(_gotoRouter, AstUtils.Constant(GotoRouterYielding)));
  448. }
  449. block.Add(Expression.Goto(_returnLabels.Peek()));
  450. return Expression.Block(block);
  451. }
  452. // Yield return
  453. block.Add(MakeAssign(_current, value));
  454. YieldMarker marker = GetYieldMarker(node);
  455. block.Add(Expression.Assign(_state, AstUtils.Constant(marker.State)));
  456. if (_inTryWithFinally) {
  457. block.Add(Expression.Assign(_gotoRouter, AstUtils.Constant(GotoRouterYielding)));
  458. }
  459. block.Add(Expression.Goto(_returnLabels.Peek()));
  460. block.Add(Expression.Label(marker.Label));
  461. block.Add(Expression.Assign(_gotoRouter, AstUtils.Constant(GotoRouterNone)));
  462. block.Add(Utils.Empty());
  463. return Expression.Block(block);
  464. }
  465. protected override Expression VisitBlock(BlockExpression node) {
  466. int yields = _yields.Count;
  467. var b = Visit(node.Expressions);
  468. if (b == node.Expressions) {
  469. return node;
  470. }
  471. if (yields == _yields.Count) {
  472. return Expression.Block(node.Variables, b);
  473. }
  474. // save the variables for later
  475. // (they'll be hoisted outside of the lambda)
  476. _vars.UnionWith(node.Variables);
  477. // Return a new block expression with the rewritten body except for that
  478. // all the variables are removed.
  479. return Expression.Block(node.Type, b);
  480. }
  481. protected override Expression VisitLambda<T>(Expression<T> node) {
  482. // don't recurse into nested lambdas
  483. return node;
  484. }
  485. #region stack spilling (to permit yield in the middle of an expression)
  486. private Expression VisitAssign(BinaryExpression node) {
  487. int yields = _yields.Count;
  488. Expression left = Visit(node.Left);
  489. Expression right = Visit(node.Right);
  490. if (left == node.Left && right == node.Right) {
  491. return node;
  492. }
  493. if (yields == _yields.Count) {
  494. return Expression.Assign(left, right);
  495. }
  496. var block = new List<Expression>();
  497. // If the left hand side did not rewrite itself, we may still need
  498. // to rewrite to ensure proper evaluation order. Essentially, we
  499. // want all of the left side evaluated first, then the value, then
  500. // the assignment
  501. if (left == node.Left) {
  502. switch (left.NodeType) {
  503. case ExpressionType.MemberAccess:
  504. var member = (MemberExpression)node.Left;
  505. Expression e = Visit(member.Expression);
  506. block.Add(ToTemp(ref e));
  507. left = Expression.MakeMemberAccess(e, member.Member);
  508. break;
  509. case ExpressionType.Index:
  510. var index = (IndexExpression)node.Left;
  511. Expression o = Visit(index.Object);
  512. ReadOnlyCollection<Expression> a = Visit(index.Arguments);
  513. if (o == index.Object && a == index.Arguments) {
  514. return index;
  515. }
  516. block.Add(ToTemp(ref o));
  517. block.Add(ToTemp(ref a));
  518. left = Expression.MakeIndex(o, index.Indexer, a);
  519. break;
  520. case ExpressionType.Parameter:
  521. // no action needed
  522. break;
  523. default:
  524. // Extension should've been reduced by Visit above,
  525. // and returned a different node
  526. throw Assert.Unreachable;
  527. }
  528. } else {
  529. // Get the last expression of the rewritten left side
  530. var leftBlock = (BlockExpression)left;
  531. left = leftBlock.Expressions[leftBlock.Expressions.Count - 1];
  532. block.AddRange(leftBlock.Expressions);
  533. block.RemoveAt(block.Count - 1);
  534. }
  535. if (right != node.Right) {
  536. block.Add(ToTemp(ref right));
  537. }
  538. block.Add(Expression.Assign(left, right));
  539. return Expression.Block(block);
  540. }
  541. protected override Expression VisitDynamic(DynamicExpression node) {
  542. int yields = _yields.Count;
  543. ReadOnlyCollection<Expression> a = Visit(node.Arguments);
  544. if (a == node.Arguments) {
  545. return node;
  546. }
  547. if (yields == _yields.Count) {
  548. return Expression.MakeDynamic(node.DelegateType, node.Binder, a);
  549. }
  550. return Expression.Block(
  551. ToTemp(ref a),
  552. Expression.MakeDynamic(node.DelegateType, node.Binder, a)
  553. );
  554. }
  555. protected override Expression VisitIndex(IndexExpression node) {
  556. int yields = _yields.Count;
  557. Expression o = Visit(node.Object);
  558. ReadOnlyCollection<Expression> a = Visit(node.Arguments);
  559. if (o == node.Object && a == node.Arguments) {
  560. return node;
  561. }
  562. if (yields == _yields.Count) {
  563. return Expression.MakeIndex(o, node.Indexer, a);
  564. }
  565. return Expression.Block(
  566. ToTemp(ref o),
  567. ToTemp(ref a),
  568. Expression.MakeIndex(o, node.Indexer, a)
  569. );
  570. }
  571. protected override Expression VisitInvocation(InvocationExpression node) {
  572. int yields = _yields.Count;
  573. Expression e = Visit(node.Expression);
  574. ReadOnlyCollection<Expression> a = Visit(node.Arguments);
  575. if (e == node.Expression && a == node.Arguments) {
  576. return node;
  577. }
  578. if (yields == _yields.Count) {
  579. return Expression.Invoke(e, a);
  580. }
  581. return Expression.Block(
  582. ToTemp(ref e),
  583. ToTemp(ref a),
  584. Expression.Invoke(e, a)
  585. );
  586. }
  587. protected override Expression VisitMethodCall(MethodCallExpression node) {
  588. int yields = _yields.Count;
  589. Expression o = Visit(node.Object);
  590. ReadOnlyCollection<Expression> a = Visit(node.Arguments);
  591. if (o == node.Object && a == node.Arguments) {
  592. return node;
  593. }
  594. if (yields == _yields.Count) {
  595. return Expression.Call(o, node.Method, a);
  596. }
  597. if (o == null) {
  598. return Expression.Block(
  599. ToTemp(ref a),
  600. Expression.Call(null, node.Method, a)
  601. );
  602. }
  603. return Expression.Block(
  604. ToTemp(ref o),
  605. ToTemp(ref a),
  606. Expression.Call(o, node.Method, a)
  607. );
  608. }
  609. protected override Expression VisitNew(NewExpression node) {
  610. int yields = _yields.Count;
  611. ReadOnlyCollection<Expression> a = Visit(node.Arguments);
  612. if (a == node.Arguments) {
  613. return node;
  614. }
  615. if (yields == _yields.Count) {
  616. return (node.Members != null)
  617. ? Expression.New(node.Constructor, a, node.Members)
  618. : Expression.New(node.Constructor, a);
  619. }
  620. return Expression.Block(
  621. ToTemp(ref a),
  622. (node.Members != null)
  623. ? Expression.New(node.Constructor, a, node.Members)
  624. : Expression.New(node.Constructor, a)
  625. );
  626. }
  627. protected override Expression VisitNewArray(NewArrayExpression node) {
  628. int yields = _yields.Count;
  629. ReadOnlyCollection<Expression> e = Visit(node.Expressions);
  630. if (e == node.Expressions) {
  631. return node;
  632. }
  633. if (yields == _yields.Count) {
  634. return (node.NodeType == ExpressionType.NewArrayInit)
  635. ? Expression.NewArrayInit(node.Type.GetElementType(), e)
  636. : Expression.NewArrayBounds(node.Type.GetElementType(), e);
  637. }
  638. return Expression.Block(
  639. ToTemp(ref e),
  640. (node.NodeType == ExpressionType.NewArrayInit)
  641. ? Expression.NewArrayInit(node.Type.GetElementType(), e)
  642. : Expression.NewArrayBounds(node.Type.GetElementType(), e)
  643. );
  644. }
  645. protected override Expression VisitMember(MemberExpression node) {
  646. int yields = _yields.Count;
  647. Expression e = Visit(node.Expression);
  648. if (e == node.Expression) {
  649. return node;
  650. }
  651. if (yields == _yields.Count) {
  652. return Expression.MakeMemberAccess(e, node.Member);
  653. }
  654. return Expression.Block(
  655. ToTemp(ref e),
  656. Expression.MakeMemberAccess(e, node.Member)
  657. );
  658. }
  659. protected override Expression VisitBinary(BinaryExpression node) {
  660. if (node.NodeType == ExpressionType.Assign) {
  661. return VisitAssign(node);
  662. }
  663. // For OpAssign nodes: if has a yield, we need to do the generator
  664. // transformation on the reduced value.
  665. if (node.CanReduce) {
  666. return Visit(node.Reduce());
  667. }
  668. int yields = _yields.Count;
  669. Expression left = Visit(node.Left);
  670. Expression right = Visit(node.Right);
  671. if (left == node.Left && right == node.Right) {
  672. return node;
  673. }
  674. if (yields == _yields.Count) {
  675. return Expression.MakeBinary(node.NodeType, left, right, node.IsLiftedToNull, node.Method, node.Conversion);
  676. }
  677. return Expression.Block(
  678. ToTemp(ref left),
  679. ToTemp(ref right),
  680. Expression.MakeBinary(node.NodeType, left, right, node.IsLiftedToNull, node.Method, node.Conversion)
  681. );
  682. }
  683. protected override Expression VisitTypeBinary(TypeBinaryExpression node) {
  684. int yields = _yields.Count;
  685. Expression e = Visit(node.Expression);
  686. if (e == node.Expression) {
  687. return node;
  688. }
  689. if (yields == _yields.Count) {
  690. return (node.NodeType == ExpressionType.TypeIs)
  691. ? Expression.TypeIs(e, node.TypeOperand)
  692. : Expression.TypeEqual(e, node.TypeOperand);
  693. }
  694. return Expression.Block(
  695. ToTemp(ref e),
  696. (node.NodeType == ExpressionType.TypeIs)
  697. ? Expression.TypeIs(e, node.TypeOperand)
  698. : Expression.TypeEqual(e, node.TypeOperand)
  699. );
  700. }
  701. protected override Expression VisitUnary(UnaryExpression node) {
  702. // For OpAssign nodes: if has a yield, we need to do the generator
  703. // transformation on the reduced value.
  704. if (node.CanReduce) {
  705. return Visit(node.Reduce());
  706. }
  707. int yields = _yields.Count;
  708. Expression o = Visit(node.Operand);
  709. if (o == node.Operand) {
  710. return node;
  711. }
  712. // Void convert can be jumped into, no need to spill
  713. // TODO: remove when that feature goes away.
  714. if (yields == _yields.Count ||
  715. (node.NodeType == ExpressionType.Convert && node.Type == typeof(void))) {
  716. return Expression.MakeUnary(node.NodeType, o, node.Type, node.Method);
  717. }
  718. return Expression.Block(
  719. ToTemp(ref o),
  720. Expression.MakeUnary(node.NodeType, o, node.Type, node.Method)
  721. );
  722. }
  723. protected override Expression VisitMemberInit(MemberInitExpression node) {
  724. // See if anything changed
  725. int yields = _yields.Count;
  726. Expression e = base.VisitMemberInit(node);
  727. if (yields == _yields.Count) {
  728. return e;
  729. }
  730. // It has a yield. Reduce to basic nodes so we can jump in
  731. return e.Reduce();
  732. }
  733. protected override Expression VisitListInit(ListInitExpression node) {
  734. // See if anything changed
  735. int yields = _yields.Count;
  736. Expression e = base.VisitListInit(node);
  737. if (yields == _yields.Count) {
  738. return e;
  739. }
  740. // It has a yield. Reduce to basic nodes so we can jump in
  741. return e.Reduce();
  742. }
  743. #endregion
  744. }
  745. }