PageRenderTime 74ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/cfa2/jscfa.js

https://github.com/smh/doctorjs
JavaScript | 4374 lines | 3399 code | 376 blank | 599 comment | 495 complexity | 3494bb25257993d63f58adbd0e432c64 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception

Large files files are truncated, but you can click here to view the full file

  1. /* ***** BEGIN LICENSE BLOCK *****
  2. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. *
  4. * The contents of this file are subject to the Mozilla Public License Version
  5. * 1.1 (the "License"); you may not use this file except in compliance with
  6. * the License. You may obtain a copy of the License at
  7. * http://www.mozilla.org/MPL/
  8. *
  9. * Software distributed under the License is distributed on an 'AS IS' basis,
  10. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. * for the specific language governing rights and limitations under the
  12. * License.
  13. *
  14. * The Original Code is Bespin.
  15. *
  16. * The Initial Developer of the Original Code is
  17. * Dimitris Vardoulakis <dimvar@gmail.com>
  18. * Portions created by the Initial Developer are Copyright (C) 2010
  19. * the Initial Developer. All Rights Reserved.
  20. *
  21. * Contributor(s):
  22. * Dimitris Vardoulakis <dimvar@gmail.com>
  23. * Patrick Walton <pcwalton@mozilla.com>
  24. *
  25. * Alternatively, the contents of this file may be used under the terms of
  26. * either the GNU General Public License Version 2 or later (the "GPL"), or
  27. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28. * in which case the provisions of the GPL or the LGPL are applicable instead
  29. * of those above. If you wish to allow use of your version of this file only
  30. * under the terms of either the GPL or the LGPL, and not to allow others to
  31. * use your version of this file under the terms of the MPL, indicate your
  32. * decision by deleting the provisions above and replace them with the notice
  33. * and other provisions required by the GPL or the LGPL. If you do not delete
  34. * the provisions above, a recipient may use your version of this file under
  35. * the terms of any one of the MPL, the GPL or the LGPL.
  36. *
  37. * ***** END LICENSE BLOCK ***** */
  38. /*
  39. * Narcissus - JS implemented in JS.
  40. *
  41. * Control-flow analysis to infer types. The output is in ctags format.
  42. */
  43. /* (Possible) TODOs:
  44. * - now all objs are in heap. If it's too imprecise, treat them as heap vars.
  45. * Create on stack & heap, and if heap changes when u need the obj then join.
  46. * - representation of Aobj: in the common case, an abstract obj has one proto
  47. * and one constructor. Specialize for this case.
  48. */
  49. /*
  50. * Semantics of: function foo (args) body:
  51. * It's not the same as: var foo = function foo (args) body
  52. * If it appears in a script then it's hoisted at the top, so it's in funDecls
  53. * If it appears in a block then it's visible after it's appearance, in the
  54. * whole rest of the script!!
  55. * {foo(); {function foo() {print("foo");}}; foo();}
  56. * The 1st call to foo throws, but if you remove it the 2nd call succeeds.
  57. */
  58. /* (POSSIBLY) UNSOUND ASSUMPTIONS:
  59. * - Won't iterate loops to fixpt.
  60. * - Return undefined not tracked, eg (if sth return 12;) always returns number.
  61. * - If the prototype property of a function object foo is accessed in a weird
  62. * way, eg foo["proto" + "type"] the analysis won't figure it out.
  63. * - when popping from an array, I do nothing. This is hard to make sound.
  64. */
  65. ////////////////////////////////////////////////////////////////////////////////
  66. ///////////////////////////// UTILITIES /////////////////////////////////////
  67. ////////////////////////////////////////////////////////////////////////////////
  68. if (!Array.prototype.forEach)
  69. Array.prototype.forEach = function(fun) {
  70. for (var i = 0, len = this.length; i < len; i++)
  71. /* if (i in this) */ fun(this[i], i, this);
  72. };
  73. // search for an elm in the array that satisfies pred
  74. Array.prototype.member = function(pred) {
  75. for (var i = 0, len = this.length; i < len; i++)
  76. /* if (i in this) */ if (pred(this[i])) return this[i];
  77. return false;
  78. };
  79. Array.prototype.memq = function(sth) {
  80. for (var i = 0, len = this.length; i < len; i++)
  81. /* if (i in this) */ if (sth === this[i]) return this[i];
  82. return false;
  83. };
  84. // starting at index, remove all elms that satisfy the pred in linear time.
  85. Array.prototype.rmElmAfterIndex = function(pred, index) {
  86. if (index >= this.length) return;
  87. for (var i = index, j = index, len = this.length; i < len; i++)
  88. if (!pred(this[i])) this[j++] = this[i];
  89. this.length = j;
  90. };
  91. // remove all duplicates from array (keep first occurence of each elm)
  92. // pred determines the equality of elms
  93. Array.prototype.rmDups = function(pred) {
  94. var i = 0, self = this;
  95. while (i < (this.length - 1)) {
  96. this.rmElmAfterIndex(function(elm) { return pred(elm, self[i]); }, i+1);
  97. i++;
  98. }
  99. };
  100. // compare two arrays for structural equality
  101. function arrayeq(eq, a1, a2) {
  102. var len = a1.length, i;
  103. if (len !== a2.length) return false;
  104. for (i=0; i<len; i++) if (!eq(a1[i], a2[i])) return false;
  105. return true;
  106. }
  107. function buildArray(size, elm) {
  108. var a = new Array(size);
  109. for (var i=0; i<size; i++) a[i] = elm;
  110. return a;
  111. }
  112. function buildString(size, elm) {
  113. return buildArray(size, elm).join("");
  114. }
  115. // merge two sorted arrays of numbers into a sorted new array, remove dups!
  116. function arraymerge(a1, a2) {
  117. var i=0, j=0, len1 = a1.length, len2 = a2.length, a = new Array();
  118. while (true) {
  119. if (i === len1) {
  120. for (; j < len2; j++) a.push(a2[j]);
  121. return a;
  122. }
  123. if (j === len2) {
  124. for (; i<len1; i++) a.push(a1[i]);
  125. return a;
  126. }
  127. var diff = a1[i] - a2[j];
  128. if (diff < 0)
  129. a.push(a1[i++]);
  130. else if (diff > 0)
  131. a.push(a2[j++]);
  132. else
  133. i++;
  134. }
  135. }
  136. const UNHANDLED_CONSTRUCT = 0;
  137. const CFA_ERROR = 1;
  138. // number, string -> Error
  139. // returns an Error w/ a "code" property, so that DrJS can classify errors
  140. function errorWithCode(code, msg) {
  141. var e = new Error(msg);
  142. e.code = code;
  143. return e;
  144. }
  145. ////////////////////////////////////////////////////////////////////////////////
  146. //////////////////// PREPARE AST FOR FLOW ANALYSIS ///////////////////////
  147. ////////////////////////////////////////////////////////////////////////////////
  148. var jsparse = require('../../narcissus/lib/parser');
  149. var Node = jsparse.Node;
  150. const DECLARED_FORM = jsparse.DECLARED_FORM;
  151. const STATEMENT_FORM = jsparse.STATEMENT_FORM;
  152. eval(require('../../narcissus/lib/definitions').consts);
  153. var print = console.log;
  154. var fs = require('fs');
  155. function printf(fd, s) { fs.writeSync(fd, s, null, encoding='utf8'); }
  156. // count is used to generate a unique ID for each node in the AST.
  157. var count;
  158. function newCount() { return ++count; }
  159. // Use token_count to get fresh IDs for new non-terminals
  160. var token_count = (require('../../narcissus/lib/definitions').tokens).length;
  161. const DOT_PROTO = token_count++;
  162. const ARGUMENTS = token_count++;
  163. const PLUS_ASSIGN = token_count++;
  164. // Instead of a flat long case dispatch, arities create a tree-like dispatch.
  165. // Nodes are grouped in arities by how we recur down their structure.
  166. const NULLARY = [NULL, THIS, TRUE, FALSE, IDENTIFIER, NUMBER, STRING, REGEXP];
  167. const UNARY = [DELETE, VOID, TYPEOF, NOT, BITWISE_NOT, UNARY_PLUS, UNARY_MINUS,
  168. NEW, INCREMENT, DECREMENT, DOT_PROTO, ARGUMENTS];
  169. const BINARY = [BITWISE_OR, BITWISE_XOR, BITWISE_AND, EQ, NE, STRICT_EQ,
  170. STRICT_NE, LT, LE, GE, GT, INSTANCEOF, LSH, RSH, URSH, PLUS,
  171. MINUS, MUL, DIV, MOD, AND, OR, ASSIGN, INDEX, IN, DOT,
  172. PLUS_ASSIGN];
  173. const N_ARY = [COMMA, ARRAY_INIT];
  174. // expr node -> stm node
  175. function semiNode(n) {
  176. var sn = new Node(n.tokenizer, {type:SEMICOLON});
  177. sn.expression = n;
  178. return sn;
  179. }
  180. // tokenizer, string -> identifier node
  181. function idNode(t, name) {
  182. var n = new Node(t, {type:IDENTIFIER});
  183. n.name = n.value = name;
  184. return n;
  185. }
  186. var astSize; // calculated for statistics
  187. // node, optional string -> node
  188. // Does some cleanup on the input expression node in-place.
  189. // funName is used to name some anonymous funs using heuristics from the context
  190. function fixExp(n, funName) {
  191. var nt = n.type, ch = n.children;
  192. astSize++;
  193. function fixe(n, i, ch) { ch[i] = fixExp(n); }
  194. if (NULLARY.memq(nt)) {
  195. if (nt === IDENTIFIER) n.name = n.value;
  196. else if (nt === STRING) n.value += "-";
  197. }
  198. else if (UNARY.memq(nt)) {
  199. if (nt === UNARY_MINUS && ch[0].type === NUMBER) {
  200. n.type = NUMBER;
  201. n.value = -ch[0].value;
  202. n.children = [];
  203. return n;
  204. }
  205. if (nt === NEW) { // unify NEW and NEW_WITH_ARGS
  206. n.type = NEW_WITH_ARGS;
  207. ch.push(new Node(n.tokenizer, {type:LIST, children:[]}));
  208. }
  209. ch[0] = fixExp(ch[0]);
  210. }
  211. else if (BINARY.memq(nt)) {
  212. if (nt === INDEX) {
  213. ch.forEach(fixe);
  214. if (ch[1].type === NUMBER) {
  215. ch[1].type = STRING;
  216. ch[1].value += "-";
  217. }
  218. return n;
  219. }
  220. else if (nt === DOT) {
  221. var ch1 = ch[1];
  222. // jsparse makes the 1st child of DOTs an ID b/c that's what is parsed.
  223. // For an evaluator it makes more sense to make the 1st child a string,
  224. // b/c functions that recur on DOTs won't try to look it up as a name.
  225. ch1.type = STRING;
  226. delete ch1.name;
  227. // DOT_PROTO has no new semantic meaning, it's an optimization so that we
  228. // dont check at every property access if the property name is "prototype"
  229. if (ch1.value === "prototype") n.type = DOT_PROTO;
  230. }
  231. else if (nt === ASSIGN && ch[0].assignOp === PLUS)
  232. n.type = PLUS_ASSIGN;
  233. else if (nt === ASSIGN && !ch[0].assignOp) {
  234. var n0 = ch[0];
  235. ch[0] = fixExp(n0);
  236. if (n0.type === IDENTIFIER)
  237. funName = n0.name;
  238. else if (n0.type === DOT) {
  239. var fname = n0.children[1].value;
  240. funName = fname.substring(0, fname.length - 1);
  241. }
  242. ch[1] = fixExp(ch[1], funName);
  243. return n;
  244. }
  245. ch.forEach(fixe);
  246. }
  247. else if (nt === HOOK || N_ARY.memq(nt)) {
  248. // Uncomment this if Narcissus produces empty COMMA nodes.
  249. if (nt === COMMA && ch.length === 0)
  250. ch.push(new Node(n.tokenizer, {type:TRUE}));
  251. if (nt === ARRAY_INIT) {
  252. ch.forEach(function(kid, i, ch) {
  253. if (kid === null) ch[i] = idNode(n.tokenizer, "undefined");
  254. });
  255. }
  256. ch.forEach(fixe);
  257. }
  258. else if (nt === CALL || nt === NEW_WITH_ARGS) {
  259. ch[0] = fixExp(ch[0]);
  260. ch[1].children.forEach(fixe);
  261. }
  262. else if (nt === FUNCTION) {
  263. fixFun(n, funName);
  264. }
  265. else if (nt === OBJECT_INIT) {
  266. ch.forEach(function(prop) {
  267. if (prop.type === GETTER || prop.type === SETTER) {
  268. // FIXME: for now, just don't crash on getters/setters
  269. prop.children = [new Node(n.tokenizer,
  270. { type : STRING, value : prop.name + "-" }),
  271. new Node(n.tokenizer, {type:TRUE})];
  272. }
  273. else {
  274. var pch = prop.children, pch0 = pch[0], pname = pch0.value;
  275. pch0.type = STRING;
  276. delete pch0.name;
  277. pch0.value += "-";
  278. pch[1] = fixExp(pch[1], pname);
  279. }
  280. });
  281. }
  282. else if (nt === LET_BLOCK) {
  283. n.varDecls.forEach(function(vd, i, vds) {
  284. vd.name = vd.value;
  285. vd.initializer && (vd.initializer = fixExp(vd.initializer, vd.value));
  286. });
  287. n.expression = fixExp(n.expression);
  288. }
  289. else if (nt === YIELD) { // FIXME: for now, just don't crash on yield
  290. n.type = TRUE;
  291. delete n.value
  292. }
  293. return n;
  294. }
  295. // node, optional string -> void
  296. function fixFun(n, funName) {
  297. var t = n.tokenizer;
  298. // replace name w/ a property fname which is an IDENTIFIER node.
  299. n.fname = idNode(t, n.name || funName);
  300. delete n.name;
  301. // turn the formals to nodes, I'll want to attach stuff to them later.
  302. n.params.forEach(function(p, i, ps) { ps[i] = idNode(t, p); });
  303. // don't tag builtin funs, they never have RETURN when used as constructors.
  304. n.hasReturn = fixStm(n.body);
  305. }
  306. // Array of id nodes, tokenizer -> comma node
  307. // Convert variable initializations (coming from VAR,CONST,LET) to assignments.
  308. function initsToAssigns(inits, t) {
  309. var n, vdecl, a, i, len, ch;
  310. n = new Node(t, {type:COMMA});
  311. ch = n.children;
  312. for (i = 0, len = inits.length; i < len; i++) {
  313. vdecl = inits[i];
  314. if (vdecl.initializer) {
  315. a = new Node(vdecl.tokenizer, {type:ASSIGN});
  316. a.children = [idNode(vdecl.tokenizer, vdecl.name), vdecl.initializer];
  317. ch.push(a);
  318. }
  319. }
  320. // if no initialization values, push dummy node to avoid empty comma node.
  321. ch.length || ch.push(new Node(t, {type:TRUE}));
  322. return n;
  323. }
  324. // node, optional script node -> boolean
  325. // returns true iff n contains RETURN directly (not RETURNs of inner functions).
  326. function fixStm(n, parentScript) {
  327. var i, j, n2, ans1, ans2, ch = n.children;
  328. astSize++;
  329. switch (n.type) {
  330. case SCRIPT:
  331. case BLOCK:
  332. var ans1 = false, parscr = (n.type === SCRIPT) ? n : parentScript;
  333. i=0;
  334. while (i < ch.length) {
  335. n2 = ch[i];
  336. switch (n2.type) {
  337. case VAR:
  338. case CONST:
  339. ch.splice(i++, 1,
  340. semiNode(fixExp(initsToAssigns(n2.children, n2.tokenizer))));
  341. break;
  342. case SWITCH:
  343. if (n2.cases.length === 0) {// switch w/out branches becomes semi node
  344. n2.discriminant = fixExp(n2.discriminant);
  345. ch[i] = semiNode(n2.discriminant);
  346. }
  347. else
  348. ans1 = fixStm(n2, parscr) || ans1;
  349. i++;
  350. break;
  351. case FUNCTION: //rm functions from Script bodies, they're in funDecls
  352. fixFun(n2);
  353. // To handle funs in blocks, unsoundly add them to funDecls
  354. if (n2.functionForm === STATEMENT_FORM) parscr.funDecls.push(n2);
  355. ch.splice(i, 1);
  356. // The code below is for when we don't handle funs in blocks.
  357. // if (n2.functionForm === DECLARED_FORM)
  358. // ch.splice(i, 1);
  359. // else
  360. // ++i;
  361. break;
  362. case SEMICOLON: // remove empty SEMICOLON nodes
  363. if (n2.expression == null) {
  364. ch.splice(i, 1);
  365. break;
  366. } // o/w fall thru to fix the node
  367. default:
  368. ans1 = fixStm(n2, parscr) || ans1;
  369. i++;
  370. break;
  371. }
  372. }
  373. return ans1;
  374. case LET: // let definitions
  375. n.children.forEach(function(vd) {
  376. vd.name = vd.value;
  377. vd.initializer && (vd.initializer = fixExp(vd.initializer));
  378. });
  379. return false;
  380. case LET_BLOCK:
  381. n.varDecls.forEach(function(vd) {
  382. vd.name = vd.value;
  383. vd.initializer && (vd.initializer = fixExp(vd.initializer));
  384. });
  385. if (n.expression) { // let-exp in stm context
  386. n.expression = fixExp(n.expression);
  387. n.block = semiNode(n.expression);
  388. delete n.expression;
  389. return false;
  390. }
  391. else // let-stm
  392. return fixStm(n.block, parentScript);
  393. case BREAK: case CONTINUE: case DEBUGGER:
  394. n.type = BLOCK;
  395. n.varDecls = [];
  396. n.children = [];
  397. return false;
  398. case SEMICOLON:
  399. if (!n.expression)
  400. n.expression = new Node(n.tokenizer, {type:TRUE});
  401. else
  402. n.expression = fixExp(n.expression); //n.expression can't be null
  403. return false;
  404. case IF:
  405. n.condition = fixExp(n.condition);
  406. ans1 = fixStm(n.thenPart, parentScript);
  407. return (n.elsePart && fixStm(n.elsePart, parentScript)) || ans1;
  408. case SWITCH:
  409. n.discriminant = fixExp(n.discriminant);
  410. ans1 = false;
  411. n.cases.forEach( function(branch) {
  412. branch.caseLabel && (branch.caseLabel = fixExp(branch.caseLabel));
  413. ans2 = fixStm(branch.statements, parentScript);
  414. ans1 = ans1 || ans2;
  415. });
  416. return ans1;
  417. case FOR:
  418. n2 = n.setup;
  419. if (n2) {
  420. if (n2.type === VAR || n2.type === CONST)
  421. n.setup = fixExp(initsToAssigns(n2.children, n2.tokenizer));
  422. else
  423. n.setup = fixExp(n2);
  424. }
  425. n.condition && (n.condition = fixExp(n.condition));
  426. n.update && (n.update = fixExp(n.update));
  427. return fixStm(n.body, parentScript);
  428. case FOR_IN:
  429. n.iterator = fixExp(n.iterator);
  430. n.object = fixExp(n.object);
  431. if (n.body.type !== BLOCK) {
  432. var fibody = new Node(n.tokenizer, {type:BLOCK});
  433. fibody.children = [n.body];
  434. n.body = fibody;
  435. }
  436. return fixStm(n.body, parentScript);
  437. case WHILE:
  438. case DO:
  439. n.condition = fixExp(n.condition);
  440. return fixStm(n.body, parentScript);
  441. case TRY: // turn the varName of each catch-clause to a node called exvar
  442. ans1 = fixStm(n.tryBlock, parentScript);
  443. n.catchClauses.forEach(function(clause) {
  444. clause.exvar = idNode(clause.tokenizer, clause.varName);
  445. clause.guard && (clause.guard = fixExp(clause.guard));
  446. ans2 = fixStm(clause.block, parentScript);
  447. ans1 = ans1 || ans2;
  448. });
  449. return (n.finallyBlock && fixStm(n.finallyBlock, parentScript)) || ans1;
  450. case THROW:
  451. n.exception = fixExp(n.exception);
  452. return false;
  453. case RETURN:
  454. if (n.value === undefined)
  455. n.value = idNode(n.tokenizer, "undefined");
  456. else
  457. n.value = fixExp(n.value);
  458. return true;
  459. case VAR: case CONST: // very rare to not appear in a block or script.
  460. n.type = SEMICOLON;
  461. n.expression = fixExp(initsToAssigns(n.children, n.tokenizer));
  462. ch = [];
  463. return false;
  464. case FUNCTION:
  465. n2 = new Node(n.tokenizer, {type:FUNCTION});
  466. n2.name = n.name; delete n.name;
  467. n2.params = n.params; delete n.params;
  468. n2.functionForm = n.functionForm; delete n.functionForm;
  469. n2.body = n.body; delete n.body;
  470. fixFun(n2);
  471. // To handle funs not in scripts, unsoundly add them to funDecls
  472. parentScript.funDecls.push(n2);
  473. n.type = SEMICOLON;
  474. n.expression = new Node(n.tokenizer, {type:TRUE});
  475. return false;
  476. case WITH:
  477. n.object = fixExp(n.object);
  478. return fixStm(n.body, parentScript);
  479. case LABEL: //replace LABEL nodes by their statement (forget labels)
  480. n.type = BLOCK;
  481. n.varDecls = [];
  482. n.children = [n.statement];
  483. delete n.statement;
  484. return fixStm(n.children[0], parentScript);
  485. default:
  486. throw errorWithCode(UNHANDLED_CONSTRUCT,
  487. "fixStm: " + n.type + ", line " + n.lineno);
  488. }
  489. }
  490. // Invariants of the AST after fixStm:
  491. // - no NEW nodes, they became NEW_WITH_ARGS
  492. // - the formals of functions are nodes, not strings
  493. // - functions have a property fname which is an IDENTIFIER node, name deleted
  494. // - no VAR and CONST nodes, they've become semicolon comma nodes.
  495. // - no BREAK and CONTINUE nodes.
  496. // Unfortunately, this isn't independent of exceptions.
  497. // If a finally-block breaks or continues, the exception isn't propagated.
  498. // I will falsely propagate it (still sound, just more approximate).
  499. // - no LABEL nodes
  500. // - function nodes only in blocks, not in scripts
  501. // - no empty SEMICOLON nodes
  502. // - no switches w/out branches
  503. // - each catch clause has a property exvar which is an IDENTIFIER node
  504. // - all returns have .value (the ones that didn't, got an undefined)
  505. // - the lhs of a property initializer of an OBJECT_INIT is always a string
  506. // - the property names in DOT and OBJECT_INIT end with a dash.
  507. // - there is no DOT whose 2nd arg is "prototype", they've become NODE_PROTOs.
  508. // - the second kid of DOT is always a STRING, not an IDENTIFIER.
  509. // - value of a NUMBER can be negative (UNARY_MINUS of constant became NUMBER).
  510. // - the operator += has its own non-terminal, PLUS_ASSIGN.
  511. // - each function node has a property hasReturn to show if it uses RETURN.
  512. // - there is no INDEX whose 2nd arg has type NUMBER, they've become STRINGs.
  513. // - The body of a LET_BLOCK in statement context is always a statement.
  514. // - FUNCTIONs in BLOCKs are added to funDecls of enclosing SCRIPT.
  515. // - Array literals don't have holes (null children) in them.
  516. // - The body of FOR_IN is always a BLOCK.
  517. function walkASTgenerator() {
  518. var jt = new Array(token_count); // jump table
  519. var o = {}; // dummy object for the calls to apply
  520. function recur(n) { return jt[n.type].apply(o, arguments); }
  521. function override(tt, fun) { jt[tt] = fun; }
  522. function _nokids() {}
  523. NULLARY.forEach(function(tt) { jt[tt] = _nokids; });
  524. function _haskids() {
  525. var args = arguments, ch = args[0].children;
  526. ch.forEach(function(kid) {
  527. args[0] = kid;
  528. recur.apply(o, args);
  529. });
  530. }
  531. [HOOK].concat(N_ARY, UNARY, BINARY).forEach(function(tt) {jt[tt]=_haskids;});
  532. jt[CALL] = jt[NEW_WITH_ARGS] = function() {
  533. var args = arguments, n = args[0], ch = n.children;
  534. args[0] = ch[0];
  535. recur.apply(o, args);
  536. ch[1].children.forEach(function(kid) {
  537. args[0] = kid;
  538. recur.apply(o, args);
  539. });
  540. };
  541. jt[OBJECT_INIT] = function() {
  542. var args = arguments;
  543. args[0].children.forEach(function(kid) {
  544. var ch = kid.children;
  545. args[0] = ch[0];
  546. recur.apply(o, args);
  547. args[0] = ch[1];
  548. recur.apply(o, args);
  549. });
  550. };
  551. jt[FUNCTION] = function() {
  552. arguments[0] = arguments[0].body;
  553. recur.apply(o, arguments);
  554. };
  555. jt[SCRIPT] = function() {
  556. var args = arguments, n = args[0];
  557. n.funDecls.forEach(function(fd) {
  558. args[0] = fd;
  559. recur.apply(o, args);
  560. });
  561. n.children.forEach(function(kid) {
  562. args[0] = kid;
  563. recur.apply(o, args);
  564. });
  565. };
  566. jt[BLOCK] = function() {
  567. var args = arguments;
  568. args[0].children.forEach(function(kid) {
  569. args[0] = kid;
  570. recur.apply(o, args);
  571. });
  572. };
  573. jt[SEMICOLON] = function() {
  574. arguments[0] = arguments[0].expression;
  575. recur.apply(o, arguments);
  576. };
  577. jt[IF] = function() {
  578. var n = arguments[0];
  579. arguments[0] = n.condition;
  580. recur.apply(o, arguments);
  581. arguments[0] = n.thenPart;
  582. recur.apply(o, arguments);
  583. if (n.elsePart) {
  584. arguments[0] = n.elsePart;
  585. recur.apply(o, arguments);
  586. }
  587. };
  588. jt[SWITCH] = function() {
  589. var args = arguments, n = args[0];
  590. args[0] = n.discriminant;
  591. recur.apply(o, args);
  592. n.cases.forEach(function(branch) {
  593. if (branch.caseLabel) {
  594. args[0] = branch.caseLabel;
  595. recur.apply(o, args);
  596. }
  597. args[0] = branch.statements;
  598. recur.apply(o, args);
  599. });
  600. };
  601. jt[FOR] = function() {
  602. var n = arguments[0];
  603. if (n.setup) {
  604. arguments[0] = n.setup;
  605. recur.apply(o, arguments);
  606. }
  607. if (n.condition) {
  608. arguments[0] = n.condition;
  609. recur.apply(o, arguments);
  610. }
  611. if (n.update) {
  612. arguments[0] = n.update;
  613. recur.apply(o, arguments);
  614. }
  615. arguments[0] = n.body;
  616. recur.apply(o, arguments);
  617. };
  618. jt[FOR_IN] = function() {
  619. var n = arguments[0];
  620. arguments[0] = n.iterator;
  621. recur.apply(o, arguments);
  622. arguments[0] = n.object;
  623. recur.apply(o, arguments);
  624. arguments[0] = n.body;
  625. recur.apply(o, arguments);
  626. };
  627. jt[WHILE] = jt[DO] = function() {
  628. var n = arguments[0];
  629. arguments[0] = n.condition;
  630. recur.apply(o, arguments);
  631. arguments[0] = n.body;
  632. recur.apply(o, arguments);
  633. };
  634. jt[TRY] = function() {
  635. var args = arguments, n = args[0];
  636. args[0] = n.tryBlock;
  637. recur.apply(o, args);
  638. n.catchClauses.forEach(function(clause) {
  639. if (clause.guard) {
  640. args[0] = clause.guard;
  641. recur.apply(o, args);
  642. }
  643. args[0] = clause.block;
  644. recur.apply(o, args);
  645. });
  646. if (n.finallyBlock) {
  647. args[0] = n.finallyBlock;
  648. recur.apply(o, args);
  649. }
  650. };
  651. jt[THROW] = function() {
  652. arguments[0] = arguments[0].exception;
  653. recur.apply(o, arguments);
  654. };
  655. jt[RETURN] = function() {
  656. var n = arguments[0];
  657. if (n.value) {
  658. arguments[0] = n.value;
  659. recur.apply(o, arguments);
  660. }
  661. };
  662. jt[WITH] = function() {
  663. var n = arguments[0];
  664. arguments[0] = n.object;
  665. recur.apply(o, arguments);
  666. arguments[0] = n.body;
  667. recur.apply(o, arguments);
  668. };
  669. jt[LET_BLOCK] = function() {
  670. var args = arguments, n = args[0];
  671. n.varDecls.forEach(function(vd) {
  672. (args[0] = vd.initializer) && recur.apply(o, args);
  673. });
  674. (args[0] = n.expression) || (args[0] = n.block);
  675. recur.apply(o, args);
  676. };
  677. jt[LET] = function() {
  678. var args = arguments;
  679. args[0].children.forEach(function(vd) {
  680. (args[0] = vd.initializer) && recur.apply(o, args);
  681. });
  682. };
  683. return { walkAST: recur, override: override};
  684. }
  685. // node -> void
  686. // Adds an "addr" property to nodes, which is a number unique for each node.
  687. var labelAST, labelAST_override;
  688. (function() {
  689. var walkerobj = walkASTgenerator(), override = walkerobj.override;
  690. labelAST = walkerobj.walkAST;
  691. labelAST_override = override;
  692. override(ARRAY_INIT, function(n) {
  693. n.addr = newCount();
  694. n.children.forEach(labelAST);
  695. });
  696. override(NEW_WITH_ARGS, function(n) {
  697. n.addr = newCount();
  698. labelAST(n.children[0]);
  699. n.children[1].children.forEach(labelAST);
  700. });
  701. override(REGEXP, function(n) { n.addr = newCount(); });
  702. override(OBJECT_INIT, function(n) {
  703. n.addr = newCount();
  704. n.children.forEach(function(prop) {
  705. labelAST(prop.children[0]);
  706. labelAST(prop.children[1]);
  707. });
  708. });
  709. override(TRY, function(n) {
  710. labelAST(n.tryBlock);
  711. n.catchClauses.forEach(function(c) {
  712. c.exvar.addr = newCount();
  713. c.guard && labelAST(c.guard);
  714. labelAST(c.block);
  715. });
  716. n.finallyBlock && labelAST(n.finallyBlock);
  717. });
  718. override(SCRIPT, function(n) {
  719. n.varDecls.forEach(function(vd) {vd.addr = newCount();});
  720. n.funDecls.forEach(labelAST);
  721. n.children.forEach(labelAST);
  722. });
  723. override(FUNCTION, function _function(n) {
  724. n.addr = newCount();
  725. n.defaultProtoAddr = newCount();
  726. n.fname.addr = newCount();
  727. n.params.forEach(function(p) { p.addr = newCount(); });
  728. labelAST(n.body);
  729. });
  730. })();
  731. // Node, number, Array of id nodes -> void
  732. // WITH node, address of the fresh variable, bound variables
  733. // After desugarWith is executed, there are no WITHs in the AST.
  734. var desugarWith;
  735. // FIXME: the desugaring handles nested WITHs incorrectly.
  736. (function () {
  737. var walkerobj = walkASTgenerator(), override = walkerobj.override;
  738. desugarWith = walkerobj.walkAST;
  739. function withIdNode(t, addr) {
  740. var n = idNode(t, "internalVar: with");
  741. n.addr = addr;
  742. return n;
  743. }
  744. override(SCRIPT, function(n, withAddr, boundvars) {
  745. n.funDecls.forEach(function(fd) { desugarWith(fd, withAddr, boundvars); });
  746. var blen = boundvars.length;
  747. Array.prototype.push.apply(boundvars, n.varDecls);
  748. n.children.forEach(function(stm) {
  749. desugarWith(stm, withAddr, boundvars, n.varDecls);
  750. });
  751. boundvars.splice(blen, boundvars.length);
  752. });
  753. override(FUNCTION, function(n, withAddr, boundvars) {
  754. var blen = boundvars.length;
  755. Array.prototype.push.apply(boundvars, n.params);
  756. desugarWith(n.body, withAddr, boundvars);
  757. boundvars.splice(blen, boundvars.length);
  758. });
  759. // Turn to a block with two statements.
  760. // The 4th argument is the varDecls of the innermost enclosing SCRIPT, so that
  761. // the fresh variable of the WITH can be added there.
  762. override(WITH, function(n, withAddr, boundvars, vardecls) {
  763. var newaddr = newCount(), t = n.tokenizer;
  764. var freshvar = withIdNode(t, newaddr), assgn;
  765. vardecls.push(freshvar);
  766. assgn = new Node(t, {type:ASSIGN});
  767. assgn.children = [freshvar, n.object];
  768. desugarWith(n.body, newaddr, [], vardecls);
  769. n.type = BLOCK;
  770. n.varDecls = [];
  771. n.children = [semiNode(assgn), n.body];
  772. delete n.object;
  773. delete n.body;
  774. });
  775. // Add the CATCH variable to the bound variables
  776. override(TRY, function(n, withAddr, boundvars, vardecls) {
  777. desugarWith(n.tryBlock, withAddr, boundvars, vardecls);
  778. n.catchClauses.forEach(function(clause) {
  779. boundvars.push(clause.exvar);
  780. clause.guard && desugarWith(clause.guard, withAddr, boundvars);
  781. desugarWith(clause.block, withAddr, boundvars, vardecls);
  782. boundvars.pop();
  783. });
  784. n.finallyBlock && desugarWith(n.finallyBlock,withAddr,boundvars,vardecls);
  785. });
  786. // May change n to an IF node
  787. override(FOR_IN, function(n, withAddr, boundvars, vardecls) {
  788. var it = n.iterator, it, obj = n.object, b = n.body;
  789. if (it.type === IDENTIFIER)
  790. // (Temporary) Copy to avoid sharing introduced by desugaring.
  791. n.iterator = it = idNode(it.tokenizer, it.value);
  792. desugarWith(obj, withAddr, boundvars);
  793. desugarWith(b, withAddr, boundvars, vardecls);
  794. if (it.type !== IDENTIFIER) {
  795. desugarWith(it, withAddr, boundvars);
  796. return;
  797. }
  798. if (_makeHook(n, it, withAddr, boundvars)) {
  799. var t = n.tokenizer, ch = n.children;
  800. n.type = IF;
  801. n.children = [];
  802. n.condition = ch[0];
  803. var forinn = new Node(t, {type:FOR_IN});
  804. forinn.iterator = ch[1];
  805. forinn.object = obj;
  806. forinn.body = b;
  807. n.thenPart = forinn;
  808. forinn = new Node(t, {type:FOR_IN});
  809. forinn.iterator = ch[2];
  810. forinn.object = obj;
  811. // The body b must be cloned o/w markConts will overwrite the conts of the
  812. // true branch when it processes the false branch.
  813. forinn.body = b;
  814. n.elsePart = forinn;
  815. }
  816. });
  817. // Change the 1st arg to a HOOK node.
  818. // n may be an ID node, but it doesn't appear in varDecls b/c it's an rvalue,
  819. // it doesn't come from a VAR, so it can be mutated safely.
  820. // Returns true iff it edits the node.
  821. function _makeHook(n, idn, withAddr, boundvars) {
  822. var t = idn.tokenizer, name = idn.name;
  823. if (!withAddr) return;
  824. if (boundvars.member(function(bv) { return bv.name === name; })) return;
  825. var inn = new Node(t, {type : IN});
  826. inn.children = [new Node(t, {type : STRING, value : name + "-"}),
  827. withIdNode(t, withAddr)];
  828. var dotn = new Node(t, {type: DOT});
  829. dotn.children = [withIdNode(t, withAddr),
  830. new Node(t, {type:STRING, value : name + "-"})];
  831. n.type = HOOK;
  832. n.children = [inn, dotn, idNode(t, name)];
  833. return true;
  834. }
  835. override(IDENTIFIER, function(n, withAddr, boundvars) {
  836. _makeHook(n, n, withAddr, boundvars);
  837. });
  838. // May change n to a HOOK node
  839. function _assign(n, withAddr, boundvars) {
  840. var t = n.tokenizer, type = n.type, lval = n.children[0],
  841. aop = lval.assignOp, rval = n.children[1];
  842. desugarWith(rval, withAddr, boundvars);
  843. if (lval.type !== IDENTIFIER) {
  844. desugarWith(lval, withAddr, boundvars);
  845. return;
  846. }
  847. if (_makeHook(n, lval, withAddr, boundvars)) {
  848. var branch = n.children[1], assgn = new Node(t, {type:type});
  849. branch.assignOp = aop;
  850. assgn.children = [branch, rval];
  851. n.children[1] = assgn;
  852. branch = n.children[2], assgn = new Node(t, {type:type});
  853. branch.assignOp = aop;
  854. assgn.children = [branch, rval];
  855. n.children[2] = assgn;
  856. }
  857. }
  858. override(ASSIGN, _assign);
  859. override(PLUS_ASSIGN, _assign);
  860. })();
  861. // Node, Map from strings to id nodes, Array of id nodes -> void
  862. // Rename variables according to the input substitution map
  863. var subst;
  864. (function () {
  865. var walkerobj = walkASTgenerator(), override = walkerobj.override;
  866. subst = walkerobj.walkAST;
  867. // The only substitution happens here. All other cases are binders, so they
  868. // update boundvars to prevent erroneous substitutions.
  869. override(IDENTIFIER, function(n, smap, boundvars) {
  870. var vname = n.value;
  871. if (boundvars.member(function(bv) { return bv.value === vname; })) return;
  872. var newvar = smap[vname];
  873. if (newvar) {
  874. n.name = n.value = newvar.value;
  875. n.addr = newvar.addr;
  876. }
  877. });
  878. override(FUNCTION, function(n, smap, boundvars) {
  879. var blen = boundvars.length;
  880. boundvars.push(n.fname);
  881. Array.prototype.push.apply(boundvars, n.params);
  882. subst(n.body, smap, boundvars);
  883. boundvars.splice(blen, boundvars.length);
  884. });
  885. function _block(vds, fds, stms, smap, boundvars) {
  886. var blen = boundvars.length;
  887. Array.prototype.push.apply(boundvars, vds);
  888. if (fds) {
  889. fds.forEach(function(fd) { boundvars.push(fd.fname); });
  890. fds.forEach(function(fd) { subst(fd, smap, boundvars); });
  891. }
  892. stms.forEach(function(stm) { subst(stm, smap, boundvars); });
  893. boundvars.splice(blen, boundvars.length);
  894. }
  895. override(SCRIPT, function(n, smap, boundvars) {
  896. _block(n.varDecls, n.funDecls, n.children, smap, boundvars);
  897. });
  898. override(BLOCK, function(n, smap, boundvars) {
  899. _block(n.varDecls, undefined, n.children, smap, boundvars);
  900. });
  901. override(FOR, function(n, smap, boundvars) {
  902. var blen = boundvars.length;
  903. n.varDecls && Array.prototype.push.apply(boundvars, n.varDecls);
  904. n.setup && subst(n.setup, smap, boundvars);
  905. n.condition && subst(n.condition, smap, boundvars);
  906. n.update && subst(n.update, smap, boundvars);
  907. subst(n.body, smap, boundvars);
  908. boundvars.splice(blen, boundvars.length);
  909. });
  910. override(LET_BLOCK, function(n, smap, boundvars) {
  911. var vds = n.varDecls, stms;
  912. if (n.expression)
  913. stms = [semiNode(n.expression)];
  914. else {
  915. vds.concat(n.block.varDecls);
  916. stms = n.block.children;
  917. }
  918. _block(vds, undefined, stms, smap, boundvars);
  919. });
  920. override(TRY, function(n, smap, boundvars) {
  921. subst(n.tryBlock, smap, boundvars);
  922. n.catchClauses.forEach(function(clause) {
  923. boundvars.push(clause.exvar);
  924. clause.guard && subst(clause.guard, smap, boundvars);
  925. subst(clause.block, smap, boundvars);
  926. boundvars.pop();
  927. });
  928. n.finallyBlock && subst(n.finallyBlock, smap, boundvars);
  929. });
  930. })();
  931. // Must happen after desugaring of WITH, o/w when I create fresh vars for LETs,
  932. // I may subst erroneously in the body of a WITH.
  933. // After desugarLet is executed, there are no LETs or LET_BLOCKs in the AST.
  934. var desugarLet;
  935. (function () {
  936. var walkerobj = walkASTgenerator(), override = walkerobj.override;
  937. desugarLet = walkerobj.walkAST;
  938. // Array of id nodes, Array of id nodes, tokenizer ->
  939. // { smap : Substitution map, comman : comma node }
  940. function letInitsToAssigns(vds, scriptVds, t) {
  941. var smap = {}, newaddr, freshvar, comman;
  942. for (var i = 0, len = vds.length; i < len; i++) {
  943. newaddr = newCount();
  944. // New vars must have different names for tagVarRefs to work.
  945. freshvar = idNode(t, "internalVar: let" + newaddr);
  946. freshvar.addr = newaddr;
  947. smap[vds[i].value] = freshvar;
  948. scriptVds.push(freshvar);
  949. }
  950. comman = initsToAssigns(vds, t);
  951. subst(comman, smap, []);
  952. desugarLet(comman, scriptVds);
  953. return {smap : smap, comman : comman};
  954. }
  955. override(LET_BLOCK, function(n, scriptVds) {
  956. var tmp = letInitsToAssigns(n.varDecls, scriptVds, n.tokenizer),
  957. smap = tmp.smap,
  958. comman = tmp.comman;
  959. delete n.variables;
  960. var body;
  961. if (body = n.expression) {
  962. subst(body, smap, []);
  963. desugarLet(body, scriptVds);
  964. n.type = COMMA;
  965. n.children = comman.children;
  966. n.children.push(body);
  967. delete n.expression;
  968. }
  969. else if (body = n.block) {
  970. subst(body, smap, []);
  971. desugarLet(body, scriptVds, body);
  972. n.type = BLOCK;
  973. n.children = body.children;
  974. n.children.unshift(semiNode(comman));
  975. delete n.block;
  976. n.varDecls = [];
  977. }
  978. });
  979. override(BLOCK, function(n, scriptVds) {
  980. n.children.forEach(function(stm) { desugarLet(stm, scriptVds, n); });
  981. });
  982. override(FOR, function(n, scriptVds) {
  983. n.setup && desugarLet(n.setup, scriptVds, n); // LET can appear in setup
  984. n.condition && desugarLet(n.condition, scriptVds);
  985. n.update && desugarLet(n.update, scriptVds);
  986. desugarLet(n.body, scriptVds);
  987. });
  988. // LET definitions can appear in SCRIPTs or BLOCKs
  989. override(LET, function(n, scriptVds, innerBlock) {
  990. var tmp = letInitsToAssigns(n.children, scriptVds, n.tokenizer),
  991. smap = tmp.smap,
  992. comman = tmp.comman;
  993. // Call subst on the sub-nodes of innerBlock directly, to avoid binding vars
  994. // that must be substituted.
  995. if (innerBlock.type === FOR) {
  996. subst(innerBlock.setup, smap, []);
  997. innerBlock.condition && subst(innerBlock.condition, smap, []);
  998. innerBlock.update && subst(innerBlock.update, smap, []);
  999. subst(innerBlock.body, smap, []);
  1000. n.type = COMMA;
  1001. n.children = comman.children;
  1002. }
  1003. else {
  1004. innerBlock.children.forEach(function(stm) { subst(stm, smap, []); });
  1005. n.type = SEMICOLON;
  1006. n.expression = comman;
  1007. delete n.children;
  1008. }
  1009. });
  1010. override(SCRIPT, function(n) {
  1011. var vds = n.varDecls;
  1012. n.funDecls.forEach(desugarLet);
  1013. n.children.forEach(function(stm) { desugarLet(stm, vds, n); });
  1014. });
  1015. })();
  1016. const STACK = 0, HEAP = 1;
  1017. // helper function for the IDENTIFIER case of tagVarRefs
  1018. function tagVarRefsId(classifyEvents) {
  1019. return function(n, innerscope, otherscopes) {
  1020. var boundvar, varname = n.name;
  1021. // search var in innermost scope
  1022. for (var i = innerscope.length - 1; i >= 0; i--) {
  1023. boundvar = innerscope[i];
  1024. if (boundvar.name === varname) {
  1025. //print("stack ref: " + varname);
  1026. n.kind = STACK;
  1027. // if boundvar is a heap var and some of its heap refs get mutated,
  1028. // we may need to update bindings in frames during the cfa.
  1029. n.addr = boundvar.addr;
  1030. return;
  1031. }
  1032. }
  1033. // search var in other scopes
  1034. for (var i = otherscopes.length - 1; i >= 0; i--) {
  1035. boundvar = otherscopes[i];
  1036. if (boundvar.name === varname) {
  1037. // print("heap ref: " + varname);
  1038. n.kind = boundvar.kind = HEAP;
  1039. n.addr = boundvar.addr;
  1040. flags[boundvar.addr] = true;
  1041. return;
  1042. }
  1043. }
  1044. // var has no binder in the program
  1045. if (commonJSmode && varname === "exports") {
  1046. n.kind = HEAP;
  1047. n.addr = exports_object_av_addr;
  1048. var p = arguments[3]; // exported property name passed as extra arg
  1049. if (p && p.type === STRING)
  1050. exports_object.lines[p.value.slice(0, -1)] = p.lineno;
  1051. return;
  1052. }
  1053. //print("global: " + varname + " :: " + n.lineno);
  1054. n.type = DOT;
  1055. var nthis = idNode({lineno: n.lineno}, "internalVar: global object");
  1056. nthis.kind = HEAP;
  1057. if (classifyEvents && varname === "addEventListener")
  1058. nthis.addr = chrome_obj_av_addr;
  1059. else
  1060. nthis.addr = global_object_av_addr;
  1061. n.children = [nthis, new Node({}, {type:STRING, value:n.name + "-"})];
  1062. };
  1063. }
  1064. // node, array of id nodes, array of id nodes -> void
  1065. // Classify variable references as either stack or heap references.
  1066. var tagVarRefs, tagVarRefs_override;
  1067. (function() {
  1068. var walkerobj = walkASTgenerator(), override = walkerobj.override;
  1069. tagVarRefs = walkerobj.walkAST;
  1070. tagVarRefs_override = override;
  1071. override(DOT, function(n, innerscope, otherscopes) {
  1072. var ch = n.children;
  1073. var n0 = ch[0];
  1074. // don't classify property names
  1075. if (commonJSmode && n0.type === IDENTIFIER && n0.name === "exports")
  1076. tagVarRefs(n0, innerscope, otherscopes, ch[1]);
  1077. else
  1078. tagVarRefs(n0, innerscope, otherscopes);
  1079. });
  1080. override(INDEX, function(n, innerscope, otherscopes) {
  1081. var ch = n.children, n0 = ch[0], shadowed = false;
  1082. // use new non-terminal only if "arguments" refers to the arguments array
  1083. if (n0.type === IDENTIFIER && n0.name === "arguments") {
  1084. for (var i = innerscope.length - 1; i >= 0; i--)
  1085. if (innerscope[i].name === "arguments") {
  1086. shadowed = true;
  1087. break;
  1088. }
  1089. if (!shadowed) {
  1090. n.type = ARGUMENTS;
  1091. n.arguments = innerscope; //hack: innerscope is params (maybe extended)
  1092. ch[0] = ch[1];
  1093. ch.splice(1, 1);
  1094. // undo the changes made for INDEX nodes only in fixExp
  1095. if (ch[0].type === STRING && propIsNumeric(ch[0].value)) {
  1096. ch[0].type = NUMBER;
  1097. ch[0].value = ch[0].value.slice(0, -1) - 0;
  1098. }
  1099. }
  1100. }
  1101. ch.forEach(function(kid) { tagVarRefs(kid, innerscope, otherscopes); });
  1102. });
  1103. override(IDENTIFIER, tagVarRefsId(false));
  1104. override(FUNCTION, function(n, innerscope, otherscopes) {
  1105. var fn = n.fname, len, params = n.params;
  1106. len = otherscopes.length;
  1107. // extend otherscopes
  1108. Array.prototype.push.apply(otherscopes, innerscope);
  1109. // fun name is visible in the body & not a heap ref, add it to scope
  1110. params.push(fn);
  1111. tagVarRefs(n.body, params, otherscopes);
  1112. params.pop();
  1113. if (fn.kind !== HEAP) fn.kind = STACK;
  1114. params.forEach(function(p) {if (p.kind !== HEAP) p.kind=STACK;});
  1115. // trim otherscopes
  1116. otherscopes.splice(len, innerscope.length);
  1117. });
  1118. override(SCRIPT, function(n, innerscope, otherscopes) {
  1119. var i, j, len, vdecl, vdecls = n.varDecls, fdecl, fdecls = n.funDecls;
  1120. // extend inner scope
  1121. j = innerscope.length;
  1122. Array.prototype.push.apply(innerscope, vdecls);
  1123. fdecls.forEach(function(fd) { innerscope.push(fd.fname); });
  1124. // tag refs in fun decls
  1125. fdecls.forEach(function(fd) { tagVarRefs(fd, innerscope, otherscopes); });
  1126. // tag the var refs in the body
  1127. var as = arguments;
  1128. n.children.forEach(function(stm) {tagVarRefs(stm,innerscope,otherscopes);});
  1129. // tag formals
  1130. vdecls.forEach(function(vd) {
  1131. // for toplevel vars, assigning flags causes the Aval`s to be recorded
  1132. // in the heap. After the analysis, we use that for ctags.
  1133. if (as[3] === "toplevel") flags[vd.addr] = true;
  1134. if (vd.kind !== HEAP) vd.kind = STACK;
  1135. });
  1136. fdecls.forEach(function(fd) { if (fd.kind !== HEAP) fd.kind = STACK; });
  1137. //trim inner scope
  1138. innerscope.splice(j, vdecls.length + fdecls.length);
  1139. });
  1140. override(TRY, function(n, innerscope, otherscopes) {
  1141. tagVarRefs(n.tryBlock, innerscope, otherscopes);
  1142. n.catchClauses.forEach(function(clause) {
  1143. var xv = clause.exvar;
  1144. innerscope.push(xv);
  1145. clause.guard &&
  1146. tagVarRefs(clause.guard, innerscope, otherscopes);
  1147. tagVarRefs(clause.block, innerscope, otherscopes);
  1148. innerscope.pop();
  1149. if (xv.kind !== HEAP) xv.kind = STACK;
  1150. });
  1151. n.finallyBlock && tagVarRefs(n.finallyBlock, innerscope, otherscopes);
  1152. });
  1153. })();
  1154. // node, node, node -> void
  1155. // For every node N in the AST, add refs from N to the node that is normally
  1156. // exec'd after N and to the node that is exec'd if N throws an exception.
  1157. var markConts;
  1158. (function() {
  1159. var walkerobj = walkASTgenerator(), override = walkerobj.override;
  1160. markConts = walkerobj.walkAST;
  1161. function _fun(n) { markConts(n.body, undefined, undefined); }
  1162. override(FUNCTION, _fun);
  1163. function _block(n, kreg, kexc) {
  1164. var ch = n.children, i, len;
  1165. n.kexc = kexc;
  1166. len = ch.length;
  1167. if (len === 0)
  1168. n.kreg = kreg;
  1169. else {
  1170. n.kreg = ch[0];
  1171. len--;
  1172. for (i=0; i < len; i++) markConts(ch[i], ch[i+1], kexc);
  1173. markConts(ch[len], kreg, kexc);
  1174. }
  1175. }
  1176. override(BLOCK, _block);
  1177. override(SCRIPT, function(n, kreg, kexc) {
  1178. n.funDecls.forEach(_fun);
  1179. _block(n, kreg, kexc);
  1180. });
  1181. override(SEMICOLON, function(n, kreg, kexc) {
  1182. n.kreg = kreg;
  1183. n.kexc = kexc;
  1184. markConts(n.expression);
  1185. });
  1186. // normally, return & throw don't use their kreg. But this analysis allows
  1187. // more permissive control flow, to be faster.
  1188. override(THROW, function(n, kreg, kexc) {
  1189. n.kreg = kreg;
  1190. n.kexc = kexc;
  1191. markConts(n.exception);
  1192. });
  1193. override(RETURN, function(n, kreg, kexc) {
  1194. n.kreg = kreg;
  1195. n.kexc = kexc;
  1196. markConts(n.value);
  1197. });
  1198. override(IF, function(n, kreg, kexc) {
  1199. var thenp = n.thenPart, elsep = n.elsePart, condStm;
  1200. condStm = semiNode(n.condition);
  1201. n.kreg = condStm; // first run the test
  1202. n.kexc = kexc;
  1203. markConts(condStm, thenp, kexc); // ignore result & run the true branch
  1204. markConts(thenp, elsep || kreg, kexc); // then run the false branch
  1205. elsep && markConts(elsep, kreg, kexc);
  1206. });
  1207. function markContsCase(n, kreg, kexc) {
  1208. var clabel = n.caseLabel, clabelStm, stms = n.statements;
  1209. n.kexc = kexc;
  1210. if (clabel) {
  1211. clabelStm = semiNode(clabel);
  1212. n.kreg = clabelStm;
  1213. markConts(clabelStm, stms, kexc);
  1214. }
  1215. else n.kreg = stms;
  1216. markConts(stms, kreg, kexc);
  1217. }
  1218. override(SWITCH, function(n, kreg, kexc) {
  1219. var cases = n.cases, discStm, i, len;
  1220. discStm = semiNode(n.discriminant);
  1221. n.kreg = discStm; // first run the discriminant, then all branches in order
  1222. n.kexc = kexc;
  1223. markConts(discStm, cases[0], kexc);
  1224. for (i = 0, len = cases.length - 1; i < len; i++) //no empty switch, len >=0
  1225. markContsCase(cases[i], cases[i+1], kexc);
  1226. markContsCase(cases[len], kreg, kexc);
  1227. });
  1228. override(FOR, function(n, kreg, kexc) {
  1229. var body = n.body;
  1230. n.kexc = kexc;
  1231. // Do setup, condition, body & update once.
  1232. var setup = n.setup, setupStm, condition = n.condition, condStm;
  1233. var update = n.update, updStm;
  1234. n.kexc = kexc;
  1235. if (!setup && !condition)
  1236. n.kreg = body;
  1237. else if (setup && !condition) {
  1238. setupStm = semiNode(setup);
  1239. n.kreg = setupStm;
  1240. markConts(setupStm, body, kexc);
  1241. }
  1242. else {// condition exists
  1243. condStm = semiNode(condition);
  1244. markConts(condStm, body, kexc);
  1245. if (setup) {
  1246. setupStm = semiNode(setup);
  1247. n.kreg = setupStm;
  1248. markConts(setupStm, condStm, kexc);
  1249. }
  1250. else n.kreg = condStm;
  1251. }
  1252. if (update) {
  1253. updStm = semiNode(update);
  1254. markConts(body, updStm, kexc);
  1255. markConts(updStm, kreg, kexc);
  1256. }
  1257. else markConts(body, kreg, kexc);
  1258. });
  1259. override(FOR_IN, function(n, kreg, kexc) {
  1260. var body = n.body;
  1261. n.kexc = kexc;
  1262. n.kreg = body;
  1263. markConts(body, kreg, kexc);
  1264. });
  1265. override(WHILE, function(n, kreg, kexc) {
  1266. var condStm = semiNode(n.condition), body = n.body;
  1267. n.kreg = condStm;
  1268. n.kexc = kexc;
  1269. markConts(condStm, body, kexc);
  1270. markConts(body, kreg, kexc);
  1271. });
  1272. override(DO, function(n, kreg, kexc) {
  1273. var condStm = semiNode(n.condition), body = n.body;
  1274. n.kreg = body;
  1275. n.kexc = kexc;
  1276. markConts(body, condStm, kexc);
  1277. markConts(condStm, kreg, kexc);
  1278. });
  1279. function markContsCatch(n, knocatch, kreg, kexc) {
  1280. var guard = n.guard, guardStm, block = n.block;
  1281. if (guard) {// Mozilla catch
  1282. // The guard is of the form (var if expr).
  1283. // If expr is truthy, the catch body is run w/ var bound to the exception.
  1284. // If expr is falsy, we go to the next block (another catch or finally).
  1285. // If the guard or the body throw, the next catches (if any) can't handle
  1286. // the exception, we go to the finally block (if any) directly.
  1287. markConts(guard);
  1288. guardStm = semiNode(guard);
  1289. n.kreg = guardStm;
  1290. guardStm.kcatch = block; // this catch handles the exception
  1291. guardStm.knocatch = knocatch; // this catch doesn't handle the exception
  1292. guardStm.kexc = kexc; // the guard throws a new exception
  1293. }
  1294. markConts(block, kreg, kexc);
  1295. }
  1296. override(TRY, function(n, kreg, kexc) {
  1297. var fin = n.finallyBlock, clause, clauses=n.catchClauses, knocatch, i, len;
  1298. // process back-to-front to avoid if-madness
  1299. if (fin) {
  1300. markConts(fin, kreg, kexc);
  1301. knocatch = kexc = kreg = fin; // TRY & CATCHes go to fin no matter what
  1302. }
  1303. for (len = clauses.length, i = len-1; i>=0; i--) {
  1304. clause = clauses[i];
  1305. markContsCatch(clause, knocatch, kreg, kexc);
  1306. knocatch = clause;
  1307. }
  1308. markConts(n.tryBlock, kreg, knocatch || kexc);
  1309. n.kreg = n.tryBlock;
  1310. });
  1311. })();
  1312. ////////////////////////////////////////////////////////////////////////////////
  1313. //////////////////////////// CFA2 code /////////////////////////////////////
  1314. ////////////////////////////////////////////////////////////////////////////////
  1315. // abstract objects and abstract values are different!!!
  1316. var heap;
  1317. // modified[addr] is a timestamp that shows the last time heap[addr] was updated
  1318. var modified;
  1319. var timestamp;
  1320. // If i is the addr of a var, flags[i] is true if the var is a heap var.
  1321. var flags;
  1322. var exports_object;
  1323. var exports_object_av_addr;
  1324. var commonJSmode;
  1325. var timedout = false, timeout = 120; // stop after 2 minutes if you're not done
  1326. // A summary contains a function node (fn), an array of abstract values (args),
  1327. // a timestamp (ts) and abstract values (res) and (err). It means: when we call
  1328. // fn w/ args and the heap's timestamp is ts, the result is res and if fn can
  1329. // throw an exception, the value thrown is err.
  1330. // summaries: a map from addresses of fun nodes to triples {ts, insouts, type},
  1331. // where ts is a timestamp, insouts is an array of args-result pairs,
  1332. // and type is the join of all args-result pairs.
  1333. var summaries;
  1334. // pending contains info that exists in the runtime stack. For each pending call
  1335. // to evalFun, pending contains an object {args, ts} where args is the arguments
  1336. // of the call and ts is the timestamp at the time of the call.
  1337. var pending;
  1338. // core JS functions also use pending but differently.
  1339. // when initGlobals is called, count has its final value (core objs are in heap)
  1340. // FIXME, I'm violating the invariant in "function cfa2". Change it?
  1341. function initGlobals() {
  1342. big_ts = 1000; // used only when debugging
  1343. timestamp = 0;
  1344. heap = new Array(count); // reserve heap space, don't grow it gradually
  1345. modified = buildArray(count, timestamp);
  1346. summaries = {}; // We use {} instead of an Array b/c it's sparse.
  1347. pending = {};
  1348. flags = {};
  1349. exports_object = {lines : {}};
  1350. }
  1351. // string -> void
  1352. // works only in NodeJS
  1353. function dumpHeap(filename) {
  1354. var fd = fs.openSync(filename, "w", mode=0777);
  1355. for (var i = 0, l = heap.length; i < l; i++)
  1356. printf(fd, "[" + i + "]\n" + (heap[i] ? heap[i].toString(2) : "") + "\n");
  1357. fs.closeSync(fd);
  1358. }
  1359. // non-empty array of strings -> void
  1360. function normalizeUnionType(types) {
  1361. // any is a supertype of all types
  1362. if (types.memq("any")) {
  1363. types[0] = "any";
  1364. types.length = 1;
  1365. }
  1366. else
  1367. types.rmDups(function(e1, e2) {return e1 === e2;});
  1368. }
  1369. // Constructor for abstract properties
  1370. // Takes an object w/ the property's attributes
  1371. // Don't call from outside the abstract-values module, use addProp instead.
  1372. function Aprop(attribs){
  1373. this.aval = attribs.aval;
  1374. // writable, enumerable and configurable default to true
  1375. this.write = ("write" in attribs) ? attribs.write : true;
  1376. this.enum = ("enum" in attribs) ? attribs.enum : true;
  1377. this.config = ("config" in attribs) ? attribs.config : true;
  1378. }
  1379. // optional number -> string
  1380. Aprop.prototype.toString = function(indent) {
  1381. return this.aval.toString(indent);
  1382. };
  1383. // An abstract object o1 is represented as a JS object o2.
  1384. // A property…

Large files files are truncated, but you can click here to view the full file