PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/DICK.B1/IronPython/Compiler/Ast/TryStatement.cs

https://bitbucket.org/williamybs/uidipythontool
C# | 515 lines | 336 code | 63 blank | 116 comment | 64 complexity | 62089fa966b76aa814edaae674a188c4 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. using System;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using System.Runtime.CompilerServices;
  19. using Microsoft.Scripting;
  20. using Microsoft.Scripting.Runtime;
  21. using Microsoft.Scripting.Utils;
  22. using IronPython.Runtime.Binding;
  23. #if !CLR2
  24. using MSAst = System.Linq.Expressions;
  25. #else
  26. using MSAst = Microsoft.Scripting.Ast;
  27. #endif
  28. using AstUtils = Microsoft.Scripting.Ast.Utils;
  29. namespace IronPython.Compiler.Ast {
  30. using Ast = MSAst.Expression;
  31. public class TryStatement : Statement {
  32. private SourceLocation _header;
  33. /// <summary>
  34. /// The statements under the try-block.
  35. /// </summary>
  36. private Statement _body;
  37. /// <summary>
  38. /// Array of except (catch) blocks associated with this try. NULL if there are no except blocks.
  39. /// </summary>
  40. private readonly TryStatementHandler[] _handlers;
  41. /// <summary>
  42. /// The body of the optional Else block for this try. NULL if there is no Else block.
  43. /// </summary>
  44. private Statement _else;
  45. /// <summary>
  46. /// The body of the optional finally associated with this try. NULL if there is no finally block.
  47. /// </summary>
  48. private Statement _finally;
  49. public TryStatement(Statement body, TryStatementHandler[] handlers, Statement else_, Statement finally_) {
  50. _body = body;
  51. _handlers = handlers;
  52. _else = else_;
  53. _finally = finally_;
  54. }
  55. public SourceLocation Header {
  56. set { _header = value; }
  57. }
  58. public Statement Body {
  59. get { return _body; }
  60. }
  61. public Statement Else {
  62. get { return _else; }
  63. }
  64. public Statement Finally {
  65. get { return _finally; }
  66. }
  67. public IList<TryStatementHandler> Handlers {
  68. get { return _handlers; }
  69. }
  70. public override MSAst.Expression Reduce() {
  71. // allocated all variables here so they won't be shared w/ other
  72. // locals allocated during the body or except blocks.
  73. MSAst.ParameterExpression noNestedException = null;
  74. if (_finally != null) {
  75. noNestedException = Ast.Variable(typeof(bool), "$noException");
  76. }
  77. MSAst.ParameterExpression lineUpdated = null;
  78. MSAst.ParameterExpression runElse = null;
  79. if (_else != null || (_handlers != null && _handlers.Length > 0)) {
  80. lineUpdated = Ast.Variable(typeof(bool), "$lineUpdated_try");
  81. if (_else != null) {
  82. runElse = Ast.Variable(typeof(bool), "run_else");
  83. }
  84. }
  85. // don't allocate locals below here...
  86. MSAst.Expression body = _body;
  87. MSAst.Expression @else = _else;
  88. MSAst.Expression @catch, result;
  89. MSAst.ParameterExpression exception;
  90. if (_handlers != null && _handlers.Length > 0) {
  91. exception = Ast.Variable(typeof(Exception), "$exception");
  92. @catch = TransformHandlers(exception);
  93. } else {
  94. exception = null;
  95. @catch = null;
  96. }
  97. // We have else clause, must generate guard around it
  98. if (@else != null) {
  99. Debug.Assert(@catch != null);
  100. // run_else = true;
  101. // try {
  102. // try_body
  103. // } catch ( ... ) {
  104. // run_else = false;
  105. // catch_body
  106. // }
  107. // if (run_else) {
  108. // else_body
  109. // }
  110. result =
  111. Ast.Block(
  112. Ast.Assign(runElse, AstUtils.Constant(true)),
  113. // save existing line updated, we could choose to do this only for nested exception handlers.
  114. PushLineUpdated(false, lineUpdated),
  115. AstUtils.Try(
  116. Parent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, _header)),
  117. body
  118. ).Catch(exception,
  119. Ast.Assign(runElse, AstUtils.Constant(false)),
  120. @catch,
  121. // restore existing line updated after exception handler completes
  122. PopLineUpdated(lineUpdated),
  123. Ast.Assign(exception, Ast.Constant(null, typeof(Exception))),
  124. AstUtils.Default(body.Type)
  125. ),
  126. AstUtils.IfThen(runElse,
  127. @else
  128. ),
  129. AstUtils.Empty()
  130. );
  131. } else if (@catch != null) { // no "else" clause
  132. // try {
  133. // <try body>
  134. // } catch (Exception e) {
  135. // ... catch handling ...
  136. // }
  137. //
  138. result =
  139. AstUtils.Try(
  140. GlobalParent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, _header)),
  141. // save existing line updated
  142. PushLineUpdated(false, lineUpdated),
  143. body
  144. ).Catch(exception,
  145. @catch,
  146. // restore existing line updated after exception handler completes
  147. PopLineUpdated(lineUpdated),
  148. Ast.Call(AstMethods.ExceptionHandled, Parent.LocalContext),
  149. Ast.Assign(exception, Ast.Constant(null, typeof(Exception))),
  150. AstUtils.Default(body.Type)
  151. );
  152. } else {
  153. result = body;
  154. }
  155. return Ast.Block(
  156. GetVariables(noNestedException, lineUpdated, runElse),
  157. AddFinally(result, noNestedException)
  158. );
  159. }
  160. private static ReadOnlyCollectionBuilder<MSAst.ParameterExpression> GetVariables(MSAst.ParameterExpression noNestedException, MSAst.ParameterExpression lineUpdated, MSAst.ParameterExpression runElse) {
  161. var paramList = new ReadOnlyCollectionBuilder<MSAst.ParameterExpression>();
  162. if(noNestedException != null) {
  163. paramList.Add(noNestedException);
  164. }
  165. if(lineUpdated != null) {
  166. paramList.Add(lineUpdated);
  167. }
  168. if(runElse != null) {
  169. paramList.Add(runElse);
  170. }
  171. return paramList;
  172. }
  173. private MSAst.Expression AddFinally(MSAst.Expression/*!*/ body, MSAst.ParameterExpression nestedException) {
  174. if (_finally != null) {
  175. Debug.Assert(nestedException != null);
  176. MSAst.ParameterExpression nestedFrames = Ast.Variable(typeof(List<DynamicStackFrame>), "$nestedFrames");
  177. MSAst.Expression @finally = _finally;
  178. // lots is going on here. We need to consider:
  179. // 1. Exceptions propagating out of try/except/finally. Here we need to save the line #
  180. // from the exception block and not save the # from the finally block later.
  181. // 2. Exceptions propagating out of the finally block. Here we need to report the line number
  182. // from the finally block and leave the existing stack traces cleared.
  183. // 3. Returning from the try block: Here we need to run the finally block and not update the
  184. // line numbers.
  185. body = AstUtils.Try( // we use a fault to know when we have an exception and when control leaves normally (via
  186. // either a return or the body completing successfully).
  187. AstUtils.Try(
  188. Parent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, _header)),
  189. Ast.Assign(nestedException, AstUtils.Constant(false)),
  190. body
  191. ).Fault(
  192. // fault
  193. Ast.Assign(nestedException, AstUtils.Constant(true))
  194. )
  195. ).FinallyWithJumps(
  196. // if we had an exception save the line # that was last executing during the try
  197. AstUtils.If(
  198. nestedException,
  199. Parent.GetSaveLineNumberExpression(false)
  200. ),
  201. // clear the frames incase thae finally throws, and allow line number
  202. // updates to proceed
  203. UpdateLineUpdated(false),
  204. Ast.Assign(
  205. nestedFrames,
  206. Ast.Call(AstMethods.GetAndClearDynamicStackFrames)
  207. ),
  208. // run the finally code
  209. @finally,
  210. // if the finally exits normally restore any previous exception info
  211. Ast.Call(
  212. AstMethods.SetDynamicStackFrames,
  213. nestedFrames
  214. ),
  215. // if we took an exception in the try block we have saved the line number. Otherwise
  216. // we have no line number saved and will need to continue saving them if
  217. // other exceptions are thrown.
  218. AstUtils.If(
  219. nestedException,
  220. UpdateLineUpdated(true)
  221. )
  222. );
  223. body = Ast.Block(new[] { nestedFrames }, body);
  224. }
  225. return body;
  226. }
  227. /// <summary>
  228. /// Transform multiple python except handlers for a try block into a single catch body.
  229. /// </summary>
  230. /// <param name="exception">The variable for the exception in the catch block.</param>
  231. /// <returns>Null if there are no except handlers. Else the statement to go inside the catch handler</returns>
  232. private MSAst.Expression TransformHandlers(MSAst.ParameterExpression exception) {
  233. Assert.NotEmpty(_handlers);
  234. MSAst.ParameterExpression extracted = Ast.Variable(typeof(object), "$extracted");
  235. var tests = new List<Microsoft.Scripting.Ast.IfStatementTest>(_handlers.Length);
  236. MSAst.ParameterExpression converted = null;
  237. MSAst.Expression catchAll = null;
  238. for (int index = 0; index < _handlers.Length; index++) {
  239. TryStatementHandler tsh = _handlers[index];
  240. if (tsh.Test != null) {
  241. Microsoft.Scripting.Ast.IfStatementTest ist;
  242. // translating:
  243. // except Test ...
  244. //
  245. // generate following AST for the Test (common part):
  246. // CheckException(exception, Test)
  247. MSAst.Expression test =
  248. Ast.Call(
  249. AstMethods.CheckException,
  250. Parent.LocalContext,
  251. extracted,
  252. AstUtils.Convert(tsh.Test, typeof(object))
  253. );
  254. if (tsh.Target != null) {
  255. // translating:
  256. // except Test, Target:
  257. // <body>
  258. // into:
  259. // if ((converted = CheckException(exception, Test)) != null) {
  260. // Target = converted;
  261. // traceback-header
  262. // <body>
  263. // }
  264. if (converted == null) {
  265. converted = Ast.Variable(typeof(object), "$converted");
  266. }
  267. ist = AstUtils.IfCondition(
  268. Ast.NotEqual(
  269. Ast.Assign(converted, test),
  270. AstUtils.Constant(null)
  271. ),
  272. Ast.Block(
  273. tsh.Target.TransformSet(SourceSpan.None, converted, PythonOperationKind.None),
  274. GlobalParent.AddDebugInfo(
  275. GetTracebackHeader(
  276. this,
  277. exception,
  278. tsh.Body
  279. ),
  280. new SourceSpan(tsh.Start, tsh.Header)
  281. ),
  282. AstUtils.Empty()
  283. )
  284. );
  285. } else {
  286. // translating:
  287. // except Test:
  288. // <body>
  289. // into:
  290. // if (CheckException(exception, Test) != null) {
  291. // traceback-header
  292. // <body>
  293. // }
  294. ist = AstUtils.IfCondition(
  295. Ast.NotEqual(
  296. test,
  297. AstUtils.Constant(null)
  298. ),
  299. GlobalParent.AddDebugInfo(
  300. GetTracebackHeader(
  301. this,
  302. exception,
  303. tsh.Body
  304. ),
  305. new SourceSpan(tsh.Start, tsh.Header)
  306. )
  307. );
  308. }
  309. // Add the test to the if statement test cascade
  310. tests.Add(ist);
  311. } else {
  312. Debug.Assert(index == _handlers.Length - 1);
  313. Debug.Assert(catchAll == null);
  314. // translating:
  315. // except:
  316. // <body>
  317. // into:
  318. // {
  319. // traceback-header
  320. // <body>
  321. // }
  322. catchAll = GlobalParent.AddDebugInfo(
  323. GetTracebackHeader(this, exception, tsh.Body),
  324. new SourceSpan(tsh.Start, tsh.Header)
  325. );
  326. }
  327. }
  328. MSAst.Expression body = null;
  329. if (tests.Count > 0) {
  330. // rethrow the exception if we have no catch-all block
  331. if (catchAll == null) {
  332. catchAll = Ast.Block(
  333. Parent.GetSaveLineNumberExpression(true),
  334. Ast.Throw(
  335. Ast.Call(
  336. typeof(ExceptionHelpers).GetMethod("UpdateForRethrow"),
  337. exception
  338. )
  339. )
  340. );
  341. }
  342. body = AstUtils.If(
  343. tests.ToArray(),
  344. catchAll
  345. );
  346. } else {
  347. Debug.Assert(catchAll != null);
  348. body = catchAll;
  349. }
  350. IList<MSAst.ParameterExpression> args;
  351. if (converted != null) {
  352. args = new ReadOnlyCollectionBuilder<MSAst.ParameterExpression> { converted, extracted };
  353. } else {
  354. args = new ReadOnlyCollectionBuilder<MSAst.ParameterExpression> { extracted };
  355. }
  356. // Codegen becomes:
  357. // extracted = PythonOps.SetCurrentException(exception)
  358. // < dynamic exception analysis >
  359. return Ast.Block(
  360. args,
  361. Ast.Assign(
  362. extracted,
  363. Ast.Call(
  364. AstMethods.SetCurrentException,
  365. Parent.LocalContext,
  366. exception
  367. )
  368. ),
  369. body,
  370. Ast.Assign(extracted, Ast.Constant(null)),
  371. AstUtils.Empty()
  372. );
  373. }
  374. /// <summary>
  375. /// Surrounds the body of an except block w/ the appropriate code for maintaining the traceback.
  376. /// </summary>
  377. internal static MSAst.Expression GetTracebackHeader(Statement node, MSAst.ParameterExpression exception, MSAst.Expression body) {
  378. // we are about to enter a except block. We need to emit the line number update so we track
  379. // the line that the exception was thrown from. We then need to build exc_info() so that
  380. // it's available. Finally we clear the list of dynamic stack frames because they've all
  381. // been associated with this exception.
  382. return Ast.Block(
  383. // pass false so if we take another exception we'll add it to the frame list
  384. node.Parent.GetSaveLineNumberExpression(false),
  385. Ast.Call(
  386. AstMethods.BuildExceptionInfo,
  387. node.Parent.LocalContext,
  388. exception
  389. ),
  390. body,
  391. AstUtils.Empty()
  392. );
  393. }
  394. public override void Walk(PythonWalker walker) {
  395. if (walker.Walk(this)) {
  396. if (_body != null) {
  397. _body.Walk(walker);
  398. }
  399. if (_handlers != null) {
  400. foreach (TryStatementHandler handler in _handlers) {
  401. handler.Walk(walker);
  402. }
  403. }
  404. if (_else != null) {
  405. _else.Walk(walker);
  406. }
  407. if (_finally != null) {
  408. _finally.Walk(walker);
  409. }
  410. }
  411. walker.PostWalk(this);
  412. }
  413. }
  414. // A handler corresponds to the except block.
  415. public class TryStatementHandler : Node {
  416. private SourceLocation _header;
  417. private readonly Expression _test, _target;
  418. private readonly Statement _body;
  419. public TryStatementHandler(Expression test, Expression target, Statement body) {
  420. _test = test;
  421. _target = target;
  422. _body = body;
  423. }
  424. public SourceLocation Header {
  425. get { return _header; }
  426. set { _header = value; }
  427. }
  428. public Expression Test {
  429. get { return _test; }
  430. }
  431. public Expression Target {
  432. get { return _target; }
  433. }
  434. public Statement Body {
  435. get { return _body; }
  436. }
  437. public override void Walk(PythonWalker walker) {
  438. if (walker.Walk(this)) {
  439. if (_test != null) {
  440. _test.Walk(walker);
  441. }
  442. if (_target != null) {
  443. _target.Walk(walker);
  444. }
  445. if (_body != null) {
  446. _body.Walk(walker);
  447. }
  448. }
  449. walker.PostWalk(this);
  450. }
  451. }
  452. }