PageRenderTime 44ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/williamybs/uidipythontool
C# | 531 lines | 316 code | 85 blank | 130 comment | 50 complexity | a318faf678e774d35e898eac4b19df6b 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
  6. * License. A copy of the license can be found in the License.html file at the
  7. * root of this distribution. If you cannot locate the Microsoft Public
  8. * License, please send an email to dlr@microsoft.com. By using this source
  9. * code in any fashion, you are agreeing to be bound by the terms of the
  10. * Microsoft Public License.
  11. *
  12. * You must not remove this notice, or any other, from this software.
  13. *
  14. * ***************************************************************************/
  15. using System.Collections;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using Microsoft.Scripting;
  19. /*
  20. * The data flow.
  21. *
  22. * Each local name is represented as 2 bits:
  23. * One is for definitive assignment, the other is for uninitialized use detection.
  24. * The only difference between the two is behavior on delete.
  25. * On delete, the name is not assigned to meaningful value (we need to check at runtime if it's initialized),
  26. * but it is not uninitialized either (because delete statement will set it to Uninitialized.instance).
  27. * This way, codegen doesn�t have to emit an explicit initialization for it.
  28. *
  29. * Consider:
  30. *
  31. * def f():
  32. * print a # uninitialized use
  33. * a = 10
  34. *
  35. * We compile this into:
  36. *
  37. * static void f$f0() {
  38. * object a = Uninitialized.instance; // explicit initialization because of the uninitialized use
  39. * // print statement
  40. * if(a == Uninitialized.instance)
  41. * throw ThrowUnboundLocalError("a");
  42. * else
  43. * Ops.Print(a);
  44. * // a = 10
  45. * a = 10
  46. * }
  47. *
  48. * Whereas:
  49. *
  50. * def f():
  51. * a = 10
  52. * del a # explicit deletion which will set to Uninitialized.instance
  53. * print a
  54. *
  55. * compiles into:
  56. *
  57. * static void f$f0() {
  58. * object a = 10; // a = 10
  59. * a = Uninitialized.instance; // del a
  60. * if(a == Uninitialized.instance) // print a
  61. * throw ThrowUnboundLocalError("a");
  62. * else
  63. * Ops.Print(a);
  64. * }
  65. *
  66. * The bit arrays in the flow checker hold the state and upon encountering NameExpr we figure
  67. * out whether the name has not yet been initialized at all (in which case we need to emit the
  68. * first explicit assignment to Uninitialized.instance and guard the use with an inlined check
  69. * or whether it is definitely assigned (we don't need to inline the check)
  70. * or whether it may be uninitialized, in which case we must only guard the use by inlining the Uninitialized check
  71. *
  72. * More details on the bits.
  73. *
  74. * First bit (variable is assigned a value):
  75. * 1 .. variable is definitely assigned to a value
  76. * 0 .. variable is not assigned to a value at this point (it could have been deleted or just not assigned yet)
  77. * Second bit (variable is assigned a value or is deleted):
  78. * 1 .. variable is definitely initialized (either by a value or by deletion)
  79. * 0 .. variable is not initialized at this point (neither assigned nor deleted)
  80. *
  81. * Valid combinations:
  82. * 11 .. initialized
  83. * 01 .. deleted
  84. * 00 .. may not be initialized
  85. */
  86. namespace IronPython.Compiler.Ast {
  87. class FlowDefiner : PythonWalkerNonRecursive {
  88. private readonly FlowChecker _fc;
  89. public FlowDefiner(FlowChecker fc) {
  90. _fc = fc;
  91. }
  92. public override bool Walk(NameExpression node) {
  93. _fc.Define(node.Name);
  94. return false;
  95. }
  96. public override bool Walk(MemberExpression node) {
  97. node.Walk(_fc);
  98. return false;
  99. }
  100. public override bool Walk(IndexExpression node) {
  101. node.Walk(_fc);
  102. return false;
  103. }
  104. public override bool Walk(ParenthesisExpression node) {
  105. return true;
  106. }
  107. public override bool Walk(TupleExpression node) {
  108. return true;
  109. }
  110. public override bool Walk(ListExpression node) {
  111. return true;
  112. }
  113. public override bool Walk(Parameter node) {
  114. _fc.Define(node.Name);
  115. return true;
  116. }
  117. public override bool Walk(SublistParameter node) {
  118. return true;
  119. }
  120. }
  121. class FlowDeleter : PythonWalkerNonRecursive {
  122. private readonly FlowChecker _fc;
  123. public FlowDeleter(FlowChecker fc) {
  124. _fc = fc;
  125. }
  126. public override bool Walk(NameExpression node) {
  127. _fc.Delete(node.Name);
  128. return false;
  129. }
  130. }
  131. class FlowChecker : PythonWalker {
  132. private BitArray _bits;
  133. private Stack<BitArray> _loops;
  134. private Dictionary<string, PythonVariable> _variables;
  135. private readonly ScopeStatement _scope;
  136. private readonly FlowDefiner _fdef;
  137. private readonly FlowDeleter _fdel;
  138. private FlowChecker(ScopeStatement scope) {
  139. _variables = scope.Variables;
  140. _bits = new BitArray(_variables.Count * 2);
  141. int index = 0;
  142. foreach (var binding in _variables) {
  143. binding.Value.Index = index++;
  144. }
  145. _scope = scope;
  146. _fdef = new FlowDefiner(this);
  147. _fdel = new FlowDeleter(this);
  148. }
  149. [Conditional("DEBUG")]
  150. public void Dump(BitArray bits) {
  151. System.Text.StringBuilder sb = new System.Text.StringBuilder();
  152. sb.AppendFormat("FlowChecker ({0})", _scope is FunctionDefinition ? ((FunctionDefinition)_scope).Name :
  153. _scope is ClassDefinition ? ((ClassDefinition)_scope).Name : "");
  154. sb.Append('{');
  155. bool comma = false;
  156. foreach (var binding in _variables) {
  157. if (comma) sb.Append(", ");
  158. else comma = true;
  159. int index = 2 * binding.Value.Index;
  160. sb.AppendFormat("{0}:{1}{2}",
  161. binding.Key,
  162. bits.Get(index) ? "*" : "-",
  163. bits.Get(index + 1) ? "-" : "*");
  164. if (binding.Value.ReadBeforeInitialized)
  165. sb.Append("#");
  166. }
  167. sb.Append('}');
  168. Debug.WriteLine(sb.ToString());
  169. }
  170. private void SetAssigned(PythonVariable/*!*/ variable, bool value) {
  171. _bits.Set(variable.Index * 2, value);
  172. }
  173. private void SetInitialized(PythonVariable/*!*/ variable, bool value) {
  174. _bits.Set(variable.Index * 2 + 1, value);
  175. }
  176. private bool IsAssigned(PythonVariable/*!*/ variable) {
  177. return _bits.Get(variable.Index * 2);
  178. }
  179. private bool IsInitialized(PythonVariable/*!*/ variable) {
  180. return _bits.Get(variable.Index * 2 + 1);
  181. }
  182. public static void Check(ScopeStatement scope) {
  183. if (scope.Variables != null) {
  184. FlowChecker fc = new FlowChecker(scope);
  185. scope.Walk(fc);
  186. }
  187. }
  188. public void Define(string name) {
  189. PythonVariable binding;
  190. if (_variables.TryGetValue(name, out binding)) {
  191. SetAssigned(binding, true);
  192. SetInitialized(binding, true);
  193. }
  194. }
  195. public void Delete(string name) {
  196. PythonVariable binding;
  197. if (_variables.TryGetValue(name, out binding)) {
  198. SetAssigned(binding, false);
  199. SetInitialized(binding, true);
  200. }
  201. }
  202. private void PushLoop(BitArray ba) {
  203. if (_loops == null) {
  204. _loops = new Stack<BitArray>();
  205. }
  206. _loops.Push(ba);
  207. }
  208. private BitArray PeekLoop() {
  209. return _loops != null && _loops.Count > 0 ? _loops.Peek() : null;
  210. }
  211. private void PopLoop() {
  212. if (_loops != null) _loops.Pop();
  213. }
  214. #region AstWalker Methods
  215. // LambdaExpr
  216. public override bool Walk(LambdaExpression node) { return false; }
  217. // ListComp
  218. public override bool Walk(ListComprehension node) {
  219. BitArray save = _bits;
  220. _bits = new BitArray(_bits);
  221. foreach (ListComprehensionIterator iter in node.Iterators) iter.Walk(this);
  222. node.Item.Walk(this);
  223. _bits = save;
  224. return false;
  225. }
  226. // NameExpr
  227. public override bool Walk(NameExpression node) {
  228. PythonVariable binding;
  229. if (_variables.TryGetValue(node.Name, out binding)) {
  230. node.Assigned = IsAssigned(binding);
  231. if (!IsInitialized(binding)) {
  232. binding.ReadBeforeInitialized = true;
  233. }
  234. }
  235. return true;
  236. }
  237. public override void PostWalk(NameExpression node) { }
  238. // AssignStmt
  239. public override bool Walk(AssignmentStatement node) {
  240. node.Right.Walk(this);
  241. foreach (Expression e in node.Left) {
  242. e.Walk(_fdef);
  243. }
  244. return false;
  245. }
  246. public override void PostWalk(AssignmentStatement node) { }
  247. // AugAssignStmt
  248. public override bool Walk(AugmentedAssignStatement node) { return true; }
  249. public override void PostWalk(AugmentedAssignStatement node) {
  250. node.Left.Walk(_fdef);
  251. }
  252. // BreakStmt
  253. public override bool Walk(BreakStatement node) {
  254. BitArray exit = PeekLoop();
  255. if (exit != null) { // break outside loop
  256. exit.And(_bits);
  257. }
  258. return true;
  259. }
  260. // ClassDef
  261. public override bool Walk(ClassDefinition node) {
  262. if (_scope == node) {
  263. // the class body is being analyzed, go deep:
  264. return true;
  265. } else {
  266. // analyze the class definition itself (it is visited while analyzing parent scope):
  267. Define(node.Name);
  268. foreach (Expression e in node.Bases) {
  269. e.Walk(this);
  270. }
  271. return false;
  272. }
  273. }
  274. // ContinueStmt
  275. public override bool Walk(ContinueStatement node) { return true; }
  276. // DelStmt
  277. public override void PostWalk(DelStatement node) {
  278. foreach (Expression e in node.Expressions) {
  279. e.Walk(_fdel);
  280. }
  281. }
  282. // ForStmt
  283. public override bool Walk(ForStatement node) {
  284. // Walk the expression
  285. node.List.Walk(this);
  286. BitArray opte = new BitArray(_bits);
  287. BitArray exit = new BitArray(_bits.Length, true);
  288. PushLoop(exit);
  289. // Define the lhs
  290. node.Left.Walk(_fdef);
  291. // Walk the body
  292. node.Body.Walk(this);
  293. PopLoop();
  294. _bits.And(exit);
  295. if (node.Else != null) {
  296. // Flow the else
  297. BitArray save = _bits;
  298. _bits = opte;
  299. node.Else.Walk(this);
  300. // Restore the bits
  301. _bits = save;
  302. }
  303. // Intersect
  304. _bits.And(opte);
  305. return false;
  306. }
  307. // FromImportStmt
  308. public override bool Walk(FromImportStatement node) {
  309. if (node.Names != FromImportStatement.Star) {
  310. for (int i = 0; i < node.Names.Count; i++) {
  311. Define(node.AsNames[i] != null ? node.AsNames[i] : node.Names[i]);
  312. }
  313. }
  314. return true;
  315. }
  316. // FuncDef
  317. public override bool Walk(FunctionDefinition node) {
  318. if (node == _scope) {
  319. // the function body is being analyzed, go deep:
  320. foreach (Parameter p in node.Parameters) {
  321. p.Walk(_fdef);
  322. }
  323. return true;
  324. } else {
  325. // analyze the function definition itself (it is visited while analyzing parent scope):
  326. Define(node.Name);
  327. foreach (Parameter p in node.Parameters) {
  328. if (p.DefaultValue != null) {
  329. p.DefaultValue.Walk(this);
  330. }
  331. }
  332. return false;
  333. }
  334. }
  335. // IfStmt
  336. public override bool Walk(IfStatement node) {
  337. BitArray result = new BitArray(_bits.Length, true);
  338. BitArray save = _bits;
  339. _bits = new BitArray(_bits.Length);
  340. foreach (IfStatementTest ist in node.Tests) {
  341. // Set the initial branch value to bits
  342. _bits.SetAll(false);
  343. _bits.Or(save);
  344. // Flow the test first
  345. ist.Test.Walk(this);
  346. // Flow the body
  347. ist.Body.Walk(this);
  348. // Intersect
  349. result.And(_bits);
  350. }
  351. // Set the initial branch value to bits
  352. _bits.SetAll(false);
  353. _bits.Or(save);
  354. if (node.ElseStatement != null) {
  355. // Flow the else_
  356. node.ElseStatement.Walk(this);
  357. }
  358. // Intersect
  359. result.And(_bits);
  360. _bits = save;
  361. // Remember the result
  362. _bits.SetAll(false);
  363. _bits.Or(result);
  364. return false;
  365. }
  366. // ImportStmt
  367. public override bool Walk(ImportStatement node) {
  368. for (int i = 0; i < node.Names.Count; i++) {
  369. Define(node.AsNames[i] != null ? node.AsNames[i] : node.Names[i].Names[0]);
  370. }
  371. return true;
  372. }
  373. public override void PostWalk(ReturnStatement node) { }
  374. // WithStmt
  375. public override bool Walk(WithStatement node) {
  376. // Walk the expression
  377. node.ContextManager.Walk(this);
  378. BitArray save = _bits;
  379. _bits = new BitArray(_bits);
  380. // Define the Rhs
  381. if (node.Variable != null)
  382. node.Variable.Walk(_fdef);
  383. // Flow the body
  384. node.Body.Walk(this);
  385. _bits = save;
  386. return false;
  387. }
  388. // TryStmt
  389. public override bool Walk(TryStatement node) {
  390. BitArray save = _bits;
  391. _bits = new BitArray(_bits);
  392. // Flow the body
  393. node.Body.Walk(this);
  394. if (node.Else != null) {
  395. // Else is flown only after completion of Try with same bits
  396. node.Else.Walk(this);
  397. }
  398. if (node.Handlers != null) {
  399. foreach (TryStatementHandler tsh in node.Handlers) {
  400. // Restore to saved state
  401. _bits.SetAll(false);
  402. _bits.Or(save);
  403. // Flow the test
  404. if (tsh.Test != null) {
  405. tsh.Test.Walk(this);
  406. }
  407. // Define the target
  408. if (tsh.Target != null) {
  409. tsh.Target.Walk(_fdef);
  410. }
  411. // Flow the body
  412. tsh.Body.Walk(this);
  413. }
  414. }
  415. _bits = save;
  416. if (node.Finally != null) {
  417. // Flow finally - this executes no matter what
  418. node.Finally.Walk(this);
  419. }
  420. return false;
  421. }
  422. // WhileStmt
  423. public override bool Walk(WhileStatement node) {
  424. // Walk the expression
  425. node.Test.Walk(this);
  426. BitArray opte = node.ElseStatement != null ? new BitArray(_bits) : null;
  427. BitArray exit = new BitArray(_bits.Length, true);
  428. PushLoop(exit);
  429. node.Body.Walk(this);
  430. PopLoop();
  431. _bits.And(exit);
  432. if (node.ElseStatement != null) {
  433. // Flow the else
  434. BitArray save = _bits;
  435. _bits = opte;
  436. node.ElseStatement.Walk(this);
  437. // Restore the bits
  438. _bits = save;
  439. // Intersect
  440. _bits.And(opte);
  441. }
  442. return false;
  443. }
  444. #endregion
  445. }
  446. }