else if (lambda) pn->pn_op = JSOP_OBJECT;
else if (lambda || !fun->atom) pn->pn_op = fun->atom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
static JSParser UnaryExpr;static JSMemberParser MemberExpr;static JSParser PrimaryExpr;/* * Insist that the next token be of type tt, or report errno and return null. * NB: this macro uses cx and ts from its lexical environment. */#define MUST_MATCH_TOKEN(tt, errno)                                           \    JS_BEGIN_MACRO                                                            \	if (js_GetToken(cx, ts) != tt) {                                      \	    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR, errno);       \	    return NULL;                                                      \	}                                                                     \    JS_END_MACRO/* * Allocate a JSParseNode from cx's temporary arena. */static JSParseNode *NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity){    JSParseNode *pn;    JS_ARENA_ALLOCATE(pn, &cx->tempPool, sizeof(JSParseNode));    if (!pn)	return NULL;    pn->pn_type = tok->type;    pn->pn_pos = tok->pos;    pn->pn_arity = arity;    pn->pn_next = NULL;    return pn;}static JSParseNode *NewBinary(JSContext *cx, JSTokenType tt,	  JSOp op, JSParseNode *left, JSParseNode *right){    JSParseNode *pn;    if (!left || !right)	return NULL;    JS_ARENA_ALLOCATE(pn, &cx->tempPool, sizeof(JSParseNode));    if (!pn)	return NULL;    pn->pn_type = tt;    pn->pn_pos.begin = left->pn_pos.begin;    pn->pn_pos.end = right->pn_pos.end;    pn->pn_op = op;    pn->pn_arity = PN_BINARY;    pn->pn_left = left;    pn->pn_right = right;    pn->pn_next = NULL;    return pn;}static JSBoolWellTerminated(JSContext *cx, JSTokenStream *ts, JSTokenType lastExprType){    JSTokenType tt;    tt = js_PeekTokenSameLine(cx, ts);    if (tt == TOK_ERROR)	return JS_FALSE;    if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {#if JS_HAS_LEXICAL_CLOSURE	if ((tt == TOK_FUNCTION || lastExprType == TOK_FUNCTION) &&	    cx->version < JSVERSION_1_2) {	    /*	     * Checking against version < 1.2 and version >= 1.0	     * in the above line breaks old javascript, so we keep it	     * this way for now... XXX warning needed?	     */	    return JS_TRUE;	}#endif	js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,				    JSMSG_SEMI_BEFORE_STMNT);	return JS_FALSE;    }    return JS_TRUE;}#if JS_HAS_GETTER_SETTERstatic JSTokenTypeCheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt){    JSAtom *atom;    JSRuntime *rt;    JSOp op;    JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);    atom = CURRENT_TOKEN(ts).t_atom;    rt = cx->runtime;    if (atom == rt->atomState.getterAtom)        op = JSOP_GETTER;    else if (atom == rt->atomState.setterAtom)        op = JSOP_SETTER;    else        return TOK_NAME;    if (js_PeekTokenSameLine(cx, ts) != tt)        return TOK_NAME;    (void) js_GetToken(cx, ts);    if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {        js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,                                    JSMSG_BAD_GETTER_OR_SETTER,                                    (op == JSOP_GETTER)                                    ? js_getter_str                                    : js_setter_str);        return TOK_ERROR;    }    CURRENT_TOKEN(ts).t_op = op;    return tt;}#endif/* * Parse a top-level JS script. */JS_FRIEND_API(JSBool)js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,		      JSCodeGenerator *cg){    JSStackFrame *fp, frame;    JSTokenType tt;    JSBool ok;    JSParseNode *pn;    fp = cx->fp;    if (!fp || fp->scopeChain != chain) {	memset(&frame, 0, sizeof frame);	frame.scopeChain = chain;	frame.down = fp;	cx->fp = &frame;    }    /*     * Prevent GC activation on this context (possible if out of memory when     * atomizing, or from pre-ECMAv2 switch case expr eval in the unlikely     * case of a branch-callback -- unlikely because it means the switch case     * must have called a function).     */    JS_DISABLE_GC(cx->runtime);    ok = JS_TRUE;    do {	ts->flags |= TSF_REGEXP;	tt = js_GetToken(cx, ts);	ts->flags &= ~TSF_REGEXP;	if (tt <= TOK_EOF) {	    if (tt == TOK_ERROR)		ok = JS_FALSE;	    break;	}#if JS_HAS_GETTER_SETTER        if (tt == TOK_NAME) {            tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);            if (tt == TOK_ERROR) {		ok = JS_FALSE;                break;            }        }#endif	if (tt == TOK_FUNCTION) {	    pn = FunctionStmt(cx, ts, &cg->treeContext);	    if (pn && pn->pn_pos.end.lineno == ts->lineno) {                ok = WellTerminated(cx, ts, TOK_FUNCTION);                if (!ok)                    break;	    }        } else {	    js_UngetToken(ts);	    pn = Statement(cx, ts, &cg->treeContext);	}	if (!pn) {	    ok = JS_FALSE;            break;	}        ok = js_FoldConstants(cx, pn);        if (!ok)            break;        ok = js_AllocTryNotes(cx, cg);        if (!ok)            break;        ok = js_EmitTree(cx, cg, pn);    } while (ok);    JS_ENABLE_GC(cx->runtime);    ts->flags &= ~TSF_ERROR;    cx->fp = fp;    return ok;}/* * Insist on a final return before control flows out of pn, but don't be too * smart about loops (do {...; return e2;} while(0) at the end of a function * that contains an early return e1 will get an error XXX should be warning * option). */static JSBoolCheckFinalReturn(JSParseNode *pn){    JSBool ok, hasDefault;    JSParseNode *pn2, *pn3;    switch (pn->pn_type) {      case TOK_LC:	if (!pn->pn_head)	    return JS_FALSE;	return CheckFinalReturn(PN_LAST(pn));      case TOK_IF:	ok = CheckFinalReturn(pn->pn_kid2);	ok &= pn->pn_kid3 && CheckFinalReturn(pn->pn_kid3);	return ok;#if JS_HAS_SWITCH_STATEMENT      case TOK_SWITCH:	ok = JS_TRUE;	for (pn2 = pn->pn_kid2->pn_head; ok && pn2; pn2 = pn2->pn_next) {	    if (pn2->pn_type == TOK_DEFAULT)		hasDefault = JS_TRUE;	    pn3 = pn2->pn_right;	    JS_ASSERT(pn3->pn_type == TOK_LC);	    if (pn3->pn_head)		ok &= CheckFinalReturn(PN_LAST(pn3));	}	/* If a final switch has no default case, we judge it harshly. */	ok &= hasDefault;	return ok;#endif /* JS_HAS_SWITCH_STATEMENT */      case TOK_WITH:	return CheckFinalReturn(pn->pn_right);      case TOK_RETURN:	return JS_TRUE;      default:	return JS_FALSE;    }}static JSParseNode *FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,	     JSTreeContext *tc){    JSStackFrame *fp, frame;    uintN oldflags;    JSParseNode *pn;    fp = cx->fp;    if (!fp || fp->scopeChain != fun->object) {	memset(&frame, 0, sizeof frame);	frame.scopeChain = fun->object;	frame.down = fp;	cx->fp = &frame;    }    oldflags = tc->flags;    tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);    tc->flags |= TCF_IN_FUNCTION;    pn = Statements(cx, ts, tc);    /* Check for falling off the end of a function that returns a value. */    if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {	if (!CheckFinalReturn(pn)) {            if (fun->atom) {                char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom));                js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,                                            JSMSG_NO_RETURN_VALUE, name);            } else {                js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,                                            JSMSG_ANON_NO_RETURN_VALUE);            }	    pn = NULL;	}    }    cx->fp = fp;    tc->flags = oldflags;    return pn;}/* * Compile a JS function body, which might appear as the value of an event * handler attribute in an HTML <INPUT> tag. */JSBooljs_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun){    JSCodeGenerator funcg;    JSParseNode *pn;    JSBool ok;    if (!js_InitCodeGenerator(cx, &funcg, ts->filename, ts->lineno,			      ts->principals)) {	return JS_FALSE;    }    /* Prevent GC activation on this context during compilation. */    JS_DISABLE_GC(cx->runtime);    /* Satisfy the assertion at the top of Statements. */    CURRENT_TOKEN(ts).type = TOK_LC;    pn = FunctionBody(cx, ts, fun, &funcg.treeContext);    if (!pn) {	ok = JS_FALSE;    } else {	ok = js_FoldConstants(cx, pn) &&	     js_EmitFunctionBody(cx, &funcg, pn, fun);    }    JS_ENABLE_GC(cx->runtime);    js_FinishCodeGenerator(cx, &funcg);    return ok;}static JSBoolInWithStatement(JSTreeContext *tc){    JSStmtInfo *stmt;    for (stmt = tc->topStmt; stmt; stmt = stmt->down) {	if (stmt->type == STMT_WITH)	    return JS_TRUE;    }    return JS_FALSE;}static JSParseNode *FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,            JSBool lambda){    JSParseNode *pn, *pn2;    JSOp op;    JSAtom *funAtom, *argAtom;    JSFunction *fun, *outerFun;    JSObject *parent;    JSObject *pobj;    JSScopeProperty *sprop;    JSTreeContext funtc;    jsid oldArgId;    /* Make a TOK_FUNCTION node. */    pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_FUNC);    if (!pn)	return NULL;    op = CURRENT_TOKEN(ts).t_op;    /* Scan the optional function name into funAtom. */    if (js_MatchToken(cx, ts, TOK_NAME))	funAtom = CURRENT_TOKEN(ts).t_atom;    else	funAtom = NULL;    /* Find the nearest variable-declaring scope and use it as our parent. */    parent = js_FindVariableScope(cx, &outerFun);    if (!parent)	return NULL;    fun = js_NewFunction(cx, NULL, NULL, 0, 0, parent, funAtom);    if (!fun)        return NULL;#if JS_HAS_GETTER_SETTER    if (op != JSOP_NOP)        fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;#endif    /* Now parse formal argument list and compute fun->nargs. */    MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);    if (!js_MatchToken(cx, ts, TOK_RP)) {	do {	    MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL);	    argAtom = CURRENT_TOKEN(ts).t_atom;	    pobj = NULL;	    if (!js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj,				   (JSProperty **)&sprop)) {		return NULL;            }	    if (sprop && pobj == fun->object) {		if (SPROP_GETTER(sprop, pobj) == js_GetArgument) {                    if (JS_HAS_STRICT_OPTION(cx)) {                        OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);                        js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,                                                    JSMSG_DUPLICATE_FORMAL,                                                    ATOM_BYTES(argAtom));                        return NULL;                    }		    /*		     * A duplicate parameter name. We create a dummy symbol		     * entry with property id of the parameter number and set		     * the id to the name of the parameter.		     * The decompiler will know to treat this case specially.		     */		    oldArgId = (jsid) sprop->id;		    OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);		    sprop = NULL;		    if (!js_DefineProperty(cx, fun->object,					   oldArgId, JSVAL_VOID,					   js_GetArgument, js_SetArgument,					   JSPROP_ENUMERATE | JSPROP_PERMANENT,					   (JSProperty **)&sprop)) {			return NULL;                    }		    sprop->id = (jsid) argAtom;		}	    }	    if (sprop) {		OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);		sprop = NULL;	    }	    if (!js_DefineProperty(cx, fun->object,				   (jsid)argAtom, JSVAL_VOID,				   js_GetArgument, js_SetArgument,				   JSPROP_ENUMERATE | JSPROP_PERMANENT,				   (JSProperty **)&sprop)) {		return NULL;            }	    JS_ASSERT(sprop);	    sprop->id = INT_TO_JSVAL(fun->nargs++);	    OBJ_DROP_PROPERTY(cx, fun->object, (JSProperty *)sprop);	} while (js_MatchToken(cx, ts, TOK_COMMA));	MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);    }    MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);    pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;    TREE_CONTEXT_INIT(&funtc);    pn2 = FunctionBody(cx, ts, fun, &funtc);    if (!pn2)	return NULL;    MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);    pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;#if JS_HAS_LEXICAL_CLOSURE    /*     * Generate a closure if in a nested function, or if in 'with (o) eval(s)'     * where s contains a function definition, or if in a with statement being     * compiled from the same source that contains this function.  Otherwise,     * if in a lambda expression, generate a function object reference.  Else,     * generate an annotated NOP for a top-level function.     */    if (outerFun || cx->fp->scopeChain != parent || InWithStatement(tc))        pn->pn_op = JSOP_CLOSURE;    else if (lambda)        pn->pn_op = JSOP_OBJECT;    else#endif        pn->pn_op = JSOP_NOP;    pn->pn_fun = fun;    pn->pn_body = pn2;    pn->pn_tryCount = funtc.tryCount;    TREE_CONTEXT_FREE(&funtc);    return pn;}static JSParseNode *FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    return FunctionDef(cx, ts, tc, JS_FALSE);}#if JS_HAS_LEXICAL_CLOSUREstatic JSParseNode *FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    return FunctionDef(cx, ts, tc, JS_TRUE);}#endif/* * Parse the statements in a block, creating a TOK_LC node that lists the * statements' trees.  Our caller must match { before and } after. */static JSParseNode *Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn, *pn2;    JSTokenType tt;    JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);    pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST);    if (!pn)	return NULL;    PN_INIT_LIST(pn);    while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {	pn2 = Statement(cx, ts, tc);	if (!pn2)            return NULL;	PN_APPEND(pn, pn2);    }    pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;    if (tt == TOK_ERROR)	pn = NULL;    return pn;}static JSParseNode *Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn, *pn2;    MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);    pn = Expr(cx, ts, tc);    if (!pn)	return NULL;    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);    /*     * Check for (a = b) and "correct" it to (a == b).     * XXX not ECMA, but documented in several books -- need a compile option     */    if (pn->pn_type == TOK_ASSIGN && pn->pn_op == JSOP_NOP) {        JSBool rewrite = JS_HAS_STRICT_OPTION(cx) ||                         !JSVERSION_IS_ECMA(cx->version);	js_ReportCompileErrorNumber(cx, ts, JSREPORT_WARNING,				    JSMSG_EQUAL_AS_ASSIGN,				    rewrite ? "\nAssuming equality test" : "");	if (!rewrite)	    return pn;	pn->pn_type = TOK_EQOP;	pn->pn_op = (JSOp)cx->jsop_eq;	pn2 = pn->pn_left;	switch (pn2->pn_op) {	  case JSOP_SETARG:	    pn2->pn_op = JSOP_GETARG;	    break;	  case JSOP_SETVAR:	    pn2->pn_op = JSOP_GETVAR;	    break;	  case JSOP_SETNAME:	    pn2->pn_op = JSOP_NAME;	    break;	  case JSOP_SETPROP:	    pn2->pn_op = JSOP_GETPROP;	    break;	  case JSOP_SETELEM:	    pn2->pn_op = JSOP_GETELEM;	    break;	  default:	    JS_ASSERT(0);	}    }    return pn;}static JSBoolMatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn){    JSAtom *label;#if JS_HAS_LABEL_STATEMENT    JSTokenType tt;    tt = js_PeekTokenSameLine(cx, ts);    if (tt == TOK_ERROR)	return JS_FALSE;    if (tt == TOK_NAME) {	(void) js_GetToken(cx, ts);	label = CURRENT_TOKEN(ts).t_atom;    } else {	label = NULL;    }#else    label = NULL;#endif    pn->pn_atom = label;    if (pn->pn_pos.end.lineno == ts->lineno)	return WellTerminated(cx, ts, TOK_ERROR);    return JS_TRUE;}#if JS_HAS_EXPORT_IMPORTstatic JSParseNode *ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn, *pn2, *pn3;    JSTokenType tt;    MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);    pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY);    if (!pn)	return NULL;    pn->pn_op = JSOP_NAME;    pn->pn_atom = CURRENT_TOKEN(ts).t_atom;    pn->pn_slot = -1;    pn->pn_attrs = 0;    ts->flags |= TSF_REGEXP;    while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {	ts->flags &= ~TSF_REGEXP;	if (pn->pn_op == JSOP_IMPORTALL)	    goto bad_import;	if (tt == TOK_DOT) {	    pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME);	    if (!pn2)		return NULL;	    pn2->pn_expr = pn;	    if (js_MatchToken(cx, ts, TOK_STAR)) {		pn2->pn_op = JSOP_IMPORTALL;		pn2->pn_atom = NULL;	    } else {		MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);		pn2->pn_op = JSOP_GETPROP;		pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;		pn2->pn_slot = -1;                pn2->pn_attrs = 0;	    }	    pn2->pn_pos.begin = pn->pn_pos.begin;	    pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;	} else {	    /* Make a TOK_LB node. */	    pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY);	    if (!pn2)		return NULL;	    pn3 = Expr(cx, ts, tc);	    if (!pn3)		return NULL;	    MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);	    pn2->pn_pos.begin = pn->pn_pos.begin;	    pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;            pn2->pn_op = JSOP_GETELEM;            pn2->pn_left = pn;            pn2->pn_right = pn3;	}	pn = pn2;	ts->flags |= TSF_REGEXP;    }    ts->flags &= ~TSF_REGEXP;    if (tt == TOK_ERROR)	return NULL;    js_UngetToken(ts);    switch (pn->pn_op) {      case JSOP_GETPROP:	pn->pn_op = JSOP_IMPORTPROP;	break;      case JSOP_GETELEM:	pn->pn_op = JSOP_IMPORTELEM;	break;      case JSOP_IMPORTALL:	break;      default:	goto bad_import;    }    return pn;  bad_import:    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR, JSMSG_BAD_IMPORT);    return NULL;}#endif /* JS_HAS_EXPORT_IMPORT */static JSParseNode *Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSTokenType tt, lastExprType;    JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;    JSStmtInfo stmtInfo, *stmt, *stmt2;    JSAtom *label;    ts->flags |= TSF_REGEXP;    tt = js_GetToken(cx, ts);    ts->flags &= ~TSF_REGEXP;    switch (tt) {#if JS_HAS_EXPORT_IMPORT      case TOK_EXPORT:	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST);	if (!pn)	    return NULL;	PN_INIT_LIST(pn);	if (js_MatchToken(cx, ts, TOK_STAR)) {	    pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY);	    if (!pn2)		return NULL;	    PN_APPEND(pn, pn2);	} else {	    do {		MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);		pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY);		if (!pn2)		    return NULL;		pn2->pn_op = JSOP_NAME;		pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;		pn2->pn_slot = -1;                pn2->pn_attrs = 0;		PN_APPEND(pn, pn2);	    } while (js_MatchToken(cx, ts, TOK_COMMA));	}	pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;	if (pn->pn_pos.end.lineno == ts->lineno &&	    !WellTerminated(cx, ts, TOK_ERROR)) {	    return NULL;	}	break;      case TOK_IMPORT:	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST);	if (!pn)	    return NULL;	PN_INIT_LIST(pn);	do {	    pn2 = ImportExpr(cx, ts, tc);	    if (!pn2)		return NULL;	    PN_APPEND(pn, pn2);	} while (js_MatchToken(cx, ts, TOK_COMMA));	pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;	if (pn->pn_pos.end.lineno == ts->lineno &&	    !WellTerminated(cx, ts, TOK_ERROR)) {	    return NULL;	}	break;#endif /* JS_HAS_EXPORT_IMPORT */      case TOK_IF:	/* An IF node has three kids: condition, then, and optional else. */	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY);	if (!pn)	    return NULL;	pn1 = Condition(cx, ts, tc);	if (!pn1)	    return NULL;	js_PushStatement(tc, &stmtInfo, STMT_IF, -1);	pn2 = Statement(cx, ts, tc);	if (!pn2)	    return NULL;	if (js_MatchToken(cx, ts, TOK_ELSE)) {	    stmtInfo.type = STMT_ELSE;	    pn3 = Statement(cx, ts, tc);	    if (!pn3)		return NULL;	    pn->pn_pos.end = pn3->pn_pos.end;	} else {	    pn3 = NULL;	    pn->pn_pos.end = pn2->pn_pos.end;	}	js_PopStatement(tc);	pn->pn_kid1 = pn1;	pn->pn_kid2 = pn2;	pn->pn_kid3 = pn3;	return pn;#if JS_HAS_SWITCH_STATEMENT      case TOK_SWITCH:      {	JSParseNode *pn5;	JSBool seenDefault = JS_FALSE;	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY);	if (!pn)	    return NULL;	MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);	/* pn1 points to the switch's discriminant. */	pn1 = Expr(cx, ts, tc);	if (!pn1)	    return NULL;	MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);	MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);	/* pn2 is a list of case nodes. The default case has pn_left == NULL */	pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST);	if (!pn2)	    return NULL;	PN_INIT_LIST(pn2);	js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);	while ((tt = js_GetToken(cx, ts)) != TOK_RC) {	    switch (tt) {	      case TOK_DEFAULT:		if (seenDefault) {		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,						JSMSG_TOO_MANY_DEFAULTS);		    return NULL;		}		seenDefault = JS_TRUE;		/* fall through */	      case TOK_CASE:		pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY);		if (!pn3)		    return NULL;		if (tt == TOK_DEFAULT) {		    pn3->pn_left = NULL;		} else {		    pn3->pn_left = Expr(cx, ts, tc);		    if (!pn3->pn_left)			return NULL;		}		PN_APPEND(pn2, pn3);		if (pn2->pn_count == JS_BIT(16)) {		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,						JSMSG_TOO_MANY_CASES);		    return NULL;		}		break;	      case TOK_ERROR:		return NULL;	      default:		js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,					    JSMSG_BAD_SWITCH);                return NULL;	    }	    MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);	    pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST);	    if (!pn4)                return NULL;	    pn4->pn_type = TOK_LC;	    PN_INIT_LIST(pn4);	    while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&		    tt != TOK_CASE && tt != TOK_DEFAULT) {		if (tt == TOK_ERROR)                    return NULL;		pn5 = Statement(cx, ts, tc);		if (!pn5)                    return NULL;		pn4->pn_pos.end = pn5->pn_pos.end;		PN_APPEND(pn4, pn5);	    }	    pn3->pn_pos.end = pn4->pn_pos.end;	    pn3->pn_right = pn4;	}	js_PopStatement(tc);	pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;	pn->pn_kid1 = pn1;	pn->pn_kid2 = pn2;	return pn;      }#endif /* JS_HAS_SWITCH_STATEMENT */      case TOK_WHILE:	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY);	if (!pn)	    return NULL;	js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);	pn2 = Condition(cx, ts, tc);	if (!pn2)	    return NULL;	pn->pn_left = pn2;	pn2 = Statement(cx, ts, tc);	if (!pn2)	    return NULL;	js_PopStatement(tc);	pn->pn_pos.end = pn2->pn_pos.end;	pn->pn_right = pn2;	return pn;#if JS_HAS_DO_WHILE_LOOP      case TOK_DO:	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY);	if (!pn)	    return NULL;	js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);	pn2 = Statement(cx, ts, tc);	if (!pn2)	    return NULL;	pn->pn_left = pn2;	MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);	pn2 = Condition(cx, ts, tc);	if (!pn2)	    return NULL;	js_PopStatement(tc);	pn->pn_pos.end = pn2->pn_pos.end;	pn->pn_right = pn2;	break;#endif /* JS_HAS_DO_WHILE_LOOP */      case TOK_FOR:	/* A FOR node is binary, left is loop control and right is the body. */	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY);	if (!pn)	    return NULL;	js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);	MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);	tt = js_PeekToken(cx, ts);	if (tt == TOK_SEMI) {	    /* No initializer -- set first kid of left sub-node to null. */	    pn1 = NULL;	} else {	    /* Set pn1 to a var list or an initializing expression. */#if JS_HAS_IN_OPERATOR	    /*	     * Set the TCF_IN_FOR_INIT flag during parsing of the first clause	     * of the for statement.  This flag will be used by the RelExpr	     * production; if it is set, then the 'in' keyword will not be	     * recognized as an operator, leaving it available to be parsed as	     * part of a for/in loop.  A side effect of this restriction is	     * that (unparenthesized) expressions involving an 'in' operator	     * are illegal in the init clause of an ordinary for loop.	     */	    tc->flags |= TCF_IN_FOR_INIT;#endif /* JS_HAS_IN_OPERATOR */	    if (tt == TOK_VAR) {		(void) js_GetToken(cx, ts);		pn1 = Variables(cx, ts, tc);	    } else {		pn1 = Expr(cx, ts, tc);	    }#if JS_HAS_IN_OPERATOR	    tc->flags &= ~TCF_IN_FOR_INIT;#endif /* JS_HAS_IN_OPERATOR */	    if (!pn1)		return NULL;	}	/*	 * We can be sure that if it's a for/in loop, there's still an 'in'	 * keyword here, even if Javascript recognizes it as an operator,	 * because we've excluded it from being parsed in RelExpr by setting	 * the TCF_IN_FOR_INIT flag in our JSTreeContext.	 */	if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {	    stmtInfo.type = STMT_FOR_IN_LOOP;	    /* Check that the left side of the 'in' is valid. */            if ((pn1->pn_type == TOK_VAR)                ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST)                : (pn1->pn_type != TOK_NAME &&                   pn1->pn_type != TOK_DOT &&                   pn1->pn_type != TOK_LB)) {		js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,					    JSMSG_BAD_FOR_LEFTSIDE);		return NULL;	    }	    /* Parse the object expression as the right operand of 'in'. */	    pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc));	    if (!pn2)		return NULL;	    pn->pn_left = pn2;	} else {	    /* Parse the loop condition or null into pn2. */	    MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);	    if (js_PeekToken(cx, ts) == TOK_SEMI) {		pn2 = NULL;	    } else {		pn2 = Expr(cx, ts, tc);		if (!pn2)		    return NULL;	    }	    /* Parse the update expression or null into pn3. */	    MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);	    if (js_PeekToken(cx, ts) == TOK_RP) {		pn3 = NULL;	    } else {		pn3 = Expr(cx, ts, tc);		if (!pn3)		    return NULL;	    }	    /* Build the RESERVED node to use as the left kid of pn. */	    pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY);	    if (!pn4)		return NULL;	    pn4->pn_type = TOK_RESERVED;	    pn4->pn_kid1 = pn1;	    pn4->pn_kid2 = pn2;	    pn4->pn_kid3 = pn3;	    pn->pn_left = pn4;	}	MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);	/* Parse the loop body into pn->pn_right. */	pn2 = Statement(cx, ts, tc);	if (!pn2)	    return NULL;	pn->pn_right = pn2;	js_PopStatement(tc);	/* Record the absolute line number for source note emission. */	pn->pn_pos.end = pn2->pn_pos.end;	return pn;#if JS_HAS_EXCEPTIONS      case TOK_TRY: {	JSParseNode *catchtail = NULL;	/*	 * try nodes are ternary.	 * kid1 is the try Statement	 * kid2 is the catch node	 * kid3 is the finally Statement	 *	 * catch nodes are ternary.	 * kid1 is the discriminant	 * kid2 is the next catch node, or NULL	 * kid3 is the catch block (on kid3 so that we can always append a	 *                          new catch pn on catchtail->kid2)	 *	 * catch discriminant nodes are binary	 * atom is the receptacle	 * expr is the discriminant code	 *	 * finally nodes are unary (just the finally expression)	 */	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY);	pn->pn_op = JSOP_NOP;	MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);	pn->pn_kid1 = Statements(cx, ts, tc);	if (!pn->pn_kid1)	    return NULL;	MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);	pn->pn_kid2 = NULL;	catchtail = pn;	while(js_PeekToken(cx, ts) == TOK_CATCH) {	    /*	     * legal catch form is:	     * catch (v)             *              * The form	     * catch (v : <boolean_expression>)             * has been pulled pending resolution in ECMA.	     */	    /* catch node */	    pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY);	    pn2->pn_op = JSOP_NOP;	    /*	     * We use a PN_NAME for the discriminant (catchguard) node	     * with the actual discriminant code in the initializer spot	     */	    pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME);	    if (!pn2 || !pn3)		return NULL;	    (void)js_GetToken(cx, ts); /* eat `catch' */	    MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);	    MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER);	    pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;            pn3->pn_expr = NULL;#if JS_HAS_CATCH_GUARD            /*             * We use `catch (x if x === 5)' (not `catch (x : x === 5)') to             * avoid conflicting with the JS2/ECMA2 proposed catchguard syntax.             */	    if (js_PeekToken(cx, ts) == TOK_IF) {		(void)js_GetToken(cx, ts); /* eat `if' */		pn3->pn_expr = Expr(cx, ts, tc);		if (!pn3->pn_expr)		    return NULL;	    } #endif	    pn2->pn_kid1 = pn3;	    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);	    MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);	    pn2->pn_kid3 = Statements(cx, ts, tc);	    if (!pn2->pn_kid3)		return NULL;	    MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);	    catchtail = catchtail->pn_kid2 = pn2;	}	catchtail->pn_kid2 = NULL;	if (js_MatchToken(cx, ts, TOK_FINALLY)) {	    tc->tryCount++;	    MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);	    pn->pn_kid3 = Statements(cx, ts, tc);	    if (!pn->pn_kid3)		return NULL;	    MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);	} else {	    pn->pn_kid3 = NULL;	}	if (!pn->pn_kid2 && !pn->pn_kid3) {	    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,					JSMSG_CATCH_OR_FINALLY);	    return NULL;	}	tc->tryCount++;	return pn;      }      case TOK_THROW:	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY);	if (!pn)	    return NULL;	pn2 = Expr(cx, ts, tc);	if (!pn2)	    return NULL;	pn->pn_pos.end = pn2->pn_pos.end;	if (pn->pn_pos.end.lineno == ts->lineno &&	    !WellTerminated(cx, ts, TOK_ERROR)) {	    return NULL;	}	pn->pn_op = JSOP_THROW;	pn->pn_kid = pn2;	break;      /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */      case TOK_CATCH:	js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,				    JSMSG_CATCH_WITHOUT_TRY);	return NULL;      case TOK_FINALLY:	js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,				    JSMSG_FINALLY_WITHOUT_TRY);	return NULL;#endif /* JS_HAS_EXCEPTIONS */      case TOK_BREAK:	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY);	if (!pn)	    return NULL;	if (!MatchLabel(cx, ts, pn))	    return NULL;	stmt = tc->topStmt;	label = pn->pn_atom;	if (label) {	    for (; ; stmt = stmt->down) {		if (!stmt) {		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,						JSMSG_LABEL_NOT_FOUND);		    return NULL;		}		if (stmt->type == STMT_LABEL && stmt->label == label)		    break;	    }	} else {	    for (; ; stmt = stmt->down) {		if (!stmt) {		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,						JSMSG_TOUGH_BREAK);		    return NULL;		}		if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)		    break;	    }	}	if (label)	    pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;	break;      case TOK_CONTINUE:	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY);	if (!pn)	    return NULL;	if (!MatchLabel(cx, ts, pn))	    return NULL;	stmt = tc->topStmt;	label = pn->pn_atom;	if (label) {	    for (stmt2 = NULL; ; stmt = stmt->down) {		if (!stmt) {		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,						JSMSG_LABEL_NOT_FOUND);		    return NULL;		}		if (stmt->type == STMT_LABEL) {		    if (stmt->label == label) {			if (!stmt2 || !STMT_IS_LOOP(stmt2)) {			    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,							JSMSG_BAD_CONTINUE);			    return NULL;			}			break;		    }		} else {		    stmt2 = stmt;		}	    }	} else {	    for (; ; stmt = stmt->down) {		if (!stmt) {		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,						JSMSG_BAD_CONTINUE);		    return NULL;		}		if (STMT_IS_LOOP(stmt))		    break;	    }	}	if (label)	    pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;	break;      case TOK_WITH:	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY);	if (!pn)	    return NULL;	MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);	pn2 = Expr(cx, ts, tc);	if (!pn2)	    return NULL;	MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);	pn->pn_left = pn2;	js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);	pn2 = Statement(cx, ts, tc);	if (!pn2)	    return NULL;	js_PopStatement(tc);	pn->pn_pos.end = pn2->pn_pos.end;	pn->pn_right = pn2;	return pn;      case TOK_VAR:	pn = Variables(cx, ts, tc);	if (!pn)	    return NULL;	if (pn->pn_pos.end.lineno == ts->lineno &&	    !WellTerminated(cx, ts, TOK_ERROR)) {	    return NULL;	}	/* Tell js_EmitTree to generate a final POP. */	pn->pn_extra = JS_TRUE;	break;      case TOK_RETURN:	if (!(tc->flags & TCF_IN_FUNCTION)) {	    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,					JSMSG_BAD_RETURN);	    return NULL;	}	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY);	if (!pn)	    return NULL;	/* This is ugly, but we don't want to require a semicolon. */	ts->flags |= TSF_REGEXP;	tt = js_PeekTokenSameLine(cx, ts);	ts->flags &= ~TSF_REGEXP;	if (tt == TOK_ERROR)	    return NULL;	if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {	    pn2 = Expr(cx, ts, tc);	    if (!pn2)		return NULL;	    if (pn2->pn_pos.end.lineno == ts->lineno &&		!WellTerminated(cx, ts, TOK_ERROR)) {		return NULL;	    }	    tc->flags |= TCF_RETURN_EXPR;	    pn->pn_pos.end = pn2->pn_pos.end;	    pn->pn_kid = pn2;	} else {	    tc->flags |= TCF_RETURN_VOID;	    pn->pn_kid = NULL;	}	if (JS_HAS_STRICT_OPTION(cx) &&            (tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) ==	    (TCF_RETURN_EXPR | TCF_RETURN_VOID)) {            JSStackFrame *fp = cx->fp;            JSFunction *fun;            while (fp && !(fun = fp->fun))                fp = fp->down;            /*             * We must first find a frame with a non-native function, because             * we're compiling one.  We test against non-null fp above so that             * we actually trip this assertion if something goes horribly wrong.             */            JS_ASSERT(fp && fun && !fun->call);            if (fun->atom) {                char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom));                js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,                                            JSMSG_NO_RETURN_VALUE, name);            } else {                js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,                                            JSMSG_ANON_NO_RETURN_VALUE);            }	    return NULL;	}	break;      case TOK_LC:	js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);	pn = Statements(cx, ts, tc);	if (!pn)	    return NULL;	MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);	js_PopStatement(tc);	return pn;      case TOK_EOL:      case TOK_SEMI:	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY);	if (!pn)	    return NULL;	pn->pn_type = TOK_SEMI;	pn->pn_kid = NULL;	return pn;#if JS_HAS_DEBUGGER_KEYWORD      case TOK_DEBUGGER:	if(!WellTerminated(cx, ts, TOK_ERROR))	    return NULL;	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY);	if (!pn)	    return NULL;	pn->pn_type = TOK_DEBUGGER;	return pn;#endif /* JS_HAS_DEBUGGER_KEYWORD */      case TOK_ERROR:	return NULL;      default:	lastExprType = CURRENT_TOKEN(ts).type;	js_UngetToken(ts);	pn2 = Expr(cx, ts, tc);	if (!pn2)	    return NULL;	if (js_PeekToken(cx, ts) == TOK_COLON) {	    if (pn2->pn_type != TOK_NAME) {		js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,					    JSMSG_BAD_LABEL);		return NULL;	    }	    label = pn2->pn_atom;	    for (stmt = tc->topStmt; stmt; stmt = stmt->down) {		if (stmt->type == STMT_LABEL && stmt->label == label) {		    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,						JSMSG_DUPLICATE_LABEL);		    return NULL;		}	    }	    (void) js_GetToken(cx, ts);	    /* Push a label struct and parse the statement. */	    js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);	    stmtInfo.label = label;	    pn = Statement(cx, ts, tc);	    if (!pn)		return NULL;	    /* Pop the label, set pn_expr, and return early. */	    js_PopStatement(tc);	    pn2->pn_type = TOK_COLON;	    pn2->pn_pos.end = pn->pn_pos.end;	    pn2->pn_expr = pn;	    return pn2;	}	/* Check termination of (possibly multi-line) function expression. */	if (pn2->pn_pos.end.lineno == ts->lineno &&	    !WellTerminated(cx, ts, lastExprType)) {	    return NULL;	}#if JS_HAS_FUN_EXPR_STMT        /* ECMA ed. 3 extension: function expression statement is a closure. */        if (pn2->pn_type == TOK_FUNCTION)            pn2->pn_op = JSOP_CLOSURE;#endif	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY);	if (!pn)	    return NULL;	pn->pn_type = TOK_SEMI;	pn->pn_pos = pn2->pn_pos;	pn->pn_kid = pn2;	break;    }    (void) js_MatchToken(cx, ts, TOK_SEMI);    return pn;}static JSParseNode *Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn, *pn2;    JSObject *obj, *pobj;    JSFunction *fun;    JSClass *clasp;    JSPropertyOp getter, setter, currentGetter, currentSetter;    JSAtom *atom;    JSAtomListElement *ale;    JSProperty *prop;    JSScopeProperty *sprop;    JSBool ok;    /*     * The tricky part of this code is to create special parsenode opcodes for     * getting and setting variables (which will be stored as special slots in     * the frame).  The complex special case is an eval() inside a function.     * If the evaluated string references variables in the enclosing function,     * then we need to generate the special variable opcodes.  We determine     * this by looking up the variable id in the current variable scope.     */    JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_VAR);    pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST);    if (!pn)	return NULL;    pn->pn_op = CURRENT_TOKEN(ts).t_op;    pn->pn_extra = JS_FALSE;            /* assume no JSOP_POP needed */    PN_INIT_LIST(pn);    obj = js_FindVariableScope(cx, &fun);    if (!obj)	return NULL;    clasp = OBJ_GET_CLASS(cx, obj);    if (fun && clasp == &js_FunctionClass) {	/* We are compiling code inside a function */	getter = js_GetLocalVariable;	setter = js_SetLocalVariable;    } else if (fun && clasp == &js_CallClass) {	/* We are compiling code from an eval inside a function */	getter = js_GetCallVariable;	setter = js_SetCallVariable;    } else {	getter = clasp->getProperty;	setter = clasp->setProperty;    }    ok = JS_TRUE;    do {        currentGetter = getter;        currentSetter = setter;	MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);	atom = CURRENT_TOKEN(ts).t_atom;        ATOM_LIST_SEARCH(ale, &tc->decls, atom);        if (ale &&            (JS_HAS_STRICT_OPTION(cx) ||             pn->pn_op == JSOP_DEFCONST ||             ale->index == JSOP_DEFCONST))        {            js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,                                        JSMSG_REDECLARED_VAR,                                        (ale->index == JSOP_DEFCONST)                                        ? js_const_str                                        : js_var_str,                                        ATOM_BYTES(atom));            return NULL;        }        ale = js_IndexAtom(cx, atom, &tc->decls);        if (!ale)            return NULL;        ale->index = (jsatomid) pn->pn_op;	pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME);	if (!pn2)	    return NULL;	pn2->pn_op = JSOP_NAME;	pn2->pn_atom = atom;	pn2->pn_expr = NULL;	pn2->pn_slot = -1;        pn2->pn_attrs = 0;	PN_APPEND(pn, pn2);	if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop))	    return NULL;	if (pobj == obj &&	    OBJ_IS_NATIVE(pobj) &&	    (sprop = (JSScopeProperty *)prop) != NULL) {	    if (SPROP_GETTER(sprop, pobj) == js_GetArgument) {		currentGetter = js_GetArgument;		currentSetter = js_SetArgument;                if (JS_HAS_STRICT_OPTION(cx)) {                    js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,                                                JSMSG_VAR_HIDES_ARG,                                                ATOM_BYTES(atom));                    ok = JS_FALSE;                }	    } else {		ok = JS_TRUE;		if (fun) {		    /* Not an argument, must be a redeclared local var. */		    if (clasp == &js_FunctionClass) {			JS_ASSERT(SPROP_GETTER(sprop,pobj) == js_GetLocalVariable);			JS_ASSERT(JSVAL_IS_INT(sprop->id) &&				  JSVAL_TO_INT(sprop->id) < fun->nvars);		    } else if (clasp == &js_CallClass) {			if (SPROP_GETTER(sprop, pobj) == js_GetCallVariable) {			    /*			     * Referencing a variable introduced by a var			     * statement in the enclosing function. Check			     * that the slot number we have is in range.			     */			    JS_ASSERT(JSVAL_IS_INT(sprop->id) &&				      JSVAL_TO_INT(sprop->id) < fun->nvars);			} else {			    /*			     * A variable introduced through another eval:			     * don't use the special getters and setters			     * since we can't allocate a slot in the frame.			     */			    currentGetter = SPROP_GETTER(sprop, pobj);			    currentSetter = SPROP_SETTER(sprop, pobj);			}		    }		} else {		    /* Global var: (re-)set id a la js_DefineProperty. */		    sprop->id = ATOM_KEY(atom);		}		SPROP_GETTER(sprop, pobj) = currentGetter;		SPROP_SETTER(sprop, pobj) = currentSetter;		sprop->attrs |= JSPROP_ENUMERATE | JSPROP_PERMANENT;		sprop->attrs &= ~JSPROP_READONLY;	    }	} else {	    /*	     * Property not found in current variable scope: we have not seen             * this variable before.  Define a new local variable by adding a             * property to the function's scope, allocating one slot in the             * function's frame.  Global variables and any locals declared in             * with statement bodies are handled at runtime, by script prolog             * JSOP_DEFVAR bytecodes generated for slot-less vars.	     */	    sprop = NULL;	    if (prop) {		OBJ_DROP_PROPERTY(cx, pobj, prop);		prop = NULL;	    }	    if (currentGetter == js_GetCallVariable) {		/* Can't increase fun->nvars in an active frame! */		currentGetter = clasp->getProperty;		currentSetter = clasp->setProperty;	    }	    if (currentGetter == js_GetLocalVariable && !InWithStatement(tc)) {		ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID,					 currentGetter, currentSetter,                                         (pn->pn_op == JSOP_DEFCONST)					 ? JSPROP_ENUMERATE | JSPROP_PERMANENT |                                           JSPROP_READONLY                                         : JSPROP_ENUMERATE | JSPROP_PERMANENT,					 &prop);		if (ok) {		    pobj = obj;		    /*		     * Allocate more room for variables in the function's		     * frame.  We can do this only before the function is		     * first called.		     */		    sprop = (JSScopeProperty *)prop;		    sprop->id = INT_TO_JSVAL(fun->nvars++);		}	    }	}	if (js_MatchToken(cx, ts, TOK_ASSIGN)) {	    if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {		js_ReportCompileErrorNumber(cx, ts,JSREPORT_ERROR,					    JSMSG_BAD_VAR_INIT);		ok = JS_FALSE;	    } else {		pn2->pn_expr = AssignExpr(cx, ts, tc);		if (!pn2->pn_expr)		    ok = JS_FALSE;                else if (pn->pn_op == JSOP_DEFCONST)                    pn2->pn_op = JSOP_SETCONST;                else                    pn2->pn_op = JSOP_SETNAME;	    }	}	if (ok &&            fun &&            (clasp == &js_FunctionClass || clasp == &js_CallClass) &&	    !InWithStatement(tc))	{	    /* Depending on the value of the getter, change the	     * opcodes to the forms for arguments and variables.	     */	    if (currentGetter == js_GetArgument) {		JS_ASSERT(sprop && JSVAL_IS_INT(sprop->id));		pn2->pn_op = (pn2->pn_op == JSOP_NAME)			     ? JSOP_GETARG			     : JSOP_SETARG;		pn2->pn_slot = JSVAL_TO_INT(sprop->id);	    } else if (currentGetter == js_GetLocalVariable ||		       currentGetter == js_GetCallVariable)	    {		JS_ASSERT(sprop && JSVAL_IS_INT(sprop->id));		pn2->pn_op = (pn2->pn_op == JSOP_NAME)			     ? JSOP_GETVAR			     : JSOP_SETVAR;		pn2->pn_slot = JSVAL_TO_INT(sprop->id);	    }	}	if (prop)	    OBJ_DROP_PROPERTY(cx, pobj, prop);	if (!ok)	    return NULL;    } while (js_MatchToken(cx, ts, TOK_COMMA));    pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;    return pn;}static JSParseNode *Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn, *pn2;    pn = AssignExpr(cx, ts, tc);    if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {	pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST);	if (!pn2)	    return NULL;	pn2->pn_pos.begin = pn->pn_pos.begin;	PN_INIT_LIST_1(pn2, pn);	pn = pn2;	do {	    pn2 = AssignExpr(cx, ts, tc);	    if (!pn2)		return NULL;	    PN_APPEND(pn, pn2);	} while (js_MatchToken(cx, ts, TOK_COMMA));	pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;    }    return pn;}static JSBoolLookupArgOrVar(JSContext *cx, JSAtom *atom, JSTreeContext *tc,	       JSOp *opp, jsint *slotp, uintN *attrsp){    JSObject *obj, *pobj;    JSClass *clasp;    JSFunction *fun;    JSScopeProperty *sprop;    obj = js_FindVariableScope(cx, &fun);    clasp = OBJ_GET_CLASS(cx, obj);    *opp = JSOP_NAME;    if (clasp != &js_FunctionClass && clasp != &js_CallClass)	return JS_TRUE;    if (InWithStatement(tc))	return JS_TRUE;    if (!js_LookupProperty(cx, obj, (jsid)atom, &pobj, (JSProperty **)&sprop))	return JS_FALSE;    *slotp = -1;    if (sprop) {        JSPropertyOp getter = SPROP_GETTER(sprop, pobj);	if (getter == js_GetArgument) {	    *opp = JSOP_GETARG;	    *slotp = JSVAL_TO_INT(sprop->id);	} else if (getter == js_GetLocalVariable ||		   getter == js_GetCallVariable)	{	    *opp = JSOP_GETVAR;	    *slotp = JSVAL_TO_INT(sprop->id);	}        *attrsp = sprop->attrs;	OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);    }    return JS_TRUE;}static JSParseNode *AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn, *pn2;    JSTokenType tt;    JSOp op;    pn = CondExpr(cx, ts, tc);    if (!pn)        return NULL;    tt = js_GetToken(cx, ts);#if JS_HAS_GETTER_SETTER    if (tt == TOK_NAME) {        tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);        if (tt == TOK_ERROR)            return NULL;    }#endif    if (tt != TOK_ASSIGN) {        js_UngetToken(ts);        return pn;    }    op = CURRENT_TOKEN(ts).t_op;    for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)        ;    switch (pn2->pn_type) {      case TOK_NAME:        if (pn2->pn_slot >= 0) {            JS_ASSERT(pn2->pn_op == JSOP_GETARG || pn2->pn_op == JSOP_GETVAR);            if (pn2->pn_op == JSOP_GETARG) {                pn2->pn_op = JSOP_SETARG;            } else {                JS_ASSERT(pn2->pn_op == JSOP_GETVAR);                if (pn2->pn_attrs & JSPROP_READONLY)                    return AssignExpr(cx, ts, tc);                pn2->pn_op = JSOP_SETVAR;            }        } else {            pn2->pn_op = JSOP_SETNAME;        }        break;      case TOK_DOT:        pn2->pn_op = JSOP_SETPROP;        break;      case TOK_LB:        pn2->pn_op = JSOP_SETELEM;        break;      default:        js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,                                    JSMSG_BAD_LEFTSIDE_OF_ASS);        return NULL;    }    pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc));    return pn;}static JSParseNode *CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn, *pn1, *pn2, *pn3;#if JS_HAS_IN_OPERATOR    uintN oldflags;#endif /* JS_HAS_IN_OPERATOR */    pn = OrExpr(cx, ts, tc);    if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {	pn1 = pn;	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY);	if (!pn)	    return NULL;#if JS_HAS_IN_OPERATOR	/*	 * Always accept the 'in' operator in the middle clause of a ternary,	 * where it's unambiguous, even if we might be parsing the init of a	 * for statement.	 */	oldflags = tc->flags;	tc->flags &= ~TCF_IN_FOR_INIT;#endif /* JS_HAS_IN_OPERATOR */	pn2 = AssignExpr(cx, ts, tc);#if JS_HAS_IN_OPERATOR	tc->flags = oldflags;#endif /* JS_HAS_IN_OPERATOR */	if (!pn2)	    return NULL;	MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);	pn3 = AssignExpr(cx, ts, tc);	if (!pn3)	    return NULL;	pn->pn_pos.begin = pn1->pn_pos.begin;	pn->pn_pos.end = pn3->pn_pos.end;	pn->pn_kid1 = pn1;	pn->pn_kid2 = pn2;	pn->pn_kid3 = pn3;    }    return pn;}static JSParseNode *OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn;    pn = AndExpr(cx, ts, tc);    if (pn && js_MatchToken(cx, ts, TOK_OR))	pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc));    return pn;}static JSParseNode *AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn;    pn = BitOrExpr(cx, ts, tc);    if (pn && js_MatchToken(cx, ts, TOK_AND))	pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc));    return pn;}static JSParseNode *BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn;    pn = BitXorExpr(cx, ts, tc);    while (pn && js_MatchToken(cx, ts, TOK_BITOR))	pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc));    return pn;}static JSParseNode *BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn;    pn = BitAndExpr(cx, ts, tc);    while (pn && js_MatchToken(cx, ts, TOK_BITXOR))	pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc));    return pn;}static JSParseNode *BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn;    pn = EqExpr(cx, ts, tc);    while (pn && js_MatchToken(cx, ts, TOK_BITAND))	pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc));    return pn;}static JSParseNode *EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn;    JSOp op;    pn = RelExpr(cx, ts, tc);    while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {	op = CURRENT_TOKEN(ts).t_op;	pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc));    }    return pn;}static JSParseNode *RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn;    JSTokenType tt;    JSOp op;#if JS_HAS_IN_OPERATOR    uintN inForInitFlag;    inForInitFlag = tc->flags & TCF_IN_FOR_INIT;    /*     * Uses of the in operator in ShiftExprs are always unambiguous,     * so unset the flag that prohibits recognizing it.     */    tc->flags &= ~TCF_IN_FOR_INIT;#endif /* JS_HAS_IN_OPERATOR */    pn = ShiftExpr(cx, ts, tc);    while (pn &&	   (js_MatchToken(cx, ts, TOK_RELOP)#if JS_HAS_IN_OPERATOR	    /*	     * Recognize the 'in' token as an operator only if we're not	     * currently in the init expr of a for loop.	     */	    || (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN))#endif /* JS_HAS_IN_OPERATOR */#if JS_HAS_INSTANCEOF	    || js_MatchToken(cx, ts, TOK_INSTANCEOF)#endif /* JS_HAS_INSTANCEOF */	    )) {	tt = CURRENT_TOKEN(ts).type;	op = CURRENT_TOKEN(ts).t_op;	pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc));    }#if JS_HAS_IN_OPERATOR    /* Restore previous state of inForInit flag. */    tc->flags |= inForInitFlag;#endif /* JS_HAS_IN_OPERATOR */    return pn;}static JSParseNode *ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn;    JSOp op;    pn = AddExpr(cx, ts, tc);    while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {	op = CURRENT_TOKEN(ts).t_op;	pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc));    }    return pn;}static JSParseNode *AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn;    JSTokenType tt;    JSOp op;    pn = MulExpr(cx, ts, tc);    while (pn &&	   (js_MatchToken(cx, ts, TOK_PLUS) ||	    js_MatchToken(cx, ts, TOK_MINUS))) {	tt = CURRENT_TOKEN(ts).type;	op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;	pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc));    }    return pn;}static JSParseNode *MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSParseNode *pn;    JSTokenType tt;    JSOp op;    pn = UnaryExpr(cx, ts, tc);    while (pn &&	   (js_MatchToken(cx, ts, TOK_STAR) ||	    js_MatchToken(cx, ts, TOK_DIVOP))) {	tt = CURRENT_TOKEN(ts).type;	op = CURRENT_TOKEN(ts).t_op;	pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc));    }    return pn;}static JSParseNode *SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,	   const char *name){    while (kid->pn_type == TOK_RP)	kid = kid->pn_kid;    if (kid->pn_type != TOK_NAME &&	kid->pn_type != TOK_DOT &&	kid->pn_type != TOK_LB) {	js_ReportCompileErrorNumber(cx, ts, JSREPORT_ERROR,				    JSMSG_BAD_OPERAND, name);	return NULL;    }    pn->pn_kid = kid;    return kid;}static const char *incop_name_str[] = {"increment", "decrement"};static JSBoolSetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,	    JSParseNode *pn, JSParseNode *kid,	    JSTokenType tt, JSBool preorder){    jsint num;    JSOp op;    uintN attrs;    kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);    if (!kid)	return JS_FALSE;    num = -1;    switch (kid->pn_type) {      case TOK_NAME:	if (!LookupArgOrVar(cx, kid->pn_atom, tc, &op, &num, &attrs))	    return JS_FALSE;	if (op == JSOP_GETARG) {	    op = (tt == TOK_INC)		 ? (preorder ? JSOP_INCARG : JSOP_ARGINC)		 : (preorder ? JSOP_DECARG : JSOP_ARGDEC);	} else if (op == JSOP_GETVAR) {            if ((attrs & JSPROP_READONLY) == 0) {                op = (tt == TOK_INC)                     ? (preorder ? JSOP_INCVAR : JSOP_VARINC)                     : (preorder ? JSOP_DECVAR : JSOP_VARDEC);            }	} else {	    op = (tt == TOK_INC)		 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)		 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);	}	break;      case TOK_DOT:	op = (tt == TOK_INC)	     ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)	     : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);	break;      case TOK_LB:	op = (tt == TOK_INC)	     ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)	     : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);	break;      default:	JS_ASSERT(0);    }    pn->pn_op = op;    pn->pn_num = num;    return JS_TRUE;}static JSParseNode *UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc){    JSTokenType tt;    JSParseNode *pn, *pn2;    ts->flags |= TSF_REGEXP;    tt = js_GetToken(cx, ts);    ts->flags &= ~TSF_REGEXP;    switch (tt) {      case TOK_UNARYOP:      case TOK_PLUS:      case TOK_MINUS:	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY);	if (!pn)	    return NULL;	pn->pn_type = TOK_UNARYOP;	/* PLUS and MINUS are binary */	pn->pn_op = CURRENT_TOKEN(ts).t_op;	pn2 = UnaryExpr(cx, ts, tc);	if (!pn2)	    return NULL;	pn->pn_pos.end = pn2->pn_pos.end;	pn->pn_kid = pn2;	break;      case TOK_INC:      case TOK_DEC:	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY);	if (!pn)	    return NULL;	pn2 = MemberExpr(cx, ts, tc, JS_TRUE);	if (!pn2)	    return NULL;	if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))	    return NULL;	pn->pn_pos.end = pn2->pn_pos.end;	break;      case TOK_DELETE:	pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY);	if (!pn)	    return NULL;	pn2 = UnaryExpr(cx, ts, tc);	if (!pn2)	    return NULL;	if (!SetLvalKid(cx, ts, pn, pn2, js_delete_str))	    return NULL;	pn->pn_pos.end = pn2->pn_pos.end;	break;      case TOK_ERROR:	return NULL;      default:	js_UngetToken(ts);	pn = MemberExpr(cx, ts, tc, JS_TRUE);	if (!pn)	    return NULL;	/* Don't look across a newline boundary for a postfix incop. */	if (pn->pn_pos.end.lineno == ts->lineno) {	    tt = js_PeekTokenSameLine(cx, ts);	    if (tt == TOK_INC || tt == TOK_DEC) {		(void) js_GetToken(cx, ts);		pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY);		if (!pn2)		    return NULL;		if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))		    return NULL;		pn2->pn_pos.begin = pn->pn_pos.begin;		pn = pn2;	    }	}	break;    }    return pn;}