Bug 408271: check for missing return when parsing a function body in one place. r,a=brendan

This commit is contained in:
igor@mir2.org 2007-12-21 11:11:46 -08:00
parent ca0549b22f
commit 365cedcc72
3 changed files with 102 additions and 107 deletions

View File

@ -89,7 +89,6 @@ js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, JSParseContext *pc,
{ {
memset(cg, 0, sizeof *cg); memset(cg, 0, sizeof *cg);
TREE_CONTEXT_INIT(&cg->treeContext, pc); TREE_CONTEXT_INIT(&cg->treeContext, pc);
cg->treeContext.flags |= TCF_COMPILING;
cg->codePool = codePool; cg->codePool = codePool;
cg->notePool = notePool; cg->notePool = notePool;
cg->codeMark = JS_ARENA_MARK(codePool); cg->codeMark = JS_ARENA_MARK(codePool);
@ -1842,9 +1841,10 @@ EmitSlotIndexOp(JSContext *cx, JSOp op, uintN slot, uintN index,
* in js_EmitTree. * in js_EmitTree.
*/ */
static JSBool static JSBool
BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
uintN decltype) uintN decltype)
{ {
JSTreeContext *tc;
JSAtom *atom; JSAtom *atom;
JSStmtInfo *stmt; JSStmtInfo *stmt;
jsint slot; jsint slot;
@ -1869,6 +1869,7 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
* as this node. FIXME: we should be able to optimize catch vars to be * as this node. FIXME: we should be able to optimize catch vars to be
* block-locals. * block-locals.
*/ */
tc = &cg->treeContext;
atom = pn->pn_atom; atom = pn->pn_atom;
if (decltype != VAR_DECL && if (decltype != VAR_DECL &&
(stmt = js_LexicalLookup(tc, atom, &slot, decltype))) { (stmt = js_LexicalLookup(tc, atom, &slot, decltype))) {
@ -1960,8 +1961,7 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
constOp = (ALE_JSOP(ale) == JSOP_DEFCONST); constOp = (ALE_JSOP(ale) == JSOP_DEFCONST);
/* Index atom so we can map fast global number to name. */ /* Index atom so we can map fast global number to name. */
JS_ASSERT(tc->flags & TCF_COMPILING); ale = js_IndexAtom(cx, atom, &cg->atomList);
ale = js_IndexAtom(cx, atom, &((JSCodeGenerator *) tc)->atomList);
if (!ale) if (!ale)
return JS_FALSE; return JS_FALSE;
@ -2071,7 +2071,7 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
* pop bytecode. * pop bytecode.
*/ */
static JSBool static JSBool
CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, CheckSideEffects(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
JSBool *answer) JSBool *answer)
{ {
JSBool ok; JSBool ok;
@ -2120,14 +2120,14 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
*answer = JS_TRUE; *answer = JS_TRUE;
} else { } else {
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
ok &= CheckSideEffects(cx, tc, pn2, answer); ok &= CheckSideEffects(cx, cg, pn2, answer);
} }
break; break;
case PN_TERNARY: case PN_TERNARY:
ok = CheckSideEffects(cx, tc, pn->pn_kid1, answer) && ok = CheckSideEffects(cx, cg, pn->pn_kid1, answer) &&
CheckSideEffects(cx, tc, pn->pn_kid2, answer) && CheckSideEffects(cx, cg, pn->pn_kid2, answer) &&
CheckSideEffects(cx, tc, pn->pn_kid3, answer); CheckSideEffects(cx, cg, pn->pn_kid3, answer);
break; break;
case PN_BINARY: case PN_BINARY:
@ -2145,9 +2145,9 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
if (pn2->pn_type != TOK_NAME) { if (pn2->pn_type != TOK_NAME) {
*answer = JS_TRUE; *answer = JS_TRUE;
} else { } else {
if (!BindNameToSlot(cx, tc, pn2, 0)) if (!BindNameToSlot(cx, cg, pn2, 0))
return JS_FALSE; return JS_FALSE;
if (!CheckSideEffects(cx, tc, pn->pn_right, answer)) if (!CheckSideEffects(cx, cg, pn->pn_right, answer))
return JS_FALSE; return JS_FALSE;
if (!*answer && if (!*answer &&
(pn->pn_op != JSOP_NOP || (pn->pn_op != JSOP_NOP ||
@ -2168,7 +2168,7 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
case PN_UNARY: case PN_UNARY:
switch (pn->pn_type) { switch (pn->pn_type) {
case TOK_RP: case TOK_RP:
ok = CheckSideEffects(cx, tc, pn->pn_kid, answer); ok = CheckSideEffects(cx, cg, pn->pn_kid, answer);
break; break;
case TOK_DELETE: case TOK_DELETE:
@ -2187,7 +2187,7 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
*answer = JS_TRUE; *answer = JS_TRUE;
break; break;
default: default:
ok = CheckSideEffects(cx, tc, pn2, answer); ok = CheckSideEffects(cx, cg, pn2, answer);
break; break;
} }
break; break;
@ -2211,7 +2211,7 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
* defaulted to JSOP_NOP). * defaulted to JSOP_NOP).
*/ */
if (pn->pn_type == TOK_NAME && pn->pn_op != JSOP_NOP) { if (pn->pn_type == TOK_NAME && pn->pn_op != JSOP_NOP) {
if (!BindNameToSlot(cx, tc, pn, 0)) if (!BindNameToSlot(cx, cg, pn, 0))
return JS_FALSE; return JS_FALSE;
if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) { if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) {
/* /*
@ -2223,10 +2223,8 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
} }
pn2 = pn->pn_expr; pn2 = pn->pn_expr;
if (pn->pn_type == TOK_DOT) { if (pn->pn_type == TOK_DOT) {
if (pn2->pn_type == TOK_NAME && if (pn2->pn_type == TOK_NAME && !BindNameToSlot(cx, cg, pn2, 0))
!BindNameToSlot(cx, tc, pn2, 0)) {
return JS_FALSE; return JS_FALSE;
}
if (!(pn2->pn_op == JSOP_ARGUMENTS && if (!(pn2->pn_op == JSOP_ARGUMENTS &&
pn->pn_atom == cx->runtime->atomState.lengthAtom)) { pn->pn_atom == cx->runtime->atomState.lengthAtom)) {
/* /*
@ -2236,7 +2234,7 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
*answer = JS_TRUE; *answer = JS_TRUE;
} }
} }
ok = CheckSideEffects(cx, tc, pn2, answer); ok = CheckSideEffects(cx, cg, pn2, answer);
break; break;
case PN_NULLARY: case PN_NULLARY:
@ -2253,7 +2251,7 @@ EmitNameOp(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
{ {
JSOp op; JSOp op;
if (!BindNameToSlot(cx, &cg->treeContext, pn, 0)) if (!BindNameToSlot(cx, cg, pn, 0))
return JS_FALSE; return JS_FALSE;
op = PN_OP(pn); op = PN_OP(pn);
@ -2349,7 +2347,7 @@ EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg,
* - varname.prop into JSOP_GETVARPROP * - varname.prop into JSOP_GETVARPROP
* - localname.prop into JSOP_GETLOCALPROP * - localname.prop into JSOP_GETLOCALPROP
*/ */
if (!BindNameToSlot(cx, &cg->treeContext, pn2, 0)) if (!BindNameToSlot(cx, cg, pn2, 0))
return JS_FALSE; return JS_FALSE;
switch (pn2->pn_op) { switch (pn2->pn_op) {
case JSOP_ARGUMENTS: case JSOP_ARGUMENTS:
@ -2461,7 +2459,7 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
* one or more index expression and JSOP_GETELEM op pairs. * one or more index expression and JSOP_GETELEM op pairs.
*/ */
if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) { if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) {
if (!BindNameToSlot(cx, &cg->treeContext, left, 0)) if (!BindNameToSlot(cx, cg, left, 0))
return JS_FALSE; return JS_FALSE;
if (left->pn_op == JSOP_ARGUMENTS && if (left->pn_op == JSOP_ARGUMENTS &&
JSDOUBLE_IS_INT(next->pn_dval, slot) && JSDOUBLE_IS_INT(next->pn_dval, slot) &&
@ -2536,7 +2534,7 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
if (op == JSOP_GETELEM && if (op == JSOP_GETELEM &&
left->pn_type == TOK_NAME && left->pn_type == TOK_NAME &&
right->pn_type == TOK_NUMBER) { right->pn_type == TOK_NUMBER) {
if (!BindNameToSlot(cx, &cg->treeContext, left, 0)) if (!BindNameToSlot(cx, cg, left, 0))
return JS_FALSE; return JS_FALSE;
if (left->pn_op == JSOP_ARGUMENTS && if (left->pn_op == JSOP_ARGUMENTS &&
JSDOUBLE_IS_INT(right->pn_dval, slot) && JSDOUBLE_IS_INT(right->pn_dval, slot) &&
@ -3273,13 +3271,12 @@ static JSBool
EmitDestructuringDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, EmitDestructuringDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
JSParseNode *pn) JSParseNode *pn)
{ {
JSOp decltype;
JS_ASSERT(pn->pn_type == TOK_NAME); JS_ASSERT(pn->pn_type == TOK_NAME);
if (!BindNameToSlot(cx, &cg->treeContext, pn, decltype = (prologOp == JSOP_NOP) ? LET_DECL : VAR_DECL;
(prologOp == JSOP_NOP) if (!BindNameToSlot(cx, cg, pn, decltype))
? LET_DECL
: VAR_DECL)) {
return JS_FALSE; return JS_FALSE;
}
JS_ASSERT(pn->pn_op != JSOP_ARGUMENTS); JS_ASSERT(pn->pn_op != JSOP_ARGUMENTS);
return MaybeEmitVarDecl(cx, cg, prologOp, pn, NULL); return MaybeEmitVarDecl(cx, cg, prologOp, pn, NULL);
@ -3340,10 +3337,8 @@ EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (js_Emit1(cx, cg, JSOP_POP) < 0) if (js_Emit1(cx, cg, JSOP_POP) < 0)
return JS_FALSE; return JS_FALSE;
} else { } else {
if (pn->pn_type == TOK_NAME && if (pn->pn_type == TOK_NAME && !BindNameToSlot(cx, cg, pn, 0))
!BindNameToSlot(cx, &cg->treeContext, pn, 0)) {
return JS_FALSE; return JS_FALSE;
}
switch (pn->pn_op) { switch (pn->pn_op) {
case JSOP_SETNAME: case JSOP_SETNAME:
@ -3715,7 +3710,7 @@ EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
JSBool useful = JS_FALSE; JSBool useful = JS_FALSE;
JS_ASSERT(pn->pn_count == 1); JS_ASSERT(pn->pn_count == 1);
if (!CheckSideEffects(cx, tc, pn2->pn_right, &useful)) if (!CheckSideEffects(cx, cg, pn2->pn_right, &useful))
return JS_FALSE; return JS_FALSE;
if (!useful) if (!useful)
return JS_TRUE; return JS_TRUE;
@ -3762,8 +3757,7 @@ EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
JS_ASSERT(pn2->pn_type == TOK_NAME); JS_ASSERT(pn2->pn_type == TOK_NAME);
#endif #endif
if (!BindNameToSlot(cx, &cg->treeContext, pn2, if (!BindNameToSlot(cx, cg, pn2, let ? LET_DECL : VAR_DECL))
let ? LET_DECL : VAR_DECL))
return JS_FALSE; return JS_FALSE;
JS_ASSERT(pn2->pn_slot >= 0 || !let); JS_ASSERT(pn2->pn_slot >= 0 || !let);
@ -3790,7 +3784,7 @@ EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
JSBool useful = JS_FALSE; JSBool useful = JS_FALSE;
JS_ASSERT(pn->pn_count == 1); JS_ASSERT(pn->pn_count == 1);
if (!CheckSideEffects(cx, tc, pn3, &useful)) if (!CheckSideEffects(cx, cg, pn3, &useful))
return JS_FALSE; return JS_FALSE;
if (!useful) if (!useful)
return JS_TRUE; return JS_TRUE;
@ -4409,7 +4403,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
} }
} else { } else {
pn3->pn_op = JSOP_FORNAME; pn3->pn_op = JSOP_FORNAME;
if (!BindNameToSlot(cx, &cg->treeContext, pn3, 0)) if (!BindNameToSlot(cx, cg, pn3, 0))
return JS_FALSE; return JS_FALSE;
op = PN_OP(pn3); op = PN_OP(pn3);
} }
@ -4428,10 +4422,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
case TOK_DOT: case TOK_DOT:
useful = JS_FALSE; useful = JS_FALSE;
if (!CheckSideEffects(cx, &cg->treeContext, pn3->pn_expr, if (!CheckSideEffects(cx, cg, pn3->pn_expr, &useful))
&useful)) {
return JS_FALSE; return JS_FALSE;
}
if (!useful) { if (!useful) {
if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg, JS_FALSE)) if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg, JS_FALSE))
return JS_FALSE; return JS_FALSE;
@ -5194,7 +5186,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
*/ */
useful = wantval = !(cg->treeContext.flags & TCF_IN_FUNCTION); useful = wantval = !(cg->treeContext.flags & TCF_IN_FUNCTION);
if (!useful) { if (!useful) {
if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) if (!CheckSideEffects(cx, cg, pn2, &useful))
return JS_FALSE; return JS_FALSE;
} }
@ -5308,7 +5300,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
atomIndex = (jsatomid) -1; /* quell GCC overwarning */ atomIndex = (jsatomid) -1; /* quell GCC overwarning */
switch (pn2->pn_type) { switch (pn2->pn_type) {
case TOK_NAME: case TOK_NAME:
if (!BindNameToSlot(cx, &cg->treeContext, pn2, 0)) if (!BindNameToSlot(cx, cg, pn2, 0))
return JS_FALSE; return JS_FALSE;
if (pn2->pn_slot >= 0) { if (pn2->pn_slot >= 0) {
atomIndex = (jsatomid) pn2->pn_slot; atomIndex = (jsatomid) pn2->pn_slot;
@ -5681,7 +5673,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
switch (pn2->pn_type) { switch (pn2->pn_type) {
case TOK_NAME: case TOK_NAME:
pn2->pn_op = op; pn2->pn_op = op;
if (!BindNameToSlot(cx, &cg->treeContext, pn2, 0)) if (!BindNameToSlot(cx, cg, pn2, 0))
return JS_FALSE; return JS_FALSE;
op = PN_OP(pn2); op = PN_OP(pn2);
if (pn2->pn_slot >= 0) { if (pn2->pn_slot >= 0) {
@ -5754,7 +5746,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
switch (pn2->pn_type) { switch (pn2->pn_type) {
case TOK_NAME: case TOK_NAME:
pn2->pn_op = JSOP_DELNAME; pn2->pn_op = JSOP_DELNAME;
if (!BindNameToSlot(cx, &cg->treeContext, pn2, 0)) if (!BindNameToSlot(cx, cg, pn2, 0))
return JS_FALSE; return JS_FALSE;
op = PN_OP(pn2); op = PN_OP(pn2);
if (op == JSOP_FALSE) { if (op == JSOP_FALSE) {
@ -5797,7 +5789,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* also JSOP_GROUP for correctly parenthesized decompilation). * also JSOP_GROUP for correctly parenthesized decompilation).
*/ */
useful = JS_FALSE; useful = JS_FALSE;
if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) if (!CheckSideEffects(cx, cg, pn2, &useful))
return JS_FALSE; return JS_FALSE;
if (!useful) { if (!useful) {
off = noteIndex = -1; off = noteIndex = -1;

View File

@ -175,24 +175,34 @@ struct JSTreeContext { /* tree context for semantic checks */
names when flags & TCF_IN_FUNCTION */ names when flags & TCF_IN_FUNCTION */
}; };
#define TCF_COMPILING 0x01 /* generating bytecode; this tc is a cg */ #define TCF_IN_FUNCTION 0x01 /* parsing inside function body */
#define TCF_IN_FUNCTION 0x02 /* parsing inside function body */ #define TCF_RETURN_EXPR 0x02 /* function has 'return expr;' */
#define TCF_RETURN_EXPR 0x04 /* function has 'return expr;' */ #define TCF_RETURN_VOID 0x04 /* function has 'return;' */
#define TCF_RETURN_VOID 0x08 /* function has 'return;' */ #define TCF_IN_FOR_INIT 0x08 /* parsing init expr of for; exclude 'in' */
#define TCF_RETURN_FLAGS 0x0C /* propagate these out of blocks */ #define TCF_FUN_CLOSURE_VS_VAR 0x10 /* function and var with same name */
#define TCF_IN_FOR_INIT 0x10 /* parsing init expr of for; exclude 'in' */ #define TCF_FUN_USES_NONLOCALS 0x20 /* function refers to non-local names */
#define TCF_FUN_CLOSURE_VS_VAR 0x20 /* function and var with same name */ #define TCF_FUN_HEAVYWEIGHT 0x40 /* function needs Call object per call */
#define TCF_FUN_USES_NONLOCALS 0x40 /* function refers to non-local names */ #define TCF_FUN_IS_GENERATOR 0x80 /* parsed yield statement in function */
#define TCF_FUN_HEAVYWEIGHT 0x80 /* function needs Call object per call */ #define TCF_HAS_DEFXMLNS 0x100 /* default xml namespace = ...; parsed */
#define TCF_FUN_IS_GENERATOR 0x100 /* parsed yield statement in function */ #define TCF_HAS_FUNCTION_STMT 0x200 /* block contains a function statement */
#define TCF_FUN_FLAGS 0x1E0 /* flags to propagate from FunctionBody */ #define TCF_GENEXP_LAMBDA 0x400 /* flag lambda from generator expression */
#define TCF_HAS_DEFXMLNS 0x200 /* default xml namespace = ...; parsed */ #define TCF_COMPILE_N_GO 0x800 /* compiler-and-go mode of script, can
#define TCF_HAS_FUNCTION_STMT 0x400 /* block contains a function statement */
#define TCF_GENEXP_LAMBDA 0x800 /* flag lambda from generator expression */
#define TCF_COMPILE_N_GO 0x1000 /* compiler-and-go mode of script, can
optimize name references based on scope optimize name references based on scope
chain */ chain */
/*
* Flags to propagate out of the blocks.
*/
#define TCF_RETURN_FLAGS (TCF_RETURN_EXPR | TCF_RETURN_VOID)
/*
* Flags to propagate from FunctionBody.
*/
#define TCF_FUN_FLAGS (TCF_FUN_IS_GENERATOR | \
TCF_FUN_HEAVYWEIGHT | \
TCF_FUN_USES_NONLOCALS | \
TCF_FUN_CLOSURE_VS_VAR)
#define TREE_CONTEXT_INIT(tc, pc) \ #define TREE_CONTEXT_INIT(tc, pc) \
((tc)->flags = (tc)->ngvars = 0, \ ((tc)->flags = (tc)->ngvars = 0, \
(tc)->globalUses = (tc)->loopyGlobalUses = 0, \ (tc)->globalUses = (tc)->loopyGlobalUses = 0, \

View File

@ -567,6 +567,7 @@ js_CompileScript(JSContext *cx, JSObject *obj, JSPrincipals *principals,
JSStackFrame *fp, frame; JSStackFrame *fp, frame;
JSArenaPool codePool, notePool; JSArenaPool codePool, notePool;
JSCodeGenerator cg; JSCodeGenerator cg;
JSTokenType tt;
JSParseNode *pn; JSParseNode *pn;
JSScript *script; JSScript *script;
#ifdef METER_PARSENODES #ifdef METER_PARSENODES
@ -599,17 +600,42 @@ js_CompileScript(JSContext *cx, JSObject *obj, JSPrincipals *principals,
/* From this point the control must flow via the label out. */ /* From this point the control must flow via the label out. */
cg.treeContext.flags |= tcflags; cg.treeContext.flags |= tcflags;
pn = Statements(cx, &pc.tokenStream, &cg.treeContext);
if (!pn) { /*
script = NULL; * Inline Statements() to emit as we go to save space.
goto out; */
} for (;;) {
if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) { pc.tokenStream.flags |= TSF_OPERAND;
js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL, JSREPORT_ERROR, tt = js_PeekToken(cx, &pc.tokenStream);
JSMSG_SYNTAX_ERROR); pc.tokenStream.flags &= ~TSF_OPERAND;
script = NULL; if (tt <= TOK_EOF) {
goto out; if (tt == TOK_EOF)
break;
JS_ASSERT(tt == TOK_ERROR);
script = NULL;
goto out;
}
pn = Statement(cx, &pc.tokenStream, &cg.treeContext);
if (!pn) {
script = NULL;
goto out;
}
/*
* FIXME bug 346749: let declarations at the top level in a script are
* turned into var declarations and do not introduce block nodes.
*/
JS_ASSERT(!cg.treeContext.blockNode);
if (!js_FoldConstants(cx, pn, &cg.treeContext) ||
!js_EmitTree(cx, &cg, pn)) {
script = NULL;
goto out;
}
RecycleTree(pn, &cg.treeContext);
} }
#ifdef METER_PARSENODES #ifdef METER_PARSENODES
printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n", printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
(char *)sbrk(0) - (char *)before, (char *)sbrk(0) - (char *)before,
@ -620,16 +646,9 @@ js_CompileScript(JSContext *cx, JSObject *obj, JSPrincipals *principals,
#endif #endif
/* /*
* No need to emit bytecode here -- Statements already has, for each
* statement in turn. Search for TCF_COMPILING in Statements, below.
* That flag is set for every tc == &cg->treeContext, and it implies
* that the tc can be downcast to a cg and used to emit code during
* parsing, rather than at the end of the parse phase.
*
* Nowadays the threaded interpreter needs a stop instruction, so we * Nowadays the threaded interpreter needs a stop instruction, so we
* do have to emit that here. * do have to emit that here.
*/ */
JS_ASSERT(cg.treeContext.flags & TCF_COMPILING);
if (js_Emit1(cx, &cg, JSOP_STOP) < 0) { if (js_Emit1(cx, &cg, JSOP_STOP) < 0) {
script = NULL; script = NULL;
goto out; goto out;
@ -1420,50 +1439,24 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
tc->blockNode = pn; tc->blockNode = pn;
PN_INIT_LIST(pn); PN_INIT_LIST(pn);
ts->flags |= TSF_OPERAND; for (;;) {
while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) { ts->flags |= TSF_OPERAND;
tt = js_PeekToken(cx, ts);
ts->flags &= ~TSF_OPERAND; ts->flags &= ~TSF_OPERAND;
if (tt <= TOK_EOF || tt == TOK_RC)
break;
pn2 = Statement(cx, ts, tc); pn2 = Statement(cx, ts, tc);
if (!pn2) { if (!pn2) {
if (ts->flags & TSF_EOF) if (ts->flags & TSF_EOF)
ts->flags |= TSF_UNEXPECTED_EOF; ts->flags |= TSF_UNEXPECTED_EOF;
return NULL; return NULL;
} }
ts->flags |= TSF_OPERAND;
/* Detect a function statement for the TOK_LC case in Statement. */ /* Detect a function statement for the TOK_LC case in Statement. */
if (pn2->pn_type == TOK_FUNCTION && !AT_TOP_LEVEL(tc)) if (pn2->pn_type == TOK_FUNCTION && !AT_TOP_LEVEL(tc))
tc->flags |= TCF_HAS_FUNCTION_STMT; tc->flags |= TCF_HAS_FUNCTION_STMT;
/* If compiling top-level statements, emit as we go to save space. */ PN_APPEND(pn, pn2);
if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {
if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {
/*
* Check pn2 for lack of a final return statement if it is the
* last statement in the block.
*/
tt = js_PeekToken(cx, ts);
if ((tt == TOK_EOF || tt == TOK_RC) &&
!CheckFinalReturn(cx, tc, pn2)) {
tt = TOK_ERROR;
break;
}
/*
* Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
* CheckFinalReturn again.
*/
tc->flags &= ~TCF_RETURN_EXPR;
}
if (!js_FoldConstants(cx, pn2, tc) ||
!js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
tt = TOK_ERROR;
break;
}
RecycleTree(pn2, tc);
} else {
PN_APPEND(pn, pn2);
}
} }
/* /*