Set up let declarations properly by finding the correct JSStmtInfo (not missing try or catch

blocks). This required making the 'scope' statements have a bit instead of a statement type
since we can now morph e.g. try statements. This patch also fixes the numbering for let
blocks after the first let block. bug 344370, r=brendan
This commit is contained in:
mrbkap%gmail.com 2006-07-18 21:23:30 +00:00
parent 098829e91d
commit 07a112a37e
3 changed files with 111 additions and 103 deletions

View File

@ -237,13 +237,12 @@ const char js_finally_block_str[] = "finally block";
const char js_script_str[] = "script";
static const char *statementName[] = {
"block", /* BLOCK */
"label statement", /* LABEL */
"if statement", /* IF */
"else statement", /* ELSE */
"switch statement", /* SWITCH */
"block", /* BLOCK */
js_with_statement_str, /* WITH */
"block scope", /* BLOCK_SCOPE */
"catch block", /* CATCH */
"try block", /* TRY */
js_finally_block_str, /* FINALLY */
@ -1230,12 +1229,7 @@ js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,
stmt->label = NULL;
stmt->down = tc->topStmt;
tc->topStmt = stmt;
if (STMT_TYPE_IS_SCOPE(type)) {
stmt->downScope = tc->topScopeStmt;
tc->topScopeStmt = stmt;
} else {
stmt->downScope = NULL;
}
stmt->downScope = NULL;
stmt->blockObj = NULL;
}
@ -1243,8 +1237,11 @@ void
js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSObject *blockObj,
ptrdiff_t top)
{
js_PushStatement(tc, stmt, STMT_BLOCK_SCOPE, top);
js_PushStatement(tc, stmt, STMT_BLOCK, top);
stmt->flags |= SIF_SCOPE;
blockObj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain);
stmt->downScope = tc->topScopeStmt;
tc->topScopeStmt = stmt;
tc->blockChain = stmt->blockObj = blockObj;
}
@ -1365,9 +1362,11 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
return JS_FALSE;
break;
default:;
}
#if JS_HAS_BLOCK_SCOPE
case STMT_BLOCK_SCOPE:
{
if (stmt->flags & SIF_SCOPE) {
uintN i;
/* There is a Block object with locals on the stack to pop. */
@ -1376,11 +1375,8 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
i = OBJ_BLOCK_COUNT(cx, stmt->blockObj);
EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i);
break;
}
#endif
default:;
}
#endif
}
cg->stackDepth = depth;
@ -1441,14 +1437,12 @@ void
js_PopStatement(JSTreeContext *tc)
{
JSStmtInfo *stmt;
JSStmtType type;
stmt = tc->topStmt;
tc->topStmt = stmt->down;
type = stmt->type;
if (STMT_TYPE_IS_SCOPE(type)) {
if (STMT_IS_SCOPE(stmt)) {
tc->topScopeStmt = stmt->downScope;
if (type == STMT_BLOCK_SCOPE) {
if (stmt->flags & SIF_SCOPE) {
tc->blockChain =
JSVAL_TO_OBJECT(stmt->blockObj->slots[JSSLOT_PARENT]);
}
@ -1523,7 +1517,7 @@ LexicalLookup(JSContext *cx, JSTreeContext *tc, JSAtom *atom, jsint *slotp)
continue;
}
JS_ASSERT(stmt->type == STMT_BLOCK_SCOPE);
JS_ASSERT(stmt->flags & SIF_SCOPE);
obj = stmt->blockObj;
if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
return NULL;
@ -1880,7 +1874,7 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
return JS_TRUE;
}
JS_ASSERT(stmt->type == STMT_BLOCK_SCOPE);
JS_ASSERT(stmt->flags & SIF_SCOPE);
JS_ASSERT(slot >= 0);
op = pn->pn_op;
switch (op) {

View File

@ -61,28 +61,30 @@ JS_BEGIN_EXTERN_C
* Also remember to keep the statementName array in jsemit.c in sync.
*/
typedef enum JSStmtType {
STMT_BLOCK = 0, /* compound statement: { s1[;... sN] } */
STMT_LABEL = 1, /* labeled statement: L: s */
STMT_IF = 2, /* if (then) statement */
STMT_ELSE = 3, /* else clause of if statement */
STMT_SWITCH = 4, /* switch statement */
STMT_WITH = 5, /* with statement */
STMT_BLOCK_SCOPE = 6, /* let block/expr or array comprehension */
STMT_CATCH = 7, /* catch block */
STMT_TRY = 8, /* try block */
STMT_FINALLY = 9, /* finally block */
STMT_SUBROUTINE = 10, /* gosub-target subroutine body */
STMT_DO_LOOP = 11, /* do/while loop statement */
STMT_FOR_LOOP = 12, /* for loop statement */
STMT_FOR_IN_LOOP = 13, /* for/in loop statement */
STMT_WHILE_LOOP = 14 /* while loop statement */
STMT_LABEL, /* labeled statement: L: s */
STMT_IF, /* if (then) statement */
STMT_ELSE, /* else clause of if statement */
STMT_SWITCH, /* switch statement */
STMT_BLOCK, /* compound statement: { s1[;... sN] } */
STMT_WITH, /* with statement */
STMT_CATCH, /* catch block */
STMT_TRY, /* try block */
STMT_FINALLY, /* finally block */
STMT_SUBROUTINE, /* gosub-target subroutine body */
STMT_DO_LOOP, /* do/while loop statement */
STMT_FOR_LOOP, /* for loop statement */
STMT_FOR_IN_LOOP, /* for/in loop statement */
STMT_WHILE_LOOP /* while loop statement */
} JSStmtType;
#define STMT_TYPE_IS_SCOPE(type) \
((uintN)((type) - STMT_WITH) <= (uintN)(STMT_CATCH - STMT_WITH))
#define STMT_TYPE_MAYBE_SCOPE(type) \
((type) >= STMT_BLOCK && (type) <= STMT_FINALLY)
#define STMT_TYPE_IS_LOOP(type) ((type) >= STMT_DO_LOOP)
#define STMT_IS_SCOPE(stmt) STMT_TYPE_IS_SCOPE((stmt)->type)
#define STMT_MAYBE_SCOPE(stmt) STMT_TYPE_MAYBE_SCOPE((stmt)->type)
#define STMT_IS_SCOPE(stmt) \
((uintN)(((stmt)->type) - STMT_WITH) <= (uintN)(STMT_CATCH - STMT_WITH) ||\
((stmt)->flags & SIF_SCOPE))
#define STMT_IS_LOOP(stmt) STMT_TYPE_IS_LOOP((stmt)->type)
typedef struct JSStmtInfo JSStmtInfo;
@ -102,6 +104,7 @@ struct JSStmtInfo {
};
#define SIF_BODY_BLOCK 0x0001 /* STMT_BLOCK type is a function body */
#define SIF_SCOPE 0x0002 /* This statement contains a scope. */
#define AT_TOP_LEVEL(tc) \
(!(tc)->topStmt || ((tc)->topStmt->flags & SIF_BODY_BLOCK))

View File

@ -2049,13 +2049,13 @@ ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
#if JS_HAS_BLOCK_SCOPE
static JSStmtInfo *
FindBlockStatement(JSTreeContext *tc)
FindMaybeScopeStatement(JSTreeContext *tc)
{
JSStmtInfo *stmt;
stmt = tc->topStmt;
while (stmt) {
if (stmt->type == STMT_BLOCK || stmt->type == STMT_BLOCK_SCOPE)
if (STMT_MAYBE_SCOPE(stmt))
return stmt;
stmt = stmt->down;
}
@ -2907,73 +2907,84 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
JSObject *obj;
JSAtom *atom;
/* Check for a let statement or let expression. */
if (js_PeekToken(cx, ts) == TOK_LP) {
pn = LetBlock(cx, ts, tc, JS_TRUE);
if (!pn || !(pn->pn_extra & PNX_BLOCKEXPR))
return pn;
} else {
/* Set up the block object. */
stmt = FindBlockStatement(tc);
if (stmt && stmt->type == STMT_BLOCK_SCOPE) {
JS_ASSERT(stmt->blockObj);
obj = stmt->blockObj;
} else {
if (stmt) {
/* Convert the block statement into a scope statement. */
obj = js_NewBlockObject(cx);
if (!obj)
return NULL;
JS_ASSERT(stmt->type == STMT_BLOCK);
JS_ASSERT(stmt->downScope == NULL);
stmt->type = STMT_BLOCK_SCOPE;
stmt->downScope = tc->topScopeStmt;
tc->topScopeStmt = stmt;
obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain);
tc->blockChain = stmt->blockObj = obj;
} else {
/*
* XXX This is a hard case that requires more work. In
* particular, in many cases, we're trying to emit code as
* we go. However, this means that we haven't necessarily
* finished processing all let declarations in the
* implicit top-level block when we emit a reference to
* one of them. For now, punt on this and pretend this is
* a var declaration.
*/
CURRENT_TOKEN(ts).type = TOK_VAR;
CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR;
pn = Variables(cx, ts, tc);
if (!pn)
return NULL;
pn->pn_extra |= PNX_POPVAR;
break;
}
}
pn1 = tc->blockNode;
if (!pn1 || pn1->pn_type != TOK_LEXICALSCOPE) {
/* Create a new lexical scope node for these statements. */
pn1 = NewParseNode(cx, ts, PN_NAME, tc);
if (!pn1)
return NULL;
atom = js_AtomizeObject(cx, obj, 0);
if (!atom)
return NULL;
pn1->pn_type = TOK_LEXICALSCOPE;
pn1->pn_atom = atom;
pn1->pn_expr = tc->blockNode;
pn1->pn_extra = 0;
tc->blockNode = pn1;
}
pn = Variables(cx, ts, tc);
if (!pn)
return NULL;
pn->pn_extra = PNX_POPVAR;
/* Let expressions require automatic semicolon insertion. */
break;
}
/*
* This is a let declaration. We must convert the nearest JSStmtInfo
* to be our scope statement. Further let declarations in this block
* will find this scope statement and use the same block object. If we
* are the first let declaration in this block (i.e., when the nearest
* maybe-scope JSStmtInfo isn't a scope statement) then we also need
* to set tc->blockNode to be our TOK_LEXICALSCOPE.
*/
stmt = FindMaybeScopeStatement(tc);
if (stmt && (stmt->flags & SIF_SCOPE)) {
JS_ASSERT(stmt->blockObj);
obj = stmt->blockObj;
} else {
if (!stmt) {
/*
* XXX This is a hard case that requires more work. In
* particular, in many cases, we're trying to emit code as
* we go. However, this means that we haven't necessarily
* finished processing all let declarations in the
* implicit top-level block when we emit a reference to
* one of them. For now, punt on this and pretend this is
* a var declaration.
*/
CURRENT_TOKEN(ts).type = TOK_VAR;
CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR;
pn = Variables(cx, ts, tc);
if (!pn)
return NULL;
pn->pn_extra |= PNX_POPVAR;
break;
}
/* Convert the block statement into a scope statement. */
obj = js_NewBlockObject(cx);
if (!obj)
return NULL;
JS_ASSERT(stmt->downScope == NULL);
stmt->flags |= SIF_SCOPE;
stmt->downScope = tc->topScopeStmt;
tc->topScopeStmt = stmt;
obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain);
tc->blockChain = stmt->blockObj = obj;
pn1 = tc->blockNode;
JS_ASSERT(!tc->blockNode ||
tc->blockNode->pn_type != TOK_LEXICALSCOPE);
/* Create a new lexical scope node for these statements. */
pn1 = NewParseNode(cx, ts, PN_NAME, tc);
if (!pn1)
return NULL;
atom = js_AtomizeObject(cx, obj, 0);
if (!atom)
return NULL;
pn1->pn_type = TOK_LEXICALSCOPE;
pn1->pn_pos = tc->blockNode->pn_pos;
pn1->pn_atom = atom;
pn1->pn_expr = tc->blockNode;
pn1->pn_extra = 0;
tc->blockNode = pn1;
}
pn = Variables(cx, ts, tc);
if (!pn)
return NULL;
pn->pn_extra = PNX_POPVAR;
break;
}
#endif
@ -3172,7 +3183,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
: JSPROP_PERMANENT;
} else {
args.obj = tc->topScopeStmt->blockObj;
args.u.let.index = 0;
args.u.let.index = OBJ_BLOCK_COUNT(cx, args.obj);
args.u.let.overflow = JSMSG_TOO_MANY_FUN_VARS;
}