PageRenderTime 26ms CodeModel.GetById 10ms app.highlight 109ms RepoModel.GetById 1ms 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

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

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

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