PageRenderTime 433ms CodeModel.GetById 73ms app.highlight 285ms RepoModel.GetById 57ms app.codeStats 0ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsparse.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 1987 lines | 1451 code | 193 blank | 343 comment | 407 complexity | cb9396026941b3113ddbde18f4e6ddd8 MD5 | raw file

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

   1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
   2 * vim: set ts=8 sw=4 et tw=99:
   3 *
   4 * ***** BEGIN LICENSE BLOCK *****
   5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
   6 *
   7 * The contents of this file are subject to the Mozilla Public License Version
   8 * 1.1 (the "License"); you may not use this file except in compliance with
   9 * the License. You may obtain a copy of the License at
  10 * http://www.mozilla.org/MPL/
  11 *
  12 * Software distributed under the License is distributed on an "AS IS" basis,
  13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14 * for the specific language governing rights and limitations under the
  15 * License.
  16 *
  17 * The Original Code is Mozilla Communicator client code, released
  18 * March 31, 1998.
  19 *
  20 * The Initial Developer of the Original Code is
  21 * Netscape Communications Corporation.
  22 * Portions created by the Initial Developer are Copyright (C) 1998
  23 * the Initial Developer. All Rights Reserved.
  24 *
  25 * Contributor(s):
  26 *
  27 * Alternatively, the contents of this file may be used under the terms of
  28 * either of the GNU General Public License Version 2 or later (the "GPL"),
  29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30 * in which case the provisions of the GPL or the LGPL are applicable instead
  31 * of those above. If you wish to allow use of your version of this file only
  32 * under the terms of either the GPL or the LGPL, and not to allow others to
  33 * use your version of this file under the terms of the MPL, indicate your
  34 * decision by deleting the provisions above and replace them with the notice
  35 * and other provisions required by the GPL or the LGPL. If you do not delete
  36 * the provisions above, a recipient may use your version of this file under
  37 * the terms of any one of the MPL, the GPL or the LGPL.
  38 *
  39 * ***** END LICENSE BLOCK ***** */
  40
  41/*
  42 * JS parser.
  43 *
  44 * This is a recursive-descent parser for the JavaScript language specified by
  45 * "The JavaScript 1.5 Language Specification".  It uses lexical and semantic
  46 * feedback to disambiguate non-LL(1) structures.  It generates trees of nodes
  47 * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
  48 * After tree construction, it rewrites trees to fold constants and evaluate
  49 * compile-time expressions.  Finally, it calls js_EmitTree (see jsemit.h) to
  50 * generate bytecode.
  51 *
  52 * This parser attempts no error recovery.
  53 */
  54#include "jsstddef.h"
  55#include <stdlib.h>
  56#include <string.h>
  57#include <math.h>
  58#include "jstypes.h"
  59#include "jsarena.h" /* Added by JSIFY */
  60#include "jsutil.h" /* Added by JSIFY */
  61#include "jsapi.h"
  62#include "jsarray.h"
  63#include "jsatom.h"
  64#include "jscntxt.h"
  65#include "jsversion.h"
  66#include "jsemit.h"
  67#include "jsfun.h"
  68#include "jsinterp.h"
  69#include "jsiter.h"
  70#include "jslock.h"
  71#include "jsnum.h"
  72#include "jsobj.h"
  73#include "jsopcode.h"
  74#include "jsparse.h"
  75#include "jsscan.h"
  76#include "jsscope.h"
  77#include "jsscript.h"
  78#include "jsstr.h"
  79#include "jsstaticcheck.h"
  80
  81#if JS_HAS_XML_SUPPORT
  82#include "jsxml.h"
  83#endif
  84
  85#if JS_HAS_DESTRUCTURING
  86#include "jsdhash.h"
  87#endif
  88
  89/*
  90 * Asserts to verify assumptions behind pn_ macros.
  91 */
  92JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.atom) ==
  93                 offsetof(JSParseNode, pn_u.apair.atom));
  94JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.slot) ==
  95                 offsetof(JSParseNode, pn_u.lexical.slot));
  96
  97/*
  98 * JS parsers, from lowest to highest precedence.
  99 *
 100 * Each parser takes a context, a token stream, and a tree context struct.
 101 * Each returns a parse node tree or null on error.
 102 */
 103
 104typedef JSParseNode *
 105JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
 106
 107typedef JSParseNode *
 108JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
 109               JSBool allowCallSyntax);
 110
 111typedef JSParseNode *
 112JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
 113                JSTokenType tt, JSBool afterDot);
 114
 115typedef JSParseNode *
 116JSParenParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
 117              JSParseNode *pn1, JSBool *genexp);
 118
 119static JSParser FunctionStmt;
 120static JSParser FunctionExpr;
 121static JSParser Statements;
 122static JSParser Statement;
 123static JSParser Variables;
 124static JSParser Expr;
 125static JSParser AssignExpr;
 126static JSParser CondExpr;
 127static JSParser OrExpr;
 128static JSParser AndExpr;
 129static JSParser BitOrExpr;
 130static JSParser BitXorExpr;
 131static JSParser BitAndExpr;
 132static JSParser EqExpr;
 133static JSParser RelExpr;
 134static JSParser ShiftExpr;
 135static JSParser AddExpr;
 136static JSParser MulExpr;
 137static JSParser UnaryExpr;
 138static JSMemberParser  MemberExpr;
 139static JSPrimaryParser PrimaryExpr;
 140static JSParenParser   ParenExpr;
 141
 142/*
 143 * Insist that the next token be of type tt, or report errno and return null.
 144 * NB: this macro uses cx and ts from its lexical environment.
 145 */
 146#define MUST_MATCH_TOKEN(tt, errno)                                           \
 147    JS_BEGIN_MACRO                                                            \
 148        if (js_GetToken(cx, ts) != tt) {                                      \
 149            js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
 150            return NULL;                                                      \
 151        }                                                                     \
 152    JS_END_MACRO
 153
 154#ifdef METER_PARSENODES
 155static uint32 parsenodes = 0;
 156static uint32 maxparsenodes = 0;
 157static uint32 recyclednodes = 0;
 158#endif
 159
 160JSBool
 161js_InitParseContext(JSContext *cx, JSParseContext *pc, JSPrincipals *principals,
 162                    JSStackFrame *callerFrame,
 163                    const jschar *base, size_t length,
 164                    FILE *fp, const char *filename, uintN lineno)
 165{
 166    JS_ASSERT_IF(callerFrame, callerFrame->script);
 167
 168    pc->tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
 169    if (!js_InitTokenStream(cx, TS(pc), base, length, fp, filename, lineno)) {
 170        JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark);
 171        return JS_FALSE;
 172    }
 173    if (principals)
 174        JSPRINCIPALS_HOLD(cx, principals);
 175    pc->principals = principals;
 176    pc->callerFrame = callerFrame;
 177    pc->nodeList = NULL;
 178    pc->traceListHead = NULL;
 179
 180    /* Root atoms and objects allocated for the parsed tree. */
 181    JS_KEEP_ATOMS(cx->runtime);
 182    JS_PUSH_TEMP_ROOT_PARSE_CONTEXT(cx, pc, &pc->tempRoot);
 183    return JS_TRUE;
 184}
 185
 186void
 187js_FinishParseContext(JSContext *cx, JSParseContext *pc)
 188{
 189    if (pc->principals)
 190        JSPRINCIPALS_DROP(cx, pc->principals);
 191    JS_ASSERT(pc->tempRoot.u.parseContext == pc);
 192    JS_POP_TEMP_ROOT(cx, &pc->tempRoot);
 193    JS_UNKEEP_ATOMS(cx->runtime);
 194    js_CloseTokenStream(cx, TS(pc));
 195    JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark);
 196}
 197
 198void
 199js_InitCompilePrincipals(JSContext *cx, JSParseContext *pc,
 200                         JSPrincipals *principals)
 201{
 202    JS_ASSERT(!pc->principals);
 203    if (principals)
 204        JSPRINCIPALS_HOLD(cx, principals);
 205    pc->principals = principals;
 206}
 207
 208JSParsedObjectBox *
 209js_NewParsedObjectBox(JSContext *cx, JSParseContext *pc, JSObject *obj)
 210{
 211    JSParsedObjectBox *pob;
 212
 213    /*
 214     * We use JSContext.tempPool to allocate parsed objects and place them on
 215     * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
 216     * containing the entries must be alive until we are done with scanning,
 217     * parsing and code generation for the whole script or top-level function.
 218     */
 219    JS_ASSERT(obj);
 220    JS_ARENA_ALLOCATE_TYPE(pob, JSParsedObjectBox, &cx->tempPool);
 221    if (!pob) {
 222        js_ReportOutOfScriptQuota(cx);
 223        return NULL;
 224    }
 225    pob->traceLink = pc->traceListHead;
 226    pob->emitLink = NULL;
 227    pob->object = obj;
 228    pc->traceListHead = pob;
 229    return pob;
 230}
 231
 232
 233void
 234js_TraceParseContext(JSTracer *trc, JSParseContext *pc)
 235{
 236    JSParsedObjectBox *pob;
 237
 238    JS_ASSERT(pc->tempRoot.u.parseContext == pc);
 239    pob = pc->traceListHead;
 240    while (pob) {
 241        JS_CALL_OBJECT_TRACER(trc, pob->object, "parser.object");
 242        pob = pob->traceLink;
 243    }
 244}
 245
 246static JSParseNode *
 247RecycleTree(JSParseNode *pn, JSTreeContext *tc)
 248{
 249    JSParseNode *next;
 250
 251    if (!pn)
 252        return NULL;
 253
 254    /* Catch back-to-back dup recycles. */
 255    JS_ASSERT(pn != tc->parseContext->nodeList);
 256    next = pn->pn_next;
 257    pn->pn_next = tc->parseContext->nodeList;
 258    tc->parseContext->nodeList = pn;
 259#ifdef METER_PARSENODES
 260    recyclednodes++;
 261#endif
 262    return next;
 263}
 264
 265static JSParseNode *
 266NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
 267{
 268    JSParseNode *pn;
 269
 270    pn = tc->parseContext->nodeList;
 271    if (!pn) {
 272        JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
 273        if (!pn)
 274            js_ReportOutOfScriptQuota(cx);
 275    } else {
 276        tc->parseContext->nodeList = pn->pn_next;
 277
 278        /* Recycle immediate descendents only, to save work and working set. */
 279        switch (pn->pn_arity) {
 280          case PN_FUNC:
 281            RecycleTree(pn->pn_body, tc);
 282            break;
 283          case PN_LIST:
 284            if (pn->pn_head) {
 285                /* XXX check for dup recycles in the list */
 286                *pn->pn_tail = tc->parseContext->nodeList;
 287                tc->parseContext->nodeList = pn->pn_head;
 288#ifdef METER_PARSENODES
 289                recyclednodes += pn->pn_count;
 290#endif
 291            }
 292            break;
 293          case PN_TERNARY:
 294            RecycleTree(pn->pn_kid1, tc);
 295            RecycleTree(pn->pn_kid2, tc);
 296            RecycleTree(pn->pn_kid3, tc);
 297            break;
 298          case PN_BINARY:
 299            if (pn->pn_left != pn->pn_right)
 300                RecycleTree(pn->pn_left, tc);
 301            RecycleTree(pn->pn_right, tc);
 302            break;
 303          case PN_UNARY:
 304            RecycleTree(pn->pn_kid, tc);
 305            break;
 306          case PN_NAME:
 307            RecycleTree(pn->pn_expr, tc);
 308            break;
 309          case PN_NULLARY:
 310            break;
 311        }
 312    }
 313    if (pn) {
 314#ifdef METER_PARSENODES
 315        parsenodes++;
 316        if (parsenodes - recyclednodes > maxparsenodes)
 317            maxparsenodes = parsenodes - recyclednodes;
 318#endif
 319        memset(&pn->pn_u, 0, sizeof pn->pn_u);
 320        pn->pn_next = NULL;
 321    }
 322    return pn;
 323}
 324
 325/*
 326 * Allocate a JSParseNode from cx's temporary arena.
 327 */
 328static JSParseNode *
 329NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity,
 330             JSTreeContext *tc)
 331{
 332    JSParseNode *pn;
 333    JSToken *tp;
 334
 335    pn = NewOrRecycledNode(cx, tc);
 336    if (!pn)
 337        return NULL;
 338    tp = &CURRENT_TOKEN(ts);
 339    pn->pn_type = tp->type;
 340    pn->pn_pos = tp->pos;
 341    pn->pn_op = JSOP_NOP;
 342    pn->pn_arity = arity;
 343    return pn;
 344}
 345
 346static JSParseNode *
 347NewBinary(JSContext *cx, JSTokenType tt,
 348          JSOp op, JSParseNode *left, JSParseNode *right,
 349          JSTreeContext *tc)
 350{
 351    JSParseNode *pn, *pn1, *pn2;
 352
 353    if (!left || !right)
 354        return NULL;
 355
 356    /*
 357     * Flatten a left-associative (left-heavy) tree of a given operator into
 358     * a list, to reduce js_FoldConstants and js_EmitTree recursion.
 359     */
 360    if (left->pn_type == tt &&
 361        left->pn_op == op &&
 362        (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
 363        if (left->pn_arity != PN_LIST) {
 364            pn1 = left->pn_left, pn2 = left->pn_right;
 365            left->pn_arity = PN_LIST;
 366            PN_INIT_LIST_1(left, pn1);
 367            PN_APPEND(left, pn2);
 368            if (tt == TOK_PLUS) {
 369                if (pn1->pn_type == TOK_STRING)
 370                    left->pn_extra |= PNX_STRCAT;
 371                else if (pn1->pn_type != TOK_NUMBER)
 372                    left->pn_extra |= PNX_CANTFOLD;
 373                if (pn2->pn_type == TOK_STRING)
 374                    left->pn_extra |= PNX_STRCAT;
 375                else if (pn2->pn_type != TOK_NUMBER)
 376                    left->pn_extra |= PNX_CANTFOLD;
 377            }
 378        }
 379        PN_APPEND(left, right);
 380        left->pn_pos.end = right->pn_pos.end;
 381        if (tt == TOK_PLUS) {
 382            if (right->pn_type == TOK_STRING)
 383                left->pn_extra |= PNX_STRCAT;
 384            else if (right->pn_type != TOK_NUMBER)
 385                left->pn_extra |= PNX_CANTFOLD;
 386        }
 387        return left;
 388    }
 389
 390    /*
 391     * Fold constant addition immediately, to conserve node space and, what's
 392     * more, so js_FoldConstants never sees mixed addition and concatenation
 393     * operations with more than one leading non-string operand in a PN_LIST
 394     * generated for expressions such as 1 + 2 + "pt" (which should evaluate
 395     * to "3pt", not "12pt").
 396     */
 397    if (tt == TOK_PLUS &&
 398        left->pn_type == TOK_NUMBER &&
 399        right->pn_type == TOK_NUMBER) {
 400        left->pn_dval += right->pn_dval;
 401        left->pn_pos.end = right->pn_pos.end;
 402        RecycleTree(right, tc);
 403        return left;
 404    }
 405
 406    pn = NewOrRecycledNode(cx, tc);
 407    if (!pn)
 408        return NULL;
 409    pn->pn_type = tt;
 410    pn->pn_pos.begin = left->pn_pos.begin;
 411    pn->pn_pos.end = right->pn_pos.end;
 412    pn->pn_op = op;
 413    pn->pn_arity = PN_BINARY;
 414    pn->pn_left = left;
 415    pn->pn_right = right;
 416    return pn;
 417}
 418
 419#if JS_HAS_GETTER_SETTER
 420static JSTokenType
 421CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
 422{
 423    JSAtom *atom;
 424    JSRuntime *rt;
 425    JSOp op;
 426    const char *name;
 427
 428    JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
 429    atom = CURRENT_TOKEN(ts).t_atom;
 430    rt = cx->runtime;
 431    if (atom == rt->atomState.getterAtom)
 432        op = JSOP_GETTER;
 433    else if (atom == rt->atomState.setterAtom)
 434        op = JSOP_SETTER;
 435    else
 436        return TOK_NAME;
 437    if (js_PeekTokenSameLine(cx, ts) != tt)
 438        return TOK_NAME;
 439    (void) js_GetToken(cx, ts);
 440    if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
 441        js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
 442                                    JSMSG_BAD_GETTER_OR_SETTER,
 443                                    (op == JSOP_GETTER)
 444                                    ? js_getter_str
 445                                    : js_setter_str);
 446        return TOK_ERROR;
 447    }
 448    CURRENT_TOKEN(ts).t_op = op;
 449    if (JS_HAS_STRICT_OPTION(cx)) {
 450        name = js_AtomToPrintableString(cx, atom);
 451        if (!name ||
 452            !js_ReportCompileErrorNumber(cx, ts, NULL,
 453                                         JSREPORT_WARNING | JSREPORT_STRICT,
 454                                         JSMSG_DEPRECATED_USAGE,
 455                                         name)) {
 456            return TOK_ERROR;
 457        }
 458    }
 459    return tt;
 460}
 461#endif
 462
 463/*
 464 * Parse a top-level JS script.
 465 */
 466JSParseNode *
 467js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc)
 468{
 469    JSTreeContext tc;
 470    JSParseNode *pn;
 471
 472    /*
 473     * Protect atoms from being collected by a GC activation, which might
 474     * - nest on this thread due to out of memory (the so-called "last ditch"
 475     *   GC attempted within js_NewGCThing), or
 476     * - run for any reason on another thread if this thread is suspended on
 477     *   an object lock before it finishes generating bytecode into a script
 478     *   protected from the GC by a root or a stack frame reference.
 479     */
 480    TREE_CONTEXT_INIT(&tc, pc);
 481    tc.u.scopeChain = chain;
 482    pn = Statements(cx, TS(pc), &tc);
 483    if (pn) {
 484        if (!js_MatchToken(cx, TS(pc), TOK_EOF)) {
 485            js_ReportCompileErrorNumber(cx, TS(pc), NULL, JSREPORT_ERROR,
 486                                        JSMSG_SYNTAX_ERROR);
 487            pn = NULL;
 488        } else {
 489            pn->pn_type = TOK_LC;
 490            if (!js_FoldConstants(cx, pn, &tc))
 491                pn = NULL;
 492        }
 493    }
 494
 495    TREE_CONTEXT_FINISH(cx, &tc);
 496    return pn;
 497}
 498
 499/*
 500 * Compile a top-level script.
 501 */
 502extern JSScript *
 503js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
 504                 JSPrincipals *principals, uint32 tcflags,
 505                 const jschar *chars, size_t length,
 506                 FILE *file, const char *filename, uintN lineno)
 507{
 508    JSParseContext pc;
 509    JSArenaPool codePool, notePool;
 510    JSCodeGenerator cg;
 511    JSTokenType tt;
 512    JSParseNode *pn;
 513    uint32 scriptGlobals;
 514    JSScript *script;
 515#ifdef METER_PARSENODES
 516    void *sbrk(ptrdiff_t), *before = sbrk(0);
 517#endif
 518
 519    JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL |
 520                            TCF_STATIC_DEPTH_MASK)));
 521
 522    /*
 523     * The scripted callerFrame can only be given for compile-and-go scripts
 524     * and non-zero static depth requires callerFrame.
 525     */
 526    JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
 527    JS_ASSERT_IF(TCF_GET_STATIC_DEPTH(tcflags) != 0, callerFrame);
 528
 529    if (!js_InitParseContext(cx, &pc, principals, callerFrame, chars, length,
 530                             file, filename, lineno)) {
 531        return NULL;
 532    }
 533
 534    JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
 535                       &cx->scriptStackQuota);
 536    JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
 537                       &cx->scriptStackQuota);
 538    js_InitCodeGenerator(cx, &cg, &pc, &codePool, &notePool,
 539                         pc.tokenStream.lineno);
 540
 541    MUST_FLOW_THROUGH("out");
 542    cg.treeContext.flags |= (uint16) tcflags;
 543    cg.treeContext.u.scopeChain = scopeChain;
 544    cg.staticDepth = TCF_GET_STATIC_DEPTH(tcflags);
 545
 546    if ((tcflags & TCF_COMPILE_N_GO) && callerFrame && callerFrame->fun) {
 547        /*
 548         * An eval script in a caller frame needs to have its enclosing function
 549         * captured in case it uses an upvar reference, and someone wishes to
 550         * decompile it while running.
 551         */
 552        JSParsedObjectBox *pob = js_NewParsedObjectBox(cx, &pc, callerFrame->callee);
 553        pob->emitLink = cg.objectList.lastPob;
 554        cg.objectList.lastPob = pob;
 555        cg.objectList.length++;
 556    }
 557
 558    /* Inline Statements() to emit as we go to save space. */
 559    for (;;) {
 560        pc.tokenStream.flags |= TSF_OPERAND;
 561        tt = js_PeekToken(cx, &pc.tokenStream);
 562        pc.tokenStream.flags &= ~TSF_OPERAND;
 563        if (tt <= TOK_EOF) {
 564            if (tt == TOK_EOF)
 565                break;
 566            JS_ASSERT(tt == TOK_ERROR);
 567            script = NULL;
 568            goto out;
 569        }
 570
 571        pn = Statement(cx, &pc.tokenStream, &cg.treeContext);
 572        if (!pn) {
 573            script = NULL;
 574            goto out;
 575        }
 576        JS_ASSERT(!cg.treeContext.blockNode);
 577
 578        if (!js_FoldConstants(cx, pn, &cg.treeContext) ||
 579            !js_EmitTree(cx, &cg, pn)) {
 580            script = NULL;
 581            goto out;
 582        }
 583        RecycleTree(pn, &cg.treeContext);
 584    }
 585
 586    /*
 587     * Global variables and regexps shares the index space with locals. Due to
 588     * incremental code generation we need to patch the bytecode to adjust the
 589     * local references to skip the globals.
 590     */
 591    scriptGlobals = cg.treeContext.ngvars + cg.regexpList.length;
 592    if (scriptGlobals != 0) {
 593        jsbytecode *code, *end;
 594        JSOp op;
 595        const JSCodeSpec *cs;
 596        uintN len, slot;
 597
 598        if (scriptGlobals >= SLOTNO_LIMIT)
 599            goto too_many_slots;
 600        code = CG_BASE(&cg);
 601        for (end = code + CG_OFFSET(&cg); code != end; code += len) {
 602            JS_ASSERT(code < end);
 603            op = (JSOp) *code;
 604            cs = &js_CodeSpec[op];
 605            len = (cs->length > 0)
 606                  ? (uintN) cs->length
 607                  : js_GetVariableBytecodeLength(code);
 608            if (JOF_TYPE(cs->format) == JOF_LOCAL ||
 609                (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
 610                /*
 611                 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
 612                 * emitted only for a function.
 613                 */
 614                JS_ASSERT((JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
 615                          (op == JSOP_GETLOCALPROP));
 616                slot = GET_SLOTNO(code);
 617                slot += scriptGlobals;
 618                if (slot >= SLOTNO_LIMIT)
 619                    goto too_many_slots;
 620                SET_SLOTNO(code, slot);
 621            }
 622        }
 623    }
 624
 625#ifdef METER_PARSENODES
 626    printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
 627           (char *)sbrk(0) - (char *)before,
 628           parsenodes,
 629           maxparsenodes,
 630           parsenodes - recyclednodes);
 631    before = sbrk(0);
 632#endif
 633
 634    /*
 635     * Nowadays the threaded interpreter needs a stop instruction, so we
 636     * do have to emit that here.
 637     */
 638    if (js_Emit1(cx, &cg, JSOP_STOP) < 0) {
 639        script = NULL;
 640        goto out;
 641    }
 642#ifdef METER_PARSENODES
 643    printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
 644           (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
 645#endif
 646#ifdef JS_ARENAMETER
 647    JS_DumpArenaStats(stdout);
 648#endif
 649    script = js_NewScriptFromCG(cx, &cg);
 650
 651#ifdef JS_SCOPE_DEPTH_METER
 652    if (script) {
 653        JSObject *obj = scopeChain;
 654        uintN depth = 1;
 655        while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
 656            ++depth;
 657        JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
 658    }
 659#endif
 660
 661  out:
 662    js_FinishCodeGenerator(cx, &cg);
 663    JS_FinishArenaPool(&codePool);
 664    JS_FinishArenaPool(&notePool);
 665    js_FinishParseContext(cx, &pc);
 666    return script;
 667
 668  too_many_slots:
 669    js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
 670                                JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
 671    script = NULL;
 672    goto out;
 673}
 674
 675/*
 676 * Insist on a final return before control flows out of pn.  Try to be a bit
 677 * smart about loops: do {...; return e2;} while(0) at the end of a function
 678 * that contains an early return e1 will get a strict warning.  Similarly for
 679 * iloops: while (true){...} is treated as though ... returns.
 680 */
 681#define ENDS_IN_OTHER   0
 682#define ENDS_IN_RETURN  1
 683#define ENDS_IN_BREAK   2
 684
 685static int
 686HasFinalReturn(JSParseNode *pn)
 687{
 688    JSParseNode *pn2, *pn3;
 689    uintN rv, rv2, hasDefault;
 690
 691    switch (pn->pn_type) {
 692      case TOK_LC:
 693        if (!pn->pn_head)
 694            return ENDS_IN_OTHER;
 695        return HasFinalReturn(PN_LAST(pn));
 696
 697      case TOK_IF:
 698        if (!pn->pn_kid3)
 699            return ENDS_IN_OTHER;
 700        return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
 701
 702      case TOK_WHILE:
 703        pn2 = pn->pn_left;
 704        if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
 705            return ENDS_IN_RETURN;
 706        if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
 707            return ENDS_IN_RETURN;
 708        return ENDS_IN_OTHER;
 709
 710      case TOK_DO:
 711        pn2 = pn->pn_right;
 712        if (pn2->pn_type == TOK_PRIMARY) {
 713            if (pn2->pn_op == JSOP_FALSE)
 714                return HasFinalReturn(pn->pn_left);
 715            if (pn2->pn_op == JSOP_TRUE)
 716                return ENDS_IN_RETURN;
 717        }
 718        if (pn2->pn_type == TOK_NUMBER) {
 719            if (pn2->pn_dval == 0)
 720                return HasFinalReturn(pn->pn_left);
 721            return ENDS_IN_RETURN;
 722        }
 723        return ENDS_IN_OTHER;
 724
 725      case TOK_FOR:
 726        pn2 = pn->pn_left;
 727        if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
 728            return ENDS_IN_RETURN;
 729        return ENDS_IN_OTHER;
 730
 731      case TOK_SWITCH:
 732        rv = ENDS_IN_RETURN;
 733        hasDefault = ENDS_IN_OTHER;
 734        pn2 = pn->pn_right;
 735        if (pn2->pn_type == TOK_LEXICALSCOPE)
 736            pn2 = pn2->pn_expr;
 737        for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
 738            if (pn2->pn_type == TOK_DEFAULT)
 739                hasDefault = ENDS_IN_RETURN;
 740            pn3 = pn2->pn_right;
 741            JS_ASSERT(pn3->pn_type == TOK_LC);
 742            if (pn3->pn_head) {
 743                rv2 = HasFinalReturn(PN_LAST(pn3));
 744                if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
 745                    /* Falling through to next case or default. */;
 746                else
 747                    rv &= rv2;
 748            }
 749        }
 750        /* If a final switch has no default case, we judge it harshly. */
 751        rv &= hasDefault;
 752        return rv;
 753
 754      case TOK_BREAK:
 755        return ENDS_IN_BREAK;
 756
 757      case TOK_WITH:
 758        return HasFinalReturn(pn->pn_right);
 759
 760      case TOK_RETURN:
 761        return ENDS_IN_RETURN;
 762
 763      case TOK_COLON:
 764      case TOK_LEXICALSCOPE:
 765        return HasFinalReturn(pn->pn_expr);
 766
 767      case TOK_THROW:
 768        return ENDS_IN_RETURN;
 769
 770      case TOK_TRY:
 771        /* If we have a finally block that returns, we are done. */
 772        if (pn->pn_kid3) {
 773            rv = HasFinalReturn(pn->pn_kid3);
 774            if (rv == ENDS_IN_RETURN)
 775                return rv;
 776        }
 777
 778        /* Else check the try block and any and all catch statements. */
 779        rv = HasFinalReturn(pn->pn_kid1);
 780        if (pn->pn_kid2) {
 781            JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
 782            for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
 783                rv &= HasFinalReturn(pn2);
 784        }
 785        return rv;
 786
 787      case TOK_CATCH:
 788        /* Check this catch block's body. */
 789        return HasFinalReturn(pn->pn_kid3);
 790
 791      case TOK_LET:
 792        /* Non-binary let statements are let declarations. */
 793        if (pn->pn_arity != PN_BINARY)
 794            return ENDS_IN_OTHER;
 795        return HasFinalReturn(pn->pn_right);
 796
 797      default:
 798        return ENDS_IN_OTHER;
 799    }
 800}
 801
 802static JSBool
 803ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
 804                uintN anonerrnum)
 805{
 806    const char *name;
 807
 808    JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
 809    if (tc->u.fun->atom) {
 810        name = js_AtomToPrintableString(cx, tc->u.fun->atom);
 811    } else {
 812        errnum = anonerrnum;
 813        name = NULL;
 814    }
 815    return js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, flags,
 816                                       errnum, name);
 817}
 818
 819static JSBool
 820CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
 821{
 822    JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
 823    return HasFinalReturn(pn) == ENDS_IN_RETURN ||
 824           ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
 825                           JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
 826}
 827
 828static JSParseNode *
 829FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 830{
 831    JSStmtInfo stmtInfo;
 832    uintN oldflags, firstLine;
 833    JSParseNode *pn;
 834
 835    JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
 836    js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
 837    stmtInfo.flags = SIF_BODY_BLOCK;
 838
 839    oldflags = tc->flags;
 840    tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
 841
 842    /*
 843     * Save the body's first line, and store it in pn->pn_pos.begin.lineno
 844     * later, because we may have not peeked in ts yet, so Statements won't
 845     * acquire a valid pn->pn_pos.begin from the current token.
 846     */
 847    firstLine = ts->lineno;
 848#if JS_HAS_EXPR_CLOSURES
 849    if (CURRENT_TOKEN(ts).type == TOK_LC) {
 850        pn = Statements(cx, ts, tc);
 851    } else {
 852        pn = NewParseNode(cx, ts, PN_UNARY, tc);
 853        if (pn) {
 854            pn->pn_kid = AssignExpr(cx, ts, tc);
 855            if (!pn->pn_kid) {
 856                pn = NULL;
 857            } else {
 858                if (tc->flags & TCF_FUN_IS_GENERATOR) {
 859                    ReportBadReturn(cx, tc, JSREPORT_ERROR,
 860                                    JSMSG_BAD_GENERATOR_RETURN,
 861                                    JSMSG_BAD_ANON_GENERATOR_RETURN);
 862                    pn = NULL;
 863                } else {
 864                    pn->pn_type = TOK_RETURN;
 865                    pn->pn_op = JSOP_RETURN;
 866                    pn->pn_pos.end = pn->pn_kid->pn_pos.end;
 867                }
 868            }
 869        }
 870    }
 871#else
 872    pn = Statements(cx, ts, tc);
 873#endif
 874
 875    if (pn) {
 876        js_PopStatement(tc);
 877        pn->pn_pos.begin.lineno = firstLine;
 878
 879        /* Check for falling off the end of a function that returns a value. */
 880        if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) &&
 881            !CheckFinalReturn(cx, tc, pn)) {
 882            pn = NULL;
 883        }
 884    }
 885
 886    tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));
 887    return pn;
 888}
 889
 890/*
 891 * Compile a JS function body, which might appear as the value of an event
 892 * handler attribute in an HTML <INPUT> tag.
 893 */
 894JSBool
 895js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
 896                       const jschar *chars, size_t length,
 897                       const char *filename, uintN lineno)
 898{
 899    JSParseContext pc;
 900    JSArenaPool codePool, notePool;
 901    JSCodeGenerator funcg;
 902    JSParseNode *pn;
 903
 904    if (!js_InitParseContext(cx, &pc, principals, NULL, chars, length, NULL,
 905                             filename, lineno)) {
 906        return JS_FALSE;
 907    }
 908
 909    /* No early return from this point until js_FinishParseContext call. */
 910    JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
 911                       &cx->scriptStackQuota);
 912    JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
 913                       &cx->scriptStackQuota);
 914    js_InitCodeGenerator(cx, &funcg, &pc, &codePool, &notePool,
 915                         pc.tokenStream.lineno);
 916    funcg.treeContext.flags |= TCF_IN_FUNCTION;
 917    funcg.treeContext.u.fun = fun;
 918
 919    /*
 920     * Farble the body so that it looks like a block statement to js_EmitTree,
 921     * which is called beneath FunctionBody; see Statements, further below in
 922     * this file.  FunctionBody pushes a STMT_BLOCK record around its call to
 923     * Statements, so Statements will not compile each statement as it loops
 924     * to save JSParseNode space -- it will not compile at all, only build a
 925     * JSParseNode tree.
 926     *
 927     * Therefore we must fold constants, allocate try notes, and generate code
 928     * for this function, including a stop opcode at the end.
 929     */
 930    CURRENT_TOKEN(&pc.tokenStream).type = TOK_LC;
 931    pn = FunctionBody(cx, &pc.tokenStream, &funcg.treeContext);
 932    if (pn) {
 933        if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) {
 934            js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
 935                                        JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
 936            pn = NULL;
 937        } else {
 938            if (!js_FoldConstants(cx, pn, &funcg.treeContext) ||
 939                !js_EmitFunctionScript(cx, &funcg, pn)) {
 940                pn = NULL;
 941            }
 942        }
 943    }
 944
 945    /* Restore saved state and release code generation arenas. */
 946    js_FinishCodeGenerator(cx, &funcg);
 947    JS_FinishArenaPool(&codePool);
 948    JS_FinishArenaPool(&notePool);
 949    js_FinishParseContext(cx, &pc);
 950    return pn != NULL;
 951}
 952
 953/*
 954 * Parameter block types for the several Binder functions.  We use a common
 955 * helper function signature in order to share code among destructuring and
 956 * simple variable declaration parsers.  In the destructuring case, the binder
 957 * function is called indirectly from the variable declaration parser by way
 958 * of CheckDestructuring and its friends.
 959 */
 960typedef struct BindData BindData;
 961
 962typedef JSBool
 963(*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
 964
 965struct BindData {
 966    JSParseNode             *pn;                /* error source coordinate */
 967    JSOp                    op;                 /* prolog bytecode or nop */
 968    Binder                  binder;             /* binder, discriminates u */
 969    union {
 970        struct {
 971            uintN           overflow;
 972        } let;
 973    } u;
 974};
 975
 976static JSBool
 977BindArg(JSContext *cx, JSAtom *atom, JSTreeContext *tc)
 978{
 979    const char *name;
 980
 981    /*
 982     * Check for a duplicate parameter name, a "feature" required by ECMA-262.
 983     */
 984    JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
 985    if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {
 986        name = js_AtomToPrintableString(cx, atom);
 987        if (!name ||
 988            !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL,
 989                                         JSREPORT_WARNING | JSREPORT_STRICT,
 990                                         JSMSG_DUPLICATE_FORMAL,
 991                                         name)) {
 992            return JS_FALSE;
 993        }
 994    }
 995
 996    return js_AddLocal(cx, tc->u.fun, atom, JSLOCAL_ARG);
 997}
 998
 999static JSBool
1000BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
1001                  JSLocalKind localKind)
1002{
1003    JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1004
1005    /*
1006     * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1007     * Instead 'var arguments' always restates the predefined property of the
1008     * activation objects with unhidden name 'arguments'.  Assignment to such
1009     * a variable must be handled specially.
1010     */
1011    if (atom == cx->runtime->atomState.argumentsAtom)
1012        return JS_TRUE;
1013
1014    return js_AddLocal(cx, fun, atom, localKind);
1015}
1016
1017#if JS_HAS_DESTRUCTURING
1018/*
1019 * Forward declaration to maintain top-down presentation.
1020 */
1021static JSParseNode *
1022DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
1023                  JSTokenType tt);
1024
1025static JSBool
1026BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
1027                     JSTreeContext *tc)
1028{
1029    JSAtomListElement *ale;
1030    const char *name;
1031
1032    JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1033    ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1034    if (!ale) {
1035        ale = js_IndexAtom(cx, atom, &tc->decls);
1036        if (!ale)
1037            return JS_FALSE;
1038        ALE_SET_JSOP(ale, data->op);
1039    }
1040
1041    if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {
1042        name = js_AtomToPrintableString(cx, atom);
1043        if (!name ||
1044            !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1045                                         JSREPORT_WARNING | JSREPORT_STRICT,
1046                                         JSMSG_DUPLICATE_FORMAL,
1047                                         name)) {
1048            return JS_FALSE;
1049        }
1050    } else {
1051        if (!BindLocalVariable(cx, tc->u.fun, atom, JSLOCAL_VAR))
1052            return JS_FALSE;
1053    }
1054    return JS_TRUE;
1055}
1056#endif /* JS_HAS_DESTRUCTURING */
1057
1058static JSFunction *
1059NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom,
1060                    uintN lambda)
1061{
1062    JSObject *parent;
1063    JSFunction *fun;
1064
1065    JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1066    parent = (tc->flags & TCF_IN_FUNCTION)
1067             ? FUN_OBJECT(tc->u.fun)
1068             : tc->u.scopeChain;
1069    fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1070                         parent, atom);
1071    if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
1072        STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1073        STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
1074    }
1075    return fun;
1076}
1077
1078static JSParseNode *
1079FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
1080            uintN lambda)
1081{
1082    JSOp op, prevop;
1083    JSParseNode *pn, *body, *result;
1084    JSTokenType tt;
1085    JSAtom *funAtom;
1086    JSParsedObjectBox *funpob;
1087    JSAtomListElement *ale;
1088    JSFunction *fun;
1089    JSTreeContext funtc;
1090#if JS_HAS_DESTRUCTURING
1091    JSParseNode *item, *list = NULL;
1092#endif
1093
1094    /* Make a TOK_FUNCTION node. */
1095#if JS_HAS_GETTER_SETTER
1096    op = CURRENT_TOKEN(ts).t_op;
1097#endif
1098    pn = NewParseNode(cx, ts, PN_FUNC, tc);
1099    if (!pn)
1100        return NULL;
1101#ifdef DEBUG
1102    pn->pn_index = (uint32) -1;
1103#endif
1104
1105    /* Scan the optional function name into funAtom. */
1106    ts->flags |= TSF_KEYWORD_IS_NAME;
1107    tt = js_GetToken(cx, ts);
1108    ts->flags &= ~TSF_KEYWORD_IS_NAME;
1109    if (tt == TOK_NAME) {
1110        funAtom = CURRENT_TOKEN(ts).t_atom;
1111    } else {
1112        if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) {
1113            js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1114                                        JSMSG_SYNTAX_ERROR);
1115            return NULL;
1116        }
1117        funAtom = NULL;
1118        js_UngetToken(ts);
1119    }
1120
1121    /*
1122     * Record names for function statements in tc->decls so we know when to
1123     * avoid optimizing variable references that might name a function.
1124     */
1125    if (lambda == 0 && funAtom) {
1126        ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
1127        if (ale) {
1128            prevop = ALE_JSOP(ale);
1129            if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
1130                const char *name = js_AtomToPrintableString(cx, funAtom);
1131                if (!name ||
1132                    !js_ReportCompileErrorNumber(cx, ts, NULL,
1133                                                 (prevop != JSOP_DEFCONST)
1134                                                 ? JSREPORT_WARNING |
1135                                                   JSREPORT_STRICT
1136                                                 : JSREPORT_ERROR,
1137                                                 JSMSG_REDECLARED_VAR,
1138                                                 (prevop == JSOP_DEFFUN)
1139                                                 ? js_function_str
1140                                                 : (prevop == JSOP_DEFCONST)
1141                                                 ? js_const_str
1142                                                 : js_var_str,
1143                                                 name)) {
1144                    return NULL;
1145                }
1146            }
1147            if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR)
1148                tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
1149        } else {
1150            ale = js_IndexAtom(cx, funAtom, &tc->decls);
1151            if (!ale)
1152                return NULL;
1153        }
1154        ALE_SET_JSOP(ale, JSOP_DEFFUN);
1155
1156        /*
1157         * A function nested at top level inside another's body needs only a
1158         * local variable to bind its name to its value, and not an activation
1159         * object property (it might also need the activation property, if the
1160         * outer function contains with statements, e.g., but the stack slot
1161         * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
1162         * JSOP_GETLOCAL bytecode).
1163         */
1164        if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) {
1165            JSLocalKind localKind;
1166
1167            /*
1168             * Define a property on the outer function so that BindNameToSlot
1169             * can properly optimize accesses. Note that we need a variable,
1170             * not an argument, for the function statement. Thus we add a
1171             * variable even if the parameter with the given name already
1172             * exists.
1173             */
1174            localKind = js_LookupLocal(cx, tc->u.fun, funAtom, NULL);
1175            if (localKind == JSLOCAL_NONE || localKind == JSLOCAL_ARG) {
1176                if (!js_AddLocal(cx, tc->u.fun, funAtom, JSLOCAL_VAR))
1177                    return NULL;
1178            }
1179        }
1180    }
1181
1182    fun = NewCompilerFunction(cx, tc, funAtom, lambda);
1183    if (!fun)
1184        return NULL;
1185
1186#if JS_HAS_GETTER_SETTER
1187    if (op != JSOP_NOP)
1188        fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
1189#endif
1190
1191    /*
1192     * Create wrapping box for fun->object early to protect against a
1193     * last-ditch GC.
1194     */
1195    funpob = js_NewParsedObjectBox(cx, tc->parseContext, FUN_OBJECT(fun));
1196    if (!funpob)
1197        return NULL;
1198
1199    /* Initialize early for possible flags mutation via DestructuringExpr. */
1200    TREE_CONTEXT_INIT(&funtc, tc->parseContext);
1201    funtc.flags |= TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO);
1202    funtc.u.fun = fun;
1203
1204    /* Now parse formal argument list and compute fun->nargs. */
1205    MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
1206    if (!js_MatchToken(cx, ts, TOK_RP)) {
1207        do {
1208            tt = js_GetToken(cx, ts);
1209            switch (tt) {
1210#if JS_HAS_DESTRUCTURING
1211              case TOK_LB:
1212              case TOK_LC:
1213              {
1214                BindData data;
1215                JSParseNode *lhs, *rhs;
1216                jsint slot;
1217
1218                /*
1219                 * A destructuring formal parameter turns into one or more
1220                 * local variables initialized from properties of a single
1221                 * anonymous positional parameter, so here we must tweak our
1222                 * binder and its data.
1223                 */
1224                data.pn = NULL;
1225                data.op = JSOP_DEFVAR;
1226                data.binder = BindDestructuringArg;
1227                lhs = DestructuringExpr(cx, &data, &funtc, tt);
1228                if (!lhs)
1229                    return NULL;
1230
1231                /*
1232                 * Adjust fun->nargs to count the single anonymous positional
1233                 * parameter that is to be destructured.
1234                 */
1235                slot = fun->nargs;
1236                if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG))
1237                    return NULL;
1238
1239                /*
1240                 * Synthesize a destructuring assignment from the single
1241                 * anonymous positional parameter into the destructuring
1242                 * left-hand-side expression and accumulate it in list.
1243                 */
1244                rhs = NewParseNode(cx, ts, PN_NAME, tc);
1245                if (!rhs)
1246                    return NULL;
1247                rhs->pn_type = TOK_NAME;
1248                rhs->pn_op = JSOP_GETARG;
1249                rhs->pn_atom = cx->runtime->atomState.emptyAtom;
1250                rhs->pn_slot = slot;
1251
1252                item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc);
1253                if (!item)
1254                    return NULL;
1255                if (!list) {
1256                    list = NewParseNode(cx, ts, PN_LIST, tc);
1257                    if (!list)
1258                        return NULL;
1259                    list->pn_type = TOK_COMMA;
1260                    PN_INIT_LIST(list);
1261                }
1262                PN_APPEND(list, item);
1263                break;
1264              }
1265#endif /* JS_HAS_DESTRUCTURING */
1266
1267              case TOK_NAME:
1268                if (!BindArg(cx, CURRENT_TOKEN(ts).t_atom, &funtc))
1269                    return NULL;
1270                break;
1271
1272              default:
1273                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1274                                            JSMSG_MISSING_FORMAL);
1275                return NULL;
1276            }
1277        } while (js_MatchToken(cx, ts, TOK_COMMA));
1278
1279        MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
1280    }
1281
1282#if JS_HAS_EXPR_CLOSURES
1283    ts->flags |= TSF_OPERAND;
1284    tt = js_GetToken(cx, ts);
1285    ts->flags &= ~TSF_OPERAND;
1286    if (tt != TOK_LC) {
1287        js_UngetToken(ts);
1288        fun->flags |= JSFUN_EXPR_CLOSURE;
1289    }
1290#else
1291    MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
1292#endif
1293    pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
1294
1295    body = FunctionBody(cx, ts, &funtc);
1296    if (!body)
1297        return NULL;
1298
1299#if JS_HAS_EXPR_CLOSURES
1300    if (tt == TOK_LC)
1301        MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1302    else if (lambda == 0)
1303        js_MatchToken(cx, ts, TOK_SEMI);
1304#else
1305    MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1306#endif
1307    pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1308
1309#if JS_HAS_DESTRUCTURING
1310    /*
1311     * If there were destructuring formal parameters, prepend the initializing
1312     * comma expression that we synthesized to body.  If the body is a lexical
1313     * scope node, we must make a special TOK_SEQ node, to prepend the formal
1314     * parameter destructuring code without bracing the decompilation of the
1315     * function body's lexical scope.
1316     */
1317    if (list) {
1318        if (body->pn_arity != PN_LIST) {
1319            JSParseNode *block;
1320
1321            block = NewParseNode(cx, ts, PN_LIST, tc);
1322            if (!block)
1323                return NULL;
1324            block->pn_type = TOK_SEQ;
1325            block->pn_pos = body->pn_pos;
1326            PN_INIT_LIST_1(block, body);
1327
1328            body = block;
1329        }
1330
1331        item = NewParseNode(cx, ts, PN_UNARY, tc);
1332        if (!item)
1333            return NULL;
1334
1335        item->pn_type = TOK_SEMI;
1336        item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
1337        item->pn_kid = list;
1338        item->pn_next = body->pn_head;
1339        body->pn_head = item;
1340        if (body->pn_tail == &body->pn_head)
1341            body->pn_tail = &item->pn_next;
1342        ++body->pn_count;
1343    }
1344#endif
1345
1346    /*
1347     * If we collected flags that indicate nested heavyweight functions, or
1348     * this function contains heavyweight-making statements (references to
1349     * __parent__ or __proto__; use of with, or eval; and assignment to 
1350     * arguments), flag the function as heavyweight (requiring a call object 
1351     * per invocation).
1352     */
1353    if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
1354        fun->flags |= JSFUN_HEAVYWEIGHT;
1355        tc->flags |= TCF_FUN_HEAVYWEIGHT;
1356    } else {
1357        /*
1358         * If this function is a named statement function not at top-level
1359         * (i.e. not a top-level function definiton or expression), then
1360         * our enclosing function, if any, must be heavyweight.
1361         *
1362         * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator,
1363         * so it won't be set here.  Assert that it's not.  We have to check
1364         * it later, in js_EmitTree, after js_EmitFunctionScript has traversed
1365         * the function's body.
1366         */
1367        JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS));
1368        if (lambda == 0 && funAtom && !AT_TOP_LEVEL(tc))
1369            tc->flags |= TCF_FUN_HEAVYWEIGHT;
1370    }
1371
1372    result = pn;
1373    if (lambda != 0) {
1374        /*
1375         * ECMA ed. 3 standard: function expression, possibly anonymous.
1376         */
1377        op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
1378    } else if (!funAtom) {
1379        /*
1380         * If this anonymous function definition is *not* embedded within a
1381         * larger expression, we treat it as an expression statement, not as
1382         * a function declaration -- and not as a syntax error (as ECMA-262
1383         * Edition 3 would have it).  Backward compatibility must trump all,
1384         * unless JSOPTION_ANONFUNFIX is set.
1385         */
1386        result = NewParseNode(cx, ts, PN_UNARY, tc);
1387        if (!result)
1388            return NULL;
1389        result->pn_type = TOK_SEMI;
1390        result->pn_pos = pn->pn_pos;
1391        result->pn_kid = pn;
1392        op = JSOP_ANONFUNOBJ;
1393    } else if (!AT_TOP_LEVEL(tc)) {
1394        /*
1395         * ECMA ed. 3 extension: a function expression statement not at the
1396         * top level, e.g., in a compound statement such as the "then" part
1397         * of an "if" statement, binds a closure only if control reaches that
1398         * sub-statement.
1399         */
1400        op = JSOP_DEFFUN;
1401    } else {
1402        op = JSOP_NOP;
1403    }
1404
1405    pn->pn_funpob = funpob;
1406    pn->pn_op = op;
1407    pn->pn_body = body;
1408    pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS | TCF_COMPILE_N_GO);
1409    TREE_CONTEXT_FINISH(cx, &funtc);
1410    return result;
1411}
1412
1413static JSParseNode *
1414FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1415{
1416    return FunctionDef(cx, ts, tc, 0);
1417}
1418
1419static JSParseNode *
1420FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1421{
1422    return FunctionDef(cx, ts, tc, JSFUN_LAMBDA);
1423}
1424
1425/*
1426 * Parse the statements in a block, creating a TOK_LC node that lists the
1427 * statements' trees.  If called from block-parsing code, the caller must
1428 * match { before and } after.
1429 */
1430static JSParseNode *
1431Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1432{
1433    JSParseNode *pn, *pn2, *saveBlock;
1434    JSTokenType tt;
1435
1436    JS_CHECK_RECURSION(cx, return NULL);
1437
1438    pn = NewParseNode(cx, ts, PN_LIST, tc);
1439    if (!pn)
1440        return NULL;
1441    saveBlock = tc->blockNode;
1442    tc->blockNode = pn;
1443    PN_INIT_LIST(pn);
1444
1445    for (;;) {
1446        ts->flags |= TSF_OPERAND;
1447        tt = js_PeekToken(cx, ts);
1448        ts->flags &= ~TSF_OPERAND;
1449        if (tt <= TOK_EOF || tt == TOK_RC) {
1450            if (tt == TOK_ERROR)
1451                return NULL;
1452            break;
1453        }
1454        pn2 = Statement(cx, ts, tc);
1455        if (!pn2) {
1456            if (ts->flags & TSF_EOF)
1457                ts->flags |= TSF_UNEXPECTED_EOF;
1458            return NULL;
1459        }
1460
1461        if (pn2->pn_type == TOK_FUNCTION) {
1462            /*
1463             * PNX_FUNCDEFS notifies the emitter that the block contains top-
1464             * level function definitions that should be processed before the
1465             * rest of nodes.
1466             *
1467             * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
1468             * is relevant only for function definitions not at top-level,
1469             * which we call function statements.
1470             */
1471            if (AT_TOP_LEVEL(tc))
1472                pn->pn_extra |= PNX_FUNCDEFS;
1473            else
1474                tc->flags |= TCF_HAS_FUNCTION_STMT;
1475        }
1476        PN_APPEND(pn, pn2);
1477    }
1478
1479    /*
1480     * Handle the case where there was a let declaration under this block.  If
1481     * it replaced tc->blockNode with a new block node then we must refresh pn
1482     * and then restore tc->blockNode.
1483     */
1484    if (tc->blockNode != pn)
1485        pn = tc->blockNode;
1486    tc->blockNode = saveBlock;
1487
1488    pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1489    return pn;
1490}
1491
1492static JSParseNode *
1493Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1494{
1495    JSParseNode *pn;
1496
1497    MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1498    pn = ParenExpr(cx, ts, tc, NULL, NULL);
1499    if (!pn)
1500        return NULL;
1501    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1502
1503    /*
1504     * Check for (a = b) and warn about possible (a == b) mistype iff b's
1505     * operator has greater precedence than ==.
1506     */
1507    if (pn->pn_type == TOK_ASSIGN &&
1508        pn->pn_op == JSOP_NOP &&
1509        pn->pn_right->pn_type > TOK_EQOP)
1510    {
1511        if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1512                                         JSREPORT_WARNING | JSREPORT_STRICT,
1513                                         JSMSG_EQUAL_AS_ASSIGN,
1514                                         "")) {
1515            return NULL;
1516        }
1517    }
1518    return pn;
1519}
1520
1521static JSBool
1522MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1523{
1524    JSAtom *label;
1525    JSTokenType tt;
1526
1527    tt = js_PeekTokenSameLine(cx, ts);
1528    if (tt == TOK_ERROR)
1529        return JS_FALSE;
1530    if (tt == TOK_NAME) {
1531        (void) js_GetToken(cx, ts);
1532        label = CURRENT_TOKEN(ts).t_atom;
1533    } else {
1534        label = NULL;
1535    }
1536    pn->pn_atom = label;
1537    return JS_TRUE;
1538}
1539
1540static JSBool
1541BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1542{
1543    JSObje

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