static JSParser Statement; static JSParser Variables; static JSParser Expr; static JSParser AssignExpr; static JSParser CondExpr; static JSParser OrExpr; static JSParser AndExpr; static JSParser BitOrExpr; static JSParser BitXorExpr; static JSParser BitAndExpr; static JSParser EqExpr; static JSParser RelExpr; static JSParser ShiftExpr; static JSParser AddExpr; static JSParser MulExpr; static JSParser UnaryExpr; static JSParser MemberExpr; static JSParser PrimaryExpr;   #define MUST_MATCH_TOKEN_THROW(tt, err, throw)                                \ PR_BEGIN_MACRO                                                            \ if (js_GetToken(cx, ts) != tt) {                                      \ js_ReportCompileError(cx, ts, err);                               \ throw;                                                            \ }                                                                     \ PR_END_MACRO  #define MUST_MATCH_TOKEN(tt, err)                                             \ MUST_MATCH_TOKEN_THROW(tt, err, return NULL)   static JSParseNode * NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity) { JSParseNode *pn;  PR_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; PR_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 JSBool WellTerminated(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) { return JS_TRUE; } #endif js_ReportCompileError(cx, ts, "missing ; before statement"); return JS_FALSE; } return JS_TRUE; }   JS_FRIEND_API(JSBool) js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, JSCodeGenerator *cg) { JSStackFrame *fp, frame; JSTokenType stop, 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; }  if (ts->flags & TSF_INTERACTIVE) { SCAN_NEWLINES(ts); stop = TOK_EOL; } else { stop = TOK_EOF; }  ok = JS_TRUE; do { ts->flags |= TSF_REGEXP; tt = js_GetToken(cx, ts); ts->flags &= ~TSF_REGEXP; if (tt == stop || tt <= TOK_EOF) { if (tt == TOK_ERROR) ok = JS_FALSE; break; }  switch (tt) { case 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) goto out; } break;  default: js_UngetToken(ts); pn = Statement(cx, ts, &cg->treeContext); if (pn) { ok = js_FoldConstants(cx, pn); if (!ok) goto out; } break; } if (pn) { ok = js_AllocTryNotes(cx, cg); if (ok) ok = js_EmitTree(cx, cg, pn); } else { ok = JS_FALSE; } } while (ok);  out: cx->fp = fp; if (!ok) CLEAR_PUSHBACK(ts); return ok; }   static JSBool CheckFinalReturn(JSParseNode *pn) { JSBool ok; 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; case TOK_SWITCH:  ok = pn->pn_kid3 && CheckFinalReturn(pn->pn_kid3); for (pn2 = pn->pn_kid2->pn_head; ok && pn2; pn2 = pn2->pn_next) { pn3 = pn2->pn_right; PR_ASSERT(pn3->pn_type == TOK_LC); if (pn3->pn_head) ok &= CheckFinalReturn(PN_LAST(pn3)); } return ok; case TOK_WITH: return CheckFinalReturn(pn->pn_right); case TOK_RETURN: return JS_TRUE; default: return JS_FALSE; } }  static char badreturn_str[] = "function does not always return a value";  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);  #ifdef CHECK_RETURN_EXPR  if (pn && (tc->flags & TCF_RETURN_EXPR)) { if (!CheckFinalReturn(pn)) { js_ReportCompileError(cx, ts, badreturn_str); pn = NULL; } } #endif  cx->fp = fp; tc->flags = oldflags; return pn; }   JSBool js_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; }   ts->token.type = TOK_LC; pn = FunctionBody(cx, ts, fun, &funcg.treeContext); if (!pn) { CLEAR_PUSHBACK(ts); ok = JS_FALSE; } else { ok = js_FoldConstants(cx, pn); if (ok) ok = js_EmitFunctionBody(cx, &funcg, pn, fun); } js_ResetCodeGenerator(cx, &funcg); return ok; }  static JSBool InWithStatement(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; JSAtom *funAtom, *argAtom; JSObject *parent; JSFunction *fun, *outerFun; JSStmtInfo *topStmt; JSBool ok, named; JSObject *pobj; JSScopeProperty *sprop; jsval junk;   pn = NewParseNode(cx, &ts->token, PN_FUNC); if (!pn) return NULL;   if (js_MatchToken(cx, ts, TOK_NAME)) funAtom = ts->token.t_atom; else funAtom = NULL;   parent = js_FindVariableScope(cx, &outerFun); if (!parent) return NULL;   topStmt = tc->topStmt; tc->topStmt = NULL;  #if JS_HAS_LEXICAL_CLOSURE if (!funAtom || cx->fp->scopeChain != parent || InWithStatement(tc)) {  fun = js_NewFunction(cx, NULL, NULL, 0, 0, cx->fp->scopeChain, funAtom); named = JS_FALSE; } else #endif {  fun = js_DefineFunction(cx, parent, funAtom, NULL, 0, JSPROP_ENUMERATE); named = (fun != NULL); } if (!fun) { ok = JS_FALSE; goto out; }   MUST_MATCH_TOKEN_THROW(TOK_LP, "missing ( before formal parameters", ok = JS_FALSE; goto out);  if (!js_MatchToken(cx, ts, TOK_RP)) { do { MUST_MATCH_TOKEN_THROW(TOK_NAME, "missing formal parameter", ok = JS_FALSE; goto out); argAtom = ts->token.t_atom; pobj = NULL; ok = js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj, (JSProperty **)&sprop); if (!ok) goto out; if (sprop && pobj == fun->object) { #ifdef CHECK_ARGUMENT_HIDING if (sprop->getter == js_GetArgument) { OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); js_ReportCompileError(cx, ts, "duplicate formal argument %s", ATOM_BYTES(argAtom)); ok = JS_FALSE; goto out; } #endif sprop->getter = js_GetArgument; sprop->setter = js_SetArgument; sprop->attrs |= JSPROP_ENUMERATE | JSPROP_PERMANENT; } else { if (sprop) { OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); sprop = NULL; } ok = js_DefineProperty(cx, fun->object, (jsid)argAtom, JSVAL_VOID, js_GetArgument, js_SetArgument, JSPROP_ENUMERATE | JSPROP_PERMANENT, (JSProperty **)&sprop); } if (!ok) goto out; PR_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_THROW(TOK_RP, "missing ) after formal parameters", ok = JS_FALSE; goto out); }  MUST_MATCH_TOKEN_THROW(TOK_LC, "missing { before function body", ok = JS_FALSE; goto out); pn->pn_pos.begin = ts->token.pos.begin;  pn2 = FunctionBody(cx, ts, fun, tc); if (!pn2) { ok = JS_FALSE; goto out; }  MUST_MATCH_TOKEN_THROW(TOK_RC, "missing } after function body", ok = JS_FALSE; goto out); pn->pn_pos.end = ts->token.pos.end;  pn->pn_fun = fun; pn->pn_body = pn2; pn->pn_tryCount = tc->tryCount;  #if JS_HAS_LEXICAL_CLOSURE 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;  ok = JS_TRUE; out: tc->topStmt = topStmt; if (!ok) { if (named) (void) OBJ_DELETE_PROPERTY(cx, parent, (jsid)funAtom, &junk); return NULL; } return pn; }  static JSParseNode * FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { return FunctionDef(cx, ts, tc, JS_FALSE); }  #if JS_HAS_LEXICAL_CLOSURE static JSParseNode * FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { return FunctionDef(cx, ts, tc, JS_TRUE); } #endif   static JSParseNode * Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn2; uintN newlines; JSTokenType tt;  PR_ASSERT(ts->token.type == TOK_LC); pn = NewParseNode(cx, &ts->token, PN_LIST); if (!pn) return NULL; PN_INIT_LIST(pn);  newlines = ts->flags & TSF_NEWLINES; if (newlines) HIDE_NEWLINES(ts);  while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) { pn2 = Statement(cx, ts, tc); if (!pn2) { pn = NULL; goto out; } PN_APPEND(pn, pn2); }  pn->pn_pos.end = ts->token.pos.end; if (tt == TOK_ERROR) pn = NULL; out: if (newlines) SCAN_NEWLINES(ts); return pn; }  static JSParseNode * Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn2;  MUST_MATCH_TOKEN(TOK_LP, "missing ( before condition"); pn = Expr(cx, ts, tc); if (!pn) return NULL; MUST_MATCH_TOKEN(TOK_RP, "missing ) after condition");   if (pn->pn_type == TOK_ASSIGN && pn->pn_op == JSOP_NOP) { js_ReportCompileError(cx, ts, "test for equality (==) mistyped as assignment (=)?\n" "Assuming equality test"); pn->pn_type = TOK_EQOP; pn->pn_op = 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_SETNAME2: 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: PR_ASSERT(0); } } return pn; }  static JSBool MatchLabel(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 = ts->token.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_IMPORT static JSParseNode * ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { JSParseNode *pn, *pn2, *pn3; JSTokenType tt;  MUST_MATCH_TOKEN(TOK_NAME, "missing name in import statement"); pn = NewParseNode(cx, &ts->token, PN_NULLARY); if (!pn) return NULL; pn->pn_op = JSOP_NAME; pn->pn_atom = ts->token.t_atom; pn->pn_slot = -1;  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, &ts->token, 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, "missing name after . operator"); pn2->pn_op = JSOP_GETPROP; pn2->pn_atom = ts->token.t_atom; pn2->pn_slot = -1; } pn2->pn_pos.begin = pn->pn_pos.begin; pn2->pn_pos.end = ts->token.pos.end; } else {  pn2 = NewParseNode(cx, &ts->token, PN_BINARY); if (!pn2) return NULL; pn3 = Expr(cx, ts, tc); if (!pn3) return NULL;   MUST_MATCH_TOKEN(TOK_RB, "missing ] in index expression"); pn2->pn_pos.begin = pn->pn_pos.begin; pn2->pn_pos.end = ts->token.pos.end;   if (pn3->pn_type == TOK_STRING) { pn2->pn_type = TOK_DOT; pn2->pn_op = JSOP_GETPROP; pn2->pn_arity = PN_NAME; pn2->pn_expr = pn; pn2->pn_atom = pn3->pn_atom; } else { 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_ReportCompileError(cx, ts, "invalid import expression"); return NULL; } #endif   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, &ts->token, PN_LIST);	if (!pn)	    return NULL;	PN_INIT_LIST(pn);	if (js_MatchToken(cx, ts, TOK_STAR)) {	    pn2 = NewParseNode(cx, &ts->token, PN_NULLARY);	    if (!pn2)	    	return NULL;	    PN_APPEND(pn, pn2);	} else {	    do {		MUST_MATCH_TOKEN(TOK_NAME, "missing name in export statement");		pn2 = NewParseNode(cx, &ts->token, PN_NULLARY);		if (!pn2)		    return NULL;		pn2->pn_op = JSOP_NAME;		pn2->pn_atom = ts->token.t_atom;		pn2->pn_slot = -1;		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, &ts->token, 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, &ts->token, 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:      {	uintN newlines;	JSParseNode *pn5, *pn6;	pn = NewParseNode(cx, &ts->token, PN_TERNARY);	if (!pn)	    return NULL;	MUST_MATCH_TOKEN(TOK_LP, "missing ( before switch expression");	/* pn1 points to the switch's discriminant. */	pn1 = Expr(cx, ts, tc);	if (!pn1)	    return NULL;	MUST_MATCH_TOKEN(TOK_RP, "missing ) after switch expression");	MUST_MATCH_TOKEN(TOK_LC, "missing { before switch body");	/* balance} */	/* pn2 is a list of case nodes, not including the default case. */	pn2 = NewParseNode(cx, &ts->token, PN_LIST);	if (!pn2)	    return NULL;	PN_INIT_LIST(pn2);	/* If not null, pn3 points to the default case's node. */	pn3 = NULL;	js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);	newlines = ts->flags & TSF_NEWLINES;	if (newlines)	    HIDE_NEWLINES(ts);	while ((tt = js_GetToken(cx, ts)) != TOK_RC) {	    switch (tt) {	      case TOK_CASE:		pn4 = NewParseNode(cx, &ts->token, PN_BINARY);		if (!pn4)		    goto bad_switch;		pn4->pn_left = Expr(cx, ts, tc);		if (!pn4->pn_left)		    goto bad_switch;		PN_APPEND(pn2, pn4);		if (pn2->pn_count == PR_BIT(16)) {		    js_ReportCompileError(cx, ts, "too many switch cases");		    goto bad_switch;		}		break;	      case TOK_DEFAULT:		if (pn3) {		    js_ReportCompileError(cx, ts,					  "more than one switch default");		    goto bad_switch;		}		pn4 = NULL;		break;	      case TOK_ERROR:		goto bad_switch;	      default:		js_ReportCompileError(cx, ts, "invalid switch statement");		goto bad_switch;	    }	    MUST_MATCH_TOKEN(TOK_COLON, "missing : after case label");	    pn5 = NewParseNode(cx, &ts->token, PN_LIST);	    if (!pn5)	    	goto bad_switch;	    pn5->pn_type = TOK_LC;	    PN_INIT_LIST(pn5);	    while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&		    tt != TOK_CASE && tt != TOK_DEFAULT) {		if (tt == TOK_ERROR)		    goto bad_switch;		pn6 = Statement(cx, ts, tc);		if (!pn6)		    goto bad_switch;	    	pn5->pn_pos.end = pn6->pn_pos.end;	    	PN_APPEND(pn5, pn6);	    }	    if (pn4) {		pn4->pn_pos.end = pn5->pn_pos.end;		pn4->pn_right = pn5;	    } else {	    	pn3 = pn5;	    }	}	if (newlines)	    SCAN_NEWLINES(ts);	js_PopStatement(tc);	pn->pn_pos.end = pn2->pn_pos.end = ts->token.pos.end;      	pn->pn_kid1 = pn1;      	pn->pn_kid2 = pn2;      	pn->pn_kid3 = pn3;      	return pn;      bad_switch:	if (newlines)	    SCAN_NEWLINES(ts);      	return NULL;      }#endif /* JS_HAS_SWITCH_STATEMENT */      case TOK_WHILE:	pn = NewParseNode(cx, &ts->token, 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, &ts->token, 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, "missing while after do-loop body");	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, &ts->token, PN_BINARY);	if (!pn)	    return NULL;	js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);	MUST_MATCH_TOKEN(TOK_LP, "missing ( after for");	/* balance) */	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 (tt == TOK_VAR) {		(void) js_GetToken(cx, ts);		pn1 = Variables(cx, ts, tc);	    } else {		pn1 = Expr(cx, ts, tc);	    }	    if (!pn1)		return NULL;	}	/*	 * There are three kinds of for/in syntax (see ECMA 12.6.3):	 *  1. for (i in o) ...	 *  2. for (var i in o) ...	 *  3. for (var i = e in o) ...	 * The 'var i = e in o' in 3 will be parsed as a variable declaration	 * with an 'in' expression as its initializer, leaving ts->pushback at	 * the right parenthesis.  This condition tests 1, then 3, then 2:	 */	if (pn1 && 	    (pn1->pn_type == TOK_IN ||	     (pn1->pn_type == TOK_VAR && ts->pushback.type == TOK_RP) ||	     js_MatchToken(cx, ts, TOK_IN))) {	    stmtInfo.type = STMT_FOR_IN_LOOP;	    switch (pn1->pn_type) {	      case TOK_IN:		pn2 = pn1;		pn1 = pn1->pn_left;		break;	      case TOK_VAR:		if (pn1->pn_count != 1) {		    js_ReportCompileError(cx, ts, "invalid for/in variables");		    return NULL;		}		pn2 = pn1->pn_head->pn_expr;		if (pn2) {		    /* pn1 is 'var i = e' -- must pop e before loop. */		    pn1->pn_op = JSOP_POP;		    if (pn2->pn_type == TOK_IN) {			/* Found 'var i = e in o' -- make 'in' be the root. */			PR_ASSERT(ts->pushback.type == TOK_RP);			pn1->pn_head->pn_expr = pn2->pn_left;			pn2->pn_left = pn1;			break;		    }		}		/* FALL THROUGH */	      default:		pn2 = NULL;		break;	    }	    /* Check that the left side of the 'in' is valid. */	    if (pn1->pn_type != TOK_VAR &&		pn1->pn_type != TOK_NAME &&		pn1->pn_type != TOK_DOT &&		pn1->pn_type != TOK_LB) {		js_ReportCompileError(cx, ts, "invalid for/in left-hand side");		return NULL;	    }	    /* Parse the object expression as the right operand of 'in'. */	    if (!pn2) {		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, "missing ; after for-loop initializer");	    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, "missing ; after for-loop condition");	    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, &ts->token, 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;	}	/* (balance: */	MUST_MATCH_TOKEN(TOK_RP, "missing ) after for-loop control");	/* 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:	/*	 * try nodes are ternary.	 * kid1 is the try Statement	 * kid2 is the catch node	 * kid3 is the finally Statement	 */	pn = NewParseNode(cx, &ts->token, PN_TERNARY);	pn1 = Statement(cx, ts, tc);	if (!pn1)	    return NULL;	if (js_PeekToken(cx, ts) == TOK_CATCH) {	    pn2 = NewParseNode(cx, &ts->token, PN_BINARY);	    if (!pn2)		return NULL;	    (void)js_GetToken(cx, ts);	    MUST_MATCH_TOKEN(TOK_LP, "missing ( after catch"); /* balance) */	    tt = js_PeekToken(cx, ts);	    if (tt == TOK_VAR) {		(void)js_GetToken(cx, ts);		pn4 = Variables(cx, ts, tc);		if (!pn4)		    return NULL;		if (pn4->pn_count != 1) {		    js_ReportCompileError(cx, ts,	       "only one variable declaration permitted in catch declaration");		    return NULL;		}	    } else {		pn4 = Expr(cx, ts, tc);		if (!pn4)		    return NULL;		/* restrict the expression to instanceof */		if (pn4->pn_op != JSOP_INSTANCEOF &&		    pn4->pn_op != JSOP_NAME &&		    pn4->pn_op != JSOP_GETARG &&		    pn4->pn_op != JSOP_GETVAR) {		    js_ReportCompileError(cx, ts,	       "catch conditional must be instanceof or variable declaration");		    PR_ASSERT(0);		    return NULL;		}	    }	    /* rewrite the declaration/conditional expr as appropriate */	    switch(pn4->pn_type) {	      case TOK_NAME:		switch(pn4->pn_op) {		  case JSOP_NAME:		    pn4->pn_op = JSOP_SETNAME;		    break;		  case JSOP_GETARG:		    pn4->pn_op = JSOP_SETARG;		    break;		default:		    PR_ASSERT(0);		}		break;	      case TOK_VAR: 		PR_ASSERT(pn4->pn_head->pn_type == TOK_NAME);		switch(pn4->pn_head->pn_op) {		  case JSOP_GETVAR:		    pn4->pn_head->pn_op = JSOP_SETVAR;		    break;		  case JSOP_GETARG:		    pn4->pn_head->pn_op = JSOP_SETARG;		    break;		  case JSOP_NAME:		    pn4->pn_head->pn_op = JSOP_SETNAME;		  case JSOP_NOP:		    break;		  default:		    PR_ASSERT(0);		}		break;	    case TOK_INSTANCEOF:		PR_ASSERT(0);	    default:		PR_ASSERT(0);	    }	    pn2->pn_left = pn4;	    	    /* (balance: */	    MUST_MATCH_TOKEN(TOK_RP, "missing ) after catch declaration");	    pn2->pn_right = Statement(cx, ts, tc);	    if (!pn2->pn_right)		return NULL;	} else {	    pn2 = NULL;	}	if (js_MatchToken(cx, ts, TOK_FINALLY)) {	    pn3 = Statement(cx, ts, tc);	    if (!pn3)		return NULL;	} else {	    pn3 = NULL;	}	if (!pn2 && !pn3) {	    js_ReportCompileError(cx, ts,				  "missing catch or finally after try");	    return NULL;	}	tc->tryCount++;	pn->pn_kid1 = pn1;	pn->pn_kid2 = pn2;	pn->pn_kid3 = pn3;	return pn;      case TOK_THROW:	pn = NewParseNode(cx, &ts->token, 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;#endif /* JS_HAS_EXCEPTIONS */      case TOK_BREAK:	pn = NewParseNode(cx, &ts->token, 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_ReportCompileError(cx, ts, "label not found");		    return NULL;		}		if (stmt->type == STMT_LABEL && stmt->label == label)		    break;	    }	} else {	    for (; ; stmt = stmt->down) {		if (!stmt) {		    js_ReportCompileError(cx, ts, "invalid break");		    return NULL;		}		if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)		    break;	    }	}	if (label)	    pn->pn_pos.end = ts->token.pos.end;	break;      case TOK_CONTINUE:	pn = NewParseNode(cx, &ts->token, 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_ReportCompileError(cx, ts, "label not found");		    return NULL;		}		if (stmt->type == STMT_LABEL) {		    if (stmt->label == label) {			if (!stmt2 || !STMT_IS_LOOP(stmt2)) {			    js_ReportCompileError(cx, ts, "invalid continue");			    return NULL;			}			break;		    }		} else {		    stmt2 = stmt;		}	    }	} else {	    for (; ; stmt = stmt->down) {		if (!stmt) {		    js_ReportCompileError(cx, ts, "invalid continue");		    return NULL;		}		if (STMT_IS_LOOP(stmt))		    break;	    }	}	if (label)	    pn->pn_pos.end = ts->token.pos.end;	break;      case TOK_WITH:	pn = NewParseNode(cx, &ts->token, PN_BINARY);	if (!pn)	    return NULL;	MUST_MATCH_TOKEN(TOK_LP, "missing ( before with-statement object");	/* balance) */	pn2 = Expr(cx, ts, tc);	if (!pn2)	    return NULL;	/* (balance: */	MUST_MATCH_TOKEN(TOK_RP, "missing ) after with-statement object");	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_op = JSOP_POP;	break;      case TOK_RETURN:	if (!(tc->flags & TCF_IN_FUNCTION)) {	    js_ReportCompileError(cx, ts, "invalid return");	    return NULL;	}	pn = NewParseNode(cx, &ts->token, 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 ((tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) ==	    (TCF_RETURN_EXPR | TCF_RETURN_VOID)) {	    js_ReportCompileError(cx, ts, badreturn_str);	    return NULL;	}	break;      case TOK_LC:	js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);	pn = Statements(cx, ts, tc);	if (!pn)	    return NULL;	/* {balance: */	MUST_MATCH_TOKEN(TOK_RC, "missing } in compound statement");	js_PopStatement(tc);	return pn;      case TOK_EOL:      case TOK_SEMI:	pn = NewParseNode(cx, &ts->token, PN_UNARY);	if (!pn)	    return NULL;	pn->pn_type = TOK_SEMI;	pn->pn_kid = NULL;	return pn;      case TOK_ERROR:	return NULL;      default:	lastExprType = ts->token.type;	js_UngetToken(ts);	pn2 = Expr(cx, ts, tc);	if (!pn2)	    return NULL;	tt = ts->pushback.type;	if (tt == TOK_COLON) {	    if (pn2->pn_type != TOK_NAME) {		js_ReportCompileError(cx, ts, "invalid label");		return NULL;	    }	    label = pn2->pn_atom;	    for (stmt = tc->topStmt; stmt; stmt = stmt->down) {		if (stmt->type == STMT_LABEL && stmt->label == label) {		    js_ReportCompileError(cx, ts, "duplicate label");		    return NULL;		}	    }	    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 explicity against (multi-line) function statement */	if (pn2->pn_pos.end.lineno == ts->lineno &&	    !WellTerminated(cx, ts, lastExprType)) {	    return NULL;	}	pn = NewParseNode(cx, &ts->token, 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;}