PageRenderTime 31ms CodeModel.GetById 8ms 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
  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 of o1 named p has the name p- in o2.
  1385. // A special property p of o1 has the name -p in o2.
  1386. // Special properties: -number, -string, -proto, -fun, -addr
  1387. // -addr: the address of o1 in the heap
  1388. // -proto: the address of o1's prototype in the heap
  1389. // -fun: may point to a function node
  1390. // -number: may contain an Aval (join of properties w/ "numeric" names)
  1391. // -string: may contain an Aval (join of all properties)
  1392. function Aobj(specialProps) {
  1393. for (var p in specialProps) this["-" + p] = specialProps[p];
  1394. heap[specialProps.addr] = this; // put new object in heap
  1395. }
  1396. Aobj.prototype = new Array();
  1397. // Takes an optional array for cycle detection.
  1398. Aobj.prototype.toType = function(seenObjs) {
  1399. var self = this;
  1400. if (seenObjs) {
  1401. if (seenObjs.memq(self))
  1402. return "any";
  1403. else
  1404. seenObjs.push(self);
  1405. }
  1406. else
  1407. seenObjs = [self];
  1408. if (this["-fun"]) return funToType(this["-fun"], seenObjs);
  1409. var c = this.getProp("constructor-");
  1410. var types = [];
  1411. if (c === undefined) return "Global Object";
  1412. c.forEachObj(function(o) {if (o["-fun"]) types.push(o["-fun"].fname.name);});
  1413. if (types.length === 0)
  1414. throw errorWithCode(CFA_ERROR, "Didn't find a name for constructor");
  1415. normalizeUnionType(types);
  1416. if (types.length === 1) {
  1417. if (types[0] === "Array") return this.toArrayType(seenObjs);
  1418. return types[0];
  1419. }
  1420. return ("<" + types.join(" | ") + ">");
  1421. };
  1422. // void -> boolean
  1423. Aobj.prototype.isArray = function() {
  1424. var c = this.getProp("constructor-"), cobj;
  1425. if (c === undefined) return false;
  1426. if (c.objs.length !== 1) return false;
  1427. cobj = heap[c.objs[0]];
  1428. return cobj["-fun"] && cobj["-fun"].fname.name === "Array";
  1429. }
  1430. // For homogeneous arrays, include the type of their elms.
  1431. // Takes an optional array for cycle detection.
  1432. Aobj.prototype.toArrayType = function(seenObjs) {
  1433. var elmtype = BOTTOM, self = this;
  1434. this.forEachOwnProp(function(p) {
  1435. if (propIsNumeric(p)) elmtype = avjoin(elmtype, self.getOwnExactProp(p));
  1436. });
  1437. elmtype = elmtype.toType(seenObjs);
  1438. if (/\|/.test(elmtype) || elmtype === "any")
  1439. return "Array";
  1440. else
  1441. return "Array[" + elmtype + "]";
  1442. };
  1443. // void -> function node
  1444. Aobj.prototype.getFun = function() { return this["-fun"]; };
  1445. // Aval -> void
  1446. Aobj.prototype.updateProto = function(av) {
  1447. if (this["-proto"]) {
  1448. if (!avlt(av, this["-proto"])) {
  1449. this["-proto"] = avjoin(this["-proto"], av);
  1450. //print("++ts: 1");
  1451. ++timestamp;
  1452. }
  1453. return;
  1454. }
  1455. this["-proto"] = av;
  1456. //print("++ts: 2");
  1457. ++timestamp;
  1458. };
  1459. // string -> boolean
  1460. function propIsNumeric(p) {
  1461. return p === "-number" || (/-$/.test(p) && !isNaN(p.slice(0, -1)));
  1462. }
  1463. // act: Aobj, optional Array[string] -> {val : A, stop : boolean}
  1464. // combine: Array[A] -> A
  1465. function foldProtoChain(o, act, combine) {
  1466. function recur(o, seenObjs, seenProps) {
  1467. var v = act(o, seenProps), val = v.val;
  1468. if (v.stop) return val; // stop going up the chain
  1469. if (!o["-proto"]) return val; // reached the end of the prototype chain
  1470. if (seenObjs.memq(o))
  1471. return val;
  1472. else
  1473. seenObjs.push(o);
  1474. var a = [], solen = seenObjs.length, splen = seenProps.length;
  1475. o["-proto"].forEachObj(function(proto) {
  1476. a.push(recur(proto, seenObjs, seenProps));
  1477. seenObjs.splice(solen, seenObjs.length);
  1478. seenProps.splice(splen, seenProps.length);
  1479. });
  1480. a.push(val); // The current level's result is last, 'combine' pops to get it
  1481. return combine(a);
  1482. }
  1483. return recur(o, [], []);
  1484. }
  1485. // string -> Aval or undefined
  1486. // doesn't look in prototype chain
  1487. Aobj.prototype.getOwnExactProp = function(pname) {
  1488. return this[pname] && this[pname].aval;
  1489. };
  1490. // string -> Aval or undefined
  1491. // doesn't look in prototype chain
  1492. // pname is not "-number" or "-string"
  1493. Aobj.prototype.getOwnProp = function(pname) {
  1494. if (this.hasOwnProperty(pname))
  1495. return this[pname].aval;
  1496. if (this.numPropsMerged && propIsNumeric(pname))
  1497. return this["-number"].aval;
  1498. if (this.strPropsMerged)
  1499. return this["-string"].aval;
  1500. };
  1501. // string -> Aval or undefined
  1502. // May include "-number" and "-string" in its result
  1503. Aobj.prototype.getProp = function(pname) {
  1504. return getProp(this, pname, true);
  1505. };
  1506. // Aobj, string, boolean -> Aval or undefined
  1507. // pname is not "-number" or "-string"
  1508. // Looks in prototype chain. Returns undefined iff the property doesn't exist in
  1509. // *all* paths up the prototype chain o/w it returns an Aval.
  1510. // function getProp(o, pname, lax) {
  1511. // function act(o) {
  1512. // if (o.hasOwnProperty(pname))
  1513. // return {val : o[pname].aval, stop : true};
  1514. // if (lax && o.numPropsMerged) {
  1515. // if (propIsNumeric(pname))
  1516. // return {val : o["-number"].aval, stop : true};
  1517. // if (o.strPropsMerged)
  1518. // return {val : o["-string"].aval, stop : false};
  1519. // }
  1520. // return { stop : false };
  1521. // }
  1522. // function combine(avs) {
  1523. // var notfoundinsomechain = false, val = avs.pop();
  1524. // avs.forEach(function(av) {
  1525. // if (!av)
  1526. // notfoundinsomechain = true;
  1527. // else
  1528. // val = maybeavjoin(val, av);
  1529. // });
  1530. // return val && (notfoundinsomechain ? avjoin(AUNDEF, val) : val);
  1531. // }
  1532. // return foldProtoChain(o, act, combine);
  1533. // }
  1534. function getProp(o, pname, lax) {
  1535. var levels = 0, av;
  1536. while (o !== undefined && levels <= 2) {
  1537. if (o.hasOwnProperty(pname))
  1538. return o[pname].aval;
  1539. if (lax && o.numPropsMerged) {
  1540. if (propIsNumeric(pname))
  1541. return o["-number"].aval;
  1542. if (o.strPropsMerged)
  1543. av = maybeavjoin(av, o["-string"].aval);
  1544. }
  1545. levels++;
  1546. o = o["-proto"] && heap[o["-proto"].objs[0]];
  1547. }
  1548. return av;
  1549. }
  1550. Aobj.prototype.getNumProps = function() {
  1551. this.mergeNumProps();
  1552. return this["-number"].aval;
  1553. };
  1554. // Aobj.prototype.getStrProps = function() {
  1555. // function act(o, seenProps) {
  1556. // if (o.strPropsMerged)
  1557. // return {val : o["-string"].aval, stop : false};
  1558. // var val = BOTTOM;
  1559. // o.forEachOwnProp(function(pname, pval) {
  1560. // if (pval.enum && !seenProps.memq(pname)) {
  1561. // val = avjoin(val, o[pname].aval);
  1562. // (pname !== "-number") && seenProps.push(pname);
  1563. // }
  1564. // });
  1565. // return {val : val, stop : false};
  1566. // }
  1567. // function combine(avs) {
  1568. // var val = BOTTOM;
  1569. // avs.forEach(function(av) { val = avjoin(val, av); });
  1570. // return val;
  1571. // }
  1572. // return foldProtoChain(this, act, combine);
  1573. // };
  1574. Aobj.prototype.getStrProps = function() {
  1575. var levels = 0, av = BOTTOM, seenProps = [], o = this;
  1576. while (o !== undefined && levels <= 2) {
  1577. if (o.strPropsMerged)
  1578. av = avjoin(av, o["-string"].aval);
  1579. else
  1580. o.forEachOwnProp(function(pname, pval) {
  1581. if (pval.enum && !seenProps.memq(pname)) {
  1582. av = avjoin(av, o[pname].aval);
  1583. (pname !== "-number") && seenProps.push(pname);
  1584. }
  1585. });
  1586. levels++;
  1587. o = o["-proto"] && heap[o["-proto"].objs[0]];
  1588. }
  1589. return av;
  1590. };
  1591. // string, Object -> void
  1592. // attribs.aval is the property's value
  1593. // The property shouldn't be already present, it'll be overwritten
  1594. Aobj.prototype.addProp = function(prop, attribs) {
  1595. this[prop] = new Aprop(attribs);
  1596. };
  1597. // string, Aval -> void
  1598. Aobj.prototype.updateExactProp = function(pname, newv) {
  1599. if (this.hasOwnProperty(pname)) {
  1600. var p = this[pname];
  1601. if (!avlt(newv, p.aval)) {
  1602. p.aval = avjoin(p.aval, newv);
  1603. //print("++ts: 3");
  1604. ++timestamp;
  1605. }
  1606. return;
  1607. }
  1608. this[pname] = new Aprop({aval : newv});
  1609. //print("++ts: 4");
  1610. ++timestamp;
  1611. };
  1612. // string, Aval -> void
  1613. // pname is not "-number" or "-string"
  1614. Aobj.prototype.updateProp = function(pname, newv) {
  1615. function upd(pname, pval, newv) {
  1616. if (!avlt(newv, pval.aval)) {
  1617. pval.aval = avjoin(pval.aval, newv);
  1618. //print("++ts: 5");
  1619. ++timestamp;
  1620. }
  1621. }
  1622. if (this.hasOwnProperty(pname))
  1623. // either it's not enumerable, or it doesn't interfere with "-number"
  1624. upd(pname, this[pname], newv);
  1625. else {// the new property is enumerable
  1626. if (this.strPropsMerged)
  1627. upd("-string", this["-string"], newv);
  1628. if (this.numPropsMerged && propIsNumeric(pname))
  1629. upd("-number", this["-number"], newv);
  1630. else if (!this.strPropsMerged) {
  1631. this[pname] = new Aprop({aval : newv});
  1632. //print("++ts: 6 " + pname);
  1633. ++timestamp;
  1634. }
  1635. }
  1636. };
  1637. // Aval -> void
  1638. Aobj.prototype.updateNumProps = function(newv) {
  1639. this.mergeNumProps();
  1640. this.updateExactProp("-number", newv);
  1641. };
  1642. // Aval -> void
  1643. Aobj.prototype.updateStrProps = function(newv) {
  1644. this.mergeStrProps();
  1645. this.updateExactProp("-number", newv);
  1646. this.updateExactProp("-string", newv);
  1647. };
  1648. // merge all numeric properties of the object to one generic number property
  1649. Aobj.prototype.mergeNumProps = function() {
  1650. if (this.numPropsMerged) return;
  1651. var av = BOTTOM, self = this;
  1652. this.forEachOwnProp(function(p) {
  1653. if (propIsNumeric(p)) {
  1654. av = avjoin(av, self[p].aval);
  1655. delete self[p]; // don't keep a mix of merged and unmerged properties.
  1656. }
  1657. });
  1658. this.updateExactProp("-number", av); // this bumps timestamp, don't bump again
  1659. this.numPropsMerged = true;
  1660. };
  1661. Aobj.prototype.mergeStrProps = function() {
  1662. if (this.strPropsMerged) return;
  1663. if (!this.numPropsMerged) this.mergeNumProps();
  1664. var av = BOTTOM, self = this;
  1665. this.forEachOwnProp(function(pname, pval) {
  1666. if (pval.enum) {
  1667. av = avjoin(av, pval.aval);
  1668. if (pname !== "-number") delete self[pname];
  1669. }
  1670. });
  1671. this.updateExactProp("-string", av); // this bumps timestamp, don't bump again
  1672. this.strPropsMerged = true;
  1673. };
  1674. Aobj.prototype.forEachOwnProp = function(f) {
  1675. for (var p in this)
  1676. if (this[p] instanceof Aprop)
  1677. f(p, this[p]); // f may ignore its second argument
  1678. };
  1679. // Aobj.prototype.forEachEnumProp = function(f) {
  1680. // function act(o, seenProps) {
  1681. // for (var p in o) {
  1682. // var pval = o[p];
  1683. // if ((pval instanceof Aprop) && pval.enum
  1684. // && !seenProps.memq(p)) {
  1685. // f(p, pval.aval); // f may ignore its second argument
  1686. // seenProps.push(p);
  1687. // }
  1688. // }
  1689. // return { stop : false };
  1690. // }
  1691. // foldProtoChain(this, act, function() {});
  1692. // };
  1693. Aobj.prototype.forEachEnumProp = function(f) {
  1694. var levels = 0, av = BOTTOM, seenProps = [], o = this;
  1695. while (o !== undefined && levels <= 2) {
  1696. for (var p in o) {
  1697. var pval = o[p];
  1698. if ((pval instanceof Aprop) && pval.enum && !seenProps.memq(p)) {
  1699. f(p, pval.aval); // f may ignore its second argument
  1700. seenProps.push(p);
  1701. }
  1702. }
  1703. levels++;
  1704. o = o["-proto"] && heap[o["-proto"].objs[0]];
  1705. }
  1706. return av;
  1707. };
  1708. // optional number -> string
  1709. // Returns a multiline string, each line starts with indent (or more) spaces
  1710. Aobj.prototype.toString = function(indent) {
  1711. indent = indent || 0;
  1712. var i1 = buildString(indent, " "), i2 = i1 + " ";
  1713. var s = i1 + "<proto>:\n";
  1714. s += (this["-proto"] ? this["-proto"].toString(indent + 2) : (i2 + "??\n"));
  1715. if (this["-fun"]) {
  1716. s += i1 + "<function>:\n" + i2 + this["-fun"].fname.name +
  1717. (this["-fun"].lineno ? ("@" + this["-fun"].lineno) : "") + "\n";
  1718. }
  1719. var self = this;
  1720. this.forEachOwnProp(function(p) {
  1721. var pname = p;
  1722. if (p !== "-number" && p !== "-string")
  1723. pname = p.slice(0, -1);
  1724. s += i1 + pname + ":\n" + self[p].toString(indent + 2);
  1725. });
  1726. return s;
  1727. };
  1728. // An abstract value is an obj w/ 2 properties: base is a number whose bits
  1729. // encode the base values, objs is an array of sorted heap addresses that
  1730. // denotes a set of objects.
  1731. const BOTTOM = makeBaseAval(0), ANUM = makeBaseAval(1), ASTR = makeBaseAval(2);
  1732. const ATRU = makeBaseAval(4), AFALS = makeBaseAval(8), ABOOL = makeBaseAval(12);
  1733. const AUNDEF = makeBaseAval(16), ANULL = makeBaseAval(32);
  1734. const RESTARGS = -1;
  1735. const INVALID_TIMESTAMP = -1;
  1736. // constructor for abstract values. Should only be called from the wrappers.
  1737. function Aval() {}
  1738. // number -> Aval
  1739. function makeBaseAval(base) {
  1740. var v = new Aval();
  1741. v.base = base;
  1742. v.objs = [];
  1743. return v;
  1744. }
  1745. // number -> Aval
  1746. // When creating an abs. value, it can contain at most one object
  1747. function makeObjAval(objaddr) {
  1748. var v = new Aval();
  1749. v.base = 0;
  1750. v.objs = [objaddr];
  1751. return v;
  1752. }
  1753. // string -> Aval
  1754. function makeStrLitAval(s) {
  1755. var v = new Aval();
  1756. v.base = 2;
  1757. v.objs = [];
  1758. v.str = s;
  1759. return v;
  1760. }
  1761. // used by parts of the code that don't know the representation of Aval
  1762. Aval.prototype.getBase = function() { return makeBaseAval(this.base); };
  1763. // void -> string or undefined
  1764. Aval.prototype.getStrLit = function() { return this.str; };
  1765. // Merge ATRU and AFALS to one generic boolean in the base b
  1766. function mergeBoolsInBase(b) {
  1767. if (b & 8) b |= 4;
  1768. b = ((b & 48) >> 1) | (b & 7);
  1769. return b;
  1770. }
  1771. // Takes an optional array for cycle detection.
  1772. Aval.prototype.toType = function(seenObjs) {
  1773. var i = 1, base = mergeBoolsInBase(this.base), types = [];
  1774. var basetypes = {1 : "number", 2 : "string", 4 : "boolean",
  1775. 8 : "undefined", 16 : "null"};
  1776. while (i <= 16) {
  1777. if ((base & i) === i) types.push(basetypes[i]);
  1778. i *= 2;
  1779. }
  1780. // If uncommented, tags show string constants where possible.
  1781. // if ((base & 2) && (this.str !== undefined)) {
  1782. // types.rmElmAfterIndex(function(s) {return s === "string";}, 0);
  1783. // types.push("\"" + this.str + "\"");
  1784. // }
  1785. seenObjs || (seenObjs = []);
  1786. var slen = seenObjs.length;
  1787. this.forEachObj(function (o) {
  1788. types.push(o.toType(seenObjs));
  1789. seenObjs.splice(slen, seenObjs.length);
  1790. });
  1791. if (types.length === 0) return "any";
  1792. normalizeUnionType(types);
  1793. if (types.length === 1) return types[0];
  1794. return ("<" + types.join(" | ") + ">");
  1795. };
  1796. // void -> Aval
  1797. // Used when scalars need to be converted to objects
  1798. Aval.prototype.baseToObj = function() {
  1799. var base = this.base;
  1800. if (base & 15 === 0) return this;
  1801. var av = makeBaseAval(0);
  1802. av.objs = this.objs;
  1803. if (base & 2) av = avjoin(av, generic_string_av);
  1804. if (base & 1) av = avjoin(av, generic_number_av);
  1805. if (base & 12) av = avjoin(av, generic_boolean_av);
  1806. return av;
  1807. };
  1808. // fun takes an Aobj
  1809. Aval.prototype.forEachObj = function(fun) {
  1810. var objaddrs = this.objs;
  1811. if (objaddrs.length === 1) // make common case faster
  1812. fun(heap[objaddrs[0]]);
  1813. else
  1814. objaddrs.forEach(function(addr) { fun(heap[addr]); });
  1815. };
  1816. // Like forEachObj but fun returns a boolean; if it's true, we stop.
  1817. Aval.prototype.forEachObjWhile = function(fun) {
  1818. var objaddrs = this.objs, len = objaddrs.length;
  1819. if (len === 1) // make common case faster
  1820. fun(heap[objaddrs[0]]);
  1821. else {
  1822. var i = 0, cont = false;
  1823. while (!cont && i < len)
  1824. cont = fun(heap[objaddrs[i++]]);
  1825. }
  1826. };
  1827. // string -> Aval
  1828. Aval.prototype.getProp = function(pname) {
  1829. var av = BOTTOM;
  1830. this.forEachObj(function(o) { av = avjoin(av, o.getProp(pname) || AUNDEF); });
  1831. return av;
  1832. };
  1833. // string, Aval -> void
  1834. Aval.prototype.updateProp = function(pname, av) {
  1835. this.forEachObj(function(o) { o.updateProp(pname, av); });
  1836. };
  1837. // array of Aval, node -> Ans
  1838. // Call each function with args. args[0] is what THIS is bound to.
  1839. // FIXME: record error if rator contains base vals and non-functions
  1840. Aval.prototype.callFun = function(args, callNode) {
  1841. var retval = BOTTOM, errval, ans, debugCalls = 0;
  1842. this.baseToObj().forEachObj(function(o) {
  1843. var clos = o.getFun();
  1844. if (!clos) return;
  1845. debugCalls++;
  1846. ans = evalFun(clos, args, false, callNode);
  1847. retval = avjoin(retval, ans.v);
  1848. errval = maybeavjoin(errval, ans.err);
  1849. });
  1850. // var ratorNode = callNode && callNode.children[0];
  1851. // if (!debugCalls) {
  1852. // var funName, ln = ratorNode.lineno;
  1853. // switch (ratorNode.type) {
  1854. // case IDENTIFIER:
  1855. // funName = ratorNode.name;
  1856. // break;
  1857. // case FUNCTION:
  1858. // funName = ratorNode.fname.name;
  1859. // break;
  1860. // case DOT:
  1861. // if (ratorNode.children[1].type === STRING) {
  1862. // funName = ratorNode.children[1].value.slice(0, -1);
  1863. // break;
  1864. // }
  1865. // // fall thru
  1866. // default:
  1867. // funName = "??";
  1868. // break;
  1869. // }
  1870. // if (args[0] === global_object_av)
  1871. // print("unknown function: " + funName + ", line " + (ln || "??"));
  1872. // else
  1873. // print("unknown method: " + funName + ", line " + (ln || "??"));
  1874. // }
  1875. return new Ans(retval, undefined, errval);
  1876. };
  1877. Aval.prototype.hasNum = function() { return this.base & 1; };
  1878. Aval.prototype.hasStr = function() { return this.base & 2; };
  1879. Aval.prototype.hasObjs = function() { return this.objs.length > 0; };
  1880. // returns true if it can guarantee that the Aval is falsy.
  1881. Aval.prototype.isFalsy = function() {
  1882. var base = this.base;
  1883. return this.objs.length === 0 && base !== 0 &&
  1884. (base % 8 === 0 || (base === 2 && this.str === "-"));
  1885. };
  1886. // returns true if it can guarantee that the Aval is truthy.
  1887. Aval.prototype.isTruthy = function() {
  1888. var base = this.base;
  1889. return (this.objs.length === 0 && ((base === 2 && this.str !== "-") ||
  1890. base === 4));
  1891. };
  1892. // optional number -> string
  1893. Aval.prototype.toString = function(indent) {
  1894. var i1 = buildString(indent || 0, " ");
  1895. var i = 1, base = mergeBoolsInBase(this.base), types = [];
  1896. var basetypes = {1 : "number", 2 : "string", 4 : "boolean",
  1897. 8 : "undefined", 16 : "null"};
  1898. while (i <= 16) {
  1899. if ((base & i) === i) types.push(basetypes[i]);
  1900. i *= 2;
  1901. }
  1902. return (i1 + "Base: " + types.join(", ") + "\n" +
  1903. i1 + "Objects: " + this.objs.join(", ") + "\n");
  1904. };
  1905. // Aval, Aval -> Aval
  1906. function avjoin(v1, v2) {
  1907. var os1 = v1.objs, os1l = os1.length, os2 = v2.objs, os2l = os2.length;
  1908. var b1 = v1.base, b2 = v2.base, av = makeBaseAval(b1 | b2);
  1909. if (av.base & 2) {
  1910. if (b1 & 2) {
  1911. if (!(b2 & 2) || v1.str === v2.str)
  1912. av.str = v1.str;
  1913. }
  1914. else // (b2 & 2) is truthy
  1915. av.str = v2.str;
  1916. }
  1917. if (os1l === 0)
  1918. av.objs = os2; // need a copy of os2 here? I think not.
  1919. else if (os2l === 0)
  1920. av.objs = os1; // need a copy of os1 here? I think not.
  1921. else if (os1 === os2)
  1922. av.objs = os1;
  1923. else if (os1l === os2l) {
  1924. for (var i = 0; i < os1l; i++) if (os1[i] !== os2[i]) break;
  1925. if (i === os1l) {
  1926. av.objs = v2.objs = os1;
  1927. return av;
  1928. }
  1929. else
  1930. av.objs = arraymerge(os1, os2);
  1931. }
  1932. else // merge the two arrays
  1933. av.objs = arraymerge(os1, os2);
  1934. return av;
  1935. }
  1936. // Aval or undefined, Aval or undefined -> Aval or undefined
  1937. function maybeavjoin(v1, v2) {
  1938. if (!v1) return v2;
  1939. if (!v2) return v1;
  1940. return avjoin(v1, v2);
  1941. }
  1942. // Aval, Aval -> boolean
  1943. // compares abstract values for equality
  1944. function aveq(v1, v2) {
  1945. if (v1.base !== v2.base) return false;
  1946. if (v1.str !== v2.str) return false;
  1947. var os1 = v1.objs, os2 = v2.objs, len = os1.length, i;
  1948. if (len !== os2.length) return false;
  1949. if (os1 === os2) return true;
  1950. for (i=0; i<len; i++) if (os1[i] !== os2[i]) return false;
  1951. // os2 = os1; // Some extra sharing is possible.
  1952. return true;
  1953. }
  1954. // Aval, Aval -> boolean
  1955. // returns true if v1 is less than v2
  1956. function avlt(v1, v2) {
  1957. var b1 = v1.base, b2 = v2.base;
  1958. if (b1 > (b1 & b2)) return false;
  1959. if ((b1 & 2) && ("str" in v2) && v2.str !== v1.str)
  1960. return false;
  1961. var os1 = v1.objs, os1l = os1.length, os2 = v2.objs, os2l = os2.length;
  1962. if (os1l === 0 || os1 === os2) return true;
  1963. if (os1l > os2l) return false;
  1964. for (var i = 0, j = 0; i < os1l; i++) {
  1965. while (os2[j] < os1[i]) j++;
  1966. if (j === os2l || os1[i] !== os2[j])
  1967. return false; // there's an elm is os1 that's not in os2
  1968. }
  1969. return true;
  1970. }
  1971. // function node -> Aval
  1972. // If the program doesn't set a function's prototype property, create default.
  1973. function makeDefaultProto(n) {
  1974. var paddr = n.defaultProtoAddr;
  1975. var o = new Aobj({ addr: paddr, proto: object_prototype_av });
  1976. o["constructor-"] = new Aprop({aval: makeObjAval(n.addr), enum: false});
  1977. return makeObjAval(paddr);
  1978. }
  1979. // heap address, Aval -> void
  1980. function updateHeapAv(addr, newv) {
  1981. var oldv = heap[addr]; //oldv shouldn't be undefined
  1982. if (!avlt(newv, oldv)) {
  1983. heap[addr] = avjoin(oldv, newv);
  1984. //print("++ts: 7");
  1985. modified[addr] = ++timestamp;
  1986. }
  1987. }
  1988. // abstract plus
  1989. function aplus(v1, v2) {
  1990. if (v1.objs.length !== 0 || v2.objs.length !== 0)
  1991. return makeBaseAval(3);
  1992. var base = ((v1.base | v2.base) & 2); // base is 0 or 2
  1993. if ((v1.base & 61) !== 0 && (v2.base & 61) !== 0) base |= 1;
  1994. return makeBaseAval(base);
  1995. }
  1996. // Invariant: the following code should know nothing about the representation
  1997. // of abstract values.
  1998. ////////////////////////////////////////////////////////////////////////////////
  1999. ///////////////////// CORE AND CLIENT-SIDE OBJECTS //////////////////////////
  2000. ////////////////////////////////////////////////////////////////////////////////
  2001. var global_object_av;
  2002. var global_object_av_addr;
  2003. var object_prototype_av;
  2004. var function_prototype_av;
  2005. var array_constructor;
  2006. var regexp_constructor;
  2007. // Used to automatically convert base values to objs and call methods on them
  2008. var generic_number_av;
  2009. var generic_string_av;
  2010. var generic_boolean_av;
  2011. // string, function, number -> function node
  2012. function funToNode(name, code, arity) {
  2013. var n = new Node({}, {type:FUNCTION});
  2014. n.fname = idNode({}, name);
  2015. n.builtin = true;
  2016. n.addr = newCount();
  2017. pending[count] = 0;
  2018. // built-in funs have no params property but they have an arity property
  2019. // instead. It's only used by the apply method.
  2020. n.arity = arity;
  2021. n.body = code;
  2022. return n;
  2023. }
  2024. // Aobj, string, function, number -> void
  2025. function attachMethod(o, mname, mcode, arity) {
  2026. var n = funToNode(mname, mcode, arity), addr = n.addr;
  2027. var fobj = new Aobj({ addr: addr,
  2028. proto: function_prototype_av,
  2029. fun: n });
  2030. o.addProp(mname + "-", { aval: makeObjAval(addr), enum : false });
  2031. return fobj;
  2032. }
  2033. // create the JS core objects in heap & fill in core
  2034. function initCoreObjs() {
  2035. function toStr(args) { return new Ans(ASTR); }
  2036. function toNum(args) { return new Ans(ANUM); }
  2037. function toBool(args) { return new Ans(ABOOL); }
  2038. function toThis(args) { return new Ans(args[0]); }
  2039. // Global object
  2040. var go = new Aobj({ addr: newCount() }), goav = makeObjAval(count);
  2041. global_object_av = heap[newCount()] = goav;
  2042. global_object_av_addr = count;
  2043. // global identifiers and methods
  2044. go.addProp("Infinity-", {aval:ANUM, write:false, enum:false, config:false});
  2045. go.addProp("NaN-", {aval:ANUM, write:false, enum:false, config:false});
  2046. go.addProp("undefined-",{aval:AUNDEF, write:false, enum:false, config:false});
  2047. // Object.prototype
  2048. var op = new Aobj({ addr: newCount() }), opav = makeObjAval(count);
  2049. object_prototype_av = opav;
  2050. // Object.__proto__ (same as Function.prototype)
  2051. var o_p = new Aobj({ addr: newCount(), proto: opav });
  2052. var o_pav = makeObjAval(count);
  2053. function_prototype_av = o_pav;
  2054. // Function.prototype.prototype
  2055. var fpp = new Aobj({ addr: newCount(), proto: opav });
  2056. o_p.addProp("prototype-",{aval:makeObjAval(count), enum:false, config:false});
  2057. fpp.addProp("constructor-", {aval : o_pav, enum : false});
  2058. // Object
  2059. var _Object = (function () {
  2060. // This object is used when Object is called w/out new.
  2061. // In reality the behavior doesn't change. In CFA, when there is no new,
  2062. // it's better to bind THIS to nonewav instead of the global object.
  2063. new Aobj({ addr: newCount(), proto: opav });
  2064. var nonewav = makeObjAval(count);
  2065. return function (args, withNew) {
  2066. var retval = withNew ? args[0] : nonewav;
  2067. var arg = args[1];
  2068. if (!arg) {
  2069. retval.forEachObj(function (o) { o.updateProto(opav); });
  2070. return new Ans(retval);
  2071. }
  2072. else {
  2073. // throw errorWithCode(CFA_ERROR, "call a suitable constructor, " +
  2074. // "hasn't been defined yet. FIXME");
  2075. retval.forEachObj(function (o) { o.updateProto(opav); });
  2076. return new Ans(retval);
  2077. }
  2078. };
  2079. })();
  2080. // Object is a heap var that will contain an Aval that points to o
  2081. var o = attachMethod(go, "Object", _Object, 0), oav = makeObjAval(count);
  2082. o.addProp("prototype-", {aval:opav, write:false, enum:false, config:false});
  2083. op.addProp("constructor-", {aval: oav, enum: false});
  2084. // Function
  2085. var f = new Aobj({addr: newCount(), proto: o_pav}), fav = makeObjAval(count);
  2086. go.addProp("Function-",{aval : fav, enum : false});
  2087. f.addProp("prototype-", {aval:o_pav, write:false, enum:false, config:false});
  2088. o_p.addProp("constructor-", {aval : fav, enum : false});
  2089. // Methods are attached here because o_pav must be defined already.
  2090. attachMethod(go, "isFinite", toBool, 1);
  2091. attachMethod(go, "isNaN", toBool, 1);
  2092. attachMethod(go, "parseInt", toNum, 1);
  2093. attachMethod(op, "hasOwnProperty", toBool, 1);
  2094. attachMethod(op, "toString", toStr, 0);
  2095. attachMethod(op, "valueOf", toThis, 0);
  2096. attachMethod(o_p, "toString", toStr, 0);
  2097. attachMethod(o_p, "call",
  2098. function(args, withNew, cn) {
  2099. var f = args.shift();
  2100. args[0] || args.unshift(global_object_av);
  2101. return f.callFun(args, cn);
  2102. }, 0);
  2103. attachMethod(o_p, "apply",
  2104. function(args, withNew, cn) {
  2105. var recv = args[1] || global_object_av, a2 = args[2], rands,
  2106. av, maxArity = 0, restargs, i, ans, retval = BOTTOM, errval;
  2107. // We can't compute the arguments once for all functions that
  2108. // may be applied. The functions may have different arity which
  2109. // impacts what goes to the restargs for each function.
  2110. args[0].forEachObj(function(o) {
  2111. var clos = o.getFun(), pslen, i;
  2112. if (!clos) return;
  2113. if (clos.builtin)
  2114. pslen = clos.arity;
  2115. else
  2116. pslen = clos.params.length;
  2117. // compute arguments
  2118. restargs = BOTTOM;
  2119. rands = buildArray(pslen, BOTTOM);
  2120. if (a2) { // a2 is the array passed at the call to apply.
  2121. a2.forEachObj(function(o) {
  2122. if (o.numPropsMerged) {
  2123. av = o.getNumProps();
  2124. restargs = avjoin(restargs, av);
  2125. for (i = 0; i < pslen; i++)
  2126. rands[i] = avjoin(rands[i], av);
  2127. }
  2128. else {
  2129. for (i = 0; i < pslen; i++) {
  2130. av = o.getOwnExactProp(i + "-") || AUNDEF;
  2131. rands[i] = avjoin(rands[i], av);
  2132. }
  2133. while (true) { // search for extra arguments
  2134. av = o.getOwnExactProp(i++ + "-");
  2135. // to find the end of the array, we must see that
  2136. // an elm *definitely* doesn't exist, different
  2137. // from AUNDEF
  2138. if (!av) break;
  2139. restargs = avjoin(restargs, av);
  2140. }
  2141. }
  2142. });
  2143. }
  2144. else {
  2145. rands = buildArray(pslen, BOTTOM);
  2146. }
  2147. // do function call
  2148. rands.unshift(recv);
  2149. rands.push(restargs);
  2150. ans = evalFun(clos, rands, false, cn);
  2151. retval = avjoin(retval, ans.v);
  2152. errval = maybeavjoin(errval, ans.err);
  2153. });
  2154. return new Ans(retval, undefined, errval);
  2155. }, 2);
  2156. (function () {
  2157. // Array.prototype
  2158. var ap = new Aobj({ addr: newCount(), proto: opav });
  2159. var apav = makeObjAval(count);
  2160. function putelms(args) {
  2161. var i, len = args.length;
  2162. args[0].forEachObj(function (o) {
  2163. for (i = 1; i < len; i++) o.updateNumProps(args[i]);
  2164. });
  2165. return new Ans(ANUM);
  2166. }
  2167. function getelms(args) {
  2168. var av = BOTTOM;
  2169. args[0].forEachObj(function (o) { av = avjoin(av, o.getNumProps()); });
  2170. return new Ans(av);
  2171. }
  2172. attachMethod(ap, "concat",
  2173. // lose precision by not creating a new array
  2174. function(args) {
  2175. var thisarr = args[0], av = BOTTOM;
  2176. // if arg is base, join it, if it's array join its elms
  2177. for (var i = 1, l = args.length; i < l; i++) {
  2178. var avarg = args[i];
  2179. av = avjoin(av, avarg.getBase());
  2180. avarg.forEachObj(function(o) {
  2181. if (o.isArray()) av = avjoin(av, o.getNumProps());
  2182. });
  2183. thisarr.forEachObj(function(o) { o.updateNumProps(av); });
  2184. }
  2185. return new Ans(thisarr);
  2186. }, 0);
  2187. attachMethod(ap, "join", toStr, 1);
  2188. attachMethod(ap, "pop", getelms, 0);
  2189. attachMethod(ap, "push", putelms, 0);
  2190. attachMethod(ap, "slice", toThis, 2);
  2191. attachMethod(ap, "sort", toThis, 1);
  2192. attachMethod(ap, "splice", toThis, 0);
  2193. attachMethod(ap, "shift", getelms, 0);
  2194. attachMethod(ap, "toString", toStr, 0);
  2195. attachMethod(ap, "unshift", putelms, 0);
  2196. // Array
  2197. var _Array = (function () {
  2198. // This object is used when Array is called w/out new
  2199. new Aobj({ addr: newCount(), proto: apav });
  2200. var nonewav = makeObjAval(count);
  2201. return function(args, withNew) {
  2202. var retval = withNew ? args[0] : nonewav;
  2203. var arglen = args.length;
  2204. retval.forEachObj(function (o) {
  2205. o.updateProto(apav);
  2206. if (o.getOwnExactProp("length-"))
  2207. o.updateProp("length-", ANUM);
  2208. else
  2209. o.addProp("length-", {aval : ANUM, enum : false});
  2210. });
  2211. if (arglen <= 2) // new Array(), new Array(size)
  2212. ;
  2213. else { // new Array(elm1, ... , elmN)
  2214. retval.forEachObj(function (o) {
  2215. for (var i = 1; i < arglen; i++)
  2216. o.updateProp((i - 1) + "-", args[i]);
  2217. });
  2218. }
  2219. return new Ans(retval);
  2220. };
  2221. })();
  2222. array_constructor = _Array;
  2223. var a = attachMethod(go, "Array", _Array, 0), aav = makeObjAval(count);
  2224. a.addProp("prototype-", {aval:apav, write:false, enum:false, config:false});
  2225. ap.addProp("constructor-", {aval : aav, enum : false});
  2226. })();
  2227. (function () {
  2228. // Number.prototype
  2229. var np = new Aobj({ addr: newCount(), proto: opav });
  2230. var npav = makeObjAval(count);
  2231. attachMethod(np, "toString", toStr, 0);
  2232. attachMethod(np, "valueOf", toNum, 0);
  2233. // create generic number object
  2234. new Aobj({ addr: newCount(), proto: npav});
  2235. generic_number_av = makeObjAval(count);
  2236. // Number
  2237. function _Number(args, withNew) {
  2238. if (withNew) {
  2239. args[0].forEachObj(function (o) { o.updateProto(npav); });
  2240. return new Ans(args[0]);
  2241. }
  2242. return new Ans(ANUM);
  2243. }
  2244. var n = attachMethod(go, "Number", _Number, 0), nav = makeObjAval(count);
  2245. n.addProp("prototype-", {aval:npav, write:false, enum:false, config:false});
  2246. np.addProp("constructor-", {aval : nav, enum : false});
  2247. })();
  2248. (function () {
  2249. // String.prototype
  2250. var sp = new Aobj({ addr: newCount(), proto: opav });
  2251. var spav = makeObjAval(count);
  2252. attachMethod(sp, "charAt", toStr, 1);
  2253. attachMethod(sp, "charCodeAt", toNum, 1);
  2254. attachMethod(sp, "indexOf", toNum, 2);
  2255. attachMethod(sp, "lastIndexOf", toNum, 2);
  2256. // all Arrays returned by calls to match are merged in one
  2257. var omatch = new Aobj({ addr: newCount() });
  2258. var omatchav = avjoin(ANULL, makeObjAval(count));
  2259. array_constructor([omatchav], true);
  2260. omatch.updateNumProps(ASTR);
  2261. omatch.addProp("index-", {aval : ANUM});
  2262. omatch.addProp("input-", {aval : ASTR});
  2263. attachMethod(sp, "match", function(args) { return new Ans(omatchav); }, 1);
  2264. attachMethod(sp, "replace", toStr, 2);
  2265. attachMethod(sp, "slice", toStr, 2);
  2266. attachMethod(sp, "substr", toStr, 2);
  2267. attachMethod(sp, "substring", toStr, 2);
  2268. attachMethod(sp, "toLowerCase", toStr, 0);
  2269. attachMethod(sp, "toString", toStr, 0);
  2270. attachMethod(sp, "toUpperCase", toStr, 0);
  2271. // all Arrays returned by calls to split are merged in one
  2272. var osplit = new Aobj({ addr: newCount() });
  2273. var osplitav = makeObjAval(count);
  2274. array_constructor([osplitav], true);
  2275. osplit.updateNumProps(ASTR);
  2276. attachMethod(sp, "split", function(args) {
  2277. return new Ans(osplitav);
  2278. }, 2);
  2279. attachMethod(sp, "valueOf", toStr, 0);
  2280. // create generic string object
  2281. new Aobj({ addr: newCount(), proto: spav });
  2282. generic_string_av = makeObjAval(count);
  2283. // String
  2284. function _String(args, withNew) {
  2285. if (withNew) {
  2286. args[0].forEachObj(function (o) { o.updateProto(spav); });
  2287. return new Ans(args[0]);
  2288. }
  2289. return new Ans(ASTR);
  2290. }
  2291. var s = attachMethod(go, "String", _String, 1), sav = makeObjAval(count);
  2292. s.addProp("prototype-", {aval:spav, write:false, enum:false, config:false});
  2293. sp.addProp("constructor-", {aval : sav, enum : false});
  2294. attachMethod(s, "fromCharCode", toStr, 0);
  2295. })();
  2296. (function () {
  2297. // Error.prototype
  2298. var ep = new Aobj({ addr: newCount(), proto: opav });
  2299. var epav = makeObjAval(count);
  2300. attachMethod(ep, "toString", toStr, 0);
  2301. // Error
  2302. function _Error(args) {
  2303. args[0].forEachObj(function (o) {
  2304. o.updateProto(epav);
  2305. o.updateProp("message-", args[1] || ASTR);
  2306. });
  2307. return new Ans(args[0]);
  2308. }
  2309. var e = attachMethod(go, "Error", _Error, 1), eav = makeObjAval(count);
  2310. e.addProp("prototype-", {aval:epav, write:false, enum:false, config:false});
  2311. ep.addProp("constructor-", {aval : eav, enum : false});
  2312. ep.addProp("name-", {aval : ASTR, enum : false});
  2313. // SyntaxError.prototype
  2314. var sep = new Aobj({ addr: newCount(), proto: epav });
  2315. var sepav = makeObjAval(count);
  2316. // SyntaxError
  2317. function _SyntaxError(args) {
  2318. args[0].forEachObj(function (o) {
  2319. o.updateProto(sepav);
  2320. o.addProp("message-", {aval : ASTR});
  2321. });
  2322. return new Ans(args[0]);
  2323. }
  2324. var se = attachMethod(go, "SyntaxError", _SyntaxError, 1);
  2325. var seav = makeObjAval(count);
  2326. se.addProp("prototype-",{aval:sepav, write:false, enum:false,config:false});
  2327. sep.addProp("constructor-", {aval : seav, enum : false});
  2328. sep.addProp("name-", {aval : ASTR});
  2329. })();
  2330. (function () {
  2331. // RegExp.prototype
  2332. var rp = new Aobj({ addr: newCount(), proto: opav });
  2333. var rpav = makeObjAval(count);
  2334. // all Arrays returned by calls to exec are merged in one
  2335. var oexec = new Aobj({ addr: newCount() });
  2336. var oexecav = avjoin(ANULL, makeObjAval(count));
  2337. array_constructor([oexecav], true);
  2338. oexec.updateNumProps(ASTR);
  2339. oexec.addProp("index-", {aval : ANUM});
  2340. oexec.addProp("input-", {aval : ASTR});
  2341. attachMethod(rp, "exec", function(args) { return new Ans(oexecav); }, 1);
  2342. attachMethod(rp, "test", toBool, 1);
  2343. // RegExp
  2344. function _RegExp(args) {
  2345. args[0].forEachObj(function (o) {
  2346. o.updateProto(rpav);
  2347. o.addProp("global-",{aval:ABOOL, write:false, enum:false,config:false});
  2348. o.addProp("ignoreCase-",
  2349. {aval:ABOOL, write:false, enum:false, config:false});
  2350. o.addProp("lastIndex-", {aval : ANUM, enum : false, config : false});
  2351. o.addProp("multiline-",
  2352. {aval:ABOOL, write:false, enum:false, config:false});
  2353. o.addProp("source-",{aval:ASTR, write:false, enum:false, config:false});
  2354. });
  2355. return new Ans(args[0]);
  2356. }
  2357. regexp_constructor = _RegExp;
  2358. var r = attachMethod(go, "RegExp", _RegExp, 2), rav = makeObjAval(count);
  2359. r.addProp("prototype-", {aval:rpav, write:false, enum:false, config:false});
  2360. rp.addProp("constructor-", {aval : rav, enum : false});
  2361. })();
  2362. (function () {
  2363. // Date.prototype
  2364. var dp = new Aobj({ addr: newCount(), proto: opav });
  2365. var dpav = makeObjAval(count);
  2366. attachMethod(dp, "getDate", toNum, 0);
  2367. attachMethod(dp, "getDay", toNum, 0);
  2368. attachMethod(dp, "getFullYear", toNum, 0);
  2369. attachMethod(dp, "getHours", toNum, 0);
  2370. attachMethod(dp, "getMilliseconds", toNum, 0);
  2371. attachMethod(dp, "getMinutes", toNum, 0);
  2372. attachMethod(dp, "getMonth", toNum, 0);
  2373. attachMethod(dp, "getSeconds", toNum, 0);
  2374. attachMethod(dp, "getTime", toNum, 0);
  2375. attachMethod(dp, "getTimezoneOffset", toNum, 0);
  2376. attachMethod(dp, "getYear", toNum, 0);
  2377. attachMethod(dp, "setTime", toNum, 1);
  2378. attachMethod(dp, "toString", toStr, 0);
  2379. attachMethod(dp, "valueOf", toNum, 0);
  2380. // Date
  2381. function _Date(args, withNew) {
  2382. if (withNew) {
  2383. args[0].forEachObj(function (o) { o.updateProto(dpav); });
  2384. return new Ans(args[0]);
  2385. }
  2386. return new Ans(ASTR);
  2387. }
  2388. var d = attachMethod(go, "Date", _Date, 0), dav = makeObjAval(count);
  2389. d.addProp("prototype-", {aval:dpav, write:false, enum:false, config:false});
  2390. dp.addProp("constructor-", {aval : dav, enum : false});
  2391. })();
  2392. (function () {
  2393. // Math
  2394. var m = new Aobj({ addr: newCount(), proto: opav });
  2395. var mav = makeObjAval(count);
  2396. go.addProp("Math-", {aval : mav, enum : false});
  2397. m.addProp("constructor-", {aval : oav, enum : false});
  2398. m.addProp("E-", {aval : ANUM, write : false, enum : false, config : false});
  2399. m.addProp("LN10-",{aval : ANUM, write : false, enum : false, config:false});
  2400. m.addProp("LN2-", {aval : ANUM, write : false, enum : false, config:false});
  2401. m.addProp("LOG10E-", {aval : ANUM, write:false, enum:false, config:false});
  2402. m.addProp("LOG2E-", {aval : ANUM, write:false, enum:false, config:false});
  2403. m.addProp("PI-", {aval : ANUM, write : false, enum : false, config :false});
  2404. m.addProp("SQRT1_2-", {aval : ANUM, write:false, enum:false, config:false});
  2405. m.addProp("SQRT2-",{aval:ANUM, write:false, enum:false, config:false});
  2406. attachMethod(m, "abs", toNum, 1);
  2407. attachMethod(m, "acos", toNum, 1);
  2408. attachMethod(m, "asin", toNum, 1);
  2409. attachMethod(m, "atan", toNum, 1);
  2410. attachMethod(m, "atan2", toNum, 1);
  2411. attachMethod(m, "ceil", toNum, 1);
  2412. attachMethod(m, "cos", toNum, 1);
  2413. attachMethod(m, "exp", toNum, 1);
  2414. attachMethod(m, "floor", toNum, 1);
  2415. attachMethod(m, "log", toNum, 1);
  2416. attachMethod(m, "max", toNum, 0);
  2417. attachMethod(m, "min", toNum, 0);
  2418. attachMethod(m, "pow", toNum, 2);
  2419. attachMethod(m, "random", toNum, 0);
  2420. attachMethod(m, "round", toNum, 1);
  2421. attachMethod(m, "sin", toNum, 1);
  2422. attachMethod(m, "sqrt", toNum, 1);
  2423. attachMethod(m, "tan", toNum, 1);
  2424. })();
  2425. (function () {
  2426. // Boolean.prototype
  2427. var bp = new Aobj({ addr: newCount(), proto: opav });
  2428. var bpav = makeObjAval(count);
  2429. attachMethod(bp, "toString", toStr, 0);
  2430. attachMethod(bp, "valueOf", toBool, 0);
  2431. // create generic boolean object
  2432. new Aobj({ addr: newCount(), proto: bpav });
  2433. generic_boolean_av = makeObjAval(count);
  2434. // Boolean
  2435. function _Boolean(args, withNew) {
  2436. if (withNew) {
  2437. args[0].forEachObj(function (o) { o.updateProto(bpav); });
  2438. return new Ans(args[0]);
  2439. }
  2440. return new Ans(ABOOL);
  2441. }
  2442. var b = attachMethod(go, "Boolean", _Boolean, 1), bav = makeObjAval(count);
  2443. b.addProp("prototype-", {aval:bpav, write:false, enum:false, config:false});
  2444. bp.addProp("constructor-", {aval : bav, enum : false});
  2445. })();
  2446. }
  2447. ////////////////////////////////////////////////////////////////////////////////
  2448. ////////////////////////// EVALUATION PREAMBLE /////////////////////////////
  2449. ////////////////////////////////////////////////////////////////////////////////
  2450. // frame, identifier node, Aval -> void
  2451. function frameSet(fr, param, val) {
  2452. fr[param.addr] = [val, timestamp]; // record when param was bound to val
  2453. }
  2454. // frame, identifier node -> Aval
  2455. function frameGet(fr, param) {
  2456. var pa = param.addr, binding = fr[pa];
  2457. if (binding[1] < modified[pa]) {
  2458. // if binding changed in heap, change it in frame to be sound
  2459. binding[0] = avjoin(binding[0], heap[pa]);
  2460. binding[1] = timestamp;
  2461. }
  2462. return binding[0];
  2463. }
  2464. // fun. node, array of Aval, timestamp -> [Aval, Aval] or false
  2465. function searchSummary(n, args, ts) {
  2466. var n_summaries = summaries[n.addr], insouts, summary;
  2467. if (n_summaries.ts < ts) return false;
  2468. insouts = n_summaries.insouts;
  2469. // Start from the end to find the elm that was pushed last
  2470. for (var i = insouts.length - 1; i >= 0; i--) {
  2471. summary = insouts[i];
  2472. // If no widening, turn avlt to aveq in the next line.
  2473. if (arrayeq(avlt, args, summary[0])) return summary.slice(-2);
  2474. }
  2475. return false;
  2476. }
  2477. // function node -> boolean
  2478. // check if any summary exists for this function node
  2479. function existsSummary(n) {
  2480. return summaries[n.addr].ts !== INVALID_TIMESTAMP;
  2481. }
  2482. // fun. node, array of Aval, Aval, Aval or undefined, timestamp -> void
  2483. function addSummary(n, args, retval, errval, ts) {
  2484. var addr = n.addr, summary = summaries[addr];
  2485. if (summary.ts === ts)
  2486. summary.insouts.push([args, retval, errval]);
  2487. else if (summary.ts < ts) { // discard summaries for old timestamps.
  2488. summary.ts = ts;
  2489. summary.insouts = [[args, retval, errval]];
  2490. }
  2491. // join new summary w/ earlier ones.
  2492. var insjoin = summary.type[0];
  2493. for (var i = 0, len = insjoin.length; i < len; i++)
  2494. insjoin[i] = avjoin(insjoin[i], args[i] || AUNDEF/*arg mismatch*/);
  2495. summary.type[1] = avjoin(summary.type[1], retval);
  2496. summary.type[2] = maybeavjoin(summary.type[2], errval);
  2497. }
  2498. function showSummaries() {
  2499. for (var addr in summaries) {
  2500. var f = heap[addr].getFun();
  2501. //print(f.fname.name + ": " + funToType(f));
  2502. }
  2503. }
  2504. // function node, array of Aval -> number or undefined
  2505. // How evalFun interprets the return value of searchPending
  2506. // Zero: new frame on the stack
  2507. // Positive number: throw to clear the stack b/c of timestamp increase
  2508. // Negative number: throw to clear the stack during recursion
  2509. // undefined: return w/out throwing during recursion
  2510. function searchPending(n, args) {
  2511. var bucket = pending[n.addr], len = bucket.length, i;
  2512. // We use the number of pending calls to n to clear the stack.
  2513. if (len === 0) return 0;
  2514. if (bucket[0].ts < timestamp) return len;
  2515. // Invariant: no two sets of args are related in \sqsubseteq.
  2516. for (i = 0; i < len; i++) {
  2517. // No need to keep going, a more general frame is pending.
  2518. if (arrayeq(avlt, args, bucket[i].args))
  2519. return;
  2520. // The deeper frame can be widened, throw to it.
  2521. else if (arrayeq(avlt, bucket[i].args, args))
  2522. return i - len;
  2523. }
  2524. return 0;
  2525. }
  2526. // function node, {args, timestamp} -> void
  2527. function addPending(n, elm) { pending[n.addr].push(elm); }
  2528. // function node -> {args, timestamp}
  2529. function rmPending(n) {
  2530. // // Uncomment when debugging
  2531. // var elm = pending[n.addr].pop();
  2532. // if (!elm) throw errorWithCode(CFA_ERROR, "Remove from empty pending.");
  2533. // return elm;
  2534. return pending[n.addr].pop();
  2535. }
  2536. // evalExp & friends use Ans to return tuples
  2537. function Ans(v, fr, err) {
  2538. this.v = v; // evalExp puts abstract values here, evalStm puts statements
  2539. this.fr = fr; // frame
  2540. err && (this.err = err); // Aval for exceptions thrown
  2541. }
  2542. // Initialize the heap for each fun decl, var decl and heap var.
  2543. // Because of this function, we never get undefined by reading from heap.
  2544. // Must be run after initGlobals and after initCoreObjs.
  2545. // Most Aobj`s that aren't core are created here.
  2546. var initDeclsInHeap, initDeclsInHeap_override;
  2547. (function() {
  2548. var walkerobj = walkASTgenerator(), override = walkerobj.override;
  2549. initDeclsInHeap = walkerobj.walkAST;
  2550. initDeclsInHeap_override = override;
  2551. override(REGEXP, function(n) {
  2552. new Aobj({ addr: n.addr });
  2553. regexp_constructor([makeObjAval(n.addr)]);
  2554. });
  2555. // FIXME?: when array elms have the same type, they can be prematurely merged
  2556. // to help the speed of the algo e.g. in 3d-cube
  2557. override(ARRAY_INIT, function(n) {
  2558. new Aobj({ addr: n.addr });
  2559. array_constructor([makeObjAval(n.addr)], true);
  2560. n.children.forEach(initDeclsInHeap);
  2561. });
  2562. override(OBJECT_INIT, function(n) {
  2563. new Aobj({ addr: n.addr, proto: object_prototype_av });
  2564. n.children.forEach(function(prop) {
  2565. initDeclsInHeap(prop.children[0]);
  2566. initDeclsInHeap(prop.children[1]);
  2567. });
  2568. });
  2569. override(NEW_WITH_ARGS, function(n) {
  2570. new Aobj({ addr: n.addr });
  2571. initDeclsInHeap(n.children[0]);
  2572. n.children[1].children.forEach(initDeclsInHeap);
  2573. });
  2574. override(TRY, function(n) {
  2575. initDeclsInHeap(n.tryBlock);
  2576. n.catchClauses.forEach(function(c) {
  2577. if (c.exvar.kind === HEAP) heap[c.exvar.addr] = BOTTOM;
  2578. c.guard && initDeclsInHeap(c.guard);
  2579. initDeclsInHeap(c.block);
  2580. });
  2581. n.finallyBlock && initDeclsInHeap(n.finallyBlock);
  2582. });
  2583. function _function(n) {
  2584. var objaddr = n.addr, fn = n.fname,
  2585. obj = new Aobj({ addr: objaddr, fun: n, proto: function_prototype_av });
  2586. obj.addProp("prototype-", {aval:BOTTOM, enum:false});
  2587. if (fn.kind === HEAP)
  2588. heap[fn.addr] = makeObjAval(objaddr);
  2589. n.params.forEach(function(p) {if (p.kind === HEAP) heap[p.addr] = BOTTOM;});
  2590. flags[objaddr] = n;
  2591. // initialize summaries and pending
  2592. summaries[objaddr] = {
  2593. ts: INVALID_TIMESTAMP,
  2594. insouts: [],
  2595. type: [buildArray(n.params.length + 1, BOTTOM), BOTTOM]//arg 0 is for THIS
  2596. };
  2597. pending[objaddr] = [];
  2598. initDeclsInHeap(n.body);
  2599. }
  2600. override(FUNCTION, _function);
  2601. override(SCRIPT, function(n) {
  2602. n.funDecls.forEach(_function);
  2603. n.varDecls.forEach(function(vd){if (flags[vd.addr]) heap[vd.addr]=BOTTOM;});
  2604. n.children.forEach(initDeclsInHeap);
  2605. });
  2606. })();
  2607. // void -> Aval
  2608. // Used to analyze functions that aren't called
  2609. function makeGenericObj() {
  2610. new Aobj({ addr: newCount(), proto: object_prototype_av });
  2611. return makeObjAval(count);
  2612. }
  2613. ////////////////////////////////////////////////////////////////////////////////
  2614. ////////////////////////// EVALUATION FUNCTIONS ////////////////////////////
  2615. ////////////////////////////////////////////////////////////////////////////////
  2616. // function for evaluating lvalues
  2617. // node, Ans, optional Aval -> Ans
  2618. // use n to get an lvalue, do the assignment and return the rvalue
  2619. var evalLval;
  2620. (function() {
  2621. var walkerobj = walkASTgenerator(), override = walkerobj.override;
  2622. evalLval = walkerobj.walkAST;
  2623. function _stackref(n, ans, oldlval) {
  2624. if (n.assignOp) {
  2625. if (n.assignOp === PLUS)
  2626. ans.v = aplus(ans.v, oldlval);
  2627. else
  2628. ans.v = ANUM;
  2629. }
  2630. var newav = avjoin(frameGet(ans.fr, n), ans.v);
  2631. frameSet(ans.fr, n, newav);
  2632. // if n is a heap var, update heap so its heap refs get the correct Aval.
  2633. if (flags[n.addr]) updateHeapAv(n.addr, newav);
  2634. return ans;
  2635. }
  2636. function _heapref(n, ans, oldlval) {
  2637. if (n.assignOp) {
  2638. if (n.assignOp === PLUS)
  2639. ans.v = aplus(ans.v, oldlval);
  2640. else
  2641. ans.v = ANUM;
  2642. }
  2643. updateHeapAv(n.addr, ans.v);
  2644. return ans;
  2645. }
  2646. override(IDENTIFIER, function(n, ans, oldlval) {
  2647. if (n.kind === STACK)
  2648. return _stackref(n, ans, oldlval);
  2649. else
  2650. return _heapref(n, ans, oldlval);
  2651. });
  2652. override(INDEX, function(n, ans, oldlval) {
  2653. var rval = ans.v, fr = ans.fr, errval, ch = n.children;
  2654. if (n.assignOp) {
  2655. if (n.assignOp === PLUS)
  2656. rval = aplus(rval, oldlval);
  2657. else
  2658. rval = ANUM;
  2659. }
  2660. var prop = ch[1];
  2661. var ansobj = evalExp(ch[0], fr), avobj = ansobj.v;
  2662. fr = ansobj.fr;
  2663. errval = ansobj.err;
  2664. // Unsound: ignore everything the index can eval to except numbers & strings
  2665. if (prop.type === STRING)
  2666. avobj.updateProp(prop.value, rval);
  2667. else {
  2668. var ansprop = evalExp(prop, fr), avprop = ansprop.v;
  2669. fr = ansprop.fr;
  2670. errval = maybeavjoin(errval, ansprop.err);
  2671. if (avprop.hasNum())
  2672. avobj.forEachObj(function(o) { o.updateNumProps(rval); });
  2673. if (avprop.hasStr()) {
  2674. var slit = avprop.getStrLit();
  2675. if (slit)
  2676. avobj.updateProp(slit, rval);
  2677. else
  2678. avobj.forEachObj(function(o) { o.updateStrProps(rval); });
  2679. }
  2680. }
  2681. return new Ans(rval, fr, maybeavjoin(errval, ans.err));
  2682. });
  2683. function _dot(n, ans, oldlval) {
  2684. if (n.assignOp) {
  2685. if (n.assignOp === PLUS)
  2686. ans.v = aplus(ans.v, oldlval);
  2687. else
  2688. ans.v = ANUM;
  2689. }
  2690. var ch = n.children, ans2 = evalExp(ch[0], ans.fr);
  2691. ans2.v.updateProp(ch[1].value, ans.v);
  2692. ans.fr = ans2.fr;
  2693. ans.err = maybeavjoin(ans.err, ans2.err);
  2694. return ans;
  2695. }
  2696. override(DOT, _dot);
  2697. override(DOT_PROTO, _dot);
  2698. override(ARGUMENTS, function(n, ans, oldlval) {
  2699. // FIXME: handle assignment to the arguments array
  2700. return ans;
  2701. });
  2702. // in extremely rare cases, you can see a CALL as the lhs of an assignment.
  2703. override(CALL, function(n, ans, oldlval) {
  2704. return ans;
  2705. });
  2706. })();
  2707. // function for evaluating expressions
  2708. // node, frame -> Ans
  2709. var evalExp, evalExp_override;
  2710. (function() {
  2711. var walkerobj = walkASTgenerator(), override = walkerobj.override;
  2712. evalExp = walkerobj.walkAST;
  2713. evalExp_override = override;
  2714. function _stackref(n, fr) { return new Ans(frameGet(fr, n), fr); }
  2715. function _heapref(n, fr) { return new Ans(heap[n.addr], fr); }
  2716. override(IDENTIFIER, function(n, fr) {
  2717. if (n.kind === STACK)
  2718. return _stackref(n, fr);
  2719. else
  2720. return _heapref(n, fr);
  2721. });
  2722. override(NUMBER, function(n, fr) { return new Ans(ANUM, fr); });
  2723. override(STRING,
  2724. function(n, fr) { return new Ans(makeStrLitAval(n.value), fr); });
  2725. override(TRUE, function(n, fr) { return new Ans(ATRU, fr); });
  2726. override(FALSE, function(n, fr) { return new Ans(AFALS, fr); });
  2727. override(NULL, function(n, fr) { return new Ans(ANULL, fr); });
  2728. override(REGEXP, function(n, fr){ return new Ans(makeObjAval(n.addr), fr); });
  2729. override(THIS, function(n, fr) { return new Ans(fr.thisav, fr); });
  2730. function _unary2num(n, fr) {
  2731. var ans = evalExp(n.children[0], fr);
  2732. ans.v = ANUM;
  2733. return ans;
  2734. }
  2735. [UNARY_PLUS, UNARY_MINUS, INCREMENT, DECREMENT, BITWISE_NOT].forEach(
  2736. function(tt) { override(tt, _unary2num); });
  2737. override(NOT, function(n, fr) {
  2738. var ans = evalExp(n.children[0], fr), av = ans.v;
  2739. if (av.isTruthy())
  2740. ans.v = AFALS;
  2741. else if (av.isFalsy())
  2742. ans.v = ATRU;
  2743. else
  2744. ans.v = ABOOL;
  2745. return ans;
  2746. });
  2747. override(TYPEOF, function(n, fr) {
  2748. var ans = evalExp(n.children[0], fr);
  2749. ans.v = ASTR;
  2750. return ans;
  2751. });
  2752. override(VOID, function(n, fr) {
  2753. var ans = evalExp(n.children[0], fr);
  2754. ans.v = AUNDEF;
  2755. return ans;
  2756. });
  2757. override(DELETE, function(n, fr) { // unsound: I'm not deleting anything
  2758. var ans = evalExp(n.children[0], fr);
  2759. ans.v = ABOOL;
  2760. return ans;
  2761. });
  2762. override(IN, function(n, fr) {
  2763. var ans1 = evalExp(n.children[0], fr);
  2764. var ans2 = evalExp(n.children[1], ans1.fr);
  2765. ans2.err = maybeavjoin(ans1.err, ans2.err);
  2766. var pname = ans1.v.getStrLit(), ans2v = ans2.v;
  2767. if (!ans2v.hasObjs())
  2768. ans2.v = AFALS;
  2769. else if (!pname)
  2770. ans2.v = ABOOL;
  2771. else {
  2772. var av = BOTTOM;
  2773. ans2v.forEachObj(function(o) {
  2774. if (!o.getProp(pname))
  2775. av = avjoin(av, AFALS);
  2776. else
  2777. av = avjoin(av, ATRU);
  2778. });
  2779. ans2.v = av;
  2780. }
  2781. return ans2;
  2782. });
  2783. function _binary2bool(n, fr) {
  2784. var ans1 = evalExp(n.children[0], fr);
  2785. var ans2 = evalExp(n.children[1], ans1.fr);
  2786. ans2.v = ABOOL;
  2787. ans2.err = maybeavjoin(ans1.err, ans2.err);
  2788. return ans2;
  2789. }
  2790. [EQ, NE, STRICT_EQ, STRICT_NE, LT, LE, GE, GT, INSTANCEOF].forEach(
  2791. function(tt) { override(tt, _binary2bool); });
  2792. function _andor(pred1, pred2) {
  2793. return function(n, fr) {
  2794. var ans1 = evalExp(n.children[0], fr), av = ans1.v;
  2795. if (pred1.call(av)) return ans1;
  2796. var ans2 = evalExp(n.children[1], ans1.fr);
  2797. ans2.err = maybeavjoin(ans1.err, ans2.err);
  2798. if (!pred2.call(av)) ans2.v = avjoin(av, ans2.v);
  2799. return ans2;
  2800. }
  2801. }
  2802. override(AND, _andor(Aval.prototype.isFalsy, Aval.prototype.isTruthy));
  2803. override(OR, _andor(Aval.prototype.isTruthy, Aval.prototype.isFalsy));
  2804. override(PLUS, function(n, fr) {
  2805. var ans1 = evalExp(n.children[0], fr);
  2806. var ans2 = evalExp(n.children[1], ans1.fr);
  2807. ans2.v = aplus(ans1.v, ans2.v);
  2808. ans2.err = maybeavjoin(ans1.err, ans2.err);
  2809. return ans2;
  2810. });
  2811. function _binary2num(n, fr) {
  2812. var ans1 = evalExp(n.children[0], fr);
  2813. var ans2 = evalExp(n.children[1], ans1.fr);
  2814. ans2.v = ANUM;
  2815. ans2.err = maybeavjoin(ans1.err, ans2.err);
  2816. return ans2;
  2817. }
  2818. [MINUS, MUL, DIV, MOD, BITWISE_OR, BITWISE_XOR, BITWISE_AND,
  2819. LSH, RSH, URSH].forEach(function(tt) { override(tt, _binary2num); });
  2820. override(PLUS_ASSIGN, function(n, fr) {
  2821. var ch = n.children;
  2822. // recomputing ch[0] for += is better than checking every lhs in evalLval
  2823. var ans = evalExp(ch[0], fr);
  2824. return evalLval(ch[0], evalExp(ch[1], fr), ans.v);
  2825. });
  2826. override(ASSIGN, function(n, fr) {
  2827. return evalLval(n.children[0], evalExp(n.children[1], fr));
  2828. });
  2829. override(HOOK, function(n, fr) {
  2830. var ch = n.children;
  2831. var ans = evalExp(ch[0], fr), test = ans.v, av = BOTTOM, err = ans.err;
  2832. if (!test.isFalsy()) {
  2833. ans = evalExp(ch[1], ans.fr);
  2834. av = avjoin(av, ans.v);
  2835. err = maybeavjoin(err, ans.err);
  2836. }
  2837. if (!test.isTruthy()) {
  2838. ans = evalExp(ch[2], ans.fr);
  2839. av = avjoin(av, ans.v);
  2840. err = maybeavjoin(err, ans.err);
  2841. }
  2842. return new Ans(av, ans.fr, err);
  2843. });
  2844. override(FUNCTION,
  2845. function(n, fr) { return new Ans(makeObjAval(n.addr), fr); });
  2846. override(COMMA, function(n, fr) {
  2847. var ans, av, errval;
  2848. n.children.forEach(function(exp) {
  2849. ans = evalExp(exp, fr);
  2850. av = ans.v; // keep last one
  2851. fr = ans.fr;
  2852. errval = maybeavjoin(errval, ans.err);
  2853. });
  2854. ans.v = av;
  2855. ans.err = errval;
  2856. return ans;
  2857. });
  2858. override(OBJECT_INIT, function(n, fr) {
  2859. var ans, errval, objaddr = n.addr, newobj = heap[objaddr];
  2860. n.children.forEach(function(pinit) {
  2861. ans = evalExp(pinit.children[1], fr);
  2862. fr = ans.fr;
  2863. newobj.updateProp(pinit.children[0].value, ans.v);
  2864. errval = maybeavjoin(errval, ans.err);
  2865. });
  2866. return new Ans(makeObjAval(objaddr), fr, errval);
  2867. });
  2868. override(ARRAY_INIT, function(n, fr) {
  2869. var ans, errval, arrayaddr = n.addr, newarray = heap[arrayaddr];
  2870. n.children.forEach(function(elm, i) {
  2871. ans = evalExp(elm, fr);
  2872. fr = ans.fr;
  2873. newarray.updateProp(i + "-", ans.v);
  2874. errval = maybeavjoin(errval, ans.err);
  2875. });
  2876. return new Ans(makeObjAval(arrayaddr), fr, errval);
  2877. });
  2878. override(DOT_PROTO, function(n, fr) {
  2879. var ans = evalExp(n.children[0], fr), ans2, av = BOTTOM,
  2880. av2, errval = ans.err;
  2881. // FIXME: record error if ans.v contains base values
  2882. ans.v.forEachObj(function(o) {
  2883. var clos = o.getFun(), proto;
  2884. if (!clos) { // if o isn't a function, this is just a property access
  2885. av2 = o.getProp("prototype-");
  2886. av = avjoin(av, av2 || AUNDEF);
  2887. }
  2888. else {
  2889. proto = o.getProp("prototype-");
  2890. if (!aveq(BOTTOM, proto))
  2891. av = avjoin(av, proto);
  2892. else {// create default prototype and return it
  2893. proto = makeDefaultProto(clos);
  2894. o.updateProp("prototype-", proto);
  2895. av = avjoin(av, proto);
  2896. }
  2897. }
  2898. });
  2899. ans2 = new Ans(av, ans.fr, errval);
  2900. ans2.thisav = ans.v; // used by method calls
  2901. return ans2;
  2902. });
  2903. override(INDEX, function(n, fr) {
  2904. var ansobj = evalExp(n.children[0], fr), avobj = ansobj.v.baseToObj(),
  2905. prop = n.children[1], errval = ansobj.err, av , ans;
  2906. fr = ansobj.fr;
  2907. // If [] notation is used with a constant, try to be precise.
  2908. // Unsound: ignore everything the index can eval to except numbers & strings
  2909. if (prop.type === STRING)
  2910. av = avobj.getProp(prop.value);
  2911. else {
  2912. var ansprop = evalExp(prop, fr), avprop = ansprop.v;
  2913. fr = ansprop.fr;
  2914. errval = maybeavjoin(errval, ansprop.err);
  2915. av = BOTTOM;
  2916. if (avprop.hasNum())
  2917. avobj.forEachObj(function(o) { av = avjoin(av, o.getNumProps()); });
  2918. if (avprop.hasStr()) {
  2919. var slit = avprop.getStrLit();
  2920. if (slit)
  2921. av = avjoin(av, avobj.getProp(slit));
  2922. else
  2923. avobj.forEachObj(function(o) { av = avjoin(av, o.getStrProps()); });
  2924. }
  2925. }
  2926. ans = new Ans(av, fr, errval);
  2927. ans.thisav = avobj;
  2928. return ans;
  2929. });
  2930. override(DOT, function(n, fr) {
  2931. var ans = evalExp(n.children[0], fr), avobj = ans.v.baseToObj();
  2932. ans.thisav = avobj; // used by method calls
  2933. ans.v = avobj.getProp(n.children[1].value);
  2934. return ans;
  2935. });
  2936. override(CALL, function(n, fr) {
  2937. // To see if the analysis reaches some program point in all.js, add some
  2938. // call: e10sDebug(some_msg) and uncomment the following code.
  2939. // if (n.children[0].value === "e10sDebug") {
  2940. // print(n.children[1].children[0].value);
  2941. // }
  2942. var ans = evalExp(n.children[0], fr), ans1, errval, rands = [];
  2943. rands.push(ans.thisav ? ans.thisav : global_object_av);
  2944. fr = ans.fr;
  2945. errval = ans.err;
  2946. // evaluate arguments
  2947. n.children[1].children.forEach(function(rand) {
  2948. ans1 = evalExp(rand, fr);
  2949. rands.push(ans1.v);
  2950. fr = ans1.fr;
  2951. errval = maybeavjoin(errval, ans1.err);
  2952. });
  2953. // call each function that can flow to the operator position
  2954. ans = ans.v.callFun(rands, n);
  2955. ans.fr = fr;
  2956. ans.err = maybeavjoin(errval, ans.err);
  2957. return ans;
  2958. });
  2959. override(NEW_WITH_ARGS, function(n, fr) {
  2960. var ch = n.children, rands = [], retval = BOTTOM;
  2961. var ans = evalExp(ch[0], fr), ans1, errval;
  2962. var objaddr = n.addr, thisobj = heap[objaddr];
  2963. rands.push(makeObjAval(objaddr));
  2964. fr = ans.fr;
  2965. errval = ans.err;
  2966. // evaluate arguments
  2967. ch[1].children.forEach(function(rand) {
  2968. ans1 = evalExp(rand, fr);
  2969. rands.push(ans1.v);
  2970. fr = ans1.fr;
  2971. errval = maybeavjoin(errval, ans1.err);
  2972. });
  2973. // FIXME: record error if rator contains base vals and non-functions
  2974. ans.v.baseToObj().forEachObj(function(o) {
  2975. var clos = o.getFun(), proto;
  2976. if (!clos) return;
  2977. proto = o.getProp("prototype-");
  2978. if (aveq(BOTTOM, proto)) {
  2979. // create default prototype & use it
  2980. proto = makeDefaultProto(clos);
  2981. o.updateProp("prototype-", proto);
  2982. }
  2983. thisobj.updateProto(proto);
  2984. // if a fun is called both w/ and w/out new, assume it's a constructor
  2985. clos.withNew = true;
  2986. ans = evalFun(clos, rands, true, n);
  2987. if (clos.hasReturn) // constructor uses return
  2988. retval = avjoin(retval, ans.v);
  2989. else // constructor doesn't use return
  2990. retval = avjoin(retval, rands[0]);
  2991. errval = maybeavjoin(errval, ans.err);
  2992. });
  2993. return new Ans(retval, fr, errval);
  2994. });
  2995. override(ARGUMENTS, function(n, fr) {
  2996. var index = n.children[0], ps = n.arguments;
  2997. var restargs = fr[RESTARGS] || BOTTOM, ans, av, errval;
  2998. if (index.type === NUMBER) {
  2999. var iv = index.value;
  3000. if (iv < 0)
  3001. av = AUNDEF;
  3002. else if (iv < ps.length)
  3003. av = frameGet(fr, ps[iv]);
  3004. else
  3005. av = restargs; // unsound: not checking if iv > #args
  3006. }
  3007. else {
  3008. ans = evalExp(index, fr);
  3009. fr = ans.fr;
  3010. errval = ans.err;
  3011. av = BOTTOM;
  3012. // when we don't know the index, we return the join of all args
  3013. ps.forEach(function(p) { av = avjoin(av, frameGet(fr, p)); });
  3014. av = avjoin(av, restargs);
  3015. }
  3016. return new Ans(av, fr, errval);
  3017. });
  3018. })();
  3019. // function for evaluating statements
  3020. // node, frame -> Ans
  3021. // Evaluate the statement and find which statement should be executed next.
  3022. var evalStm;
  3023. (function() {
  3024. var walkerobj = walkASTgenerator(), override = walkerobj.override;
  3025. evalStm = walkerobj.walkAST;
  3026. override(SEMICOLON, function(n, fr) {
  3027. var ans = evalExp(n.expression, fr);
  3028. ans.v = n.kreg;
  3029. return ans;
  3030. });
  3031. function _next(n, fr) { return new Ans(n.kreg, fr); }
  3032. [BLOCK, CASE, DEFAULT, DO, FINALLY, FOR, IF, SWITCH, TRY, WHILE].forEach(
  3033. function(tt) { override(tt, _next); });
  3034. override(FOR_IN, function(n, fr) {
  3035. // For most kinds of iterators at FOR/IN we have to be conservative
  3036. // (e.g. DOTs or INDEXes). Without flow sensitivity, we even have to be
  3037. // conservative for stack refs that have been initialized, we can't forget
  3038. // their current value. We can only be precise when the iterator is a stack
  3039. // reference and the variable is BOTTOM in the frame.
  3040. var ans = evalExp(n.object, fr), errval, av;
  3041. var it = n.iterator, b = n.body;
  3042. if (it.type === IDENTIFIER &&
  3043. it.kind === STACK &&
  3044. aveq(BOTTOM, frameGet(fr, it))) {
  3045. av = ans.v;
  3046. errval = ans.err;
  3047. av.forEachObj(function(o) {
  3048. o.forEachEnumProp(function(p) {
  3049. // wipe the value of it from the previous iteration
  3050. frameSet(fr, it, makeStrLitAval(p));
  3051. if (flags[it.addr]) updateHeapAv(it.addr, makeStrLitAval(p));
  3052. ans = evalStm(b, fr);
  3053. errval = maybeavjoin(errval, ans.err);
  3054. });
  3055. });
  3056. ans.v = b.kreg;
  3057. ans.err = errval;
  3058. }
  3059. else {
  3060. av = BOTTOM;
  3061. ans.v.forEachObj(function(o) {
  3062. o.forEachEnumProp(function(p) {
  3063. if (propIsNumeric(p))
  3064. av = avjoin(av, ANUM);
  3065. else
  3066. av = avjoin(av, ASTR);
  3067. });
  3068. });
  3069. ans.v = av;
  3070. ans = evalLval(n.iterator, ans);
  3071. ans.v = b;
  3072. }
  3073. return ans;
  3074. });
  3075. override(CATCH, function(n, fr) { return new Ans(n.block, fr); });
  3076. override(THROW, function(n, fr) {
  3077. var ans = evalExp(n.exception, fr);
  3078. ans.err = maybeavjoin(ans.err, ans.v);
  3079. ans.v = n.kreg;
  3080. return ans;
  3081. });
  3082. })();
  3083. var big_ts;
  3084. // StackCleaner inherits from Error and is not used to signal errors, but to
  3085. // manage the size of the runtime stack.
  3086. // function node, number, optional array of Aval
  3087. function StackCleaner(fn, howmany, args) {
  3088. this.fn = fn;
  3089. this.howmany = howmany;
  3090. if (args) this.args = args;
  3091. }
  3092. StackCleaner.prototype = new Error();
  3093. // function node, array of Aval, boolean, optional call node -> Ans w/out fr
  3094. // Arg 4 is the node that caused the function call (if there is one).
  3095. function evalFun(fn, args, withNew, cn) {
  3096. var ans, n, params, fr, w, script = fn.body, pelm1;
  3097. var retval = BOTTOM, errval = BOTTOM;
  3098. // stm node (exception continuation), av (exception value) -> void
  3099. function stmThrows(n, errav) {
  3100. if (n) {
  3101. if (n.type === CATCH) {
  3102. var exvar = n.exvar;
  3103. if (exvar.kind === HEAP)
  3104. heap[exvar.addr] = avjoin(errav, heap[exvar.addr]);
  3105. if (fr[exvar.addr]) // revealing the representation of frame here.
  3106. frameSet(fr, exvar, avjoin(errav, frameGet(fr, exvar)));
  3107. else
  3108. frameSet(fr, exvar, errav);
  3109. }
  3110. w.push(n);
  3111. }
  3112. else
  3113. errval = avjoin(errval, errav);
  3114. }
  3115. if (process.uptime() > timeout) throw new Error("timeout");
  3116. // if (timestamp > big_ts) {
  3117. // print("big ts: " + timestamp);
  3118. // dumpHeap("heapdump" + timestamp + ".txt");
  3119. // big_ts += 1000;
  3120. // if (big_ts > 100000) throw new Error("foobar");
  3121. // }
  3122. // treat built-in functions specially
  3123. if (fn.builtin) {
  3124. // return fn.body(args, withNew, cn);
  3125. // If there's an input for which built-ins cause stack overflow, uncomment.
  3126. var addr = fn.addr;
  3127. if (pending[addr] > 1) {
  3128. return new Ans(BOTTOM, undefined, BOTTOM);
  3129. }
  3130. ++pending[addr];
  3131. try {
  3132. ans = fn.body(args, withNew, cn);
  3133. --pending[addr];
  3134. return ans;
  3135. }
  3136. catch (e) {
  3137. if (e instanceof StackCleaner) --pending[addr];
  3138. throw e;
  3139. }
  3140. }
  3141. var tsAtStart;
  3142. var result = searchSummary(fn, args, timestamp);
  3143. if (result) return new Ans(result[0], undefined, result[1]);
  3144. while(true) {
  3145. try {
  3146. tsAtStart = timestamp;
  3147. // pending & exceptions prevent the runtime stack from growing too much.
  3148. var pelm2 = searchPending(fn, args);
  3149. if (pelm2 === 0) {
  3150. pelm1 = {args : args, ts : timestamp};
  3151. addPending(fn, pelm1);
  3152. }
  3153. else if (pelm2 === undefined) {
  3154. // If a call eventually leads to itself, stop analyzing & return BOTTOM.
  3155. // Add a summary that describes the least solution.
  3156. addSummary(fn, args, BOTTOM, BOTTOM, tsAtStart);
  3157. return new Ans(BOTTOM, undefined, BOTTOM);
  3158. }
  3159. else if (pelm2 > 0) {
  3160. // There are pending calls that are obsolete because their timestamp is
  3161. // old. Discard frames to not grow the stack too much.
  3162. throw new StackCleaner(fn, pelm2);
  3163. }
  3164. else /* if (pelm2 < 0) */ {
  3165. throw new StackCleaner(fn, -pelm2, args);
  3166. }
  3167. w = [];
  3168. fr = {};
  3169. params = fn.params;
  3170. frameSet(fr, fn.fname, makeObjAval(fn.addr));
  3171. // args[0] is always the obj that THIS is bound to.
  3172. // THIS never has a heap ref, so its entry in the frame is special.
  3173. fr.thisav = args[0];
  3174. // Bind formals to actuals
  3175. for (var i = 0, len = params.length; i < len; i++) {
  3176. var param = params[i], arg = args[i+1] || AUNDEF;//maybe #args < #params
  3177. if (param.kind === HEAP) updateHeapAv(param.addr, arg);
  3178. frameSet(fr, param, arg);
  3179. }
  3180. var argslen = args.length;
  3181. if ((++i) < argslen) { // handling of extra arguments
  3182. var restargs = BOTTOM;
  3183. for (; i<argslen; i++) restargs = avjoin(restargs, args[i]);
  3184. fr[RESTARGS] = restargs; // special entry in the frame.
  3185. }
  3186. // bind a non-init`d var to bottom, not undefined.
  3187. script.varDecls.forEach(function(vd) { frameSet(fr, vd, BOTTOM); });
  3188. // bind the fun names in the frame.
  3189. script.funDecls.forEach(function(fd) {
  3190. frameSet(fr, fd.fname, makeObjAval(fd.addr));
  3191. });
  3192. w.push(script.kreg);
  3193. while (w.length !== 0) {
  3194. n = w.pop();
  3195. if (n === undefined) continue;
  3196. if (n.type === RETURN) {
  3197. ans = evalExp(n.value, fr);
  3198. // fr is passed to exprs/stms & mutated, no need to join(fr, ans.fr)
  3199. fr = ans.fr;
  3200. retval = avjoin(retval, ans.v);
  3201. w.push(n.kreg);
  3202. if (ans.err) stmThrows(n.kexc, ans.err);
  3203. }
  3204. else {
  3205. ans = evalStm(n, fr);
  3206. fr = ans.fr;
  3207. w.push(ans.v);
  3208. if (ans.err) stmThrows(n.kexc, ans.err);
  3209. }
  3210. }
  3211. rmPending(fn);
  3212. if (!fn.hasReturn) retval = AUNDEF;
  3213. // Find if a summary has been added during recursion.
  3214. result = searchSummary(fn, args, tsAtStart);
  3215. if (!result || (avlt(retval, result[0]) && avlt(errval, result[1]))) {
  3216. // Either fn isn't recursive, or the fixpt computation has finished.
  3217. if (!result) addSummary(fn, args, retval, errval, tsAtStart);
  3218. return new Ans(retval, undefined, errval);
  3219. }
  3220. else {
  3221. retval = avjoin(result[0], retval);
  3222. errval = avjoin(result[1], errval);
  3223. // The result changed the last summary; update summary and keep going.
  3224. addSummary(fn, args, retval, errval, tsAtStart);
  3225. }
  3226. }
  3227. catch (e) {
  3228. if (!(e instanceof StackCleaner)) {
  3229. // analysis error, irrelevant to the stack-handling code
  3230. throw e;
  3231. }
  3232. if (!pelm1) throw e;
  3233. rmPending(fn);
  3234. if (e.fn !== fn) throw e;
  3235. if (e.howmany !== 1) {
  3236. e.howmany--;
  3237. throw e;
  3238. }
  3239. if (e.args) args = e.args;
  3240. }
  3241. }
  3242. }
  3243. // maybe merge with evalFun at some point
  3244. function evalToplevel(tl) {
  3245. var w /* workset */, fr, n, ans;
  3246. w = [];
  3247. fr = {};
  3248. initDeclsInHeap(tl);
  3249. fr.thisav = global_object_av;
  3250. // bind a non-init`d var to bottom, different from assigning undefined to it.
  3251. tl.varDecls.forEach(function(vd) { frameSet(fr, vd, BOTTOM); });
  3252. // bind the fun names in the frame.
  3253. tl.funDecls.forEach(function(fd) {
  3254. frameSet(fr, fd.fname, makeObjAval(fd.addr));
  3255. });
  3256. // evaluate the stms of the toplevel in order
  3257. w.push(tl.kreg);
  3258. while (w.length !== 0) {
  3259. n = w.pop();
  3260. if (n === undefined) continue; // end of toplevel reached
  3261. if (n.type === RETURN)
  3262. ; // record error, return in toplevel
  3263. else {
  3264. ans = evalStm(n, fr);
  3265. fr = ans.fr;
  3266. w.push(ans.v);
  3267. // FIXME: handle toplevel uncaught exception
  3268. }
  3269. }
  3270. //print("call uncalled functions");
  3271. // each function w/out a summary is called with unknown arguments
  3272. for (var addr in summaries) {
  3273. var f = heap[addr].getFun();
  3274. if (!existsSummary(f)) {
  3275. var any_args = buildArray(f.params.length, BOTTOM);
  3276. any_args.unshift(makeGenericObj());
  3277. evalFun(f, any_args, false);
  3278. }
  3279. }
  3280. //showSummaries();
  3281. }
  3282. // initGlobals and initCoreObjs are difficult to override. The next 2 vars help
  3283. // clients of the analysis add stuff to happen during initialization
  3284. var initOtherGlobals, initOtherObjs;
  3285. // consumes the ast returned by jsparse.parse
  3286. function cfa2(ast) {
  3287. count = 0;
  3288. astSize = 0;
  3289. initGlobals();
  3290. initOtherGlobals && initOtherGlobals();
  3291. //print("fixStm start");
  3292. fixStm(ast);
  3293. //print("fixStm succeeded");
  3294. initCoreObjs();
  3295. initOtherObjs && initOtherObjs();
  3296. //print("initObjs done");
  3297. if (commonJSmode) { // create the exports object
  3298. var e = new Aobj({ addr: newCount() }), eav = makeObjAval(count);
  3299. heap[newCount()] = eav;
  3300. exports_object_av_addr = count;
  3301. exports_object.obj = e;
  3302. }
  3303. labelAST(ast);
  3304. //print("labelStm done");
  3305. desugarWith(ast, undefined, []);
  3306. //print("desugarWith done");
  3307. desugarLet(ast);
  3308. tagVarRefs(ast, [], [], "toplevel");
  3309. //print("tagrefsStm done");
  3310. markConts(ast, undefined, undefined);
  3311. //print("markconts done");
  3312. try {
  3313. //print("Done with preamble. Analysis starting.");
  3314. evalToplevel(ast);
  3315. //print("after cfa2");
  3316. // print("AST size: " + astSize);
  3317. // print("ts: " + timestamp);
  3318. // dumpHeap("heapdump.txt");
  3319. }
  3320. catch (e) {
  3321. if (e.message !== "timeout") {
  3322. print(e.message);
  3323. console.trace();
  3324. if (! ("code" in e)) e.code = CFA_ERROR;
  3325. throw e;
  3326. }
  3327. else
  3328. timedout = true;
  3329. }
  3330. }
  3331. // function node -> string
  3332. function funToType(n, seenObjs) {
  3333. if (n.builtin)
  3334. return "function"; // FIXME: tag built-in nodes w/ their types
  3335. if (seenObjs) {
  3336. if (seenObjs.memq(n))
  3337. return "any";
  3338. else
  3339. seenObjs.push(n);
  3340. }
  3341. else {
  3342. seenObjs = [n];
  3343. }
  3344. var addr = n.addr, summary = summaries[addr];
  3345. if (summary.ts === INVALID_TIMESTAMP) // the function was never called
  3346. return "function";
  3347. var insjoin = summary.type[0], instypes = [], outtype, slen = seenObjs.length;
  3348. for (var i = 1, len = insjoin.length; i < len; i++) {
  3349. instypes[i - 1] = insjoin[i].toType(seenObjs);
  3350. // each argument must see the same seenObjs, the initial one.
  3351. seenObjs.splice(slen, seenObjs.length);
  3352. }
  3353. if (n.withNew && !n.hasReturn) {
  3354. outtype = n.fname.name;
  3355. // If a fun is called both w/ and w/out new, assume it's a constructor.
  3356. // If a constructor is called w/out new, THIS is bound to the global obj.
  3357. // In this case, the result type must contain void.
  3358. var thisObjType = insjoin[0].toType(seenObjs);
  3359. if (/Global Object/.test(thisObjType))
  3360. outtype = "<void | " + outtype + ">";
  3361. }
  3362. else
  3363. outtype = summary.type[1].toType(seenObjs);
  3364. if (outtype === "undefined") outtype = "void";
  3365. return (outtype + " function(" + instypes.join(", ") +")");
  3366. }
  3367. // node, string, Array of string, cmd-line options -> Array of ctags
  3368. function getTags(ast, pathtofile, lines, options) {
  3369. const REGEX_ESCAPES = { "\n": "\\n", "\r": "\\r", "\t": "\\t" };
  3370. var tags = [];
  3371. function regexify(str) {
  3372. function subst(ch) {
  3373. return (ch in REGEX_ESCAPES) ? REGEX_ESCAPES[ch] : "\\" + ch;
  3374. }
  3375. str || (str = "");
  3376. return "/^" + str.replace(/[\\/$\n\r\t]/g, subst) + "$/";
  3377. }
  3378. if (options.commonJS) commonJSmode = true;
  3379. // print(pathtofile);
  3380. cfa2(ast);
  3381. // print("Flow analysis done. Generating tags");
  3382. if (exports_object.obj) {
  3383. var eo = exports_object.obj;
  3384. eo.forEachOwnProp(function (p) {
  3385. var av = eo.getOwnExactProp(p);
  3386. var tag = {};
  3387. tag.name = /-$/.test(p) ? p.slice(0, -1) : p.slice(1);
  3388. tag.tagfile = pathtofile;
  3389. tag.addr = regexify(lines[exports_object.lines[p] - 1]);
  3390. var type = av.toType();
  3391. if (/(^<.*> function)|(^[^<>\|]*function)/.test(type))
  3392. tag.kind = "f";
  3393. else
  3394. tag.kind = "v";
  3395. tag.type = type;
  3396. tag.module = options.module;
  3397. tag.lineno = exports_object.lines[p];
  3398. tags.push(tag);
  3399. });
  3400. }
  3401. for (var addr in summaries) {
  3402. var f = heap[addr].getFun();
  3403. tags.push({ name : f.fname.name || "%anonymous_function",
  3404. tagfile : pathtofile,
  3405. addr : regexify(lines[f.lineno - 1]),
  3406. kind : "f",
  3407. type : funToType(f),
  3408. lineno : f.lineno.toString(),
  3409. sortno : f.lineno.toString()
  3410. });
  3411. }
  3412. ast.varDecls.forEach(function(vd) {
  3413. tags.push({ name : vd.name,
  3414. tagfile : pathtofile,
  3415. addr : regexify(lines[vd.lineno - 1]),
  3416. kind : "v",
  3417. type : heap[vd.addr].toType(),
  3418. lineno : vd.lineno.toString(),
  3419. sortno : vd.lineno.toString()
  3420. });
  3421. });
  3422. return tags;
  3423. }
  3424. // node -> boolean
  3425. // hacky test suite. Look in run-tests.js
  3426. function runtest(ast) {
  3427. cfa2(ast);
  3428. // find test's addr at the toplevel
  3429. var testaddr, fds = ast.funDecls;
  3430. for (var i = 0, len = fds.length; i < len; i++)
  3431. if (fds[i].fname.name === "test") {
  3432. testaddr = fds[i].addr;
  3433. break;
  3434. }
  3435. if (testaddr === undefined) throw errorWithCode(CFA_ERROR, "Malformed test");
  3436. var type = summaries[testaddr].type;
  3437. // print(type[0][1]);
  3438. // print(type[1]);
  3439. return aveq(type[0][1], type[1]);
  3440. }
  3441. exports.cfa2 = cfa2;
  3442. exports.runtest = runtest;
  3443. exports.getTags = getTags;
  3444. ////////////////////////////////////////////////////////////////////////////////
  3445. ////////////// DATA DEFINITIONS FOR THE AST RETURNED BY JSPARSE ////////////
  3446. ////////////////////////////////////////////////////////////////////////////////
  3447. function walkExp(n) {
  3448. switch (n.type){
  3449. //nullary
  3450. case NULL:
  3451. case THIS:
  3452. case TRUE:
  3453. case FALSE:
  3454. break;
  3455. case IDENTIFIER:
  3456. case NUMBER:
  3457. case STRING:
  3458. case REGEXP:
  3459. // n.value
  3460. break;
  3461. //unary
  3462. case DELETE:
  3463. case VOID:
  3464. case TYPEOF:
  3465. case NOT:
  3466. case BITWISE_NOT:
  3467. case UNARY_PLUS: case UNARY_MINUS:
  3468. case NEW:
  3469. walkExp(n.children[0]);
  3470. break;
  3471. case INCREMENT: case DECREMENT:
  3472. // n.postfix is true or undefined
  3473. walkExp(n.children[0]);
  3474. break;
  3475. //binary
  3476. case CALL:
  3477. case NEW_WITH_ARGS:
  3478. walkExp(n.children[0]);
  3479. //n[1].type === LIST
  3480. n.children[1].children.forEach(walkExp);
  3481. break;
  3482. case IN:
  3483. walkExp(n.children[0]); // an exp which must eval to string
  3484. walkExp(n.children[1]); // an exp which must eval to obj
  3485. break;
  3486. case DOT:
  3487. walkExp(n.children[0]);
  3488. walkExp(n.children[1]); // must be IDENTIFIER
  3489. break;
  3490. case BITWISE_OR: case BITWISE_XOR: case BITWISE_AND:
  3491. case EQ: case NE: case STRICT_EQ: case STRICT_NE:
  3492. case LT: case LE: case GE: case GT:
  3493. case INSTANCEOF:
  3494. case LSH: case RSH: case URSH:
  3495. case PLUS: case MINUS: case MUL: case DIV: case MOD:
  3496. case AND: case OR:
  3497. case ASSIGN: // n[0].assignOp shows which op-and-assign operator we have here
  3498. case INDEX: // property indexing
  3499. walkExp(n.children[0]);
  3500. walkExp(n.children[1]);
  3501. break;
  3502. //ternary
  3503. case HOOK:
  3504. walkExp(n.children[0]);
  3505. walkExp(n.children[1]);
  3506. walkExp(n.children[2]);
  3507. break;
  3508. //variable arity
  3509. case COMMA:
  3510. case ARRAY_INIT: // array literal
  3511. n.children.forEach(walkExp);
  3512. break;
  3513. case OBJECT_INIT:
  3514. n.children.forEach(function(prop) { // prop.type === PROPERTY_INIT
  3515. walkExp(prop.children[0]); // identifier, number or string
  3516. walkExp(prop.children[1]);
  3517. });
  3518. break;
  3519. //other
  3520. case FUNCTION:
  3521. // n.name is a string
  3522. // n.params is an array of strings
  3523. // n.functionForm === EXPRESSED_FORM
  3524. walkStm(n.body);
  3525. break;
  3526. }
  3527. }
  3528. function walkStm(n) {
  3529. switch (n.type) {
  3530. case SCRIPT:
  3531. case BLOCK:
  3532. n.children.forEach(walkStm);
  3533. break;
  3534. case FUNCTION:
  3535. // n.name is a string
  3536. // n.params is an array of strings
  3537. // n.functionForm === DECLARED_FORM or STATEMENT_FORM
  3538. // STATEMENT_FORM is for funs declared in inner blocks, like IF branches
  3539. // It doesn't extend the funDecls of the script, bad!
  3540. walkStm(n.body);
  3541. break;
  3542. case SEMICOLON:
  3543. n.expression && walkExp(n.expression);
  3544. break;
  3545. case IF:
  3546. walkExp(n.condition);
  3547. walkStm(n.thenPart);
  3548. n.elsePart && walkStm(n.elsePart);
  3549. break;
  3550. case SWITCH:
  3551. walkExp(n.discriminant);
  3552. // a switch w/out branches is legal, n.cases is []
  3553. n.cases.forEach(function(branch) {
  3554. branch.caseLabel && walkExp(branch.caseLabel);
  3555. // if the branch has no stms, branch.statements is an empty block
  3556. walkStm(branch.statements);
  3557. });
  3558. break;
  3559. case FOR:
  3560. if (n.setup) {
  3561. if (n.setup.type === VAR || n.setup.type === CONST)
  3562. walkStm(n.setup);
  3563. else walkExp(n.setup);
  3564. }
  3565. n.condition && walkExp(n.condition);
  3566. n.update && walkExp(n.update);
  3567. walkStm(n.body);
  3568. break;
  3569. case FOR_IN:
  3570. // n.varDecl may be used when there is a LET at the head of the for/in loop.
  3571. walkExp(n.iterator);
  3572. walkExp(n.object);
  3573. walkStm(n.body);
  3574. break;
  3575. case WHILE:
  3576. case DO:
  3577. walkExp(n.condition);
  3578. walkStm(n.body);
  3579. break;
  3580. case BREAK:
  3581. case CONTINUE:
  3582. // do nothing: n.label is just a name, n.target points back to ancestor
  3583. break;
  3584. case TRY:
  3585. walkStm(n.tryBlock);
  3586. n.catchClauses.forEach(function(clause) { // clause.varName is a string
  3587. clause.guard && walkExp(clause.guard);
  3588. walkStm(clause.block);
  3589. });
  3590. n.finallyBlock && walkStm(n.finallyBlock);
  3591. break;
  3592. case THROW:
  3593. walkExp(n.exception);
  3594. break;
  3595. case RETURN:
  3596. n.value && walkExp(n.value);
  3597. break;
  3598. case WITH:
  3599. walkExp(n.object);
  3600. walkStm(n.body);
  3601. break;
  3602. case LABEL:
  3603. // n.label is a string
  3604. walkStm(n.statement);
  3605. break;
  3606. case VAR:
  3607. case CONST: // variable or constant declaration
  3608. // vd.name is a string
  3609. // vd.readOnly is true for constants, false for variables
  3610. n.children.forEach(function(vd) { walkExp(vd.initializer); });
  3611. break;
  3612. }
  3613. return n;
  3614. }
  3615. ////////////////////////////////////////////////////////////////////////////////
  3616. //////////// EVENT CLASSIFICATION FOR FIREFOX ADDONS /////////////////////
  3617. ////////////////////////////////////////////////////////////////////////////////
  3618. var e10sResults, chrome_obj_av_addr, Chrome, Content;
  3619. (function() {
  3620. // Initializing functions are overriden here
  3621. initOtherGlobals = function() {
  3622. // make separate constructors for chrome and content objs, so that we can
  3623. // distinguish them w/ instanceof
  3624. Chrome = function(specialProps) { Aobj.call(this, specialProps); }
  3625. Chrome.prototype = new Aobj({ addr: newCount() });
  3626. Content = function(specialProps) { Aobj.call(this, specialProps); }
  3627. Content.prototype = new Aobj({ addr: newCount() });
  3628. e10sResults = {};
  3629. };
  3630. initOtherObjs = initDOMObjs;
  3631. tagVarRefs_override(IDENTIFIER, tagVarRefsId(true));
  3632. // Must be called *after* the desugarings.
  3633. function initDeclsInHeap_e10s(n) {
  3634. // "Attach listener" have one more property called status:
  3635. // status is an Array of four strings, describing the listener:
  3636. // event name: some specific name or unknown
  3637. // attached on: chrome, content or any
  3638. // originates from: chrome, content or any
  3639. // flagged: safe or unsafe
  3640. n.addr = newCount();
  3641. e10sResults[count] = {
  3642. lineno : n.lineno,
  3643. analyzed : false,
  3644. kind : (n.type === DOT && n.children[1].value === "addEventListener-") ?
  3645. "Attach listener" : "Touch content"
  3646. };
  3647. n.children.forEach(initDeclsInHeap);
  3648. }
  3649. initDeclsInHeap_override(DOT, initDeclsInHeap_e10s);
  3650. initDeclsInHeap_override(INDEX, initDeclsInHeap_e10s);
  3651. })();
  3652. function initDOMObjs() {
  3653. function toThis(args) { return new Ans(args[0]); }
  3654. // the whole DOM tree is modeled by 2 chrome and 1 content object
  3655. var chr = new Chrome({ addr: newCount() }), chrav = makeObjAval(count);
  3656. chrome_obj_av_addr = newCount();
  3657. heap[chrome_obj_av_addr] = chrav;
  3658. var chr2 = new Chrome({ addr: newCount() }), chr2av = makeObjAval(count);
  3659. var con = new Content({ addr: newCount() }), conav = makeObjAval(count);
  3660. chr.addProp("content-", { aval: chr2av, enum: false });
  3661. chr.addProp("contentDocument-", { aval: conav, enum: false });
  3662. chr.addProp("contentWindow-", { aval: chr2av, enum: false });
  3663. chr.addProp("defaultView-", { aval: conav, enum: false });
  3664. chr2.addProp("document-", { aval: conav, enum: false });
  3665. con.addProp("documentElement-", { aval: conav, enum: false });
  3666. chr.addProp("opener-", { aval: chrav, enum: false });
  3667. con.addProp("opener-", { aval: conav, enum: false });
  3668. chr.addProp("selectedBrowser-", { aval: chrav, enum: false });
  3669. ["firstChild-", "lastChild-", "nextSibling-", "top-"].forEach(
  3670. function(pname) {
  3671. chr.addProp(pname, { aval: chrav, enum: false });
  3672. chr2.addProp(pname, { aval: chr2av, enum: false });
  3673. con.addProp(pname, { aval: conav, enum: false });
  3674. }
  3675. );
  3676. chr.addProp("parentNode-", { aval: chrav, enum: false });
  3677. chr2.addProp("parentNode-", { aval: chrav, enum: false });
  3678. con.addProp("parentNode-", { aval: conav, enum: false });
  3679. attachMethod(chr, "getBrowser", toThis, 0);
  3680. ["getElementById", "appendChild", "removeChild", "createElement"].forEach(
  3681. function(fname) {
  3682. attachMethod(chr, fname, toThis, 1);
  3683. attachMethod(con, fname, toThis, 1);
  3684. }
  3685. );
  3686. // chrarr is for functions that normally return a Node list of chrome elms
  3687. var chrarr = new Aobj({ addr: newCount() }), chrarrav = makeObjAval(count);
  3688. array_constructor([chrarrav], true);
  3689. chrarr.updateNumProps(chrav);
  3690. // conarr is for functions that normally return a Node list of content elms
  3691. var conarr = new Aobj({ addr: newCount() }), conarrav = makeObjAval(count);
  3692. array_constructor([conarrav], true);
  3693. conarr.updateNumProps(conav);
  3694. function toNodeList(args) {
  3695. var av = BOTTOM, both = 0;
  3696. args[0].forEachObjWhile(function(o) {
  3697. if (o instanceof Chrome) {
  3698. av = avjoin(av, chrarrav);
  3699. both |= 1;
  3700. }
  3701. if (o instanceof Content) {
  3702. av = avjoin(av, conarrav);
  3703. both |= 2;
  3704. }
  3705. return both === 3;
  3706. });
  3707. return new Ans(av);
  3708. }
  3709. chr.addProp("childNodes-", { aval: chrarrav, enum: false });
  3710. chr2.addProp("childNodes-", { aval: chrarrav, enum: false });
  3711. con.addProp("childNodes-", { aval: conarrav, enum: false });
  3712. [["querySelectorAll", 1], ["getElementsByClassName", 1],
  3713. ["getElementsByAttribute", 2], ["getElementsByTagName", 1]].forEach(
  3714. function(pair) {
  3715. var p0 = pair[0], p1 = pair[1];
  3716. attachMethod(chr, p0, toNodeList, p1);
  3717. attachMethod(chr2, p0, toNodeList, p1);
  3718. attachMethod(con, p0, toNodeList, p1);
  3719. }
  3720. );
  3721. global_object_av.forEachObj(function(go) {
  3722. go.addProp("content-", { aval: chr2av, enum: false });
  3723. go.addProp("contentWindow-", { aval: chr2av, enum: false });
  3724. go.addProp("document-", { aval: chrav, enum: false });
  3725. go.addProp("gBrowser-", { aval: chrav, enum: false });
  3726. go.addProp("opener-", { aval: chrav, enum: false });
  3727. go.addProp("window-", { aval: chrav, enum: false });
  3728. });
  3729. function aEL(args, withNew, callNode) {
  3730. // oldst can be undefined
  3731. function evtjoin(oldst, newst) {
  3732. if (oldst) {
  3733. if (oldst[0] !== newst[0]) newst[0] = "unknown-";
  3734. if (oldst[1] !== newst[1]) newst[1] = "any";
  3735. if (oldst[2] !== newst[2]) newst[2] = "any";
  3736. if (oldst[3] !== newst[3]) newst[3] = "unsafe";
  3737. }
  3738. return newst;
  3739. }
  3740. var evt = (args[1] && args[1].getStrLit()) || "unknown-";
  3741. var ratorNode = callNode.children[0], raddr=ratorNode.addr, ec, evtkind, st;
  3742. if (!ratorNode) return new Ans(BOTTOM);
  3743. ec = e10sResults[raddr];
  3744. if (ec)
  3745. ec.analyzed = true;
  3746. else {
  3747. if (!raddr) raddr = ratorNode.addr = newCount();
  3748. ec = e10sResults[raddr] = {lineno : ratorNode.lineno,
  3749. analyzed : true,
  3750. kind : "Attach listener",
  3751. status : undefined};
  3752. }
  3753. evtkind = eventKinds[evt.slice(0,-1)];
  3754. if (evtkind === XUL)
  3755. st = [evt, "chrome", "chrome", "safe"];
  3756. else if (evtkind === undefined && !args[4] && evt !== "unknown-") {
  3757. // listener for custom event that can't come from content
  3758. st = [evt, "chrome", "chrome", "safe"];
  3759. }
  3760. else {
  3761. var numobjs = 0;
  3762. args[0].forEachObj(function(o) {
  3763. ++numobjs;
  3764. if (o instanceof Chrome) {
  3765. var st2 = [evt, "chrome", undefined, undefined];
  3766. if (evtkind === NO_BUBBLE) {
  3767. st2[2] = "chrome";
  3768. st2[3] = "safe";
  3769. }
  3770. else {
  3771. st2[2] = "any";
  3772. st2[3] = "unsafe"
  3773. }
  3774. st = evtjoin(st, st2);
  3775. }
  3776. else if (o instanceof Content) {
  3777. st = evtjoin(st, [evt, "content", "content", "unsafe"]);
  3778. }
  3779. else
  3780. st = evtjoin(st, [evt, "any", "any", "unsafe"]);
  3781. });
  3782. if (numobjs === 0) st = [evt, "any", "any", "unsafe"];
  3783. }
  3784. ec.status = evtjoin(ec.status, st);
  3785. return new Ans(BOTTOM);
  3786. }
  3787. var aEL_node = funToNode("addEventListener-", aEL, 3);
  3788. new Aobj({ addr: count,
  3789. proto : function_prototype_av,
  3790. fun : aEL_node });
  3791. var aELav = makeObjAval(count);
  3792. // Node, Aval, Aval -> Aval
  3793. function evalExp_e10s(n, recv, av) {
  3794. var recvchrome = false, recvcon = false;
  3795. recv.forEachObjWhile(function(o) {
  3796. if (o instanceof Chrome)
  3797. recvchrome = true;
  3798. else if (o instanceof Content)
  3799. recvcon = true;
  3800. return recvchrome && recvcon;
  3801. });
  3802. if (recvchrome && !recvcon)
  3803. av.forEachObjWhile(function(o) {
  3804. if (o instanceof Content) {
  3805. var ec = e10sResults[n.addr];
  3806. ec.analyzed = true;
  3807. ec.kind = "Touch content";
  3808. return true;
  3809. }
  3810. });
  3811. //hideous hack for properties of chrome & content elms we don't know about
  3812. if (aveq(av, AUNDEF) && (recvchrome || recvcon))
  3813. return recv;
  3814. else
  3815. return av;
  3816. }
  3817. // replace evalExp/DOT with the following function
  3818. evalExp_override(DOT, function(n, fr) {
  3819. var ch = n.children;
  3820. var ans = evalExp(ch[0], fr), avobj = ans.v.baseToObj();
  3821. ans.thisav = avobj; // used by method calls
  3822. if (ch[1].value === "addEventListener-")
  3823. ans.v = aELav;
  3824. else
  3825. ans.v = avobj.getProp(ch[1].value);
  3826. ans.v = evalExp_e10s(n, avobj, ans.v);
  3827. return ans;
  3828. });
  3829. evalExp_override(INDEX, function(n, fr) {
  3830. var ansobj = evalExp(n.children[0], fr), avobj = ansobj.v.baseToObj();
  3831. var prop = n.children[1], errval = ansobj.err, av , ans;
  3832. fr = ansobj.fr;
  3833. if (prop.type === STRING)
  3834. av = avobj.getProp(prop.value);
  3835. else {
  3836. var ansprop = evalExp(prop, fr), avprop = ansprop.v;
  3837. fr = ansprop.fr;
  3838. errval = maybeavjoin(errval, ansprop.err);
  3839. av = BOTTOM;
  3840. if (avprop.hasNum())
  3841. avobj.forEachObj(function(o) { av = avjoin(av, o.getNumProps()); });
  3842. if (avprop.hasStr()) {
  3843. var slit = avprop.getStrLit();
  3844. if (slit)
  3845. av = avjoin(av, avobj.getProp(slit));
  3846. else
  3847. avobj.forEachObj(function(o) { av = avjoin(av, o.getStrProps()); });
  3848. }
  3849. }
  3850. ans = new Ans(evalExp_e10s(n, avobj, av), fr, errval);
  3851. ans.thisav = avobj;
  3852. return ans;
  3853. });
  3854. }
  3855. const XUL = 0, BUBBLE = 1, NO_BUBBLE = 2;
  3856. var eventKinds = {
  3857. abort : BUBBLE,
  3858. blur : NO_BUBBLE,
  3859. broadcast : XUL,
  3860. change : BUBBLE,
  3861. CheckboxStateChange : XUL,
  3862. click : BUBBLE,
  3863. close : XUL,
  3864. command : XUL,
  3865. commandupdate : XUL,
  3866. contextmenu : XUL,
  3867. dblclick : BUBBLE,
  3868. DOMActivate : BUBBLE,
  3869. DOMAttrModified : BUBBLE,
  3870. DOMCharacterDataModified : BUBBLE,
  3871. DOMContentLoaded : BUBBLE,
  3872. DOMFocusIn : BUBBLE,
  3873. DOMFocusOut : BUBBLE,
  3874. DOMMenuItemActive : XUL,
  3875. DOMMenuItemInactive : XUL,
  3876. DOMMouseScroll : XUL,
  3877. DOMNodeInserted : BUBBLE,
  3878. DOMNodeInsertedIntoDocument : NO_BUBBLE,
  3879. DOMNodeRemoved : BUBBLE,
  3880. DOMNodeRemovedFromDocument : NO_BUBBLE,
  3881. DOMSubtreeModified : BUBBLE,
  3882. dragdrop : XUL,
  3883. dragenter : XUL,
  3884. dragexit : XUL,
  3885. draggesture : XUL,
  3886. dragover : XUL,
  3887. error : BUBBLE,
  3888. focus : NO_BUBBLE,
  3889. input : XUL,
  3890. keydown : BUBBLE,
  3891. keypress : BUBBLE,
  3892. keyup : BUBBLE,
  3893. load : NO_BUBBLE,
  3894. mousedown : BUBBLE,
  3895. mousemove : BUBBLE,
  3896. mouseout : BUBBLE,
  3897. mouseover : BUBBLE,
  3898. mouseup : BUBBLE,
  3899. overflow : XUL,
  3900. overflowchanged : XUL,
  3901. pagehide: BUBBLE,
  3902. pageshow: BUBBLE,
  3903. popuphidden : XUL,
  3904. popuphiding : XUL,
  3905. popupshowing : XUL,
  3906. popupshown : XUL,
  3907. RadioStateChange : XUL,
  3908. reset : BUBBLE,
  3909. resize : BUBBLE,
  3910. scroll : BUBBLE,
  3911. select : BUBBLE,
  3912. submit : BUBBLE,
  3913. TabClose : XUL,
  3914. TabOpen : XUL,
  3915. TabSelect : XUL,
  3916. underflow : XUL,
  3917. unload : NO_BUBBLE
  3918. // ViewChanged event from greasemonkey?
  3919. };
  3920. function analyze_addon(ast, tmout) {
  3921. print("Doing CFA");
  3922. timeout = tmout;
  3923. cfa2(ast);
  3924. // Addresses are irrelevant outside jscfa, convert results to array.
  3925. var rs = [];
  3926. for (var addr in e10sResults) {
  3927. var r = e10sResults[addr];
  3928. if (r.analyzed) rs.push(r);
  3929. }
  3930. rs.astSize = astSize;
  3931. rs.timedout = timedout;
  3932. print("Done with CFA: " + timestamp);
  3933. return rs;
  3934. }
  3935. exports.analyze_addon = analyze_addon;