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

View File

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

View File

@ -567,6 +567,7 @@ js_CompileScript(JSContext *cx, JSObject *obj, JSPrincipals *principals,
JSStackFrame *fp, frame;
JSArenaPool codePool, notePool;
JSCodeGenerator cg;
JSTokenType tt;
JSParseNode *pn;
JSScript *script;
#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. */
cg.treeContext.flags |= tcflags;
pn = Statements(cx, &pc.tokenStream, &cg.treeContext);
if (!pn) {
script = NULL;
goto out;
}
if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) {
js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL, JSREPORT_ERROR,
JSMSG_SYNTAX_ERROR);
script = NULL;
goto out;
/*
* Inline Statements() to emit as we go to save space.
*/
for (;;) {
pc.tokenStream.flags |= TSF_OPERAND;
tt = js_PeekToken(cx, &pc.tokenStream);
pc.tokenStream.flags &= ~TSF_OPERAND;
if (tt <= TOK_EOF) {
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
printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
(char *)sbrk(0) - (char *)before,
@ -620,16 +646,9 @@ js_CompileScript(JSContext *cx, JSObject *obj, JSPrincipals *principals,
#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
* do have to emit that here.
*/
JS_ASSERT(cg.treeContext.flags & TCF_COMPILING);
if (js_Emit1(cx, &cg, JSOP_STOP) < 0) {
script = NULL;
goto out;
@ -1420,50 +1439,24 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
tc->blockNode = pn;
PN_INIT_LIST(pn);
ts->flags |= TSF_OPERAND;
while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
for (;;) {
ts->flags |= TSF_OPERAND;
tt = js_PeekToken(cx, ts);
ts->flags &= ~TSF_OPERAND;
if (tt <= TOK_EOF || tt == TOK_RC)
break;
pn2 = Statement(cx, ts, tc);
if (!pn2) {
if (ts->flags & TSF_EOF)
ts->flags |= TSF_UNEXPECTED_EOF;
return NULL;
}
ts->flags |= TSF_OPERAND;
/* Detect a function statement for the TOK_LC case in Statement. */
if (pn2->pn_type == TOK_FUNCTION && !AT_TOP_LEVEL(tc))
tc->flags |= TCF_HAS_FUNCTION_STMT;
/* If compiling top-level statements, emit as we go to save space. */
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);
}
PN_APPEND(pn, pn2);
}
/*