/[jscoverage]/trunk/js/jsparse.cpp
ViewVC logotype

Diff of /trunk/js/jsparse.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 459 by siliconforks, Tue Dec 9 03:37:47 2008 UTC revision 460 by siliconforks, Sat Sep 26 23:15:22 2009 UTC
# Line 89  Line 89 
89  /*  /*
90   * Asserts to verify assumptions behind pn_ macros.   * Asserts to verify assumptions behind pn_ macros.
91   */   */
92  JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.atom) ==  #define pn_offsetof(m)  offsetof(JSParseNode, m)
93                   offsetof(JSParseNode, pn_u.apair.atom));  
94  JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.slot) ==  JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses));
95                   offsetof(JSParseNode, pn_u.lexical.slot));  JS_STATIC_ASSERT(pn_offsetof(pn_u.name.atom) == pn_offsetof(pn_u.apair.atom));
96    
97    #undef pn_offsetof
98    
99  /*  /*
100   * JS parsers, from lowest to highest precedence.   * JS parsers, from lowest to highest precedence.
# Line 105  Line 107 
107  JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);  JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
108    
109  typedef JSParseNode *  typedef JSParseNode *
110    JSVariablesParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
111                      bool inLetHead);
112    
113    typedef JSParseNode *
114  JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,  JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
115                 JSBool allowCallSyntax);                 JSBool allowCallSyntax);
116    
# Line 120  Line 126 
126  static JSParser FunctionExpr;  static JSParser FunctionExpr;
127  static JSParser Statements;  static JSParser Statements;
128  static JSParser Statement;  static JSParser Statement;
129  static JSParser Variables;  static JSVariablesParser Variables;
130  static JSParser Expr;  static JSParser Expr;
131  static JSParser AssignExpr;  static JSParser AssignExpr;
132  static JSParser CondExpr;  static JSParser CondExpr;
# Line 157  Line 163 
163  static uint32 recyclednodes = 0;  static uint32 recyclednodes = 0;
164  #endif  #endif
165    
166  JSBool  void
167  js_InitParseContext(JSContext *cx, JSParseContext *pc, JSPrincipals *principals,  JSParseNode::become(JSParseNode *pn2)
168                      JSStackFrame *callerFrame,  {
169                      const jschar *base, size_t length,      JS_ASSERT(!pn_defn);
170                      FILE *fp, const char *filename, uintN lineno)      JS_ASSERT(!pn2->pn_defn);
171  {  
172      JS_ASSERT_IF(callerFrame, callerFrame->script);      JS_ASSERT(!pn_used);
173        if (pn2->pn_used) {
174      pc->tempPoolMark = JS_ARENA_MARK(&cx->tempPool);          JSParseNode **pnup = &pn2->pn_lexdef->dn_uses;
175      if (!js_InitTokenStream(cx, TS(pc), base, length, fp, filename, lineno)) {          while (*pnup != pn2)
176          JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark);              pnup = &(*pnup)->pn_link;
177          return JS_FALSE;          *pnup = this;
178            pn_link = pn2->pn_link;
179            pn_used = true;
180            pn2->pn_link = NULL;
181            pn2->pn_used = false;
182        }
183    
184        /* If this is a function node fix up the pn_funbox->node back-pointer. */
185        if (PN_TYPE(pn2) == TOK_FUNCTION && pn2->pn_arity == PN_FUNC)
186            pn2->pn_funbox->node = this;
187    
188        pn_type = pn2->pn_type;
189        pn_op = pn2->pn_op;
190        pn_arity = pn2->pn_arity;
191        pn_u = pn2->pn_u;
192        pn2->clear();
193    }
194    
195    void
196    JSParseNode::clear()
197    {
198        pn_type = TOK_EOF;
199        pn_op = JSOP_NOP;
200        pn_used = pn_defn = false;
201        pn_arity = PN_NULLARY;
202    }
203    
204    bool
205    JSCompiler::init(const jschar *base, size_t length,
206                     FILE *fp, const char *filename, uintN lineno)
207    {
208        JSContext *cx = context;
209    
210        tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
211        if (!js_InitTokenStream(cx, TS(this), base, length, fp, filename, lineno)) {
212            JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
213            return false;
214      }      }
     if (principals)  
         JSPRINCIPALS_HOLD(cx, principals);  
     pc->principals = principals;  
     pc->callerFrame = callerFrame;  
     pc->nodeList = NULL;  
     pc->traceListHead = NULL;  
215    
216      /* Root atoms and objects allocated for the parsed tree. */      /* Root atoms and objects allocated for the parsed tree. */
217      JS_KEEP_ATOMS(cx->runtime);      JS_KEEP_ATOMS(cx->runtime);
218      JS_PUSH_TEMP_ROOT_PARSE_CONTEXT(cx, pc, &pc->tempRoot);      JS_PUSH_TEMP_ROOT_COMPILER(cx, this, &tempRoot);
219      return JS_TRUE;      return true;
220  }  }
221    
222  void  JSCompiler::~JSCompiler()
 js_FinishParseContext(JSContext *cx, JSParseContext *pc)  
223  {  {
224      if (pc->principals)      JSContext *cx = context;
225          JSPRINCIPALS_DROP(cx, pc->principals);  
226      JS_ASSERT(pc->tempRoot.u.parseContext == pc);      if (principals)
227      JS_POP_TEMP_ROOT(cx, &pc->tempRoot);          JSPRINCIPALS_DROP(cx, principals);
228        JS_ASSERT(tempRoot.u.compiler == this);
229        JS_POP_TEMP_ROOT(cx, &tempRoot);
230      JS_UNKEEP_ATOMS(cx->runtime);      JS_UNKEEP_ATOMS(cx->runtime);
231      js_CloseTokenStream(cx, TS(pc));      js_CloseTokenStream(cx, TS(this));
232      JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark);      JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
233  }  }
234    
235  void  void
236  js_InitCompilePrincipals(JSContext *cx, JSParseContext *pc,  JSCompiler::setPrincipals(JSPrincipals *prin)
                          JSPrincipals *principals)  
237  {  {
238      JS_ASSERT(!pc->principals);      JS_ASSERT(!principals);
239      if (principals)      if (prin)
240          JSPRINCIPALS_HOLD(cx, principals);          JSPRINCIPALS_HOLD(context, prin);
241      pc->principals = principals;      principals = prin;
242  }  }
243    
244  JSParsedObjectBox *  JSObjectBox *
245  js_NewParsedObjectBox(JSContext *cx, JSParseContext *pc, JSObject *obj)  JSCompiler::newObjectBox(JSObject *obj)
246  {  {
247      JSParsedObjectBox *pob;      JS_ASSERT(obj);
248    
249      /*      /*
250       * We use JSContext.tempPool to allocate parsed objects and place them on       * We use JSContext.tempPool to allocate parsed objects and place them on
# Line 216  Line 252 
252       * containing the entries must be alive until we are done with scanning,       * containing the entries must be alive until we are done with scanning,
253       * parsing and code generation for the whole script or top-level function.       * parsing and code generation for the whole script or top-level function.
254       */       */
255      JS_ASSERT(obj);      JSObjectBox *objbox;
256      JS_ARENA_ALLOCATE_TYPE(pob, JSParsedObjectBox, &cx->tempPool);      JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool);
257      if (!pob) {      if (!objbox) {
258          js_ReportOutOfScriptQuota(cx);          js_ReportOutOfScriptQuota(context);
259          return NULL;          return NULL;
260      }      }
261      pob->traceLink = pc->traceListHead;      objbox->traceLink = traceListHead;
262      pob->emitLink = NULL;      traceListHead = objbox;
263      pob->object = obj;      objbox->emitLink = NULL;
264      pc->traceListHead = pob;      objbox->object = obj;
265      return pob;      return objbox;
266  }  }
267    
268    JSFunctionBox *
269    JSCompiler::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
270    {
271        JS_ASSERT(obj);
272        JS_ASSERT(HAS_FUNCTION_CLASS(obj));
273    
274        /*
275         * We use JSContext.tempPool to allocate parsed objects and place them on
276         * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
277         * containing the entries must be alive until we are done with scanning,
278         * parsing and code generation for the whole script or top-level function.
279         */
280        JSFunctionBox *funbox;
281        JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool);
282        if (!funbox) {
283            js_ReportOutOfScriptQuota(context);
284            return NULL;
285        }
286        funbox->traceLink = traceListHead;
287        traceListHead = funbox;
288        funbox->emitLink = NULL;
289        funbox->object = obj;
290        funbox->node = fn;
291        funbox->siblings = tc->functionList;
292        tc->functionList = funbox;
293        ++tc->compiler->functionCount;
294        funbox->kids = NULL;
295        funbox->parent = tc->funbox;
296        funbox->queued = false;
297        funbox->inLoop = false;
298        for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
299            if (STMT_IS_LOOP(stmt)) {
300                funbox->inLoop = true;
301                break;
302            }
303        }
304        funbox->level = tc->staticLevel;
305        funbox->tcflags = TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO);
306        return funbox;
307    }
308    
309  void  void
310  js_TraceParseContext(JSTracer *trc, JSParseContext *pc)  JSCompiler::trace(JSTracer *trc)
311  {  {
312      JSParsedObjectBox *pob;      JSObjectBox *objbox;
313    
314      JS_ASSERT(pc->tempRoot.u.parseContext == pc);      JS_ASSERT(tempRoot.u.compiler == this);
315      pob = pc->traceListHead;      objbox = traceListHead;
316      while (pob) {      while (objbox) {
317          JS_CALL_OBJECT_TRACER(trc, pob->object, "parser.object");          JS_CALL_OBJECT_TRACER(trc, objbox->object, "parser.object");
318          pob = pob->traceLink;          objbox = objbox->traceLink;
319      }      }
320  }  }
321    
322    static void
323    UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc);
324    
325    static void
326    UnlinkFunctionBox(JSParseNode *pn, JSTreeContext *tc)
327    {
328        JSFunctionBox *funbox = pn->pn_funbox;
329        if (funbox) {
330            JS_ASSERT(funbox->node == pn);
331            funbox->node = NULL;
332    
333            JSFunctionBox **funboxp = &tc->functionList;
334            while (*funboxp) {
335                if (*funboxp == funbox) {
336                    *funboxp = funbox->siblings;
337                    break;
338                }
339                funboxp = &(*funboxp)->siblings;
340            }
341    
342            uint16 oldflags = tc->flags;
343            JSFunctionBox *oldlist = tc->functionList;
344    
345            tc->flags = (uint16) funbox->tcflags;
346            tc->functionList = funbox->kids;
347            UnlinkFunctionBoxes(pn->pn_body, tc);
348            funbox->kids = tc->functionList;
349            tc->flags = oldflags;
350            tc->functionList = oldlist;
351    
352            // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
353            pn->pn_funbox = NULL;
354        }
355    }
356    
357    static void
358    UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc)
359    {
360        if (pn) {
361            switch (pn->pn_arity) {
362              case PN_NULLARY:
363                return;
364              case PN_UNARY:
365                UnlinkFunctionBoxes(pn->pn_kid, tc);
366                return;
367              case PN_BINARY:
368                UnlinkFunctionBoxes(pn->pn_left, tc);
369                UnlinkFunctionBoxes(pn->pn_right, tc);
370                return;
371              case PN_TERNARY:
372                UnlinkFunctionBoxes(pn->pn_kid1, tc);
373                UnlinkFunctionBoxes(pn->pn_kid2, tc);
374                UnlinkFunctionBoxes(pn->pn_kid3, tc);
375                return;
376              case PN_LIST:
377                for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
378                    UnlinkFunctionBoxes(pn2, tc);
379                return;
380              case PN_FUNC:
381                UnlinkFunctionBox(pn, tc);
382                return;
383              case PN_NAME:
384                UnlinkFunctionBoxes(pn->maybeExpr(), tc);
385                return;
386              case PN_NAMESET:
387                UnlinkFunctionBoxes(pn->pn_tree, tc);
388            }
389        }
390    }
391    
392    static void
393    RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc);
394    
395  static JSParseNode *  static JSParseNode *
396  RecycleTree(JSParseNode *pn, JSTreeContext *tc)  RecycleTree(JSParseNode *pn, JSTreeContext *tc)
397  {  {
398      JSParseNode *next;      JSParseNode *next, **head;
399    
400      if (!pn)      if (!pn)
401          return NULL;          return NULL;
402    
403      /* Catch back-to-back dup recycles. */      /* Catch back-to-back dup recycles. */
404      JS_ASSERT(pn != tc->parseContext->nodeList);      JS_ASSERT(pn != tc->compiler->nodeList);
405      next = pn->pn_next;      next = pn->pn_next;
406      pn->pn_next = tc->parseContext->nodeList;      if (pn->pn_used || pn->pn_defn) {
407      tc->parseContext->nodeList = pn;          /*
408             * JSAtomLists own definition nodes along with their used-node chains.
409             * Defer recycling such nodes until we unwind to top level to avoid
410             * linkage overhead or (alternatively) unlinking runtime complexity.
411             * Yes, this means dead code can contribute to static analysis results!
412             *
413             * Do recycle kids here, since they are no longer needed.
414             */
415            pn->pn_next = NULL;
416            RecycleFuncNameKids(pn, tc);
417        } else {
418            UnlinkFunctionBoxes(pn, tc);
419            head = &tc->compiler->nodeList;
420            pn->pn_next = *head;
421            *head = pn;
422  #ifdef METER_PARSENODES  #ifdef METER_PARSENODES
423      recyclednodes++;          recyclednodes++;
424  #endif  #endif
425        }
426      return next;      return next;
427  }  }
428    
429    static void
430    RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc)
431    {
432        switch (pn->pn_arity) {
433          case PN_FUNC:
434            UnlinkFunctionBox(pn, tc);
435            /* FALL THROUGH */
436    
437          case PN_NAME:
438            /*
439             * Only a definition node might have a non-null strong pn_expr link
440             * to recycle, but we test !pn_used to handle PN_FUNC fall through.
441             * Every node with the pn_used flag set has a non-null pn_lexdef
442             * weak reference to its definition node.
443             */
444            if (!pn->pn_used && pn->pn_expr) {
445                RecycleTree(pn->pn_expr, tc);
446                pn->pn_expr = NULL;
447            }
448            break;
449    
450          default:
451            JS_ASSERT(PN_TYPE(pn) == TOK_FUNCTION);
452        }
453    }
454    
455  static JSParseNode *  static JSParseNode *
456  NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)  NewOrRecycledNode(JSTreeContext *tc)
457  {  {
458      JSParseNode *pn;      JSParseNode *pn, *pn2;
459    
460      pn = tc->parseContext->nodeList;      pn = tc->compiler->nodeList;
461      if (!pn) {      if (!pn) {
462            JSContext *cx = tc->compiler->context;
463    
464          JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);          JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
465          if (!pn)          if (!pn)
466              js_ReportOutOfScriptQuota(cx);              js_ReportOutOfScriptQuota(cx);
467      } else {      } else {
468          tc->parseContext->nodeList = pn->pn_next;          tc->compiler->nodeList = pn->pn_next;
469    
470          /* Recycle immediate descendents only, to save work and working set. */          /* Recycle immediate descendents only, to save work and working set. */
471          switch (pn->pn_arity) {          switch (pn->pn_arity) {
# Line 281  Line 473 
473              RecycleTree(pn->pn_body, tc);              RecycleTree(pn->pn_body, tc);
474              break;              break;
475            case PN_LIST:            case PN_LIST:
476              if (pn->pn_head) {              pn2 = pn->pn_head;
477                  /* XXX check for dup recycles in the list */              if (pn2) {
478                  *pn->pn_tail = tc->parseContext->nodeList;                  while (pn2 && !pn2->pn_used && !pn2->pn_defn)
479                  tc->parseContext->nodeList = pn->pn_head;                      pn2 = pn2->pn_next;
480                    if (pn2) {
481                        pn2 = pn->pn_head;
482                        do {
483                            pn2 = RecycleTree(pn2, tc);
484                        } while (pn2);
485                    } else {
486                        *pn->pn_tail = tc->compiler->nodeList;
487                        tc->compiler->nodeList = pn->pn_head;
488  #ifdef METER_PARSENODES  #ifdef METER_PARSENODES
489                  recyclednodes += pn->pn_count;                      recyclednodes += pn->pn_count;
490  #endif  #endif
491                        break;
492                    }
493              }              }
494              break;              break;
495            case PN_TERNARY:            case PN_TERNARY:
# Line 304  Line 506 
506              RecycleTree(pn->pn_kid, tc);              RecycleTree(pn->pn_kid, tc);
507              break;              break;
508            case PN_NAME:            case PN_NAME:
509              RecycleTree(pn->pn_expr, tc);              if (!pn->pn_used)
510                    RecycleTree(pn->pn_expr, tc);
511              break;              break;
512            case PN_NULLARY:            case PN_NULLARY:
513              break;              break;
# Line 316  Line 519 
519          if (parsenodes - recyclednodes > maxparsenodes)          if (parsenodes - recyclednodes > maxparsenodes)
520              maxparsenodes = parsenodes - recyclednodes;              maxparsenodes = parsenodes - recyclednodes;
521  #endif  #endif
522            pn->pn_used = pn->pn_defn = false;
523          memset(&pn->pn_u, 0, sizeof pn->pn_u);          memset(&pn->pn_u, 0, sizeof pn->pn_u);
524          pn->pn_next = NULL;          pn->pn_next = NULL;
525      }      }
526      return pn;      return pn;
527  }  }
528    
529    static inline void
530    InitParseNode(JSParseNode *pn, JSTokenType type, JSOp op, JSParseNodeArity arity)
531    {
532        pn->pn_type = type;
533        pn->pn_op = op;
534        pn->pn_arity = arity;
535        JS_ASSERT(!pn->pn_used);
536        JS_ASSERT(!pn->pn_defn);
537        pn->pn_next = pn->pn_link = NULL;
538    }
539    
540  /*  /*
541   * Allocate a JSParseNode from cx's temporary arena.   * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
542     * temporary arena.
543   */   */
544  static JSParseNode *  static JSParseNode *
545  NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity,  NewParseNode(JSParseNodeArity arity, JSTreeContext *tc)
              JSTreeContext *tc)  
546  {  {
547      JSParseNode *pn;      JSParseNode *pn;
548      JSToken *tp;      JSToken *tp;
549    
550      pn = NewOrRecycledNode(cx, tc);      pn = NewOrRecycledNode(tc);
551      if (!pn)      if (!pn)
552          return NULL;          return NULL;
553      tp = &CURRENT_TOKEN(ts);      tp = &CURRENT_TOKEN(&tc->compiler->tokenStream);
554      pn->pn_type = tp->type;      InitParseNode(pn, tp->type, JSOP_NOP, arity);
555      pn->pn_pos = tp->pos;      pn->pn_pos = tp->pos;
556      pn->pn_op = JSOP_NOP;      return pn;
557      pn->pn_arity = arity;  }
558    
559    static inline void
560    InitNameNodeCommon(JSParseNode *pn, JSTreeContext *tc)
561    {
562        pn->pn_expr = NULL;
563        pn->pn_cookie = FREE_UPVAR_COOKIE;
564        pn->pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0;
565        if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
566            pn->pn_dflags |= PND_BLOCKCHILD;
567        pn->pn_blockid = tc->blockid();
568    }
569    
570    static JSParseNode *
571    NewNameNode(JSContext *cx, JSTokenStream *ts, JSAtom *atom, JSTreeContext *tc)
572    {
573        JSParseNode *pn;
574    
575        pn = NewParseNode(PN_NAME, tc);
576        if (pn) {
577            pn->pn_atom = atom;
578            InitNameNodeCommon(pn, tc);
579        }
580      return pn;      return pn;
581  }  }
582    
583  static JSParseNode *  static JSParseNode *
584  NewBinary(JSContext *cx, JSTokenType tt,  NewBinary(JSTokenType tt, JSOp op, JSParseNode *left, JSParseNode *right,
           JSOp op, JSParseNode *left, JSParseNode *right,  
585            JSTreeContext *tc)            JSTreeContext *tc)
586  {  {
587      JSParseNode *pn, *pn1, *pn2;      JSParseNode *pn, *pn1, *pn2;
# Line 357  Line 593 
593       * Flatten a left-associative (left-heavy) tree of a given operator into       * Flatten a left-associative (left-heavy) tree of a given operator into
594       * a list, to reduce js_FoldConstants and js_EmitTree recursion.       * a list, to reduce js_FoldConstants and js_EmitTree recursion.
595       */       */
596      if (left->pn_type == tt &&      if (PN_TYPE(left) == tt &&
597          left->pn_op == op &&          PN_OP(left) == op &&
598          (js_CodeSpec[op].format & JOF_LEFTASSOC)) {          (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
599          if (left->pn_arity != PN_LIST) {          if (left->pn_arity != PN_LIST) {
600              pn1 = left->pn_left, pn2 = left->pn_right;              pn1 = left->pn_left, pn2 = left->pn_right;
601              left->pn_arity = PN_LIST;              left->pn_arity = PN_LIST;
602              PN_INIT_LIST_1(left, pn1);              left->initList(pn1);
603              PN_APPEND(left, pn2);              left->append(pn2);
604              if (tt == TOK_PLUS) {              if (tt == TOK_PLUS) {
605                  if (pn1->pn_type == TOK_STRING)                  if (pn1->pn_type == TOK_STRING)
606                      left->pn_extra |= PNX_STRCAT;                      left->pn_xflags |= PNX_STRCAT;
607                  else if (pn1->pn_type != TOK_NUMBER)                  else if (pn1->pn_type != TOK_NUMBER)
608                      left->pn_extra |= PNX_CANTFOLD;                      left->pn_xflags |= PNX_CANTFOLD;
609                  if (pn2->pn_type == TOK_STRING)                  if (pn2->pn_type == TOK_STRING)
610                      left->pn_extra |= PNX_STRCAT;                      left->pn_xflags |= PNX_STRCAT;
611                  else if (pn2->pn_type != TOK_NUMBER)                  else if (pn2->pn_type != TOK_NUMBER)
612                      left->pn_extra |= PNX_CANTFOLD;                      left->pn_xflags |= PNX_CANTFOLD;
613              }              }
614          }          }
615          PN_APPEND(left, right);          left->append(right);
616          left->pn_pos.end = right->pn_pos.end;          left->pn_pos.end = right->pn_pos.end;
617          if (tt == TOK_PLUS) {          if (tt == TOK_PLUS) {
618              if (right->pn_type == TOK_STRING)              if (right->pn_type == TOK_STRING)
619                  left->pn_extra |= PNX_STRCAT;                  left->pn_xflags |= PNX_STRCAT;
620              else if (right->pn_type != TOK_NUMBER)              else if (right->pn_type != TOK_NUMBER)
621                  left->pn_extra |= PNX_CANTFOLD;                  left->pn_xflags |= PNX_CANTFOLD;
622          }          }
623          return left;          return left;
624      }      }
# Line 403  Line 639 
639          return left;          return left;
640      }      }
641    
642      pn = NewOrRecycledNode(cx, tc);      pn = NewOrRecycledNode(tc);
643      if (!pn)      if (!pn)
644          return NULL;          return NULL;
645      pn->pn_type = tt;      InitParseNode(pn, tt, op, PN_BINARY);
646      pn->pn_pos.begin = left->pn_pos.begin;      pn->pn_pos.begin = left->pn_pos.begin;
647      pn->pn_pos.end = right->pn_pos.end;      pn->pn_pos.end = right->pn_pos.end;
     pn->pn_op = op;  
     pn->pn_arity = PN_BINARY;  
648      pn->pn_left = left;      pn->pn_left = left;
649      pn->pn_right = right;      pn->pn_right = right;
650      return pn;      return pn;
# Line 460  Line 694 
694  }  }
695  #endif  #endif
696    
697    static bool
698    GenerateBlockId(JSTreeContext *tc, uint32& blockid)
699    {
700        if (tc->blockidGen == JS_BIT(20)) {
701            JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
702                                 JSMSG_NEED_DIET, "program");
703            return false;
704        }
705        blockid = tc->blockidGen++;
706        return true;
707    }
708    
709    static bool
710    GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc)
711    {
712        JS_ASSERT(tc->topStmt);
713        JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt));
714        JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE);
715        if (!GenerateBlockId(tc, tc->topStmt->blockid))
716            return false;
717        pn->pn_blockid = tc->topStmt->blockid;
718        return true;
719    }
720    
721  /*  /*
722   * Parse a top-level JS script.   * Parse a top-level JS script.
723   */   */
724  JSParseNode *  JSParseNode *
725  js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc)  JSCompiler::parse(JSObject *chain)
726  {  {
     JSTreeContext tc;  
     JSParseNode *pn;  
   
727      /*      /*
728       * Protect atoms from being collected by a GC activation, which might       * Protect atoms from being collected by a GC activation, which might
729       * - nest on this thread due to out of memory (the so-called "last ditch"       * - nest on this thread due to out of memory (the so-called "last ditch"
# Line 477  Line 732 
732       *   an object lock before it finishes generating bytecode into a script       *   an object lock before it finishes generating bytecode into a script
733       *   protected from the GC by a root or a stack frame reference.       *   protected from the GC by a root or a stack frame reference.
734       */       */
735      TREE_CONTEXT_INIT(&tc, pc);      JSTreeContext tc(this);
736      tc.u.scopeChain = chain;      tc.scopeChain = chain;
737      pn = Statements(cx, TS(pc), &tc);      if (!GenerateBlockId(&tc, tc.bodyid))
738            return NULL;
739    
740        JSParseNode *pn = Statements(context, TS(this), &tc);
741      if (pn) {      if (pn) {
742          if (!js_MatchToken(cx, TS(pc), TOK_EOF)) {          if (!js_MatchToken(context, TS(this), TOK_EOF)) {
743              js_ReportCompileErrorNumber(cx, TS(pc), NULL, JSREPORT_ERROR,              js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR,
744                                          JSMSG_SYNTAX_ERROR);                                          JSMSG_SYNTAX_ERROR);
745              pn = NULL;              pn = NULL;
746          } else {          } else {
747              pn->pn_type = TOK_LC;              if (!js_FoldConstants(context, pn, &tc))
             if (!js_FoldConstants(cx, pn, &tc))  
748                  pn = NULL;                  pn = NULL;
749          }          }
750      }      }
   
     TREE_CONTEXT_FINISH(cx, &tc);  
751      return pn;      return pn;
752  }  }
753    
754    JS_STATIC_ASSERT(FREE_STATIC_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
755    
756    static inline bool
757    SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
758    {
759        /*
760         * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
761         * (0xffffffff) and other cookies with that level.
762         *
763         * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
764         * practically speaking it leaves more than enough room for upvars. In fact
765         * we might want to split cookie fields giving fewer bits for skip and more
766         * for slot, but only based on evidence.
767         */
768        if (staticLevel >= FREE_STATIC_LEVEL) {
769            JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
770                                 JSMSG_TOO_DEEP, js_function_str);
771            return false;
772        }
773        tc->staticLevel = staticLevel;
774        return true;
775    }
776    
777  /*  /*
778   * Compile a top-level script.   * Compile a top-level script.
779   */   */
780  extern JSScript *  JSScript *
781  js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,  JSCompiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
782                   JSPrincipals *principals, uint32 tcflags,                            JSPrincipals *principals, uint32 tcflags,
783                   const jschar *chars, size_t length,                            const jschar *chars, size_t length,
784                   FILE *file, const char *filename, uintN lineno)                            FILE *file, const char *filename, uintN lineno,
785                              JSString *source /* = NULL */)
786  {  {
787      JSParseContext pc;      JSCompiler jsc(cx, principals, callerFrame);
788      JSArenaPool codePool, notePool;      JSArenaPool codePool, notePool;
     JSCodeGenerator cg;  
789      JSTokenType tt;      JSTokenType tt;
790      JSParseNode *pn;      JSParseNode *pn;
791      uint32 scriptGlobals;      uint32 scriptGlobals;
# Line 517  Line 795 
795  #endif  #endif
796    
797      JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL |      JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL |
798                              TCF_STATIC_DEPTH_MASK)));                              TCF_STATIC_LEVEL_MASK)));
799    
800      /*      /*
801       * The scripted callerFrame can only be given for compile-and-go scripts       * The scripted callerFrame can only be given for compile-and-go scripts
802       * and non-zero static depth requires callerFrame.       * and non-zero static level requires callerFrame.
803       */       */
804      JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);      JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
805      JS_ASSERT_IF(TCF_GET_STATIC_DEPTH(tcflags) != 0, callerFrame);      JS_ASSERT_IF(TCF_GET_STATIC_LEVEL(tcflags) != 0, callerFrame);
806    
807      if (!js_InitParseContext(cx, &pc, principals, callerFrame, chars, length,      if (!jsc.init(chars, length, file, filename, lineno))
                              file, filename, lineno)) {  
808          return NULL;          return NULL;
     }  
809    
810      JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),      JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
811                         &cx->scriptStackQuota);                         &cx->scriptStackQuota);
812      JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),      JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
813                         &cx->scriptStackQuota);                         &cx->scriptStackQuota);
814      js_InitCodeGenerator(cx, &cg, &pc, &codePool, &notePool,  
815                           pc.tokenStream.lineno);      JSCodeGenerator cg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
816    
817      MUST_FLOW_THROUGH("out");      MUST_FLOW_THROUGH("out");
     cg.treeContext.flags |= (uint16) tcflags;  
     cg.treeContext.u.scopeChain = scopeChain;  
     cg.staticDepth = TCF_GET_STATIC_DEPTH(tcflags);  
818    
819      if ((tcflags & TCF_COMPILE_N_GO) && callerFrame && callerFrame->fun) {      /* Null script early in case of error, to reduce our code footprint. */
820          /*      script = NULL;
821           * An eval script in a caller frame needs to have its enclosing function  
822           * captured in case it uses an upvar reference, and someone wishes to      cg.flags |= uint16(tcflags);
823           * decompile it while running.      cg.scopeChain = scopeChain;
824           */      if (!SetStaticLevel(&cg, TCF_GET_STATIC_LEVEL(tcflags)))
825          JSParsedObjectBox *pob = js_NewParsedObjectBox(cx, &pc, callerFrame->callee);          goto out;
826          pob->emitLink = cg.objectList.lastPob;  
827          cg.objectList.lastPob = pob;      /*
828          cg.objectList.length++;       * If funbox is non-null after we create the new script, callerFrame->fun
829         * was saved in the 0th object table entry.
830         */
831        JSObjectBox *funbox;
832        funbox = NULL;
833    
834        if (tcflags & TCF_COMPILE_N_GO) {
835            if (source) {
836                /*
837                 * Save eval program source in script->atomMap.vector[0] for the
838                 * eval cache (see obj_eval in jsobj.cpp).
839                 */
840                JSAtom *atom = js_AtomizeString(cx, source, 0);
841                if (!atom || !cg.atomList.add(&jsc, atom))
842                    goto out;
843            }
844    
845            if (callerFrame && callerFrame->fun) {
846                /*
847                 * An eval script in a caller frame needs to have its enclosing
848                 * function captured in case it refers to an upvar, and someone
849                 * wishes to decompile it while it's running.
850                 */
851                funbox = jsc.newObjectBox(FUN_OBJECT(callerFrame->fun));
852                if (!funbox)
853                    goto out;
854                funbox->emitLink = cg.objectList.lastbox;
855                cg.objectList.lastbox = funbox;
856                cg.objectList.length++;
857            }
858      }      }
859    
860      /* Inline Statements() to emit as we go to save space. */      /*
861         * Inline Statements to emit as we go to save AST space. We must generate
862         * our script-body blockid since we aren't calling Statements.
863         */
864        uint32 bodyid;
865        if (!GenerateBlockId(&cg, bodyid))
866            goto out;
867        cg.bodyid = bodyid;
868    
869    #if JS_HAS_XML_SUPPORT
870        pn = NULL;
871        bool onlyXML;
872        onlyXML = true;
873    #endif
874    
875      for (;;) {      for (;;) {
876          pc.tokenStream.flags |= TSF_OPERAND;          jsc.tokenStream.flags |= TSF_OPERAND;
877          tt = js_PeekToken(cx, &pc.tokenStream);          tt = js_PeekToken(cx, &jsc.tokenStream);
878          pc.tokenStream.flags &= ~TSF_OPERAND;          jsc.tokenStream.flags &= ~TSF_OPERAND;
879          if (tt <= TOK_EOF) {          if (tt <= TOK_EOF) {
880              if (tt == TOK_EOF)              if (tt == TOK_EOF)
881                  break;                  break;
882              JS_ASSERT(tt == TOK_ERROR);              JS_ASSERT(tt == TOK_ERROR);
             script = NULL;  
883              goto out;              goto out;
884          }          }
885    
886          pn = Statement(cx, &pc.tokenStream, &cg.treeContext);          pn = Statement(cx, &jsc.tokenStream, &cg);
887          if (!pn) {          if (!pn)
888              script = NULL;              goto out;
889            JS_ASSERT(!cg.blockNode);
890    
891            if (!js_FoldConstants(cx, pn, &cg))
892              goto out;              goto out;
893    
894            if (cg.functionList) {
895                if (!jsc.analyzeFunctions(cg.functionList, cg.flags))
896                    goto out;
897                cg.functionList = NULL;
898          }          }
         JS_ASSERT(!cg.treeContext.blockNode);  
899    
900          if (!js_FoldConstants(cx, pn, &cg.treeContext) ||          if (!js_EmitTree(cx, &cg, pn))
             !js_EmitTree(cx, &cg, pn)) {  
             script = NULL;  
901              goto out;              goto out;
902    #if JS_HAS_XML_SUPPORT
903            if (PN_TYPE(pn) != TOK_SEMI ||
904                !pn->pn_kid ||
905                !TREE_TYPE_IS_XML(PN_TYPE(pn->pn_kid))) {
906                onlyXML = false;
907          }          }
908          RecycleTree(pn, &cg.treeContext);  #endif
909            RecycleTree(pn, &cg);
910      }      }
911    
912    #if JS_HAS_XML_SUPPORT
913      /*      /*
914       * Global variables and regexps shares the index space with locals. Due to       * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
915         * For background, see:
916         *
917         * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
918         */
919        if (pn && onlyXML && (tcflags & TCF_NO_SCRIPT_RVAL)) {
920            js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL, JSREPORT_ERROR,
921                                        JSMSG_XML_WHOLE_PROGRAM);
922            goto out;
923        }
924    #endif
925    
926        /*
927         * Global variables and regexps share the index space with locals. Due to
928       * incremental code generation we need to patch the bytecode to adjust the       * incremental code generation we need to patch the bytecode to adjust the
929       * local references to skip the globals.       * local references to skip the globals.
930       */       */
931      scriptGlobals = cg.treeContext.ngvars + cg.regexpList.length;      scriptGlobals = cg.ngvars + cg.regexpList.length;
932      if (scriptGlobals != 0) {      if (scriptGlobals != 0) {
933          jsbytecode *code, *end;          jsbytecode *code, *end;
934          JSOp op;          JSOp op;
# Line 635  Line 975 
975       * Nowadays the threaded interpreter needs a stop instruction, so we       * Nowadays the threaded interpreter needs a stop instruction, so we
976       * do have to emit that here.       * do have to emit that here.
977       */       */
978      if (js_Emit1(cx, &cg, JSOP_STOP) < 0) {      if (js_Emit1(cx, &cg, JSOP_STOP) < 0)
         script = NULL;  
979          goto out;          goto out;
     }  
980  #ifdef METER_PARSENODES  #ifdef METER_PARSENODES
981      printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",      printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
982             (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);             (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
983  #endif  #endif
984  #ifdef JS_ARENAMETER  #ifdef JS_ARENAMETER
985      JS_DumpArenaStats(stdout);      JS_DumpArenaStats(stdout);
986  #endif  #endif
987      script = js_NewScriptFromCG(cx, &cg);      script = js_NewScriptFromCG(cx, &cg);
988        if (script && funbox)
989            script->flags |= JSSF_SAVED_CALLER_FUN;
990    
991  #ifdef JS_SCOPE_DEPTH_METER  #ifdef JS_SCOPE_DEPTH_METER
992      if (script) {      if (script) {
# Line 659  Line 999 
999  #endif  #endif
1000    
1001    out:    out:
     js_FinishCodeGenerator(cx, &cg);  
1002      JS_FinishArenaPool(&codePool);      JS_FinishArenaPool(&codePool);
1003      JS_FinishArenaPool(&notePool);      JS_FinishArenaPool(&notePool);
     js_FinishParseContext(cx, &pc);  
1004      return script;      return script;
1005    
1006    too_many_slots:    too_many_slots:
1007      js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,      js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1008                                  JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);                                  JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
1009      script = NULL;      script = NULL;
1010      goto out;      goto out;
# Line 692  Line 1030 
1030        case TOK_LC:        case TOK_LC:
1031          if (!pn->pn_head)          if (!pn->pn_head)
1032              return ENDS_IN_OTHER;              return ENDS_IN_OTHER;
1033          return HasFinalReturn(PN_LAST(pn));          return HasFinalReturn(pn->last());
1034    
1035        case TOK_IF:        case TOK_IF:
1036          if (!pn->pn_kid3)          if (!pn->pn_kid3)
# Line 733  Line 1071 
1071          hasDefault = ENDS_IN_OTHER;          hasDefault = ENDS_IN_OTHER;
1072          pn2 = pn->pn_right;          pn2 = pn->pn_right;
1073          if (pn2->pn_type == TOK_LEXICALSCOPE)          if (pn2->pn_type == TOK_LEXICALSCOPE)
1074              pn2 = pn2->pn_expr;              pn2 = pn2->expr();
1075          for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {          for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
1076              if (pn2->pn_type == TOK_DEFAULT)              if (pn2->pn_type == TOK_DEFAULT)
1077                  hasDefault = ENDS_IN_RETURN;                  hasDefault = ENDS_IN_RETURN;
1078              pn3 = pn2->pn_right;              pn3 = pn2->pn_right;
1079              JS_ASSERT(pn3->pn_type == TOK_LC);              JS_ASSERT(pn3->pn_type == TOK_LC);
1080              if (pn3->pn_head) {              if (pn3->pn_head) {
1081                  rv2 = HasFinalReturn(PN_LAST(pn3));                  rv2 = HasFinalReturn(pn3->last());
1082                  if (rv2 == ENDS_IN_OTHER && pn2->pn_next)                  if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
1083                      /* Falling through to next case or default. */;                      /* Falling through to next case or default. */;
1084                  else                  else
# Line 762  Line 1100 
1100    
1101        case TOK_COLON:        case TOK_COLON:
1102        case TOK_LEXICALSCOPE:        case TOK_LEXICALSCOPE:
1103          return HasFinalReturn(pn->pn_expr);          return HasFinalReturn(pn->expr());
1104    
1105        case TOK_THROW:        case TOK_THROW:
1106          return ENDS_IN_RETURN;          return ENDS_IN_RETURN;
# Line 806  Line 1144 
1144      const char *name;      const char *name;
1145    
1146      JS_ASSERT(tc->flags & TCF_IN_FUNCTION);      JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1147      if (tc->u.fun->atom) {      if (tc->fun->atom) {
1148          name = js_AtomToPrintableString(cx, tc->u.fun->atom);          name = js_AtomToPrintableString(cx, tc->fun->atom);
1149      } else {      } else {
1150          errnum = anonerrnum;          errnum = anonerrnum;
1151          name = NULL;          name = NULL;
1152      }      }
1153      return js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, flags,      return js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL, flags,
1154                                         errnum, name);                                         errnum, name);
1155  }  }
1156    
# Line 849  Line 1187 
1187      if (CURRENT_TOKEN(ts).type == TOK_LC) {      if (CURRENT_TOKEN(ts).type == TOK_LC) {
1188          pn = Statements(cx, ts, tc);          pn = Statements(cx, ts, tc);
1189      } else {      } else {
1190          pn = NewParseNode(cx, ts, PN_UNARY, tc);          pn = NewParseNode(PN_UNARY, tc);
1191          if (pn) {          if (pn) {
1192              pn->pn_kid = AssignExpr(cx, ts, tc);              pn->pn_kid = AssignExpr(cx, ts, tc);
1193              if (!pn->pn_kid) {              if (!pn->pn_kid) {
# Line 873  Line 1211 
1211  #endif  #endif
1212    
1213      if (pn) {      if (pn) {
1214            JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
1215          js_PopStatement(tc);          js_PopStatement(tc);
1216          pn->pn_pos.begin.lineno = firstLine;          pn->pn_pos.begin.lineno = firstLine;
1217    
# Line 883  Line 1222 
1222          }          }
1223      }      }
1224    
1225      tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));      tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
1226      return pn;      return pn;
1227  }  }
1228    
1229    static JSAtomListElement *
1230    MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
1231    {
1232        JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, pn->pn_atom);
1233        if (!ale)
1234            return NULL;
1235    
1236        JSDefinition *dn = (JSDefinition *)
1237            NewNameNode(tc->compiler->context, TS(tc->compiler), pn->pn_atom, tc);
1238        if (!dn)
1239            return NULL;
1240    
1241        ALE_SET_DEFN(ale, dn);
1242        dn->pn_defn = true;
1243        dn->pn_dflags |= PND_PLACEHOLDER;
1244        return ale;
1245    }
1246    
1247    static bool
1248    Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
1249    {
1250        JS_ASSERT(!pn->pn_used);
1251        JS_ASSERT_IF(pn->pn_defn, pn->isPlaceholder());
1252    
1253        JSHashEntry **hep;
1254        JSAtomListElement *ale = NULL;
1255        JSAtomList *list = NULL;
1256    
1257        if (let)
1258            ale = (list = &tc->decls)->rawLookup(atom, hep);
1259        if (!ale)
1260            ale = (list = &tc->lexdeps)->rawLookup(atom, hep);
1261    
1262        if (ale) {
1263            JSDefinition *dn = ALE_DEFN(ale);
1264            if (dn != pn) {
1265                JSParseNode **pnup = &dn->dn_uses;
1266                JSParseNode *pnu;
1267                uintN start = let ? pn->pn_blockid : tc->bodyid;
1268    
1269                while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
1270                    JS_ASSERT(pnu->pn_used);
1271                    pnu->pn_lexdef = (JSDefinition *) pn;
1272                    pn->pn_dflags |= pnu->pn_dflags & (PND_ASSIGNED | PND_FUNARG);
1273                    pnup = &pnu->pn_link;
1274                }
1275    
1276                if (pnu != dn->dn_uses) {
1277                    *pnup = pn->dn_uses;
1278                    pn->dn_uses = dn->dn_uses;
1279                    dn->dn_uses = pnu;
1280    
1281                    if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls)
1282                        list->rawRemove(tc->compiler, ale, hep);
1283                }
1284            }
1285        }
1286    
1287        ale = tc->decls.add(tc->compiler, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
1288        if (!ale)
1289            return false;
1290        ALE_SET_DEFN(ale, pn);
1291        pn->pn_defn = true;
1292        pn->pn_dflags &= ~PND_PLACEHOLDER;
1293        return true;
1294    }
1295    
1296    static void
1297    LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
1298    {
1299        JS_ASSERT(!pn->pn_used);
1300        JS_ASSERT(!pn->pn_defn);
1301        JS_ASSERT(pn != dn->dn_uses);
1302        pn->pn_link = dn->dn_uses;
1303        dn->dn_uses = pn;
1304        pn->pn_used = true;
1305        pn->pn_lexdef = dn;
1306    }
1307    
1308    static void
1309    ForgetUse(JSParseNode *pn)
1310    {
1311        if (!pn->pn_used) {
1312            JS_ASSERT(!pn->pn_defn);
1313            return;
1314        }
1315    
1316        JSParseNode **pnup = &pn->lexdef()->dn_uses;
1317        JSParseNode *pnu;
1318        while ((pnu = *pnup) != pn)
1319            pnup = &pnu->pn_link;
1320        *pnup = pn->pn_link;
1321        pn->pn_used = false;
1322    }
1323    
1324    static JSParseNode *
1325    MakeAssignment(JSParseNode *pn, JSParseNode *rhs, JSTreeContext *tc)
1326    {
1327        JSParseNode *lhs = NewOrRecycledNode(tc);
1328        if (!lhs)
1329            return NULL;
1330        *lhs = *pn;
1331    
1332        if (pn->pn_used) {
1333            JSDefinition *dn = pn->pn_lexdef;
1334            JSParseNode **pnup = &dn->dn_uses;
1335    
1336            while (*pnup != pn)
1337                pnup = &(*pnup)->pn_link;
1338            *pnup = lhs;
1339            lhs->pn_link = pn->pn_link;
1340            pn->pn_link = NULL;
1341        }
1342    
1343        pn->pn_type = TOK_ASSIGN;
1344        pn->pn_op = JSOP_NOP;
1345        pn->pn_arity = PN_BINARY;
1346        pn->pn_used = pn->pn_defn = false;
1347        pn->pn_left = lhs;
1348        pn->pn_right = rhs;
1349        return lhs;
1350    }
1351    
1352    static JSParseNode *
1353    MakeDefIntoUse(JSDefinition *dn, JSParseNode *pn, JSAtom *atom, JSTreeContext *tc)
1354    {
1355        /*
1356         * If dn is var, const, or let, and it has an initializer, then we must
1357         * rewrite it to be an assignment node, whose freshly allocated left-hand
1358         * side becomes a use of pn.
1359         */
1360        if (dn->isBindingForm()) {
1361            JSParseNode *rhs = dn->expr();
1362            if (rhs) {
1363                JSParseNode *lhs = MakeAssignment(dn, rhs, tc);
1364                if (!lhs)
1365                    return NULL;
1366                //pn->dn_uses = lhs;
1367                dn = (JSDefinition *) lhs;
1368            }
1369    
1370            dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
1371        } else if (dn->kind() == JSDefinition::FUNCTION) {
1372            JS_ASSERT(dn->isTopLevel());
1373            JS_ASSERT(dn->pn_op == JSOP_NOP);
1374    
1375            dn->pn_u.name.funbox2 = dn->pn_funbox;
1376            dn->pn_u.name.expr2 = dn->pn_expr;
1377    
1378            dn->pn_type = TOK_NAME;
1379            dn->pn_arity = PN_NAME;
1380            dn->pn_atom = atom;
1381        }
1382    
1383        /* Now make dn no longer a definition, rather a use of pn. */
1384        JS_ASSERT(dn->pn_type == TOK_NAME);
1385        JS_ASSERT(dn->pn_arity == PN_NAME);
1386        JS_ASSERT(dn->pn_atom == atom);
1387    
1388        for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1389            JS_ASSERT(pnu->pn_used);
1390            JS_ASSERT(!pnu->pn_defn);
1391            pnu->pn_lexdef = (JSDefinition *) pn;
1392            pn->pn_dflags |= pnu->pn_dflags & (PND_ASSIGNED | PND_FUNARG);
1393        }
1394        pn->pn_dflags |= dn->pn_dflags & (PND_ASSIGNED | PND_FUNARG);
1395        pn->dn_uses = dn;
1396    
1397        dn->pn_defn = false;
1398        dn->pn_used = true;
1399        dn->pn_lexdef = (JSDefinition *) pn;
1400        dn->pn_cookie = FREE_UPVAR_COOKIE;
1401        dn->pn_dflags &= ~PND_BOUND;
1402        return dn;
1403    }
1404    
1405    static bool
1406    DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
1407    {
1408        JSParseNode *argpn, *argsbody;
1409    
1410        /* Flag tc so we don't have to lookup arguments on every use. */
1411        if (atom == tc->compiler->context->runtime->atomState.argumentsAtom)
1412            tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1413    
1414        /*
1415         * Make an argument definition node, distinguished by being in tc->decls
1416         * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1417         * list node returned via pn->pn_body.
1418         */
1419        argpn = NewNameNode(tc->compiler->context, TS(tc->compiler), atom, tc);
1420        if (!argpn)
1421            return false;
1422        JS_ASSERT(PN_TYPE(argpn) == TOK_NAME && PN_OP(argpn) == JSOP_NOP);
1423    
1424        /* Arguments are initialized by definition. */
1425        argpn->pn_dflags |= PND_INITIALIZED;
1426        if (!Define(argpn, atom, tc))
1427            return false;
1428    
1429        argsbody = pn->pn_body;
1430        if (!argsbody) {
1431            argsbody = NewParseNode(PN_LIST, tc);
1432            if (!argsbody)
1433                return false;
1434            argsbody->pn_type = TOK_ARGSBODY;
1435            argsbody->pn_op = JSOP_NOP;
1436            argsbody->makeEmpty();
1437            pn->pn_body = argsbody;
1438        }
1439        argsbody->append(argpn);
1440    
1441        argpn->pn_op = JSOP_GETARG;
1442        argpn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, i);
1443        argpn->pn_dflags |= PND_BOUND;
1444        return true;
1445    }
1446    
1447  /*  /*
1448   * Compile a JS function body, which might appear as the value of an event   * Compile a JS function body, which might appear as the value of an event
1449   * handler attribute in an HTML <INPUT> tag.   * handler attribute in an HTML <INPUT> tag.
1450   */   */
1451  JSBool  bool
1452  js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,  JSCompiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
1453                         const jschar *chars, size_t length,                                  const jschar *chars, size_t length,
1454                         const char *filename, uintN lineno)                                  const char *filename, uintN lineno)
1455  {  {
1456      JSParseContext pc;      JSCompiler jsc(cx, principals);
     JSArenaPool codePool, notePool;  
     JSCodeGenerator funcg;  
     JSParseNode *pn;  
1457    
1458      if (!js_InitParseContext(cx, &pc, principals, NULL, chars, length, NULL,      if (!jsc.init(chars, length, NULL, filename, lineno))
1459                               filename, lineno)) {          return false;
         return JS_FALSE;  
     }  
1460    
1461      /* No early return from this point until js_FinishParseContext call. */      /* No early return from after here until the js_FinishArenaPool calls. */
1462        JSArenaPool codePool, notePool;
1463      JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),      JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
1464                         &cx->scriptStackQuota);                         &cx->scriptStackQuota);
1465      JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),      JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
1466                         &cx->scriptStackQuota);                         &cx->scriptStackQuota);
1467      js_InitCodeGenerator(cx, &funcg, &pc, &codePool, &notePool,  
1468                           pc.tokenStream.lineno);      JSCodeGenerator funcg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
1469      funcg.treeContext.flags |= TCF_IN_FUNCTION;      funcg.flags |= TCF_IN_FUNCTION;
1470      funcg.treeContext.u.fun = fun;      funcg.fun = fun;
1471        if (!GenerateBlockId(&funcg, funcg.bodyid))
1472            return NULL;
1473    
1474        /* FIXME: make Function format the source for a function definition. */
1475        jsc.tokenStream.tokens[0].type = TOK_NAME;
1476        JSParseNode *fn = NewParseNode(PN_FUNC, &funcg);
1477        if (fn) {
1478            fn->pn_body = NULL;
1479            fn->pn_cookie = FREE_UPVAR_COOKIE;
1480    
1481            uintN nargs = fun->nargs;
1482            if (nargs) {
1483                jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
1484                if (!names) {
1485                    fn = NULL;
1486                } else {
1487                    for (uintN i = 0; i < nargs; i++) {
1488                        JSAtom *name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1489                        if (!DefineArg(fn, name, i, &funcg)) {
1490                            fn = NULL;
1491                            break;
1492                        }
1493                    }
1494                }
1495            }
1496        }
1497    
1498      /*      /*
1499       * Farble the body so that it looks like a block statement to js_EmitTree,       * Farble the body so that it looks like a block statement to js_EmitTree,
1500       * which is called beneath FunctionBody; see Statements, further below in       * which is called from js_EmitFunctionBody (see jsemit.cpp).  After we're
1501       * this file.  FunctionBody pushes a STMT_BLOCK record around its call to       * done parsing, we must fold constants, analyze any nested functions, and
1502       * Statements, so Statements will not compile each statement as it loops       * generate code for this function, including a stop opcode at the end.
      * to save JSParseNode space -- it will not compile at all, only build a  
      * JSParseNode tree.  
      *  
      * Therefore we must fold constants, allocate try notes, and generate code  
      * for this function, including a stop opcode at the end.  
1503       */       */
1504      CURRENT_TOKEN(&pc.tokenStream).type = TOK_LC;      CURRENT_TOKEN(&jsc.tokenStream).type = TOK_LC;
1505      pn = FunctionBody(cx, &pc.tokenStream, &funcg.treeContext);      JSParseNode *pn = fn ? FunctionBody(cx, &jsc.tokenStream, &funcg) : NULL;
1506      if (pn) {      if (pn) {
1507          if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) {          if (!js_MatchToken(cx, &jsc.tokenStream, TOK_EOF)) {
1508              js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,              js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1509                                          JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);                                          JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
1510              pn = NULL;              pn = NULL;
1511            } else if (!js_FoldConstants(cx, pn, &funcg)) {
1512                /* js_FoldConstants reported the error already. */
1513                pn = NULL;
1514            } else if (funcg.functionList &&
1515                       !jsc.analyzeFunctions(funcg.functionList, funcg.flags)) {
1516                pn = NULL;
1517          } else {          } else {
1518              if (!js_FoldConstants(cx, pn, &funcg.treeContext) ||              if (fn->pn_body) {
1519                  !js_EmitFunctionScript(cx, &funcg, pn)) {                  JS_ASSERT(PN_TYPE(fn->pn_body) == TOK_ARGSBODY);
1520                  pn = NULL;                  fn->pn_body->append(pn);
1521                    fn->pn_body->pn_pos = pn->pn_pos;
1522                    pn = fn->pn_body;
1523              }              }
1524    
1525                if (!js_EmitFunctionScript(cx, &funcg, pn))
1526                    pn = NULL;
1527          }          }
1528      }      }
1529    
1530      /* Restore saved state and release code generation arenas. */      /* Restore saved state and release code generation arenas. */
     js_FinishCodeGenerator(cx, &funcg);  
1531      JS_FinishArenaPool(&codePool);      JS_FinishArenaPool(&codePool);
1532      JS_FinishArenaPool(&notePool);      JS_FinishArenaPool(&notePool);
     js_FinishParseContext(cx, &pc);  
1533      return pn != NULL;      return pn != NULL;
1534  }  }
1535    
# Line 963  Line 1546 
1546  (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);  (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
1547    
1548  struct BindData {  struct BindData {
1549      JSParseNode             *pn;                /* error source coordinate */      BindData() : fresh(true) {}
1550      JSOp                    op;                 /* prolog bytecode or nop */  
1551      Binder                  binder;             /* binder, discriminates u */      JSParseNode     *pn;        /* name node for definition processing and
1552                                       error source coordinates */
1553        JSOp            op;         /* prolog bytecode or nop */
1554        Binder          binder;     /* binder, discriminates u */
1555      union {      union {
1556          struct {          struct {
1557              uintN           overflow;              uintN   overflow;
1558          } let;          } let;
1559      } u;      };
1560        bool fresh;
1561  };  };
1562    
1563  static JSBool  static JSBool
 BindArg(JSContext *cx, JSAtom *atom, JSTreeContext *tc)  
 {  
     const char *name;  
   
     /*  
      * Check for a duplicate parameter name, a "feature" required by ECMA-262.  
      */  
     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);  
     if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {  
         name = js_AtomToPrintableString(cx, atom);  
         if (!name ||  
             !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL,  
                                          JSREPORT_WARNING | JSREPORT_STRICT,  
                                          JSMSG_DUPLICATE_FORMAL,  
                                          name)) {  
             return JS_FALSE;  
         }  
     }  
   
     return js_AddLocal(cx, tc->u.fun, atom, JSLOCAL_ARG);  
 }  
   
 static JSBool  
1564  BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,  BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
1565                    JSLocalKind localKind)                    JSLocalKind localKind)
1566  {  {
# Line 1005  Line 1569 
1569      /*      /*
1570       * Don't bind a variable with the hidden name 'arguments', per ECMA-262.       * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1571       * Instead 'var arguments' always restates the predefined property of the       * Instead 'var arguments' always restates the predefined property of the
1572       * activation objects with unhidden name 'arguments'.  Assignment to such       * activation objects whose name is 'arguments'. Assignment to such a
1573       * a variable must be handled specially.       * variable must be handled specially.
1574       */       */
1575      if (atom == cx->runtime->atomState.argumentsAtom)      if (atom == cx->runtime->atomState.argumentsAtom)
1576          return JS_TRUE;          return JS_TRUE;
# Line 1027  Line 1591 
1591                       JSTreeContext *tc)                       JSTreeContext *tc)
1592  {  {
1593      JSAtomListElement *ale;      JSAtomListElement *ale;
1594      const char *name;      JSParseNode *pn;
1595    
1596        /* Flag tc so we don't have to lookup arguments on every use. */
1597        if (atom == tc->compiler->context->runtime->atomState.argumentsAtom)
1598            tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1599    
1600      JS_ASSERT(tc->flags & TCF_IN_FUNCTION);      JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1601      ATOM_LIST_SEARCH(ale, &tc->decls, atom);      ale = tc->decls.lookup(atom);
1602      if (!ale) {      pn = data->pn;
1603          ale = js_IndexAtom(cx, atom, &tc->decls);      if (!ale && !Define(pn, atom, tc))
1604          if (!ale)          return JS_FALSE;
             return JS_FALSE;  
         ALE_SET_JSOP(ale, data->op);  
     }  
1605    
1606      if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {      JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
1607          name = js_AtomToPrintableString(cx, atom);      if (localKind != JSLOCAL_NONE) {
1608          if (!name ||          js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL,
1609              !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,                                      JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
1610                                           JSREPORT_WARNING | JSREPORT_STRICT,          return JS_FALSE;
                                          JSMSG_DUPLICATE_FORMAL,  
                                          name)) {  
             return JS_FALSE;  
         }  
     } else {  
         if (!BindLocalVariable(cx, tc->u.fun, atom, JSLOCAL_VAR))  
             return JS_FALSE;  
1611      }      }
1612    
1613        uintN index = tc->fun->u.i.nvars;
1614        if (!BindLocalVariable(cx, tc->fun, atom, JSLOCAL_VAR))
1615            return JS_FALSE;
1616        pn->pn_op = JSOP_SETLOCAL;
1617        pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
1618        pn->pn_dflags |= PND_BOUND;
1619      return JS_TRUE;      return JS_TRUE;
1620  }  }
1621  #endif /* JS_HAS_DESTRUCTURING */  #endif /* JS_HAS_DESTRUCTURING */
1622    
1623  static JSFunction *  JSFunction *
1624  NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom,  JSCompiler::newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda)
                     uintN lambda)  
1625  {  {
1626      JSObject *parent;      JSObject *parent;
1627      JSFunction *fun;      JSFunction *fun;
1628    
1629      JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);      JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1630      parent = (tc->flags & TCF_IN_FUNCTION)  
1631               ? FUN_OBJECT(tc->u.fun)      /*
1632               : tc->u.scopeChain;       * Find the global compilation context in order to pre-set the newborn
1633      fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,       * function's parent slot to tc->scopeChain. If the global context is a
1634         * compile-and-go one, we leave the pre-set parent intact; otherwise we
1635         * clear parent and proto.
1636         */
1637        while (tc->parent)
1638            tc = tc->parent;
1639        parent = (tc->flags & TCF_IN_FUNCTION) ? NULL : tc->scopeChain;
1640    
1641        fun = js_NewFunction(context, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1642                           parent, atom);                           parent, atom);
1643    
1644      if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {      if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
1645          STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));          STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1646          STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));          STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
# Line 1075  Line 1648 
1648      return fun;      return fun;
1649  }  }
1650    
1651    static JSBool
1652    MatchOrInsertSemicolon(JSContext *cx, JSTokenStream *ts)
1653    {
1654        JSTokenType tt;
1655    
1656        ts->flags |= TSF_OPERAND;
1657        tt = js_PeekTokenSameLine(cx, ts);
1658        ts->flags &= ~TSF_OPERAND;
1659        if (tt == TOK_ERROR)
1660            return JS_FALSE;
1661        if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1662            js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1663                                        JSMSG_SEMI_BEFORE_STMNT);
1664            return JS_FALSE;
1665        }
1666        (void) js_MatchToken(cx, ts, TOK_SEMI);
1667        return JS_TRUE;
1668    }
1669    
1670    bool
1671    JSCompiler::analyzeFunctions(JSFunctionBox *funbox, uint16& tcflags)
1672    {
1673        if (!markFunArgs(funbox, tcflags))
1674            return false;
1675        setFunctionKinds(funbox, tcflags);
1676        return true;
1677    }
1678    
1679    /*
1680     * Mark as funargs any functions that reach up to one or more upvars across an
1681     * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1682     *
1683     *   function f(o, p) {
1684     *       o.m = function o_m(a) {
1685     *           function g() { return p; }
1686     *           function h() { return a; }
1687     *           return g() + h();
1688     *       }
1689     *   }
1690     *
1691     * but without this extra marking phase, function g will not be marked as a
1692     * funarg since it is called from within its parent scope. But g reaches up to
1693     * f's parameter p, so if o_m escapes f's activation scope, g does too and
1694     * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1695     * nor uses an upvar "above" o_m's level.
1696     *
1697     * If function g itself contained lambdas that contained non-lambdas that reach
1698     * up above its level, then those non-lambdas would have to be marked too. This
1699     * process is potentially exponential in the number of functions, but generally
1700     * not so complex. But it can't be done during a single recursive traversal of
1701     * the funbox tree, so we must use a work queue.
1702     *
1703     * Return the minimal "skipmin" for funbox and its siblings. This is the delta
1704     * between the static level of the bodies of funbox and its peers (which must
1705     * be funbox->level + 1), and the static level of the nearest upvar among all
1706     * the upvars contained by funbox and its peers. If there are no upvars, return
1707     * FREE_STATIC_LEVEL. Thus this function never returns 0.
1708     */
1709    static uintN
1710    FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
1711    {
1712        uintN allskipmin = FREE_STATIC_LEVEL;
1713    
1714        do {
1715            JSParseNode *fn = funbox->node;
1716            JSFunction *fun = (JSFunction *) funbox->object;
1717            int fnlevel = level;
1718    
1719            /*
1720             * An eval can leak funbox, functions along its ancestor line, and its
1721             * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1722             * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1723             * already been marked as funargs by this point. Therefore we have to
1724             * flag only funbox->node and funbox->kids' nodes here.
1725             */
1726            if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1727                fn->setFunArg();
1728                for (JSFunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
1729                    kid->node->setFunArg();
1730            }
1731    
1732            /*
1733             * Compute in skipmin the least distance from fun's static level up to
1734             * an upvar, whether used directly by fun, or indirectly by a function
1735             * nested in fun.
1736             */
1737            uintN skipmin = FREE_STATIC_LEVEL;
1738            JSParseNode *pn = fn->pn_body;
1739    
1740            if (pn->pn_type == TOK_UPVARS) {
1741                JSAtomList upvars(pn->pn_names);
1742                JS_ASSERT(upvars.count != 0);
1743    
1744                JSAtomListIterator iter(&upvars);
1745                JSAtomListElement *ale;
1746    
1747                while ((ale = iter()) != NULL) {
1748                    JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1749    
1750                    if (!lexdep->isFreeVar()) {
1751                        uintN upvarLevel = lexdep->frameLevel();
1752    
1753                        if (int(upvarLevel) <= fnlevel)
1754                            fn->setFunArg();
1755    
1756                        uintN skip = (funbox->level + 1) - upvarLevel;
1757                        if (skip < skipmin)
1758                            skipmin = skip;
1759                    }
1760                }
1761            }
1762    
1763            /*
1764             * If this function escapes, whether directly (the parser detects such
1765             * escapes) or indirectly (because this non-escaping function uses an
1766             * upvar that reaches across an outer function boundary where the outer
1767             * function escapes), enqueue it for further analysis, and bump fnlevel
1768             * to trap any non-escaping children.
1769             */
1770            if (fn->isFunArg()) {
1771                queue->push(funbox);
1772                fnlevel = int(funbox->level);
1773            }
1774    
1775            /*
1776             * Now process the current function's children, and recalibrate their
1777             * cumulative skipmin to be relative to the current static level.
1778             */
1779            if (funbox->kids) {
1780                uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
1781    
1782                JS_ASSERT(kidskipmin != 0);
1783                if (kidskipmin != FREE_STATIC_LEVEL) {
1784                    --kidskipmin;
1785                    if (kidskipmin != 0 && kidskipmin < skipmin)
1786                        skipmin = kidskipmin;
1787                }
1788            }
1789    
1790            /*
1791             * Finally, after we've traversed all of the current function's kids,
1792             * minimize fun's skipmin against our accumulated skipmin. Do likewise
1793             * with allskipmin, but minimize across funbox and all of its siblings,
1794             * to compute our return value.
1795             */
1796            if (skipmin != FREE_STATIC_LEVEL) {
1797                fun->u.i.skipmin = skipmin;
1798                if (skipmin < allskipmin)
1799                    allskipmin = skipmin;
1800            }
1801        } while ((funbox = funbox->siblings) != NULL);
1802    
1803        return allskipmin;
1804    }
1805    
1806    bool
1807    JSCompiler::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
1808    {
1809        JSFunctionBoxQueue queue;
1810        if (!queue.init(functionCount))
1811            return false;
1812    
1813        FindFunArgs(funbox, -1, &queue);
1814        while ((funbox = queue.pull()) != NULL) {
1815            JSParseNode *fn = funbox->node;
1816            JS_ASSERT(fn->isFunArg());
1817    
1818            JSParseNode *pn = fn->pn_body;
1819            if (pn->pn_type == TOK_UPVARS) {
1820                JSAtomList upvars(pn->pn_names);
1821                JS_ASSERT(upvars.count != 0);
1822    
1823                JSAtomListIterator iter(&upvars);
1824                JSAtomListElement *ale;
1825    
1826                while ((ale = iter()) != NULL) {
1827                    JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1828    
1829                    if (!lexdep->isFreeVar() &&
1830                        !lexdep->isFunArg() &&
1831                        lexdep->kind() == JSDefinition::FUNCTION) {
1832                        /*
1833                         * Mark this formerly-Algol-like function as an escaping
1834                         * function (i.e., as a funarg), because it is used from a
1835                         * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
1836                         * access upvars.
1837                         *
1838                         * Progress is guaranteed because we set the funarg flag
1839                         * here, which suppresses revisiting this function (thanks
1840                         * to the !lexdep->isFunArg() test just above).
1841                         */
1842                        lexdep->setFunArg();
1843    
1844                        JSFunctionBox *afunbox = lexdep->pn_funbox;
1845                        queue.push(afunbox);
1846    
1847                        /*
1848                         * Walk over nested functions again, now that we have
1849                         * changed the level across which it is unsafe to access
1850                         * upvars using the runtime dynamic link (frame chain).
1851                         */
1852                        if (afunbox->kids)
1853                            FindFunArgs(afunbox->kids, afunbox->level, &queue);
1854                    }
1855                }
1856            }
1857        }
1858        return true;
1859    }
1860    
1861    static uint32
1862    MinBlockId(JSParseNode *fn, uint32 id)
1863    {
1864        if (fn->pn_blockid < id)
1865            return false;
1866        if (fn->pn_defn) {
1867            for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1868                if (pn->pn_blockid < id)
1869                    return false;
1870            }
1871        }
1872        return true;
1873    }
1874    
1875    static bool
1876    OneBlockId(JSParseNode *fn, uint32 id)
1877    {
1878        if (fn->pn_blockid != id)
1879            return false;
1880        if (fn->pn_defn) {
1881            for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1882                if (pn->pn_blockid != id)
1883                    return false;
1884            }
1885        }
1886        return true;
1887    }
1888    
1889    void
1890    JSCompiler::setFunctionKinds(JSFunctionBox *funbox, uint16& tcflags)
1891    {
1892    #ifdef JS_FUNCTION_METERING
1893    # define FUN_METER(x)   JS_RUNTIME_METER(context->runtime, functionMeter.x)
1894    #else
1895    # define FUN_METER(x)   ((void)0)
1896    #endif
1897        JSFunctionBox *parent = funbox->parent;
1898    
1899        for (;;) {
1900            JSParseNode *fn = funbox->node;
1901    
1902            if (funbox->kids)
1903                setFunctionKinds(funbox->kids, tcflags);
1904    
1905            JSParseNode *pn = fn->pn_body;
1906            JSFunction *fun = (JSFunction *) funbox->object;
1907    
1908            FUN_METER(allfun);
1909            if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1910                FUN_METER(heavy);
1911                JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
1912            } else if (pn->pn_type != TOK_UPVARS) {
1913                /*
1914                 * No lexical dependencies => null closure, for best performance.
1915                 * A null closure needs no scope chain, but alas we've coupled
1916                 * principals-finding to scope (for good fundamental reasons, but
1917                 * the implementation overloads the parent slot and we should fix
1918                 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
1919                 *
1920                 * In more detail: the ES3 spec allows the implementation to create
1921                 * "joined function objects", or not, at its discretion. But real-
1922                 * world implementations always create unique function objects for
1923                 * closures, and this can be detected via mutation. Open question:
1924                 * do popular implementations create unique function objects for
1925                 * null closures?
1926                 *
1927                 * FIXME: bug 476950.
1928                 */
1929                FUN_METER(nofreeupvar);
1930                FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1931            } else {
1932                JSAtomList upvars(pn->pn_names);
1933                JS_ASSERT(upvars.count != 0);
1934    
1935                JSAtomListIterator iter(&upvars);
1936                JSAtomListElement *ale;
1937    
1938                if (!fn->isFunArg()) {
1939                    /*
1940                     * This function is Algol-like, it never escapes. So long as it
1941                     * does not assign to outer variables, it needs only an upvars
1942                     * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
1943                     * bytecode to reach up the frame stack at runtime based on
1944                     * those upvars' cookies.
1945                     *
1946                     * Any assignments to upvars from functions called by this one
1947                     * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
1948                     * which load from stack homes when interpreting or from native
1949                     * stack slots when executing a trace.
1950                     *
1951                     * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
1952                     * nested function to assign to an outer lexical variable, so
1953                     * we defer adding yet more code footprint in the absence of
1954                     * evidence motivating these opcodes.
1955                     */
1956                    bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
1957                    uintN nupvars = 0;
1958    
1959                    /*
1960                     * Check that at least one outer lexical binding was assigned
1961                     * to (global variables don't count). This is conservative: we
1962                     * could limit assignments to those in the current function,
1963                     * but that's too much work. As with flat closures (handled
1964                     * below), we optimize for the case where outer bindings are
1965                     * not reassigned anywhere.
1966                     */
1967                    while ((ale = iter()) != NULL) {
1968                        JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1969    
1970                        if (!lexdep->isFreeVar()) {
1971                            JS_ASSERT(lexdep->frameLevel() <= funbox->level);
1972                            ++nupvars;
1973                            if (lexdep->isAssigned())
1974                                break;
1975                        }
1976                    }
1977                    if (!ale)
1978                        mutation = false;
1979    
1980                    if (nupvars == 0) {
1981                        FUN_METER(onlyfreevar);
1982                        FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1983                    } else if (!mutation && !(funbox->tcflags & TCF_FUN_IS_GENERATOR)) {
1984                        /*
1985                         * Algol-like functions can read upvars using the dynamic
1986                         * link (cx->fp/fp->down). They do not need to entrain and
1987                         * search their environment.
1988                         */
1989                        FUN_METER(display);
1990                        FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1991                    } else {
1992                        if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
1993                            FUN_METER(setupvar);
1994                    }
1995                } else {
1996                    uintN nupvars = 0;
1997    
1998                    /*
1999                     * For each lexical dependency from this closure to an outer
2000                     * binding, analyze whether it is safe to copy the binding's
2001                     * value into a flat closure slot when the closure is formed.
2002                     */
2003                    while ((ale = iter()) != NULL) {
2004                        JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2005    
2006                        if (!lexdep->isFreeVar()) {
2007                            ++nupvars;
2008    
2009                            /*
2010                             * Consider the current function (the lambda, innermost
2011                             * below) using a var x defined two static levels up:
2012                             *
2013                             *  function f() {
2014                             *      // z = g();
2015                             *      var x = 42;
2016                             *      function g() {
2017                             *          return function () { return x; };
2018                             *      }
2019                             *      return g();
2020                             *  }
2021                             *
2022                             * So long as (1) the initialization in 'var x = 42'
2023                             * dominates all uses of g and (2) x is not reassigned,
2024                             * it is safe to optimize the lambda to a flat closure.
2025                             * Uncommenting the early call to g makes it unsafe to
2026                             * so optimize (z could name a global setter that calls
2027                             * its argument).
2028                             */
2029                            JSFunctionBox *afunbox = funbox;
2030                            uintN lexdepLevel = lexdep->frameLevel();
2031    
2032                            JS_ASSERT(lexdepLevel <= funbox->level);
2033                            while (afunbox->level != lexdepLevel) {
2034                                afunbox = afunbox->parent;
2035    
2036                                /*
2037                                 * afunbox can't be null because we are sure
2038                                 * to find a function box whose level == lexdepLevel
2039                                 * before walking off the top of the funbox tree.
2040                                 * See bug 493260 comments 16-18.
2041                                 *
2042                                 * Assert but check anyway, to check future changes
2043                                 * that bind eval upvars in the parser.
2044                                 */
2045                                JS_ASSERT(afunbox);
2046    
2047                                /*
2048                                 * If this function is reaching up across an
2049                                 * enclosing funarg, we cannot make a flat
2050                                 * closure. The display stops working once the
2051                                 * funarg escapes.
2052                                 */
2053                                if (!afunbox || afunbox->node->isFunArg())
2054                                    goto break2;
2055                            }
2056    
2057                            /*
2058                             * If afunbox's function (which is at the same level as
2059                             * lexdep) is in a loop, pessimistically assume the
2060                             * variable initializer may be in the same loop. A flat
2061                             * closure would then be unsafe, as the captured
2062                             * variable could be assigned after the closure is
2063                             * created. See bug 493232.
2064                             */
2065                            if (afunbox->inLoop)
2066                                break;
2067    
2068                            /*
2069                             * with and eval defeat lexical scoping; eval anywhere
2070                             * in a variable's scope can assign to it. Both defeat
2071                             * the flat closure optimization. The parser detects
2072                             * these cases and flags the function heavyweight.
2073                             */
2074                            if ((afunbox->parent ? afunbox->parent->tcflags : tcflags)
2075                                & TCF_FUN_HEAVYWEIGHT) {
2076                                break;
2077                            }
2078    
2079                            /*
2080                             * If afunbox's function is not a lambda, it will be
2081                             * hoisted, so it could capture the undefined value
2082                             * that by default initializes var/let/const
2083                             * bindings. And if lexdep is a function that comes at
2084                             * (meaning a function refers to its own name) or
2085                             * strictly after afunbox, we also break to defeat the
2086                             * flat closure optimization.
2087                             */
2088                            JSFunction *afun = (JSFunction *) afunbox->object;
2089                            if (!(afun->flags & JSFUN_LAMBDA)) {
2090                                if (lexdep->isBindingForm())
2091                                    break;
2092                                if (lexdep->pn_pos >= afunbox->node->pn_pos)
2093                                    break;
2094                            }
2095    
2096                            if (!lexdep->isInitialized())
2097                                break;
2098    
2099                            JSDefinition::Kind lexdepKind = lexdep->kind();
2100                            if (lexdepKind != JSDefinition::CONST) {
2101                                if (lexdep->isAssigned())
2102                                    break;
2103    
2104                                /*
2105                                 * Any formal could be mutated behind our back via
2106                                 * the arguments object, so deoptimize if the outer
2107                                 * function uses arguments.
2108                                 *
2109                                 * In a Function constructor call where the final
2110                                 * argument -- the body source for the function to
2111                                 * create -- contains a nested function definition
2112                                 * or expression, afunbox->parent will be null. The
2113                                 * body source might use |arguments| outside of any
2114                                 * nested functions it may contain, so we have to
2115                                 * check the tcflags parameter that was passed in
2116                                 * from JSCompiler::compileFunctionBody.
2117                                 */
2118                                if (lexdepKind == JSDefinition::ARG &&
2119                                    ((afunbox->parent ? afunbox->parent->tcflags : tcflags) &
2120                                     TCF_FUN_USES_ARGUMENTS)) {
2121                                    break;
2122                                }
2123                            }
2124    
2125                            /*
2126                             * Check quick-and-dirty dominance relation. Function
2127                             * definitions dominate their uses thanks to hoisting.
2128                             * Other binding forms hoist as undefined, of course,
2129                             * so check forward-reference and blockid relations.
2130                             */
2131                            if (lexdepKind != JSDefinition::FUNCTION) {
2132                                /*
2133                                 * Watch out for code such as
2134                                 *
2135                                 *   (function () {
2136                                 *   ...
2137                                 *   var jQuery = ... = function (...) {
2138                                 *       return new jQuery.foo.bar(baz);
2139                                 *   }
2140                                 *   ...
2141                                 *   })();
2142                                 *
2143                                 * where the jQuery var is not reassigned, but of
2144                                 * course is not initialized at the time that the
2145                                 * would-be-flat closure containing the jQuery
2146                                 * upvar is formed.
2147                                 */
2148                                if (lexdep->pn_pos.end >= afunbox->node->pn_pos.end)
2149                                    break;
2150    
2151                                if (lexdep->isTopLevel()
2152                                    ? !MinBlockId(afunbox->node, lexdep->pn_blockid)
2153                                    : !lexdep->isBlockChild() ||
2154                                      !afunbox->node->isBlockChild() ||
2155                                      !OneBlockId(afunbox->node, lexdep->pn_blockid)) {
2156                                    break;
2157                                }
2158                            }
2159                        }
2160                    }
2161    
2162                  break2:
2163                    if (nupvars == 0) {
2164                        FUN_METER(onlyfreevar);
2165                        FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2166                    } else if (!ale) {
2167                        /*
2168                         * We made it all the way through the upvar loop, so it's
2169                         * safe to optimize to a flat closure.
2170                         */
2171                        FUN_METER(flat);
2172                        FUN_SET_KIND(fun, JSFUN_FLAT_CLOSURE);
2173                        switch (PN_OP(fn)) {
2174                          case JSOP_DEFFUN:
2175                            fn->pn_op = JSOP_DEFFUN_FC;
2176                            break;
2177                          case JSOP_DEFLOCALFUN:
2178                            fn->pn_op = JSOP_DEFLOCALFUN_FC;
2179                            break;
2180                          case JSOP_LAMBDA:
2181                            fn->pn_op = JSOP_LAMBDA_FC;
2182                            break;
2183                          default:
2184                            /* js_EmitTree's case TOK_FUNCTION: will select op. */
2185                            JS_ASSERT(PN_OP(fn) == JSOP_NOP);
2186                        }
2187                    } else {
2188                        FUN_METER(badfunarg);
2189                    }
2190                }
2191            }
2192    
2193            if (FUN_KIND(fun) == JSFUN_INTERPRETED) {
2194                if (pn->pn_type != TOK_UPVARS) {
2195                    if (parent)
2196                        parent->tcflags |= TCF_FUN_HEAVYWEIGHT;
2197                } else {
2198                    JSAtomList upvars(pn->pn_names);
2199                    JS_ASSERT(upvars.count != 0);
2200    
2201                    JSAtomListIterator iter(&upvars);
2202                    JSAtomListElement *ale;
2203    
2204                    /*
2205                     * One or more upvars cannot be safely snapshot into a flat
2206                     * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2207                     * all upvars, and for each non-free upvar, ensure that its
2208                     * containing function has been flagged as heavyweight.
2209                     *
2210                     * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2211                     * generating any code for a tree of nested functions.
2212                     */
2213                    while ((ale = iter()) != NULL) {
2214                        JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2215    
2216                        if (!lexdep->isFreeVar()) {
2217                            JSFunctionBox *afunbox = funbox->parent;
2218                            uintN lexdepLevel = lexdep->frameLevel();
2219    
2220                            while (afunbox) {
2221                                /*
2222                                 * NB: afunbox->level is the static level of
2223                                 * the definition or expression of the function
2224                                 * parsed into afunbox, not the static level of
2225                                 * its body. Therefore we must add 1 to match
2226                                 * lexdep's level to find the afunbox whose
2227                                 * body contains the lexdep definition.
2228                                 */
2229                                if (afunbox->level + 1U == lexdepLevel ||
2230                                    (lexdepLevel == 0 && lexdep->isLet())) {
2231                                    afunbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
2232                                    break;
2233                                }
2234                                afunbox = afunbox->parent;
2235                            }
2236                            if (!afunbox && (tcflags & TCF_IN_FUNCTION))
2237                                tcflags |= TCF_FUN_HEAVYWEIGHT;
2238                        }
2239                    }
2240                }
2241            }
2242    
2243            funbox = funbox->siblings;
2244            if (!funbox)
2245                break;
2246            JS_ASSERT(funbox->parent == parent);
2247        }
2248    #undef FUN_METER
2249    }
2250    
2251    const char js_argument_str[] = "argument";
2252    const char js_variable_str[] = "variable";
2253    const char js_unknown_str[]  = "unknown";
2254    
2255    const char *
2256    JSDefinition::kindString(Kind kind)
2257    {
2258        static const char *table[] = {
2259            js_var_str, js_const_str, js_let_str,
2260            js_function_str, js_argument_str, js_unknown_str
2261        };
2262    
2263        JS_ASSERT(unsigned(kind) <= unsigned(ARG));
2264        return table[kind];
2265    }
2266    
2267    static JSFunctionBox *
2268    EnterFunction(JSParseNode *fn, JSTreeContext *tc, JSTreeContext *funtc,
2269                  JSAtom *funAtom = NULL, uintN lambda = JSFUN_LAMBDA)
2270    {
2271        JSFunction *fun = tc->compiler->newFunction(tc, funAtom, lambda);
2272        if (!fun)
2273            return NULL;
2274    
2275        /* Create box for fun->object early to protect against last-ditch GC. */
2276        JSFunctionBox *funbox = tc->compiler->newFunctionBox(FUN_OBJECT(fun), fn, tc);
2277        if (!funbox)
2278            return NULL;
2279    
2280        /* Initialize non-default members of funtc. */
2281        funtc->flags |= funbox->tcflags;
2282        funtc->blockidGen = tc->blockidGen;
2283        if (!GenerateBlockId(funtc, funtc->bodyid))
2284            return NULL;
2285        funtc->fun = fun;
2286        funtc->funbox = funbox;
2287        funtc->parent = tc;
2288        if (!SetStaticLevel(funtc, tc->staticLevel + 1))
2289            return NULL;
2290    
2291        return funbox;
2292    }
2293    
2294    static bool
2295    LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSTreeContext *tc,
2296                  JSAtom *funAtom = NULL, uintN lambda = JSFUN_LAMBDA)
2297    {
2298        tc->blockidGen = funtc->blockidGen;
2299    
2300        fn->pn_funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO);
2301    
2302        fn->pn_dflags |= PND_INITIALIZED;
2303        JS_ASSERT_IF(tc->atTopLevel() && lambda == 0 && funAtom,
2304                     fn->pn_dflags & PND_TOPLEVEL);
2305        if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
2306            fn->pn_dflags |= PND_BLOCKCHILD;
2307    
2308        /*
2309         * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2310         * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2311         * params and body. We do this only if there are lexical dependencies not
2312         * satisfied by the function's declarations, to avoid penalizing functions
2313         * that use only their arguments and other local bindings.
2314         */
2315        if (funtc->lexdeps.count != 0) {
2316            JSAtomListIterator iter(&funtc->lexdeps);
2317            JSAtomListElement *ale;
2318            int foundCallee = 0;
2319    
2320            while ((ale = iter()) != NULL) {
2321                JSAtom *atom = ALE_ATOM(ale);
2322                JSDefinition *dn = ALE_DEFN(ale);
2323                JS_ASSERT(dn->isPlaceholder());
2324    
2325                if (atom == funAtom && lambda != 0) {
2326                    dn->pn_op = JSOP_CALLEE;
2327                    dn->pn_cookie = MAKE_UPVAR_COOKIE(funtc->staticLevel, CALLEE_UPVAR_SLOT);
2328                    dn->pn_dflags |= PND_BOUND;
2329    
2330                    /*
2331                     * If this named function expression uses its own name other
2332                     * than to call itself, flag this function as using arguments,
2333                     * as if it had used arguments.callee instead of its own name.
2334                     *
2335                     * This abuses the plain sense of TCF_FUN_USES_ARGUMENTS, but
2336                     * we are out of tcflags bits at the moment. If it deoptimizes
2337                     * code unfairly (see JSCompiler::setFunctionKinds, where this
2338                     * flag is interpreted in its broader sense, not only to mean
2339                     * "this function might leak arguments.callee"), we can perhaps
2340                     * try to work harder to add a TCF_FUN_LEAKS_ITSELF flag and
2341                     * use that more precisely, both here and for unnamed function
2342                     * expressions.
2343                     */
2344                    if (dn->isFunArg())
2345                        fn->pn_funbox->tcflags |= TCF_FUN_USES_ARGUMENTS;
2346                    foundCallee = 1;
2347                    continue;
2348                }
2349    
2350                if (!(fn->pn_funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
2351                    dn->isAssigned()) {
2352                    /*
2353                     * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2354                     * any use of dn in funtc assigns. See NoteLValue for the easy
2355                     * backward-reference case; this is the hard forward-reference
2356                     * case where we pay a higher price.
2357                     */
2358                    for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2359                        if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
2360                            fn->pn_funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
2361                            break;
2362                        }
2363                    }
2364                }
2365    
2366                JSAtomListElement *outer_ale = tc->decls.lookup(atom);
2367                if (!outer_ale)
2368                    outer_ale = tc->lexdeps.lookup(atom);
2369                if (outer_ale) {
2370                    /*
2371                     * Insert dn's uses list at the front of outer_dn's list.
2372                     *
2373                     * Without loss of generality or correctness, we allow a dn to
2374                     * be in inner and outer lexdeps, since the purpose of lexdeps
2375                     * is one-pass coordination of name use and definition across
2376                     * functions, and if different dn's are used we'll merge lists
2377                     * when leaving the inner function.
2378                     *
2379                     * The dn == outer_dn case arises with generator expressions
2380                     * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2381                     * case), and nowhere else, currently.
2382                     */
2383                    JSDefinition *outer_dn = ALE_DEFN(outer_ale);
2384    
2385                    if (dn != outer_dn) {
2386                        JSParseNode **pnup = &dn->dn_uses;
2387                        JSParseNode *pnu;
2388    
2389                        while ((pnu = *pnup) != NULL) {
2390                            pnu->pn_lexdef = outer_dn;
2391                            pnup = &pnu->pn_link;
2392                        }
2393    
2394                        /*
2395                         * Make dn be a use that redirects to outer_dn, because we
2396                         * can't replace dn with outer_dn in all the pn_namesets in
2397                         * the AST where it may be. Instead we make it forward to
2398                         * outer_dn. See JSDefinition::resolve.
2399                         */
2400                        *pnup = outer_dn->dn_uses;
2401                        outer_dn->dn_uses = dn;
2402                        outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
2403                        dn->pn_defn = false;
2404                        dn->pn_used = true;
2405                        dn->pn_lexdef = outer_dn;
2406                    }
2407                } else {
2408                    /* Add an outer lexical dependency for ale's definition. */
2409                    outer_ale = tc->lexdeps.add(tc->compiler, atom);
2410                    if (!outer_ale)
2411                        return false;
2412                    ALE_SET_DEFN(outer_ale, ALE_DEFN(ale));
2413                }
2414            }
2415    
2416            if (funtc->lexdeps.count - foundCallee != 0) {
2417                JSParseNode *body = fn->pn_body;
2418    
2419                fn->pn_body = NewParseNode(PN_NAMESET, tc);
2420                if (!fn->pn_body)
2421                    return false;
2422    
2423                fn->pn_body->pn_type = TOK_UPVARS;
2424                fn->pn_body->pn_pos = body->pn_pos;
2425                if (foundCallee)
2426                    funtc->lexdeps.remove(tc->compiler, funAtom);
2427                fn->pn_body->pn_names = funtc->lexdeps;
2428                fn->pn_body->pn_tree = body;
2429            }
2430    
2431            funtc->lexdeps.clear();
2432        }
2433    
2434        return true;
2435    }
2436    
2437  static JSParseNode *  static JSParseNode *
2438  FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,  FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2439              uintN lambda)              uintN lambda)
2440  {  {
2441      JSOp op, prevop;      JSOp op;
2442      JSParseNode *pn, *body, *result;      JSParseNode *pn, *body, *result;
2443      JSTokenType tt;      JSTokenType tt;
2444      JSAtom *funAtom;      JSAtom *funAtom;
     JSParsedObjectBox *funpob;  
2445      JSAtomListElement *ale;      JSAtomListElement *ale;
     JSFunction *fun;  
     JSTreeContext funtc;  
2446  #if JS_HAS_DESTRUCTURING  #if JS_HAS_DESTRUCTURING
2447      JSParseNode *item, *list = NULL;      JSParseNode *item, *list = NULL;
2448        bool destructuringArg = false, duplicatedArg = false;
2449  #endif  #endif
2450    
2451      /* Make a TOK_FUNCTION node. */      /* Make a TOK_FUNCTION node. */
2452  #if JS_HAS_GETTER_SETTER  #if JS_HAS_GETTER_SETTER
2453      op = CURRENT_TOKEN(ts).t_op;      op = CURRENT_TOKEN(ts).t_op;
2454  #endif  #endif
2455      pn = NewParseNode(cx, ts, PN_FUNC, tc);      pn = NewParseNode(PN_FUNC, tc);
2456      if (!pn)      if (!pn)
2457          return NULL;          return NULL;
2458  #ifdef DEBUG      pn->pn_body = NULL;
2459      pn->pn_index = (uint32) -1;      pn->pn_cookie = FREE_UPVAR_COOKIE;
2460  #endif  
2461        /*
2462         * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2463         * is immediately applied (we clear PND_FUNARG if so -- see MemberExpr).
2464         *
2465         * Also treat function sub-statements (non-lambda, non-top-level functions)
2466         * as escaping funargs, since we can't statically analyze their definitions
2467         * and uses.
2468         */
2469        bool topLevel = tc->atTopLevel();
2470        pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0;
2471    
2472      /* Scan the optional function name into funAtom. */      /* Scan the optional function name into funAtom. */
2473      ts->flags |= TSF_KEYWORD_IS_NAME;      ts->flags |= TSF_KEYWORD_IS_NAME;
# Line 1123  Line 2490 
2490       * avoid optimizing variable references that might name a function.       * avoid optimizing variable references that might name a function.
2491       */       */
2492      if (lambda == 0 && funAtom) {      if (lambda == 0 && funAtom) {
2493          ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);          ale = tc->decls.lookup(funAtom);
2494          if (ale) {          if (ale) {
2495              prevop = ALE_JSOP(ale);              JSDefinition *dn = ALE_DEFN(ale);
2496              if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {              JSDefinition::Kind dn_kind = dn->kind();
2497    
2498                JS_ASSERT(!dn->pn_used);
2499                JS_ASSERT(dn->pn_defn);
2500    
2501                if (JS_HAS_STRICT_OPTION(cx) || dn_kind == JSDefinition::CONST) {
2502                  const char *name = js_AtomToPrintableString(cx, funAtom);                  const char *name = js_AtomToPrintableString(cx, funAtom);
2503                  if (!name ||                  if (!name ||
2504                      !js_ReportCompileErrorNumber(cx, ts, NULL,                      !js_ReportCompileErrorNumber(cx, ts, NULL,
2505                                                   (prevop != JSOP_DEFCONST)                                                   (dn_kind != JSDefinition::CONST)
2506                                                   ? JSREPORT_WARNING |                                                   ? JSREPORT_WARNING | JSREPORT_STRICT
                                                    JSREPORT_STRICT  
2507                                                   : JSREPORT_ERROR,                                                   : JSREPORT_ERROR,
2508                                                   JSMSG_REDECLARED_VAR,                                                   JSMSG_REDECLARED_VAR,
2509                                                   (prevop == JSOP_DEFFUN)                                                   JSDefinition::kindString(dn_kind),
                                                  ? js_function_str  
                                                  : (prevop == JSOP_DEFCONST)  
                                                  ? js_const_str  
                                                  : js_var_str,  
2510                                                   name)) {                                                   name)) {
2511                      return NULL;                      return NULL;
2512                  }                  }
2513              }              }
2514              if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR)  
2515                  tc->flags |= TCF_FUN_CLOSURE_VS_VAR;              if (topLevel) {
2516          } else {                  ALE_SET_DEFN(ale, pn);
2517              ale = js_IndexAtom(cx, funAtom, &tc->decls);                  pn->pn_defn = true;
2518              if (!ale)                  pn->dn_uses = dn;               /* dn->dn_uses is now pn_link */
2519    
2520                    if (!MakeDefIntoUse(dn, pn, funAtom, tc))
2521                        return NULL;
2522                }
2523            } else if (topLevel) {
2524                /*
2525                 * If this function was used before it was defined, claim the
2526                 * pre-created definition node for this function that PrimaryExpr
2527                 * put in tc->lexdeps on first forward reference, and recycle pn.
2528                 */
2529                JSHashEntry **hep;
2530    
2531                ale = tc->lexdeps.rawLookup(funAtom, hep);
2532                if (ale) {
2533                    JSDefinition *fn = ALE_DEFN(ale);
2534    
2535                    JS_ASSERT(fn->pn_defn);
2536                    fn->pn_type = TOK_FUNCTION;
2537                    fn->pn_arity = PN_FUNC;
2538                    fn->pn_pos.begin = pn->pn_pos.begin;
2539                    fn->pn_body = NULL;
2540                    fn->pn_cookie = FREE_UPVAR_COOKIE;
2541    
2542                    tc->lexdeps.rawRemove(tc->compiler, ale, hep);
2543                    RecycleTree(pn, tc);
2544                    pn = fn;
2545                }
2546    
2547                if (!Define(pn, funAtom, tc))
2548                  return NULL;                  return NULL;
2549          }          }
         ALE_SET_JSOP(ale, JSOP_DEFFUN);  
2550    
2551          /*          /*
2552           * A function nested at top level inside another's body needs only a           * A function nested at top level inside another's body needs only a
# Line 1161  Line 2556 
2556           * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a           * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2557           * JSOP_GETLOCAL bytecode).           * JSOP_GETLOCAL bytecode).
2558           */           */
2559          if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) {          if (topLevel) {
2560              JSLocalKind localKind;              pn->pn_dflags |= PND_TOPLEVEL;
2561    
2562              /*              if (tc->flags & TCF_IN_FUNCTION) {
2563               * Define a property on the outer function so that BindNameToSlot                  JSLocalKind localKind;
2564               * can properly optimize accesses. Note that we need a variable,                  uintN index;
2565               * not an argument, for the function statement. Thus we add a  
2566               * variable even if the parameter with the given name already                  /*
2567               * exists.                   * Define a local in the outer function so that BindNameToSlot
2568               */                   * can properly optimize accesses. Note that we need a local
2569              localKind = js_LookupLocal(cx, tc->u.fun, funAtom, NULL);                   * variable, not an argument, for the function statement. Thus
2570              if (localKind == JSLOCAL_NONE || localKind == JSLOCAL_ARG) {                   * we add a variable even if a parameter with the given name
2571                  if (!js_AddLocal(cx, tc->u.fun, funAtom, JSLOCAL_VAR))                   * already exists.
2572                      return NULL;                   */
2573                    localKind = js_LookupLocal(cx, tc->fun, funAtom, &index);
2574                    switch (localKind) {
2575                      case JSLOCAL_NONE:
2576                      case JSLOCAL_ARG:
2577                        index = tc->fun->u.i.nvars;
2578                        if (!js_AddLocal(cx, tc->fun, funAtom, JSLOCAL_VAR))
2579                            return NULL;
2580                        /* FALL THROUGH */
2581    
2582                      case JSLOCAL_VAR:
2583                        pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
2584                        pn->pn_dflags |= PND_BOUND;
2585                        break;
2586    
2587                      default:;
2588                    }
2589              }              }
2590          }          }
2591      }      }
2592    
2593      fun = NewCompilerFunction(cx, tc, funAtom, lambda);      /* Initialize early for possible flags mutation via DestructuringExpr. */
2594      if (!fun)      JSTreeContext funtc(tc->compiler);
2595    
2596        JSFunctionBox *funbox = EnterFunction(pn, tc, &funtc, funAtom, lambda);
2597        if (!funbox)
2598          return NULL;          return NULL;
2599    
2600        JSFunction *fun = (JSFunction *) funbox->object;
2601    
2602  #if JS_HAS_GETTER_SETTER  #if JS_HAS_GETTER_SETTER
2603      if (op != JSOP_NOP)      if (op != JSOP_NOP)
2604          fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;          fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
2605  #endif  #endif
2606    
     /*  
      * Create wrapping box for fun->object early to protect against a  
      * last-ditch GC.  
      */  
     funpob = js_NewParsedObjectBox(cx, tc->parseContext, FUN_OBJECT(fun));  
     if (!funpob)  
         return NULL;  
   
     /* Initialize early for possible flags mutation via DestructuringExpr. */  
     TREE_CONTEXT_INIT(&funtc, tc->parseContext);  
     funtc.flags |= TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO);  
     funtc.u.fun = fun;  
   
2607      /* Now parse formal argument list and compute fun->nargs. */      /* Now parse formal argument list and compute fun->nargs. */
2608      MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);      MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
2609      if (!js_MatchToken(cx, ts, TOK_RP)) {      if (!js_MatchToken(cx, ts, TOK_RP)) {
# Line 1215  Line 2618 
2618                  JSParseNode *lhs, *rhs;                  JSParseNode *lhs, *rhs;
2619                  jsint slot;                  jsint slot;
2620    
2621                    /* See comment below in the TOK_NAME case. */
2622                    if (duplicatedArg)
2623                        goto report_dup_and_destructuring;
2624                    destructuringArg = true;
2625    
2626                  /*                  /*
2627                   * A destructuring formal parameter turns into one or more                   * A destructuring formal parameter turns into one or more
2628                   * local variables initialized from properties of a single                   * local variables initialized from properties of a single
# Line 1241  Line 2649 
2649                   * anonymous positional parameter into the destructuring                   * anonymous positional parameter into the destructuring
2650                   * left-hand-side expression and accumulate it in list.                   * left-hand-side expression and accumulate it in list.
2651                   */                   */
2652                  rhs = NewParseNode(cx, ts, PN_NAME, tc);                  rhs = NewNameNode(cx, ts, cx->runtime->atomState.emptyAtom, &funtc);
2653                  if (!rhs)                  if (!rhs)
2654                      return NULL;                      return NULL;
2655                  rhs->pn_type = TOK_NAME;                  rhs->pn_type = TOK_NAME;
2656                  rhs->pn_op = JSOP_GETARG;                  rhs->pn_op = JSOP_GETARG;
2657                  rhs->pn_atom = cx->runtime->atomState.emptyAtom;                  rhs->pn_cookie = MAKE_UPVAR_COOKIE(funtc.staticLevel, slot);
2658                  rhs->pn_slot = slot;                  rhs->pn_dflags |= PND_BOUND;
2659    
2660                  item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc);                  item = NewBinary(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
2661                  if (!item)                  if (!item)
2662                      return NULL;                      return NULL;
2663                  if (!list) {                  if (!list) {
2664                      list = NewParseNode(cx, ts, PN_LIST, tc);                      list = NewParseNode(PN_LIST, &funtc);
2665                      if (!list)                      if (!list)
2666                          return NULL;                          return NULL;
2667                      list->pn_type = TOK_COMMA;                      list->pn_type = TOK_COMMA;
2668                      PN_INIT_LIST(list);                      list->makeEmpty();
2669                  }                  }
2670                  PN_APPEND(list, item);                  list->append(item);
2671                  break;                  break;
2672                }                }
2673  #endif /* JS_HAS_DESTRUCTURING */  #endif /* JS_HAS_DESTRUCTURING */
2674    
2675                case TOK_NAME:                case TOK_NAME:
2676                  if (!BindArg(cx, CURRENT_TOKEN(ts).t_atom, &funtc))                {
2677                    /*
2678                     * Check for a duplicate parameter name, a "feature" that
2679                     * ECMA-262 requires. This is a SpiderMonkey strict warning,
2680                     * soon to be an ES3.1 strict error.
2681                     *
2682                     * Further, if any argument is a destructuring pattern, forbid
2683                     * duplicates. We will report the error either now if we have
2684                     * seen a destructuring pattern already, or later when we find
2685                     * the first pattern.
2686                     */
2687                    JSAtom *atom = CURRENT_TOKEN(ts).t_atom;
2688                    if (JS_HAS_STRICT_OPTION(cx) &&
2689                        js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) {
2690    #if JS_HAS_DESTRUCTURING
2691                        if (destructuringArg)
2692                            goto report_dup_and_destructuring;
2693                        duplicatedArg = true;
2694    #endif
2695                        const char *name = js_AtomToPrintableString(cx, atom);
2696                        if (!name ||
2697                            !js_ReportCompileErrorNumber(cx, TS(funtc.compiler),
2698                                                         NULL,
2699                                                         JSREPORT_WARNING |
2700                                                         JSREPORT_STRICT,
2701                                                         JSMSG_DUPLICATE_FORMAL,
2702                                                         name)) {
2703                            return NULL;
2704                        }
2705                    }
2706                    if (!DefineArg(pn, atom, fun->nargs, &funtc))
2707                        return NULL;
2708                    if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG))
2709                      return NULL;                      return NULL;
2710                  break;                  break;
2711                  }
2712    
2713                default:                default:
2714                  js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,                  js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2715                                              JSMSG_MISSING_FORMAL);                                              JSMSG_MISSING_FORMAL);
2716                    /* FALL THROUGH */
2717                  case TOK_ERROR:
2718                  return NULL;                  return NULL;
2719    
2720    #if JS_HAS_DESTRUCTURING
2721                  report_dup_and_destructuring:
2722                    js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL,
2723                                                JSREPORT_ERROR,
2724                                                JSMSG_DESTRUCT_DUP_ARG);
2725                    return NULL;
2726    #endif
2727              }              }
2728          } while (js_MatchToken(cx, ts, TOK_COMMA));          } while (js_MatchToken(cx, ts, TOK_COMMA));
2729    
# Line 1290  Line 2741 
2741  #else  #else
2742      MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);      MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
2743  #endif  #endif
     pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;  
2744    
2745      body = FunctionBody(cx, ts, &funtc);      body = FunctionBody(cx, ts, &funtc);
2746      if (!body)      if (!body)
# Line 1299  Line 2749 
2749  #if JS_HAS_EXPR_CLOSURES  #if JS_HAS_EXPR_CLOSURES
2750      if (tt == TOK_LC)      if (tt == TOK_LC)
2751          MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);          MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2752      else if (lambda == 0)      else if (lambda == 0 && !MatchOrInsertSemicolon(cx, ts))
2753          js_MatchToken(cx, ts, TOK_SEMI);          return NULL;
2754  #else  #else
2755      MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);      MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2756  #endif  #endif
# Line 1318  Line 2768 
2768          if (body->pn_arity != PN_LIST) {          if (body->pn_arity != PN_LIST) {
2769              JSParseNode *block;              JSParseNode *block;
2770    
2771              block = NewParseNode(cx, ts, PN_LIST, tc);              block = NewParseNode(PN_LIST, tc);
2772              if (!block)              if (!block)
2773                  return NULL;                  return NULL;
2774              block->pn_type = TOK_SEQ;              block->pn_type = TOK_SEQ;
2775              block->pn_pos = body->pn_pos;              block->pn_pos = body->pn_pos;
2776              PN_INIT_LIST_1(block, body);              block->initList(body);
2777    
2778              body = block;              body = block;
2779          }          }
2780    
2781          item = NewParseNode(cx, ts, PN_UNARY, tc);          item = NewParseNode(PN_UNARY, tc);
2782          if (!item)          if (!item)
2783              return NULL;              return NULL;
2784    
# Line 1340  Line 2790 
2790          if (body->pn_tail == &body->pn_head)          if (body->pn_tail == &body->pn_head)
2791              body->pn_tail = &item->pn_next;              body->pn_tail = &item->pn_next;
2792          ++body->pn_count;          ++body->pn_count;
2793            body->pn_xflags |= PNX_DESTRUCT;
2794      }      }
2795  #endif  #endif
2796    
2797      /*      /*
2798       * If we collected flags that indicate nested heavyweight functions, or       * If we collected flags that indicate nested heavyweight functions, or
2799       * this function contains heavyweight-making statements (references to       * this function contains heavyweight-making statements (with statement,
2800       * __parent__ or __proto__; use of with, or eval; and assignment to       * visible eval call, or assignment to 'arguments'), flag the function as
2801       * arguments), flag the function as heavyweight (requiring a call object       * heavyweight (requiring a call object per invocation).
      * per invocation).  
2802       */       */
2803      if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {      if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
2804          fun->flags |= JSFUN_HEAVYWEIGHT;          fun->flags |= JSFUN_HEAVYWEIGHT;
# Line 1356  Line 2806 
2806      } else {      } else {
2807          /*          /*
2808           * If this function is a named statement function not at top-level           * If this function is a named statement function not at top-level
2809           * (i.e. not a top-level function definiton or expression), then           * (i.e. not a top-level function definiton or expression), then our
2810           * our enclosing function, if any, must be heavyweight.           * enclosing function, if any, must be heavyweight.
          *  
          * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator,  
          * so it won't be set here.  Assert that it's not.  We have to check  
          * it later, in js_EmitTree, after js_EmitFunctionScript has traversed  
          * the function's body.  
2811           */           */
2812          JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS));          if (!topLevel && lambda == 0 && funAtom)
         if (lambda == 0 && funAtom && !AT_TOP_LEVEL(tc))  
2813              tc->flags |= TCF_FUN_HEAVYWEIGHT;              tc->flags |= TCF_FUN_HEAVYWEIGHT;
2814      }      }
2815    
# Line 1374  Line 2818 
2818          /*          /*
2819           * ECMA ed. 3 standard: function expression, possibly anonymous.           * ECMA ed. 3 standard: function expression, possibly anonymous.
2820           */           */
2821          op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;          op = JSOP_LAMBDA;
2822      } else if (!funAtom) {      } else if (!funAtom) {
2823          /*          /*
2824           * If this anonymous function definition is *not* embedded within a           * If this anonymous function definition is *not* embedded within a
# Line 1383  Line 2827 
2827           * Edition 3 would have it).  Backward compatibility must trump all,           * Edition 3 would have it).  Backward compatibility must trump all,
2828           * unless JSOPTION_ANONFUNFIX is set.           * unless JSOPTION_ANONFUNFIX is set.
2829           */           */
2830          result = NewParseNode(cx, ts, PN_UNARY, tc);          result = NewParseNode(PN_UNARY, tc);
2831          if (!result)          if (!result)
2832              return NULL;              return NULL;
2833          result->pn_type = TOK_SEMI;          result->pn_type = TOK_SEMI;
2834          result->pn_pos = pn->pn_pos;          result->pn_pos = pn->pn_pos;
2835          result->pn_kid = pn;          result->pn_kid = pn;
2836          op = JSOP_ANONFUNOBJ;          op = JSOP_LAMBDA;
2837      } else if (!AT_TOP_LEVEL(tc)) {      } else if (!topLevel) {
2838          /*          /*
2839           * ECMA ed. 3 extension: a function expression statement not at the           * ECMA ed. 3 extension: a function expression statement not at the
2840           * top level, e.g., in a compound statement such as the "then" part           * top level, e.g., in a compound statement such as the "then" part
# Line 1402  Line 2846 
2846          op = JSOP_NOP;          op = JSOP_NOP;
2847      }      }
2848    
2849      pn->pn_funpob = funpob;      funbox->kids = funtc.functionList;
2850    
2851        pn->pn_funbox = funbox;
2852      pn->pn_op = op;      pn->pn_op = op;
2853      pn->pn_body = body;      if (pn->pn_body) {
2854      pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS | TCF_COMPILE_N_GO);          pn->pn_body->append(body);
2855      TREE_CONTEXT_FINISH(cx, &funtc);          pn->pn_body->pn_pos = body->pn_pos;
2856        } else {
2857            pn->pn_body = body;
2858        }
2859    
2860        pn->pn_blockid = tc->blockid();
2861    
2862        if (!LeaveFunction(pn, &funtc, tc, funAtom, lambda))
2863            return NULL;
2864    
2865      return result;      return result;
2866  }  }
2867    
# Line 1435  Line 2890 
2890    
2891      JS_CHECK_RECURSION(cx, return NULL);      JS_CHECK_RECURSION(cx, return NULL);
2892    
2893      pn = NewParseNode(cx, ts, PN_LIST, tc);      pn = NewParseNode(PN_LIST, tc);
2894      if (!pn)      if (!pn)
2895          return NULL;          return NULL;
2896        pn->pn_type = TOK_LC;
2897        pn->makeEmpty();
2898        pn->pn_blockid = tc->blockid();
2899      saveBlock = tc->blockNode;      saveBlock = tc->blockNode;
2900      tc->blockNode = pn;      tc->blockNode = pn;
     PN_INIT_LIST(pn);  
2901    
2902      for (;;) {      for (;;) {
2903          ts->flags |= TSF_OPERAND;          ts->flags |= TSF_OPERAND;
2904          tt = js_PeekToken(cx, ts);          tt = js_PeekToken(cx, ts);
2905          ts->flags &= ~TSF_OPERAND;          ts->flags &= ~TSF_OPERAND;
2906          if (tt <= TOK_EOF || tt == TOK_RC) {          if (tt <= TOK_EOF || tt == TOK_RC) {
2907              if (tt == TOK_ERROR)              if (tt == TOK_ERROR) {
2908                    if (ts->flags & TSF_EOF)
2909                        ts->flags |= TSF_UNEXPECTED_EOF;
2910                  return NULL;                  return NULL;
2911                }
2912              break;              break;
2913          }          }
2914          pn2 = Statement(cx, ts, tc);          pn2 = Statement(cx, ts, tc);
# Line 1468  Line 2928 
2928               * is relevant only for function definitions not at top-level,               * is relevant only for function definitions not at top-level,
2929               * which we call function statements.               * which we call function statements.
2930               */               */
2931              if (AT_TOP_LEVEL(tc))              if (tc->atTopLevel())
2932                  pn->pn_extra |= PNX_FUNCDEFS;                  pn->pn_xflags |= PNX_FUNCDEFS;
2933              else              else
2934                  tc->flags |= TCF_HAS_FUNCTION_STMT;                  tc->flags |= TCF_HAS_FUNCTION_STMT;
2935          }          }
2936          PN_APPEND(pn, pn2);          pn->append(pn2);
2937      }      }
2938    
2939      /*      /*
# Line 1540  Line 3000 
3000  static JSBool  static JSBool
3001  BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)  BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3002  {  {
3003        JSParseNode *pn;
3004      JSObject *blockObj;      JSObject *blockObj;
     JSScopeProperty *sprop;  
3005      JSAtomListElement *ale;      JSAtomListElement *ale;
3006      uintN n;      jsint n;
3007    
3008      blockObj = tc->blockChain;      /*
3009      sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom));       * Top-level 'let' is the same as 'var' currently -- this may change in a
3010      ATOM_LIST_SEARCH(ale, &tc->decls, atom);       * successor standard to ES3.1 that specifies 'let'.
3011      if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) {       */
3012          const char *name;      JS_ASSERT(!tc->atTopLevel());
   
         if (sprop) {  
             JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);  
             JS_ASSERT((uint16)sprop->shortid < OBJ_BLOCK_COUNT(cx, blockObj));  
         }  
3013    
3014          name = js_AtomToPrintableString(cx, atom);      pn = data->pn;
3015        blockObj = tc->blockChain;
3016        ale = tc->decls.lookup(atom);
3017        if (ale && ALE_DEFN(ale)->pn_blockid == tc->blockid()) {
3018            const char *name = js_AtomToPrintableString(cx, atom);
3019          if (name) {          if (name) {
3020              js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,              js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3021                                          JSREPORT_ERROR, JSMSG_REDECLARED_VAR,                                          JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
3022                                          (ale && ALE_JSOP(ale) == JSOP_DEFCONST)                                          (ale && ALE_DEFN(ale)->isConst())
3023                                          ? js_const_str                                          ? js_const_str
3024                                          : "variable",                                          : js_variable_str,
3025                                          name);                                          name);
3026          }          }
3027          return JS_FALSE;          return JS_FALSE;
# Line 1570  Line 3029 
3029    
3030      n = OBJ_BLOCK_COUNT(cx, blockObj);      n = OBJ_BLOCK_COUNT(cx, blockObj);
3031      if (n == JS_BIT(16)) {      if (n == JS_BIT(16)) {
3032          js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,          js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3033                                      JSREPORT_ERROR, data->u.let.overflow);                                      JSREPORT_ERROR, data->let.overflow);
3034          return JS_FALSE;          return JS_FALSE;
3035      }      }
3036    
3037      /* Use JSPROP_ENUMERATE to aid the disassembler. */      /*
3038      return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom),       * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3039                                     JSVAL_VOID, NULL, NULL,       * This is balanced by PopStatement, defined immediately below.
3040                                     JSPROP_ENUMERATE |       */
3041                                     JSPROP_PERMANENT |      if (!Define(pn, atom, tc, true))
3042                                     JSPROP_SHARED,          return JS_FALSE;
3043                                     SPROP_HAS_SHORTID, (int16) n, NULL);  
3044        /*
3045         * Assign block-local index to pn->pn_cookie right away, encoding it as an
3046         * upvar cookie whose skip tells the current static level. The emitter will
3047         * adjust the node's slot based on its stack depth model -- and, for global
3048         * and eval code, JSCompiler::compileScript will adjust the slot again to
3049         * include script->nfixed.
3050         */
3051        pn->pn_op = JSOP_GETLOCAL;
3052        pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, n);
3053        pn->pn_dflags |= PND_LET | PND_BOUND;
3054    
3055        /*
3056         * Use JSPROP_ENUMERATE to aid the disassembler. Define the let binding's
3057         * property before storing pn in a reserved slot, since block_reserveSlots
3058         * depends on OBJ_SCOPE(blockObj)->entryCount.
3059         */
3060        if (!js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom), JSVAL_VOID,
3061                                     NULL, NULL,
3062                                     JSPROP_ENUMERATE |
3063                                     JSPROP_PERMANENT |
3064                                     JSPROP_SHARED,
3065                                     SPROP_HAS_SHORTID, (int16) n, NULL)) {
3066            return JS_FALSE;
3067        }
3068    
3069        /*
3070         * Store pn temporarily in what would be reserved slots in a cloned block
3071         * object (once the prototype's final population is known, after all 'let'
3072         * bindings for this block have been parsed). We will free these reserved
3073         * slots in jsemit.cpp:EmitEnterBlock.
3074         */
3075        uintN slot = JSSLOT_FREE(&js_BlockClass) + n;
3076        if (slot >= STOBJ_NSLOTS(blockObj) &&
3077            !js_ReallocSlots(cx, blockObj, slot + 1, JS_FALSE)) {
3078            return JS_FALSE;
3079        }
3080        OBJ_SCOPE(blockObj)->freeslot = slot + 1;
3081        STOBJ_SET_SLOT(blockObj, slot, PRIVATE_TO_JSVAL(pn));
3082        return JS_TRUE;
3083    }
3084    
3085    static void
3086    PopStatement(JSTreeContext *tc)
3087    {
3088        JSStmtInfo *stmt = tc->topStmt;
3089    
3090        if (stmt->flags & SIF_SCOPE) {
3091            JSObject *obj = stmt->blockObj;
3092            JSScope *scope = OBJ_SCOPE(obj);
3093            JS_ASSERT(scope->object == obj);
3094    
3095            for (JSScopeProperty *sprop = scope->lastProp; sprop; sprop = sprop->parent) {
3096                JSAtom *atom = JSID_TO_ATOM(sprop->id);
3097    
3098                /* Beware the empty destructuring dummy. */
3099                if (atom == tc->compiler->context->runtime->atomState.emptyAtom)
3100                    continue;
3101                tc->decls.remove(tc->compiler, atom);
3102            }
3103        }
3104        js_PopStatement(tc);
3105    }
3106    
3107    static inline bool
3108    OuterLet(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *atom)
3109    {
3110        while (stmt->downScope) {
3111            stmt = js_LexicalLookup(tc, atom, NULL, stmt->downScope);
3112            if (!stmt)
3113                return false;
3114            if (stmt->type == STMT_BLOCK)
3115                return true;
3116        }
3117        return false;
3118  }  }
3119    
3120  static JSBool  static JSBool
3121  BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)  BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3122  {  {
3123      JSStmtInfo *stmt;      JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
3124      JSAtomListElement *ale;      JSParseNode *pn = data->pn;
3125      JSOp op, prevop;  
3126      const char *name;      if (stmt && stmt->type == STMT_WITH) {
3127      JSLocalKind localKind;          pn->pn_op = JSOP_NAME;
3128            data->fresh = false;
3129            return JS_TRUE;
3130        }
3131    
3132      stmt = js_LexicalLookup(tc, atom, NULL);      JSAtomListElement *ale = tc->decls.lookup(atom);
3133      ATOM_LIST_SEARCH(ale, &tc->decls, atom);      JSOp op = data->op;
3134      op = data->op;  
3135      if ((stmt && stmt->type != STMT_WITH) || ale) {      if (stmt || ale) {
3136          prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR;          JSDefinition *dn = ale ? ALE_DEFN(ale) : NULL;
3137          if (JS_HAS_STRICT_OPTION(cx)          JSDefinition::Kind dn_kind = dn ? dn->kind() : JSDefinition::VAR;
3138              ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR          const char *name;
3139              : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) {  
3140            if (dn_kind == JSDefinition::ARG) {
3141              name = js_AtomToPrintableString(cx, atom);              name = js_AtomToPrintableString(cx, atom);
3142              if (!name ||              if (!name)
3143                  !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,                  return JS_FALSE;
3144                                               (op != JSOP_DEFCONST &&  
3145                                                prevop != JSOP_DEFCONST)              if (op == JSOP_DEFCONST) {
3146                                               ? JSREPORT_WARNING |                  js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3147                                                 JSREPORT_STRICT                                              JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
3148                                               : JSREPORT_ERROR,                                              name);
                                              JSMSG_REDECLARED_VAR,  
                                              (prevop == JSOP_DEFFUN)  
                                              ? js_function_str  
                                              : (prevop == JSOP_DEFCONST)  
                                              ? js_const_str  
                                              : js_var_str,  
                                              name)) {  
3149                  return JS_FALSE;                  return JS_FALSE;
3150              }              }
3151                if (!js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3152                                                 JSREPORT_WARNING | JSREPORT_STRICT,
3153                                                 JSMSG_VAR_HIDES_ARG, name)) {
3154                    return JS_FALSE;
3155                }
3156            } else {
3157                bool error = (op == JSOP_DEFCONST ||
3158                              dn_kind == JSDefinition::CONST ||
3159                              (dn_kind == JSDefinition::LET &&
3160                               (stmt->type != STMT_CATCH || OuterLet(tc, stmt, atom))));
3161    
3162                if (JS_HAS_STRICT_OPTION(cx)
3163                    ? op != JSOP_DEFVAR || dn_kind != JSDefinition::VAR
3164                    : error) {
3165                    name = js_AtomToPrintableString(cx, atom);
3166                    if (!name ||
3167                        !js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3168                                                     !error
3169                                                     ? JSREPORT_WARNING | JSREPORT_STRICT
3170                                                     : JSREPORT_ERROR,
3171                                                     JSMSG_REDECLARED_VAR,
3172                                                     JSDefinition::kindString(dn_kind),
3173                                                     name)) {
3174                        return JS_FALSE;
3175                    }
3176                }
3177          }          }
         if (op == JSOP_DEFVAR && prevop == JSOP_DEFFUN)  
             tc->flags |= TCF_FUN_CLOSURE_VS_VAR;  
3178      }      }
3179    
3180      if (!ale) {      if (!ale) {
3181          ale = js_IndexAtom(cx, atom, &tc->decls);          if (!Define(pn, atom, tc))
3182                return JS_FALSE;
3183        } else {
3184            /*
3185             * A var declaration never recreates an existing binding, it restates
3186             * it and possibly reinitializes its value. Beware that if pn becomes a
3187             * use of ALE_DEFN(ale), and if we have an initializer for this var or
3188             * const (typically a const would ;-), then pn must be rewritten into a
3189             * TOK_ASSIGN node. See Variables, further below.
3190             *
3191             * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3192             * There the x definition is hoisted but the x = 2 assignment mutates
3193             * the block-local binding of x.
3194             */
3195            JSDefinition *dn = ALE_DEFN(ale);
3196    
3197            data->fresh = false;
3198    
3199            if (!pn->pn_used) {
3200                /* Make pnu be a fresh name node that uses dn. */
3201                JSParseNode *pnu = pn;
3202    
3203                if (pn->pn_defn) {
3204                    pnu = NewNameNode(cx, TS(tc->compiler), atom, tc);
3205                    if (!pnu)
3206                        return JS_FALSE;
3207                }
3208    
3209                LinkUseToDef(pnu, dn, tc);
3210                pnu->pn_op = JSOP_NAME;
3211            }
3212    
3213            while (dn->kind() == JSDefinition::LET) {
3214                do {
3215                    ale = ALE_NEXT(ale);
3216                } while (ale && ALE_ATOM(ale) != atom);
3217                if (!ale)
3218                    break;
3219                dn = ALE_DEFN(ale);
3220            }
3221    
3222            if (ale) {
3223                JS_ASSERT_IF(data->op == JSOP_DEFCONST,
3224                             dn->kind() == JSDefinition::CONST);
3225                return JS_TRUE;
3226            }
3227    
3228            /*
3229             * A var or const that is shadowed by one or more let bindings of the
3230             * same name, but that has not been declared until this point, must be
3231             * hoisted above the let bindings.
3232             */
3233            if (!pn->pn_defn) {
3234                JSHashEntry **hep;
3235    
3236                ale = tc->lexdeps.rawLookup(atom, hep);
3237                if (ale) {
3238                    pn = ALE_DEFN(ale);
3239                    tc->lexdeps.rawRemove(tc->compiler, ale, hep);
3240                } else {
3241                    JSParseNode *pn2 = NewNameNode(cx, TS(tc->compiler), atom, tc);
3242                    if (!pn2)
3243                        return JS_FALSE;
3244    
3245                    /* The token stream may be past the location for pn. */
3246                    pn2->pn_type = TOK_NAME;
3247                    pn2->pn_pos = pn->pn_pos;
3248                    pn = pn2;
3249                }
3250                pn->pn_op = JSOP_NAME;
3251            }
3252    
3253            ale = tc->decls.add(tc->compiler, atom, JSAtomList::HOIST);
3254          if (!ale)          if (!ale)
3255              return JS_FALSE;              return JS_FALSE;
3256            ALE_SET_DEFN(ale, pn);
3257            pn->pn_defn = true;
3258            pn->pn_dflags &= ~PND_PLACEHOLDER;
3259      }      }
3260      ALE_SET_JSOP(ale, op);  
3261        if (data->op == JSOP_DEFCONST)
3262            pn->pn_dflags |= PND_CONST;
3263    
3264      if (!(tc->flags & TCF_IN_FUNCTION)) {      if (!(tc->flags & TCF_IN_FUNCTION)) {
3265          /*          /*
3266           * Don't lookup global variables or variables in an active frame at           * If we are generating global or eval-called-from-global code, bind a
3267           * compile time.           * "gvar" here, as soon as possible. The JSOP_GETGVAR, etc., ops speed
3268             * up global variable access by memoizing name-to-slot mappings in the
3269             * script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3270             * can't be done due to a pre-existing property of the same name as the
3271             * var or const but incompatible attributes/getter/setter/etc, these
3272             * ops devolve to JSOP_NAME, etc.
3273             *
3274             * For now, don't try to lookup eval frame variables at compile time.
3275             * Seems sub-optimal: why couldn't we find eval-called-from-a-function
3276             * upvars early and possibly simplify jsemit.cpp:BindNameToSlot?
3277           */           */
3278            pn->pn_op = JSOP_NAME;
3279            if ((tc->flags & TCF_COMPILING) && !tc->compiler->callerFrame) {
3280                JSCodeGenerator *cg = (JSCodeGenerator *) tc;
3281    
3282                /* Index atom so we can map fast global number to name. */
3283                ale = cg->atomList.add(tc->compiler, atom);
3284                if (!ale)
3285                    return JS_FALSE;
3286    
3287                /* Defend against cg->ngvars 16-bit overflow. */
3288                uintN slot = ALE_INDEX(ale);
3289                if ((slot + 1) >> 16)
3290                    return JS_TRUE;
3291    
3292                if ((uint16)(slot + 1) > cg->ngvars)
3293                    cg->ngvars = (uint16)(slot + 1);
3294    
3295                pn->pn_op = JSOP_GETGVAR;
3296                pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, slot);
3297                pn->pn_dflags |= PND_BOUND | PND_GVAR;
3298            }
3299            return JS_TRUE;
3300        }
3301    
3302        if (atom == cx->runtime->atomState.argumentsAtom) {
3303            pn->pn_op = JSOP_ARGUMENTS;
3304            pn->pn_dflags |= PND_BOUND;
3305          return JS_TRUE;          return JS_TRUE;
3306      }      }
3307    
3308      localKind = js_LookupLocal(cx, tc->u.fun, atom, NULL);      JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
3309      if (localKind == JSLOCAL_NONE) {      if (localKind == JSLOCAL_NONE) {
3310          /*          /*
3311           * Property not found in current variable scope: we have not seen this           * Property not found in current variable scope: we have not seen this
3312           * variable before.  Define a new local variable by adding a property           * variable before. Define a new local variable by adding a property to
3313           * to the function's scope, allocating one slot in the function's vars           * the function's scope and allocating one slot in the function's vars
3314           * frame. Any locals declared in with statement bodies are handled at           * frame. Any locals declared in a with statement body are handled at
3315           * runtime, by script prolog JSOP_DEFVAR opcodes generated for           * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3316           * slot-less vars.           * and heavyweight-function-local vars.
3317           */           */
3318          localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;          localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
         if (!js_InWithStatement(tc) &&  
             !BindLocalVariable(cx, tc->u.fun, atom, localKind)) {  
             return JS_FALSE;  
         }  
     } else if (localKind == JSLOCAL_ARG) {  
         name = js_AtomToPrintableString(cx, atom);  
         if (!name)  
             return JS_FALSE;  
3319    
3320          if (op == JSOP_DEFCONST) {          uintN index = tc->fun->u.i.nvars;
3321              js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,          if (!BindLocalVariable(cx, tc->fun, atom, localKind))
                                         JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,  
                                         name);  
3322              return JS_FALSE;              return JS_FALSE;
3323          }          pn->pn_op = JSOP_GETLOCAL;
3324          if (!js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,          pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
3325                                           JSREPORT_WARNING | JSREPORT_STRICT,          pn->pn_dflags |= PND_BOUND;
3326                                           JSMSG_VAR_HIDES_ARG, name)) {          return JS_TRUE;
3327              return JS_FALSE;      }
3328          }  
3329        if (localKind == JSLOCAL_ARG) {
3330            /* We checked errors and strict warnings earlier -- see above. */
3331            JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
3332      } else {      } else {
3333          /* Not an argument, must be a redeclared local var. */          /* Not an argument, must be a redeclared local var. */
3334          JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);          JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
3335      }      }
3336        pn->pn_op = JSOP_NAME;
3337      return JS_TRUE;      return JS_TRUE;
3338  }  }
3339    
# Line 1683  Line 3345 
3345      JS_ASSERT(pn->pn_arity == PN_LIST);      JS_ASSERT(pn->pn_arity == PN_LIST);
3346      JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL || pn->pn_op == JSOP_APPLY);      JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL || pn->pn_op == JSOP_APPLY);
3347      pn2 = pn->pn_head;      pn2 = pn->pn_head;
3348      if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_flags & TCF_GENEXP_LAMBDA)) {      if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA)) {
3349          js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn,          js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn, JSREPORT_ERROR, msg);
                                     JSREPORT_ERROR, msg);  
3350          return JS_FALSE;          return JS_FALSE;
3351      }      }
3352      pn->pn_op = JSOP_SETCALL;      pn->pn_op = JSOP_SETCALL;
3353      return JS_TRUE;      return JS_TRUE;
3354  }  }
3355    
3356    static void
3357    NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_ASSIGNED)
3358    {
3359        if (pn->pn_used) {
3360            JSDefinition *dn = pn->pn_lexdef;
3361    
3362            /*
3363             * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
3364             * occur as direct kids of the same block with no forward refs to x.
3365             */
3366            if (!(dn->pn_dflags & (PND_INITIALIZED | PND_CONST | PND_PLACEHOLDER)) &&
3367                dn->isBlockChild() &&
3368                pn->isBlockChild() &&
3369                dn->pn_blockid == pn->pn_blockid &&
3370                dn->pn_pos.end <= pn->pn_pos.begin &&
3371                dn->dn_uses == pn) {
3372                dflag = PND_INITIALIZED;
3373            }
3374    
3375            dn->pn_dflags |= dflag;
3376    
3377            if (dn->frameLevel() != tc->staticLevel) {
3378                /*
3379                 * The above condition takes advantage of the all-ones nature of
3380                 * FREE_UPVAR_COOKIE, and the reserved level FREE_STATIC_LEVEL.
3381                 * We make a stronger assertion by excluding FREE_UPVAR_COOKIE.
3382                 */
3383                JS_ASSERT_IF(dn->pn_cookie != FREE_UPVAR_COOKIE,
3384                             dn->frameLevel() < tc->staticLevel);
3385                tc->flags |= TCF_FUN_SETS_OUTER_NAME;
3386            }
3387        }
3388    
3389        pn->pn_dflags |= dflag;
3390    
3391        if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
3392            tc->flags |= TCF_FUN_HEAVYWEIGHT;
3393    }
3394    
3395  #if JS_HAS_DESTRUCTURING  #if JS_HAS_DESTRUCTURING
3396    
3397  static JSBool  static JSBool
# Line 1713  Line 3413 
3413      data->pn = pn;      data->pn = pn;
3414      if (!data->binder(cx, data, atom, tc))      if (!data->binder(cx, data, atom, tc))
3415          return JS_FALSE;          return JS_FALSE;
     data->pn = NULL;  
3416    
3417      /*      /*
3418       * Select the appropriate name-setting opcode, which may be specialized       * Select the appropriate name-setting opcode, respecting eager selection
3419       * further for local variable and argument slot optimizations.  At this       * done by the data->binder function.
      * point, we can't select the optimal final opcode, yet we must preserve  
      * the CONST bit and convey "set", not "get".  
3420       */       */
3421      if (data->op == JSOP_DEFCONST) {      if (pn->pn_dflags & PND_BOUND) {
3422          pn->pn_op = JSOP_SETCONST;          pn->pn_op = (pn->pn_op == JSOP_ARGUMENTS)
3423          pn->pn_const = JS_TRUE;                      ? JSOP_SETNAME
3424                        : (pn->pn_dflags & PND_GVAR)
3425                        ? JSOP_SETGVAR
3426                        : JSOP_SETLOCAL;
3427      } else {      } else {
3428          pn->pn_op = JSOP_SETNAME;          pn->pn_op = (data->op == JSOP_DEFCONST)
3429          pn->pn_const = JS_FALSE;                      ? JSOP_SETCONST
3430                        : JSOP_SETNAME;
3431      }      }
3432    
3433        if (data->op == JSOP_DEFCONST)
3434            pn->pn_dflags |= PND_CONST;
3435    
3436        NoteLValue(cx, pn, tc, PND_INITIALIZED);
3437      return JS_TRUE;      return JS_TRUE;
3438  }  }
3439    
# Line 1757  Line 3463 
3463    
3464      switch (pn->pn_type) {      switch (pn->pn_type) {
3465        case TOK_NAME:        case TOK_NAME:
3466          if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)          NoteLValue(cx, pn, tc);
             tc->flags |= TCF_FUN_HEAVYWEIGHT;  
3467          /* FALL THROUGH */          /* FALL THROUGH */
3468    
3469        case TOK_DOT:        case TOK_DOT:
3470        case TOK_LB:        case TOK_LB:
3471          pn->pn_op = JSOP_SETNAME;          pn->pn_op = JSOP_SETNAME;
# Line 1782  Line 3488 
3488  #endif  #endif
3489    
3490        default:        default:
3491          js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn,          js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3492                                      JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);                                      JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
3493          return JS_FALSE;          return JS_FALSE;
3494      }      }
# Line 1803  Line 3509 
3509  } FindPropValEntry;  } FindPropValEntry;
3510    
3511  #define ASSERT_VALID_PROPERTY_KEY(pnkey)                                      \  #define ASSERT_VALID_PROPERTY_KEY(pnkey)                                      \
3512      JS_ASSERT((pnkey)->pn_arity == PN_NULLARY &&                              \      JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY &&                             \
3513                ((pnkey)->pn_type == TOK_NUMBER ||                              \                 ((pnkey)->pn_type == TOK_NUMBER ||                             \
3514                 (pnkey)->pn_type == TOK_STRING ||                              \                  (pnkey)->pn_type == TOK_STRING ||                             \
3515                 (pnkey)->pn_type == TOK_NAME))                  (pnkey)->pn_type == TOK_NAME)) ||                             \
3516                   (pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME)
3517    
3518  static JSDHashNumber  static JSDHashNumber
3519  HashFindPropValKey(JSDHashTable *table, const void *key)  HashFindPropValKey(JSDHashTable *table, const void *key)
# Line 1876  Line 3583 
3583      step = 0;      step = 0;
3584      ASSERT_VALID_PROPERTY_KEY(pnid);      ASSERT_VALID_PROPERTY_KEY(pnid);
3585      pnhead = pn->pn_head;      pnhead = pn->pn_head;
     if (pnhead && pnhead->pn_type == TOK_DEFSHARP)  
         pnhead = pnhead->pn_next;  
3586      if (pnid->pn_type == TOK_NUMBER) {      if (pnid->pn_type == TOK_NUMBER) {
3587          for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {          for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3588              JS_ASSERT(pnprop->pn_type == TOK_COLON);              JS_ASSERT(pnprop->pn_type == TOK_COLON);
# Line 1936  Line 3641 
3641   * If data is null, the caller is AssignExpr and instead of binding variables,   * If data is null, the caller is AssignExpr and instead of binding variables,
3642   * we specialize lvalues in the propery value positions of the left-hand side.   * we specialize lvalues in the propery value positions of the left-hand side.
3643   * If right is null, just check for well-formed lvalues.   * If right is null, just check for well-formed lvalues.
3644     *
3645     * See also UndominateInitializers, immediately below. If you change either of
3646     * these functions, you might have to change the other to match.
3647   */   */
3648  static JSBool  static JSBool
3649  CheckDestructuring(JSContext *cx, BindData *data,  CheckDestructuring(JSContext *cx, BindData *data,
# Line 1947  Line 3655 
3655      JSParseNode *lhs, *rhs, *pn, *pn2;      JSParseNode *lhs, *rhs, *pn, *pn2;
3656    
3657      if (left->pn_type == TOK_ARRAYCOMP) {      if (left->pn_type == TOK_ARRAYCOMP) {
3658          js_ReportCompileErrorNumber(cx, TS(tc->parseContext), left,          js_ReportCompileErrorNumber(cx, TS(tc->compiler), left,
3659                                      JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE);                                      JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE);
3660          return JS_FALSE;          return JS_FALSE;
3661      }      }
3662    
3663  #if JS_HAS_DESTRUCTURING_SHORTHAND  #if JS_HAS_DESTRUCTURING_SHORTHAND
3664      if (right && right->pn_arity == PN_LIST && (right->pn_extra & PNX_SHORTHAND)) {      if (right && right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3665          js_ReportCompileErrorNumber(cx, TS(tc->parseContext), right,          js_ReportCompileErrorNumber(cx, TS(tc->compiler), right,
3666                                      JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);                                      JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
3667          return JS_FALSE;          return JS_FALSE;
3668      }      }
# Line 1962  Line 3670 
3670    
3671      fpvd.table.ops = NULL;      fpvd.table.ops = NULL;
3672      lhs = left->pn_head;      lhs = left->pn_head;
     if (lhs && lhs->pn_type == TOK_DEFSHARP) {  
         pn = lhs;  
         goto no_var_name;  
     }  
   
3673      if (left->pn_type == TOK_RB) {      if (left->pn_type == TOK_RB) {
3674          rhs = (right && right->pn_type == left->pn_type)          rhs = (right && right->pn_type == left->pn_type)
3675                ? right->pn_head                ? right->pn_head
# Line 2067  Line 3770 
3770      if (data &&      if (data &&
3771          data->binder == BindLet &&          data->binder == BindLet &&
3772          OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) {          OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) {
3773          ok = js_DefineNativeProperty(cx, tc->blockChain,          ok = !!js_DefineNativeProperty(cx, tc->blockChain,
3774                                       ATOM_TO_JSID(cx->runtime->                                         ATOM_TO_JSID(cx->runtime->
3775                                                    atomState.emptyAtom),                                                      atomState.emptyAtom),
3776                                       JSVAL_VOID, NULL, NULL,                                         JSVAL_VOID, NULL, NULL,
3777                                       JSPROP_ENUMERATE |                                         JSPROP_ENUMERATE |
3778                                       JSPROP_PERMANENT |                                         JSPROP_PERMANENT |
3779                                       JSPROP_SHARED,                                         JSPROP_SHARED,
3780                                       SPROP_HAS_SHORTID, 0, NULL);                                         SPROP_HAS_SHORTID, 0, NULL);
3781          if (!ok)          if (!ok)
3782              goto out;              goto out;
3783      }      }
# Line 2087  Line 3790 
3790      return ok;      return ok;
3791    
3792    no_var_name:    no_var_name:
3793      js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn, JSREPORT_ERROR,      js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn, JSREPORT_ERROR,
3794                                  JSMSG_NO_VARIABLE_NAME);                                  JSMSG_NO_VARIABLE_NAME);
3795      ok = JS_FALSE;      ok = JS_FALSE;
3796      goto out;      goto out;
3797  }  }
3798    
3799    /*
3800     * This is a greatly pared down version of CheckDestructuring that extends the
3801     * pn_pos.end source coordinate of each name in a destructuring binding such as
3802     *
3803     *   var [x, y] = [function () y, 42];
3804     *
3805     * to cover its corresponding initializer, so that the initialized binding does
3806     * not appear to dominate any closures in its initializer. See bug 496134.
3807     *
3808     * The quick-and-dirty dominance computation in JSCompiler::setFunctionKinds is
3809     * not very precise. With one-pass SSA construction from structured source code
3810     * (see "Single-Pass Generation of Static Single Assignment Form for Structured
3811     * Languages", Brandis and Mössenböck), we could do much better.
3812     *
3813     * See CheckDestructuring, immediately above. If you change either of these
3814     * functions, you might have to change the other to match.
3815     */
3816    static JSBool
3817    UndominateInitializers(JSParseNode *left, JSParseNode *right, JSTreeContext *tc)
3818    {
3819        FindPropValData fpvd;
3820        JSParseNode *lhs, *rhs;
3821    
3822        JS_ASSERT(left->pn_type != TOK_ARRAYCOMP);
3823        JS_ASSERT(right);
3824    
3825    #if JS_HAS_DESTRUCTURING_SHORTHAND
3826        if (right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3827            js_ReportCompileErrorNumber(tc->compiler->context, TS(tc->compiler), right,
3828                                        JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
3829            return JS_FALSE;
3830        }
3831    #endif
3832    
3833        if (right->pn_type != left->pn_type)
3834            return JS_TRUE;
3835    
3836        fpvd.table.ops = NULL;
3837        lhs = left->pn_head;
3838        if (left->pn_type == TOK_RB) {
3839            rhs = right->pn_head;
3840    
3841            while (lhs && rhs) {
3842                /* Nullary comma is an elision; binary comma is an expression.*/
3843                if (lhs->pn_type != TOK_COMMA || lhs->pn_arity != PN_NULLARY) {
3844                    if (lhs->pn_type == TOK_RB || lhs->pn_type == TOK_RC) {
3845                        if (!UndominateInitializers(lhs, rhs, tc))
3846                            return JS_FALSE;
3847                    } else {
3848                        lhs->pn_pos.end = rhs->pn_pos.end;
3849                    }
3850                }
3851    
3852                lhs = lhs->pn_next;
3853                rhs = rhs->pn_next;
3854            }
3855        } else {
3856            JS_ASSERT(left->pn_type == TOK_RC);
3857            fpvd.numvars = left->pn_count;
3858            fpvd.maxstep = 0;
3859    
3860            while (lhs) {
3861                JS_ASSERT(lhs->pn_type == TOK_COLON);
3862                JSParseNode *pn = lhs->pn_right;
3863    
3864                rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
3865                if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3866                    if (rhs && !UndominateInitializers(pn, rhs, tc))
3867                        return JS_FALSE;
3868                } else {
3869                    if (rhs)
3870                        pn->pn_pos.end = rhs->pn_pos.end;
3871                }
3872    
3873                lhs = lhs->pn_next;
3874            }
3875        }
3876        return JS_TRUE;
3877    }
3878    
3879  static JSParseNode *  static JSParseNode *
3880  DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,  DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
3881                    JSTokenType tt)                    JSTokenType tt)
3882  {  {
3883        JSTokenStream *ts;
3884      JSParseNode *pn;      JSParseNode *pn;
3885    
3886      pn = PrimaryExpr(cx, TS(tc->parseContext), tc, tt, JS_FALSE);      ts = TS(tc->compiler);
3887        ts->flags |= TSF_DESTRUCTURING;
3888        pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
3889        ts->flags &= ~TSF_DESTRUCTURING;
3890      if (!pn)      if (!pn)
3891          return NULL;          return NULL;
3892      if (!CheckDestructuring(cx, data, pn, NULL, tc))      if (!CheckDestructuring(cx, data, pn, NULL, tc))
# Line 2107  Line 3894 
3894      return pn;      return pn;
3895  }  }
3896    
3897  // Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.  /*
3898     * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
3899     * This function assumes the cloned tree is for use in the same statement and
3900     * binding context as the original tree.
3901     */
3902  static JSParseNode *  static JSParseNode *
3903  CloneParseTree(JSContext *cx, JSParseNode *opn, JSTreeContext *tc)  CloneParseTree(JSParseNode *opn, JSTreeContext *tc)
3904  {  {
3905      JSParseNode *pn, *pn2, *opn2;