/closure/closure-compiler/lib/rhino/src/mozilla/js/rhino/src/org/mozilla/javascript/Node.java

https://code.google.com/p/plovr/ · Java · 1292 lines · 880 code · 135 blank · 277 comment · 198 complexity · c3f771da7511d074185220729a0cd1b4 MD5 · raw file

  1. /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. *
  3. * ***** BEGIN LICENSE BLOCK *****
  4. * Version: MPL 1.1/GPL 2.0
  5. *
  6. * The contents of this file are subject to the Mozilla Public License Version
  7. * 1.1 (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. * http://www.mozilla.org/MPL/
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. *
  16. * The Original Code is Rhino code, released
  17. * May 6, 1999.
  18. *
  19. * The Initial Developer of the Original Code is
  20. * Netscape Communications Corporation.
  21. * Portions created by the Initial Developer are Copyright (C) 1997-1999
  22. * the Initial Developer. All Rights Reserved.
  23. *
  24. * Contributor(s):
  25. * Norris Boyd
  26. * Roshan James
  27. * Roger Lawrence
  28. * Mike McCabe
  29. *
  30. * Alternatively, the contents of this file may be used under the terms of
  31. * the GNU General Public License Version 2 or later (the "GPL"), in which
  32. * case the provisions of the GPL are applicable instead of those above. If
  33. * you wish to allow use of your version of this file only under the terms of
  34. * the GPL and not to allow others to use your version of this file under the
  35. * MPL, indicate your decision by deleting the provisions above and replacing
  36. * them with the notice and other provisions required by the GPL. If you do
  37. * not delete the provisions above, a recipient may use your version of this
  38. * file under either the MPL or the GPL.
  39. *
  40. * ***** END LICENSE BLOCK ***** */
  41. package org.mozilla.javascript;
  42. import org.mozilla.javascript.ast.Comment;
  43. import org.mozilla.javascript.ast.FunctionNode;
  44. import org.mozilla.javascript.ast.Jump;
  45. import org.mozilla.javascript.ast.Name;
  46. import org.mozilla.javascript.ast.NumberLiteral;
  47. import org.mozilla.javascript.ast.Scope;
  48. import org.mozilla.javascript.ast.ScriptNode;
  49. import java.util.Iterator;
  50. import java.util.NoSuchElementException;
  51. /**
  52. * This class implements the root of the intermediate representation.
  53. *
  54. */
  55. public class Node implements Iterable<Node>
  56. {
  57. public static final int
  58. FUNCTION_PROP = 1,
  59. LOCAL_PROP = 2,
  60. LOCAL_BLOCK_PROP = 3,
  61. REGEXP_PROP = 4,
  62. CASEARRAY_PROP = 5,
  63. // the following properties are defined and manipulated by the
  64. // optimizer -
  65. // TARGETBLOCK_PROP - the block referenced by a branch node
  66. // VARIABLE_PROP - the variable referenced by a BIND or NAME node
  67. // ISNUMBER_PROP - this node generates code on Number children and
  68. // delivers a Number result (as opposed to Objects)
  69. // DIRECTCALL_PROP - this call node should emit code to test the function
  70. // object against the known class and call direct if it
  71. // matches.
  72. TARGETBLOCK_PROP = 6,
  73. VARIABLE_PROP = 7,
  74. ISNUMBER_PROP = 8,
  75. DIRECTCALL_PROP = 9,
  76. SPECIALCALL_PROP = 10,
  77. SKIP_INDEXES_PROP = 11, // array of skipped indexes of array literal
  78. OBJECT_IDS_PROP = 12, // array of properties for object literal
  79. INCRDECR_PROP = 13, // pre or post type of increment/decrement
  80. CATCH_SCOPE_PROP = 14, // index of catch scope block in catch
  81. LABEL_ID_PROP = 15, // label id: code generation uses it
  82. MEMBER_TYPE_PROP = 16, // type of element access operation
  83. NAME_PROP = 17, // property name
  84. CONTROL_BLOCK_PROP = 18, // flags a control block that can drop off
  85. PARENTHESIZED_PROP = 19, // expression is parenthesized
  86. GENERATOR_END_PROP = 20,
  87. DESTRUCTURING_ARRAY_LENGTH = 21,
  88. DESTRUCTURING_NAMES = 22,
  89. DESTRUCTURING_PARAMS = 23,
  90. JSDOC_PROP = 24,
  91. EXPRESSION_CLOSURE_PROP = 25, // JS 1.8 expression closure pseudo-return
  92. DESTRUCTURING_SHORTHAND = 26, // JS 1.8 destructuring shorthand
  93. LAST_PROP = 26;
  94. // values of ISNUMBER_PROP to specify
  95. // which of the children are Number types
  96. public static final int
  97. BOTH = 0,
  98. LEFT = 1,
  99. RIGHT = 2;
  100. public static final int // values for SPECIALCALL_PROP
  101. NON_SPECIALCALL = 0,
  102. SPECIALCALL_EVAL = 1,
  103. SPECIALCALL_WITH = 2;
  104. public static final int // flags for INCRDECR_PROP
  105. DECR_FLAG = 0x1,
  106. POST_FLAG = 0x2;
  107. public static final int // flags for MEMBER_TYPE_PROP
  108. PROPERTY_FLAG = 0x1, // property access: element is valid name
  109. ATTRIBUTE_FLAG = 0x2, // x.@y or x..@y
  110. DESCENDANTS_FLAG = 0x4; // x..y or x..@i
  111. private static class PropListItem
  112. {
  113. PropListItem next;
  114. int type;
  115. int intValue;
  116. Object objectValue;
  117. }
  118. public Node(int nodeType) {
  119. type = nodeType;
  120. }
  121. public Node(int nodeType, Node child) {
  122. type = nodeType;
  123. first = last = child;
  124. child.next = null;
  125. }
  126. public Node(int nodeType, Node left, Node right) {
  127. type = nodeType;
  128. first = left;
  129. last = right;
  130. left.next = right;
  131. right.next = null;
  132. }
  133. public Node(int nodeType, Node left, Node mid, Node right) {
  134. type = nodeType;
  135. first = left;
  136. last = right;
  137. left.next = mid;
  138. mid.next = right;
  139. right.next = null;
  140. }
  141. public Node(int nodeType, int line) {
  142. type = nodeType;
  143. lineno = line;
  144. }
  145. public Node(int nodeType, Node child, int line) {
  146. this(nodeType, child);
  147. lineno = line;
  148. }
  149. public Node(int nodeType, Node left, Node right, int line) {
  150. this(nodeType, left, right);
  151. lineno = line;
  152. }
  153. public Node(int nodeType, Node left, Node mid, Node right, int line) {
  154. this(nodeType, left, mid, right);
  155. lineno = line;
  156. }
  157. public static Node newNumber(double number) {
  158. NumberLiteral n = new NumberLiteral();
  159. n.setNumber(number);
  160. return n;
  161. }
  162. public static Node newString(String str) {
  163. return newString(Token.STRING, str);
  164. }
  165. public static Node newString(int type, String str) {
  166. Name name = new Name();
  167. name.setIdentifier(str);
  168. name.setType(type);
  169. return name;
  170. }
  171. public int getType() {
  172. return type;
  173. }
  174. /**
  175. * Sets the node type and returns this node.
  176. */
  177. public Node setType(int type) {
  178. this.type = type;
  179. return this;
  180. }
  181. /**
  182. * Gets the JsDoc comment string attached to this node.
  183. * @return the comment string or {@code null} if no JsDoc is attached to
  184. * this node
  185. */
  186. public String getJsDoc() {
  187. Comment comment = getJsDocNode();
  188. if (comment != null) {
  189. return comment.getValue();
  190. }
  191. return null;
  192. }
  193. /**
  194. * Gets the JsDoc Comment object attached to this node.
  195. * @return the Comment or {@code null} if no JsDoc is attached to
  196. * this node
  197. */
  198. public Comment getJsDocNode() {
  199. return (Comment) getProp(JSDOC_PROP);
  200. }
  201. /**
  202. * Sets the JsDoc comment string attached to this node.
  203. */
  204. public void setJsDocNode(Comment jsdocNode) {
  205. putProp(JSDOC_PROP, jsdocNode);
  206. }
  207. public boolean hasChildren() {
  208. return first != null;
  209. }
  210. public Node getFirstChild() {
  211. return first;
  212. }
  213. public Node getLastChild() {
  214. return last;
  215. }
  216. public Node getNext() {
  217. return next;
  218. }
  219. public Node getChildBefore(Node child) {
  220. if (child == first)
  221. return null;
  222. Node n = first;
  223. while (n.next != child) {
  224. n = n.next;
  225. if (n == null)
  226. throw new RuntimeException("node is not a child");
  227. }
  228. return n;
  229. }
  230. public Node getLastSibling() {
  231. Node n = this;
  232. while (n.next != null) {
  233. n = n.next;
  234. }
  235. return n;
  236. }
  237. public void addChildToFront(Node child) {
  238. child.next = first;
  239. first = child;
  240. if (last == null) {
  241. last = child;
  242. }
  243. }
  244. public void addChildToBack(Node child) {
  245. child.next = null;
  246. if (last == null) {
  247. first = last = child;
  248. return;
  249. }
  250. last.next = child;
  251. last = child;
  252. }
  253. public void addChildrenToFront(Node children) {
  254. Node lastSib = children.getLastSibling();
  255. lastSib.next = first;
  256. first = children;
  257. if (last == null) {
  258. last = lastSib;
  259. }
  260. }
  261. public void addChildrenToBack(Node children) {
  262. if (last != null) {
  263. last.next = children;
  264. }
  265. last = children.getLastSibling();
  266. if (first == null) {
  267. first = children;
  268. }
  269. }
  270. /**
  271. * Add 'child' before 'node'.
  272. */
  273. public void addChildBefore(Node newChild, Node node) {
  274. if (newChild.next != null)
  275. throw new RuntimeException(
  276. "newChild had siblings in addChildBefore");
  277. if (first == node) {
  278. newChild.next = first;
  279. first = newChild;
  280. return;
  281. }
  282. Node prev = getChildBefore(node);
  283. addChildAfter(newChild, prev);
  284. }
  285. /**
  286. * Add 'child' after 'node'.
  287. */
  288. public void addChildAfter(Node newChild, Node node) {
  289. if (newChild.next != null)
  290. throw new RuntimeException(
  291. "newChild had siblings in addChildAfter");
  292. newChild.next = node.next;
  293. node.next = newChild;
  294. if (last == node)
  295. last = newChild;
  296. }
  297. public void removeChild(Node child) {
  298. Node prev = getChildBefore(child);
  299. if (prev == null)
  300. first = first.next;
  301. else
  302. prev.next = child.next;
  303. if (child == last) last = prev;
  304. child.next = null;
  305. }
  306. public void replaceChild(Node child, Node newChild) {
  307. newChild.next = child.next;
  308. if (child == first) {
  309. first = newChild;
  310. } else {
  311. Node prev = getChildBefore(child);
  312. prev.next = newChild;
  313. }
  314. if (child == last)
  315. last = newChild;
  316. child.next = null;
  317. }
  318. public void replaceChildAfter(Node prevChild, Node newChild) {
  319. Node child = prevChild.next;
  320. newChild.next = child.next;
  321. prevChild.next = newChild;
  322. if (child == last)
  323. last = newChild;
  324. child.next = null;
  325. }
  326. public void removeChildren() {
  327. first = last = null;
  328. }
  329. private static final Node NOT_SET = new Node(Token.ERROR);
  330. /**
  331. * Iterates over the children of this Node. Supports child removal. Not
  332. * thread-safe. If anyone changes the child list before the iterator
  333. * finishes, the results are undefined and probably bad.
  334. */
  335. public class NodeIterator implements Iterator<Node> {
  336. private Node cursor; // points to node to be returned next
  337. private Node prev = NOT_SET;
  338. private Node prev2;
  339. private boolean removed = false;
  340. public NodeIterator() {
  341. cursor = Node.this.first;
  342. }
  343. public boolean hasNext() {
  344. return cursor != null;
  345. }
  346. public Node next() {
  347. if (cursor == null) {
  348. throw new NoSuchElementException();
  349. }
  350. removed = false;
  351. prev2 = prev;
  352. prev = cursor;
  353. cursor = cursor.next;
  354. return prev;
  355. }
  356. public void remove() {
  357. if (prev == NOT_SET) {
  358. throw new IllegalStateException("next() has not been called");
  359. }
  360. if (removed) {
  361. throw new IllegalStateException(
  362. "remove() already called for current element");
  363. }
  364. if (prev == first) {
  365. first = prev.next;
  366. } else if (prev == last) {
  367. prev2.next = null;
  368. last = prev2;
  369. } else {
  370. prev2.next = cursor;
  371. }
  372. }
  373. }
  374. /**
  375. * Returns an {@link java.util.Iterator} over the node's children.
  376. */
  377. public Iterator<Node> iterator() {
  378. return new NodeIterator();
  379. }
  380. private static final String propToString(int propType)
  381. {
  382. if (Token.printTrees) {
  383. // If Context.printTrees is false, the compiler
  384. // can remove all these strings.
  385. switch (propType) {
  386. case FUNCTION_PROP: return "function";
  387. case LOCAL_PROP: return "local";
  388. case LOCAL_BLOCK_PROP: return "local_block";
  389. case REGEXP_PROP: return "regexp";
  390. case CASEARRAY_PROP: return "casearray";
  391. case TARGETBLOCK_PROP: return "targetblock";
  392. case VARIABLE_PROP: return "variable";
  393. case ISNUMBER_PROP: return "isnumber";
  394. case DIRECTCALL_PROP: return "directcall";
  395. case SPECIALCALL_PROP: return "specialcall";
  396. case SKIP_INDEXES_PROP: return "skip_indexes";
  397. case OBJECT_IDS_PROP: return "object_ids_prop";
  398. case INCRDECR_PROP: return "incrdecr_prop";
  399. case CATCH_SCOPE_PROP: return "catch_scope_prop";
  400. case LABEL_ID_PROP: return "label_id_prop";
  401. case MEMBER_TYPE_PROP: return "member_type_prop";
  402. case NAME_PROP: return "name_prop";
  403. case CONTROL_BLOCK_PROP: return "control_block_prop";
  404. case PARENTHESIZED_PROP: return "parenthesized_prop";
  405. case GENERATOR_END_PROP: return "generator_end";
  406. case DESTRUCTURING_ARRAY_LENGTH:
  407. return "destructuring_array_length";
  408. case DESTRUCTURING_NAMES: return "destructuring_names";
  409. case DESTRUCTURING_PARAMS: return "destructuring_params";
  410. default: Kit.codeBug();
  411. }
  412. }
  413. return null;
  414. }
  415. private PropListItem lookupProperty(int propType)
  416. {
  417. PropListItem x = propListHead;
  418. while (x != null && propType != x.type) {
  419. x = x.next;
  420. }
  421. return x;
  422. }
  423. private PropListItem ensureProperty(int propType)
  424. {
  425. PropListItem item = lookupProperty(propType);
  426. if (item == null) {
  427. item = new PropListItem();
  428. item.type = propType;
  429. item.next = propListHead;
  430. propListHead = item;
  431. }
  432. return item;
  433. }
  434. public void removeProp(int propType)
  435. {
  436. PropListItem x = propListHead;
  437. if (x != null) {
  438. PropListItem prev = null;
  439. while (x.type != propType) {
  440. prev = x;
  441. x = x.next;
  442. if (x == null) { return; }
  443. }
  444. if (prev == null) {
  445. propListHead = x.next;
  446. } else {
  447. prev.next = x.next;
  448. }
  449. }
  450. }
  451. public Object getProp(int propType)
  452. {
  453. PropListItem item = lookupProperty(propType);
  454. if (item == null) { return null; }
  455. return item.objectValue;
  456. }
  457. public int getIntProp(int propType, int defaultValue)
  458. {
  459. PropListItem item = lookupProperty(propType);
  460. if (item == null) { return defaultValue; }
  461. return item.intValue;
  462. }
  463. public int getExistingIntProp(int propType)
  464. {
  465. PropListItem item = lookupProperty(propType);
  466. if (item == null) { Kit.codeBug(); }
  467. return item.intValue;
  468. }
  469. public void putProp(int propType, Object prop)
  470. {
  471. if (prop == null) {
  472. removeProp(propType);
  473. } else {
  474. PropListItem item = ensureProperty(propType);
  475. item.objectValue = prop;
  476. }
  477. }
  478. public void putIntProp(int propType, int prop)
  479. {
  480. PropListItem item = ensureProperty(propType);
  481. item.intValue = prop;
  482. }
  483. /**
  484. * Return the line number recorded for this node.
  485. * @return the line number
  486. */
  487. public int getLineno() {
  488. return lineno;
  489. }
  490. public void setLineno(int lineno) {
  491. this.lineno = lineno;
  492. }
  493. /** Can only be called when <tt>getType() == Token.NUMBER</tt> */
  494. public final double getDouble() {
  495. return ((NumberLiteral)this).getNumber();
  496. }
  497. public final void setDouble(double number) {
  498. ((NumberLiteral)this).setNumber(number);
  499. }
  500. /** Can only be called when node has String context. */
  501. public final String getString() {
  502. return ((Name)this).getIdentifier();
  503. }
  504. /** Can only be called when node has String context. */
  505. public final void setString(String s) {
  506. if (s == null) Kit.codeBug();
  507. ((Name)this).setIdentifier(s);
  508. }
  509. /** Can only be called when node has String context. */
  510. public Scope getScope() {
  511. return ((Name)this).getScope();
  512. }
  513. /** Can only be called when node has String context. */
  514. public void setScope(Scope s) {
  515. if (s == null) Kit.codeBug();
  516. if (!(this instanceof Name)) {
  517. throw Kit.codeBug();
  518. }
  519. ((Name)this).setScope(s);
  520. }
  521. public static Node newTarget()
  522. {
  523. return new Node(Token.TARGET);
  524. }
  525. public final int labelId()
  526. {
  527. if (type != Token.TARGET && type != Token.YIELD) Kit.codeBug();
  528. return getIntProp(LABEL_ID_PROP, -1);
  529. }
  530. public void labelId(int labelId)
  531. {
  532. if (type != Token.TARGET && type != Token.YIELD) Kit.codeBug();
  533. putIntProp(LABEL_ID_PROP, labelId);
  534. }
  535. /**
  536. * Does consistent-return analysis on the function body when strict mode is
  537. * enabled.
  538. *
  539. * function (x) { return (x+1) }
  540. * is ok, but
  541. * function (x) { if (x &lt; 0) return (x+1); }
  542. * is not becuase the function can potentially return a value when the
  543. * condition is satisfied and if not, the function does not explicitly
  544. * return value.
  545. *
  546. * This extends to checking mismatches such as "return" and "return <value>"
  547. * used in the same function. Warnings are not emitted if inconsistent
  548. * returns exist in code that can be statically shown to be unreachable.
  549. * Ex.
  550. * <pre>function (x) { while (true) { ... if (..) { return value } ... } }
  551. * </pre>
  552. * emits no warning. However if the loop had a break statement, then a
  553. * warning would be emitted.
  554. *
  555. * The consistency analysis looks at control structures such as loops, ifs,
  556. * switch, try-catch-finally blocks, examines the reachable code paths and
  557. * warns the user about an inconsistent set of termination possibilities.
  558. *
  559. * Caveat: Since the parser flattens many control structures into almost
  560. * straight-line code with gotos, it makes such analysis hard. Hence this
  561. * analyser is written to taken advantage of patterns of code generated by
  562. * the parser (for loops, try blocks and such) and does not do a full
  563. * control flow analysis of the gotos and break/continue statements.
  564. * Future changes to the parser will affect this analysis.
  565. */
  566. /**
  567. * These flags enumerate the possible ways a statement/function can
  568. * terminate. These flags are used by endCheck() and by the Parser to
  569. * detect inconsistent return usage.
  570. *
  571. * END_UNREACHED is reserved for code paths that are assumed to always be
  572. * able to execute (example: throw, continue)
  573. *
  574. * END_DROPS_OFF indicates if the statement can transfer control to the
  575. * next one. Statement such as return dont. A compound statement may have
  576. * some branch that drops off control to the next statement.
  577. *
  578. * END_RETURNS indicates that the statement can return (without arguments)
  579. * END_RETURNS_VALUE indicates that the statement can return a value.
  580. *
  581. * A compound statement such as
  582. * if (condition) {
  583. * return value;
  584. * }
  585. * Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
  586. */
  587. public static final int END_UNREACHED = 0;
  588. public static final int END_DROPS_OFF = 1;
  589. public static final int END_RETURNS = 2;
  590. public static final int END_RETURNS_VALUE = 4;
  591. public static final int END_YIELDS = 8;
  592. /**
  593. * Checks that every return usage in a function body is consistent with the
  594. * requirements of strict-mode.
  595. * @return true if the function satisfies strict mode requirement.
  596. */
  597. public boolean hasConsistentReturnUsage()
  598. {
  599. int n = endCheck();
  600. return (n & END_RETURNS_VALUE) == 0 ||
  601. (n & (END_DROPS_OFF|END_RETURNS|END_YIELDS)) == 0;
  602. }
  603. /**
  604. * Returns in the then and else blocks must be consistent with each other.
  605. * If there is no else block, then the return statement can fall through.
  606. * @return logical OR of END_* flags
  607. */
  608. private int endCheckIf()
  609. {
  610. Node th, el;
  611. int rv = END_UNREACHED;
  612. th = next;
  613. el = ((Jump)this).target;
  614. rv = th.endCheck();
  615. if (el != null)
  616. rv |= el.endCheck();
  617. else
  618. rv |= END_DROPS_OFF;
  619. return rv;
  620. }
  621. /**
  622. * Consistency of return statements is checked between the case statements.
  623. * If there is no default, then the switch can fall through. If there is a
  624. * default,we check to see if all code paths in the default return or if
  625. * there is a code path that can fall through.
  626. * @return logical OR of END_* flags
  627. */
  628. private int endCheckSwitch()
  629. {
  630. int rv = END_UNREACHED;
  631. // examine the cases
  632. // for (n = first.next; n != null; n = n.next)
  633. // {
  634. // if (n.type == Token.CASE) {
  635. // rv |= ((Jump)n).target.endCheck();
  636. // } else
  637. // break;
  638. // }
  639. // // we don't care how the cases drop into each other
  640. // rv &= ~END_DROPS_OFF;
  641. // // examine the default
  642. // n = ((Jump)this).getDefault();
  643. // if (n != null)
  644. // rv |= n.endCheck();
  645. // else
  646. // rv |= END_DROPS_OFF;
  647. // // remove the switch block
  648. // rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
  649. return rv;
  650. }
  651. /**
  652. * If the block has a finally, return consistency is checked in the
  653. * finally block. If all code paths in the finally returns, then the
  654. * returns in the try-catch blocks don't matter. If there is a code path
  655. * that does not return or if there is no finally block, the returns
  656. * of the try and catch blocks are checked for mismatch.
  657. * @return logical OR of END_* flags
  658. */
  659. private int endCheckTry()
  660. {
  661. int rv = END_UNREACHED;
  662. // a TryStatement isn't a jump - needs rewriting
  663. // check the finally if it exists
  664. // n = ((Jump)this).getFinally();
  665. // if(n != null) {
  666. // rv = n.next.first.endCheck();
  667. // } else {
  668. // rv = END_DROPS_OFF;
  669. // }
  670. // // if the finally block always returns, then none of the returns
  671. // // in the try or catch blocks matter
  672. // if ((rv & END_DROPS_OFF) != 0) {
  673. // rv &= ~END_DROPS_OFF;
  674. // // examine the try block
  675. // rv |= first.endCheck();
  676. // // check each catch block
  677. // n = ((Jump)this).target;
  678. // if (n != null)
  679. // {
  680. // // point to the first catch_scope
  681. // for (n = n.next.first; n != null; n = n.next.next)
  682. // {
  683. // // check the block of user code in the catch_scope
  684. // rv |= n.next.first.next.first.endCheck();
  685. // }
  686. // }
  687. // }
  688. return rv;
  689. }
  690. /**
  691. * Return statement in the loop body must be consistent. The default
  692. * assumption for any kind of a loop is that it will eventually terminate.
  693. * The only exception is a loop with a constant true condition. Code that
  694. * follows such a loop is examined only if one can statically determine
  695. * that there is a break out of the loop.
  696. * <pre>
  697. * for(&lt;&gt; ; &lt;&gt;; &lt;&gt;) {}
  698. * for(&lt;&gt; in &lt;&gt; ) {}
  699. * while(&lt;&gt;) { }
  700. * do { } while(&lt;&gt;)
  701. * </pre>
  702. * @return logical OR of END_* flags
  703. */
  704. private int endCheckLoop()
  705. {
  706. Node n;
  707. int rv = END_UNREACHED;
  708. // To find the loop body, we look at the second to last node of the
  709. // loop node, which should be the predicate that the loop should
  710. // satisfy.
  711. // The target of the predicate is the loop-body for all 4 kinds of
  712. // loops.
  713. for (n = first; n.next != last; n = n.next) {
  714. /* skip */
  715. }
  716. if (n.type != Token.IFEQ)
  717. return END_DROPS_OFF;
  718. // The target's next is the loop body block
  719. rv = ((Jump)n).target.next.endCheck();
  720. // check to see if the loop condition is true
  721. if (n.first.type == Token.TRUE)
  722. rv &= ~END_DROPS_OFF;
  723. // look for effect of breaks
  724. rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
  725. return rv;
  726. }
  727. /**
  728. * A general block of code is examined statement by statement. If any
  729. * statement (even compound ones) returns in all branches, then subsequent
  730. * statements are not examined.
  731. * @return logical OR of END_* flags
  732. */
  733. private int endCheckBlock()
  734. {
  735. Node n;
  736. int rv = END_DROPS_OFF;
  737. // check each statment and if the statement can continue onto the next
  738. // one, then check the next statement
  739. for (n=first; ((rv & END_DROPS_OFF) != 0) && n != null; n = n.next)
  740. {
  741. rv &= ~END_DROPS_OFF;
  742. rv |= n.endCheck();
  743. }
  744. return rv;
  745. }
  746. /**
  747. * A labelled statement implies that there maybe a break to the label. The
  748. * function processes the labelled statement and then checks the
  749. * CONTROL_BLOCK_PROP property to see if there is ever a break to the
  750. * particular label.
  751. * @return logical OR of END_* flags
  752. */
  753. private int endCheckLabel()
  754. {
  755. int rv = END_UNREACHED;
  756. rv = next.endCheck();
  757. rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
  758. return rv;
  759. }
  760. /**
  761. * When a break is encountered annotate the statement being broken
  762. * out of by setting its CONTROL_BLOCK_PROP property.
  763. * @return logical OR of END_* flags
  764. */
  765. private int endCheckBreak()
  766. {
  767. Node n = ((Jump) this).getJumpStatement();
  768. n.putIntProp(CONTROL_BLOCK_PROP, END_DROPS_OFF);
  769. return END_UNREACHED;
  770. }
  771. /**
  772. * endCheck() examines the body of a function, doing a basic reachability
  773. * analysis and returns a combination of flags END_* flags that indicate
  774. * how the function execution can terminate. These constitute only the
  775. * pessimistic set of termination conditions. It is possible that at
  776. * runtime certain code paths will never be actually taken. Hence this
  777. * analysis will flag errors in cases where there may not be errors.
  778. * @return logical OR of END_* flags
  779. */
  780. private int endCheck()
  781. {
  782. switch(type)
  783. {
  784. case Token.BREAK:
  785. return endCheckBreak();
  786. case Token.EXPR_VOID:
  787. if (this.first != null)
  788. return first.endCheck();
  789. return END_DROPS_OFF;
  790. case Token.YIELD:
  791. return END_YIELDS;
  792. case Token.CONTINUE:
  793. case Token.THROW:
  794. return END_UNREACHED;
  795. case Token.RETURN:
  796. if (this.first != null)
  797. return END_RETURNS_VALUE;
  798. else
  799. return END_RETURNS;
  800. case Token.TARGET:
  801. if (next != null)
  802. return next.endCheck();
  803. else
  804. return END_DROPS_OFF;
  805. case Token.LOOP:
  806. return endCheckLoop();
  807. case Token.LOCAL_BLOCK:
  808. case Token.BLOCK:
  809. // there are several special kinds of blocks
  810. if (first == null)
  811. return END_DROPS_OFF;
  812. switch(first.type) {
  813. case Token.LABEL:
  814. return first.endCheckLabel();
  815. case Token.IFNE:
  816. return first.endCheckIf();
  817. case Token.SWITCH:
  818. return first.endCheckSwitch();
  819. case Token.TRY:
  820. return first.endCheckTry();
  821. default:
  822. return endCheckBlock();
  823. }
  824. default:
  825. return END_DROPS_OFF;
  826. }
  827. }
  828. public boolean hasSideEffects()
  829. {
  830. switch (type) {
  831. case Token.EXPR_VOID:
  832. case Token.COMMA:
  833. if (last != null)
  834. return last.hasSideEffects();
  835. else
  836. return true;
  837. case Token.HOOK:
  838. if (first == null ||
  839. first.next == null ||
  840. first.next.next == null)
  841. Kit.codeBug();
  842. return first.next.hasSideEffects() &&
  843. first.next.next.hasSideEffects();
  844. case Token.AND:
  845. case Token.OR:
  846. if (first == null || last == null)
  847. Kit.codeBug();
  848. return first.hasSideEffects() || last.hasSideEffects();
  849. case Token.ERROR: // Avoid cascaded error messages
  850. case Token.EXPR_RESULT:
  851. case Token.ASSIGN:
  852. case Token.ASSIGN_ADD:
  853. case Token.ASSIGN_SUB:
  854. case Token.ASSIGN_MUL:
  855. case Token.ASSIGN_DIV:
  856. case Token.ASSIGN_MOD:
  857. case Token.ASSIGN_BITOR:
  858. case Token.ASSIGN_BITXOR:
  859. case Token.ASSIGN_BITAND:
  860. case Token.ASSIGN_LSH:
  861. case Token.ASSIGN_RSH:
  862. case Token.ASSIGN_URSH:
  863. case Token.ENTERWITH:
  864. case Token.LEAVEWITH:
  865. case Token.RETURN:
  866. case Token.GOTO:
  867. case Token.IFEQ:
  868. case Token.IFNE:
  869. case Token.NEW:
  870. case Token.DELPROP:
  871. case Token.SETNAME:
  872. case Token.SETPROP:
  873. case Token.SETELEM:
  874. case Token.CALL:
  875. case Token.THROW:
  876. case Token.RETHROW:
  877. case Token.SETVAR:
  878. case Token.CATCH_SCOPE:
  879. case Token.RETURN_RESULT:
  880. case Token.SET_REF:
  881. case Token.DEL_REF:
  882. case Token.REF_CALL:
  883. case Token.TRY:
  884. case Token.SEMI:
  885. case Token.INC:
  886. case Token.DEC:
  887. case Token.IF:
  888. case Token.ELSE:
  889. case Token.SWITCH:
  890. case Token.WHILE:
  891. case Token.DO:
  892. case Token.FOR:
  893. case Token.BREAK:
  894. case Token.CONTINUE:
  895. case Token.VAR:
  896. case Token.CONST:
  897. case Token.LET:
  898. case Token.LETEXPR:
  899. case Token.WITH:
  900. case Token.WITHEXPR:
  901. case Token.CATCH:
  902. case Token.FINALLY:
  903. case Token.BLOCK:
  904. case Token.LABEL:
  905. case Token.TARGET:
  906. case Token.LOOP:
  907. case Token.JSR:
  908. case Token.SETPROP_OP:
  909. case Token.SETELEM_OP:
  910. case Token.LOCAL_BLOCK:
  911. case Token.SET_REF_OP:
  912. case Token.YIELD:
  913. return true;
  914. default:
  915. return false;
  916. }
  917. }
  918. @Override
  919. public String toString()
  920. {
  921. if (Token.printTrees) {
  922. StringBuffer sb = new StringBuffer();
  923. toString(new ObjToIntMap(), sb);
  924. return sb.toString();
  925. }
  926. return String.valueOf(type);
  927. }
  928. private void toString(ObjToIntMap printIds, StringBuffer sb)
  929. {
  930. if (Token.printTrees) {
  931. sb.append(Token.name(type));
  932. if (this instanceof Name) {
  933. sb.append(' ');
  934. sb.append(getString());
  935. Scope scope = getScope();
  936. if (scope != null) {
  937. sb.append("[scope: ");
  938. appendPrintId(scope, printIds, sb);
  939. sb.append("]");
  940. }
  941. } else if (this instanceof Scope) {
  942. if (this instanceof ScriptNode) {
  943. ScriptNode sof = (ScriptNode)this;
  944. if (this instanceof FunctionNode) {
  945. FunctionNode fn = (FunctionNode)this;
  946. sb.append(' ');
  947. sb.append(fn.getName());
  948. }
  949. sb.append(" [source name: ");
  950. sb.append(sof.getSourceName());
  951. sb.append("] [encoded source length: ");
  952. sb.append(sof.getEncodedSourceEnd()
  953. - sof.getEncodedSourceStart());
  954. sb.append("] [base line: ");
  955. sb.append(sof.getBaseLineno());
  956. sb.append("] [end line: ");
  957. sb.append(sof.getEndLineno());
  958. sb.append(']');
  959. }
  960. if (((Scope)this).getSymbolTable() != null) {
  961. sb.append(" [scope ");
  962. appendPrintId(this, printIds, sb);
  963. sb.append(": ");
  964. Iterator<String> iter =
  965. ((Scope) this).getSymbolTable().keySet().iterator();
  966. while (iter.hasNext()) {
  967. sb.append(iter.next());
  968. sb.append(" ");
  969. }
  970. sb.append("]");
  971. }
  972. } else if (this instanceof Jump) {
  973. Jump jump = (Jump)this;
  974. if (type == Token.BREAK || type == Token.CONTINUE) {
  975. sb.append(" [label: ");
  976. appendPrintId(jump.getJumpStatement(), printIds, sb);
  977. sb.append(']');
  978. } else if (type == Token.TRY) {
  979. Node catchNode = jump.target;
  980. Node finallyTarget = jump.getFinally();
  981. if (catchNode != null) {
  982. sb.append(" [catch: ");
  983. appendPrintId(catchNode, printIds, sb);
  984. sb.append(']');
  985. }
  986. if (finallyTarget != null) {
  987. sb.append(" [finally: ");
  988. appendPrintId(finallyTarget, printIds, sb);
  989. sb.append(']');
  990. }
  991. } else if (type == Token.LABEL || type == Token.LOOP
  992. || type == Token.SWITCH)
  993. {
  994. sb.append(" [break: ");
  995. appendPrintId(jump.target, printIds, sb);
  996. sb.append(']');
  997. if (type == Token.LOOP) {
  998. sb.append(" [continue: ");
  999. appendPrintId(jump.getContinue(), printIds, sb);
  1000. sb.append(']');
  1001. }
  1002. } else {
  1003. sb.append(" [target: ");
  1004. appendPrintId(jump.target, printIds, sb);
  1005. sb.append(']');
  1006. }
  1007. } else if (type == Token.NUMBER) {
  1008. sb.append(' ');
  1009. sb.append(getDouble());
  1010. } else if (type == Token.TARGET) {
  1011. sb.append(' ');
  1012. appendPrintId(this, printIds, sb);
  1013. }
  1014. if (lineno != -1) {
  1015. sb.append(' ');
  1016. sb.append(lineno);
  1017. }
  1018. for (PropListItem x = propListHead; x != null; x = x.next) {
  1019. int type = x.type;
  1020. sb.append(" [");
  1021. sb.append(propToString(type));
  1022. sb.append(": ");
  1023. String value;
  1024. switch (type) {
  1025. case TARGETBLOCK_PROP : // can't add this as it recurses
  1026. value = "target block property";
  1027. break;
  1028. case LOCAL_BLOCK_PROP : // can't add this as it is dull
  1029. value = "last local block";
  1030. break;
  1031. case ISNUMBER_PROP:
  1032. switch (x.intValue) {
  1033. case BOTH:
  1034. value = "both";
  1035. break;
  1036. case RIGHT:
  1037. value = "right";
  1038. break;
  1039. case LEFT:
  1040. value = "left";
  1041. break;
  1042. default:
  1043. throw Kit.codeBug();
  1044. }
  1045. break;
  1046. case SPECIALCALL_PROP:
  1047. switch (x.intValue) {
  1048. case SPECIALCALL_EVAL:
  1049. value = "eval";
  1050. break;
  1051. case SPECIALCALL_WITH:
  1052. value = "with";
  1053. break;
  1054. default:
  1055. // NON_SPECIALCALL should not be stored
  1056. throw Kit.codeBug();
  1057. }
  1058. break;
  1059. case OBJECT_IDS_PROP: {
  1060. Object[] a = (Object[]) x.objectValue;
  1061. value = "[";
  1062. for (int i=0; i < a.length; i++) {
  1063. value += a[i].toString();
  1064. if (i+1 < a.length)
  1065. value += ", ";
  1066. }
  1067. value += "]";
  1068. break;
  1069. }
  1070. default :
  1071. Object obj = x.objectValue;
  1072. if (obj != null) {
  1073. value = obj.toString();
  1074. } else {
  1075. value = String.valueOf(x.intValue);
  1076. }
  1077. break;
  1078. }
  1079. sb.append(value);
  1080. sb.append(']');
  1081. }
  1082. }
  1083. }
  1084. public String toStringTree(ScriptNode treeTop) {
  1085. if (Token.printTrees) {
  1086. StringBuffer sb = new StringBuffer();
  1087. toStringTreeHelper(treeTop, this, null, 0, sb);
  1088. return sb.toString();
  1089. }
  1090. return null;
  1091. }
  1092. private static void toStringTreeHelper(ScriptNode treeTop, Node n,
  1093. ObjToIntMap printIds,
  1094. int level, StringBuffer sb)
  1095. {
  1096. if (Token.printTrees) {
  1097. if (printIds == null) {
  1098. printIds = new ObjToIntMap();
  1099. generatePrintIds(treeTop, printIds);
  1100. }
  1101. for (int i = 0; i != level; ++i) {
  1102. sb.append(" ");
  1103. }
  1104. n.toString(printIds, sb);
  1105. sb.append('\n');
  1106. for (Node cursor = n.getFirstChild(); cursor != null;
  1107. cursor = cursor.getNext())
  1108. {
  1109. if (cursor.getType() == Token.FUNCTION) {
  1110. int fnIndex = cursor.getExistingIntProp(Node.FUNCTION_PROP);
  1111. FunctionNode fn = treeTop.getFunctionNode(fnIndex);
  1112. toStringTreeHelper(fn, fn, null, level + 1, sb);
  1113. } else {
  1114. toStringTreeHelper(treeTop, cursor, printIds, level+1, sb);
  1115. }
  1116. }
  1117. }
  1118. }
  1119. private static void generatePrintIds(Node n, ObjToIntMap map)
  1120. {
  1121. if (Token.printTrees) {
  1122. map.put(n, map.size());
  1123. for (Node cursor = n.getFirstChild(); cursor != null;
  1124. cursor = cursor.getNext())
  1125. {
  1126. generatePrintIds(cursor, map);
  1127. }
  1128. }
  1129. }
  1130. private static void appendPrintId(Node n, ObjToIntMap printIds,
  1131. StringBuffer sb)
  1132. {
  1133. if (Token.printTrees) {
  1134. if (n != null) {
  1135. int id = printIds.get(n, -1);
  1136. sb.append('#');
  1137. if (id != -1) {
  1138. sb.append(id + 1);
  1139. } else {
  1140. sb.append("<not_available>");
  1141. }
  1142. }
  1143. }
  1144. }
  1145. protected int type = Token.ERROR; // type of the node, e.g. Token.NAME
  1146. protected Node next; // next sibling
  1147. protected Node first; // first element of a linked list of children
  1148. protected Node last; // last element of a linked list of children
  1149. protected int lineno = -1;
  1150. /**
  1151. * Linked list of properties. Since vast majority of nodes would have
  1152. * no more then 2 properties, linked list saves memory and provides
  1153. * fast lookup. If this does not holds, propListHead can be replaced
  1154. * by UintMap.
  1155. */
  1156. protected PropListItem propListHead;
  1157. }