Bug 410649: function statement and destructuring parameter name clash now favours the function. r,a=brendan

This commit is contained in:
igor@mir2.org 2008-01-20 02:34:06 -08:00
Родитель a4bd8f9952
Коммит 8e2dc9fe7b
5 изменённых файлов: 112 добавлений и 100 удалений

Просмотреть файл

@ -3928,6 +3928,14 @@ GettableNoteForNextOp(JSCodeGenerator *cg)
} }
#endif #endif
/* Top-level named functions need a nop for decompilation. */
static JSBool
EmitFunctionDefNop(JSContext *cx, JSCodeGenerator *cg, uintN index)
{
return js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)index) >= 0 &&
js_Emit1(cx, cg, JSOP_NOP) >= 0;
}
JSBool JSBool
js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
{ {
@ -3962,9 +3970,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
switch (pn->pn_type) { switch (pn->pn_type) {
case TOK_FUNCTION: case TOK_FUNCTION:
{ {
JSFunction *fun;
void *cg2mark; void *cg2mark;
JSCodeGenerator *cg2; JSCodeGenerator *cg2;
JSFunction *fun;
uintN slot; uintN slot;
#if JS_HAS_XML_SUPPORT #if JS_HAS_XML_SUPPORT
@ -3975,6 +3983,21 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
} }
#endif #endif
fun = GET_FUNCTION_PRIVATE(cx, pn->pn_funpob->object);
if (fun->u.i.script) {
/*
* This second pass is needed to emit JSOP_NOP with a source note
* for the already-emitted function. See comments in the TOK_LC
* case.
*/
JS_ASSERT(pn->pn_op == JSOP_NOP);
JS_ASSERT(cg->treeContext.flags & TCF_IN_FUNCTION);
JS_ASSERT(pn->pn_index != (uint32) -1);
if (!EmitFunctionDefNop(cx, cg, pn->pn_index))
return JS_FALSE;
break;
}
/* Generate code for the function's body. */ /* Generate code for the function's body. */
cg2mark = JS_ARENA_MARK(&cx->tempPool); cg2mark = JS_ARENA_MARK(&cx->tempPool);
JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, &cx->tempPool); JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, &cx->tempPool);
@ -3987,7 +4010,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
pn->pn_pos.begin.lineno); pn->pn_pos.begin.lineno);
cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION); cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION);
cg2->treeContext.maxScopeDepth = pn->pn_sclen; cg2->treeContext.maxScopeDepth = pn->pn_sclen;
fun = GET_FUNCTION_PRIVATE(cx, pn->pn_funpob->object);
cg2->treeContext.fun = fun; cg2->treeContext.fun = fun;
cg2->parent = cg; cg2->parent = cg;
if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) { if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) {
@ -4023,52 +4045,41 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
break; break;
} }
/* Top-level named functions need a nop for decompilation. */
noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)index);
if (noteIndex < 0 ||
js_Emit1(cx, cg, JSOP_NOP) < 0) {
return JS_FALSE;
}
/* /*
* Top-levels also need a prolog op to predefine their names in the * For a script we emit the code as we parse. Thus the bytecode for
* variable object, or if local, to fill their stack slots. * top-level functions should go in the prolog to predefine their
* names in the variable object before the already-generated main code
* is executed. This extra work for top-level scripts is not necessary
* when we emit the code for a function. It is fully parsed prior to
* invocation of the emitter and calls to js_EmitTree for function
* definitions can be scheduled before generating the rest of code.
*/ */
CG_SWITCH_TO_PROLOG(cg);
if (!(cg->treeContext.flags & TCF_IN_FUNCTION)) { if (!(cg->treeContext.flags & TCF_IN_FUNCTION)) {
CG_SWITCH_TO_PROLOG(cg);
/* /*
* Emit JSOP_CLOSURE for eval code to do less checks when * Emit JSOP_CLOSURE for eval code to do fewer checks when
* instantiating top-level functions in the non-eval case. * instantiating top-level functions in the non-eval case.
*/ */
JS_ASSERT(!cg->treeContext.topStmt); JS_ASSERT(!cg->treeContext.topStmt);
op = (cx->fp->flags & JSFRAME_EVAL) ? JSOP_CLOSURE : JSOP_DEFFUN; op = (cx->fp->flags & JSFRAME_EVAL) ? JSOP_CLOSURE : JSOP_DEFFUN;
EMIT_INDEX_OP(op, index); EMIT_INDEX_OP(op, index);
CG_SWITCH_TO_MAIN(cg);
/* Emit NOP for the decompiler. */
if (!EmitFunctionDefNop(cx, cg, index))
return JS_FALSE;
} else { } else {
#ifdef DEBUG #ifdef DEBUG
JSLocalKind localKind = JSLocalKind localKind =
#endif #endif
js_LookupLocal(cx, cg->treeContext.fun, fun->atom, &slot); js_LookupLocal(cx, cg->treeContext.fun, fun->atom, &slot);
JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
JS_ASSERT(pn->pn_index == (uint32) -1);
/* pn->pn_index = index;
* If this local function is declared in a body block induced
* by let declarations, reparent fun->object to the compiler-
* created body block object, so that JSOP_DEFLOCALFUN clones
* that block into the runtime scope chain.
*/
stmt = cg->treeContext.topStmt;
if (stmt && stmt->type == STMT_BLOCK &&
stmt->down && stmt->down->type == STMT_BLOCK &&
(stmt->down->flags & SIF_SCOPE)) {
JS_ASSERT(STOBJ_GET_CLASS(stmt->down->u.blockObj) ==
&js_BlockClass);
OBJ_SET_PARENT(cx, fun->object, stmt->down->u.blockObj);
}
if (!EmitSlotIndexOp(cx, JSOP_DEFLOCALFUN, slot, index, cg)) if (!EmitSlotIndexOp(cx, JSOP_DEFLOCALFUN, slot, index, cg))
return JS_FALSE; return JS_FALSE;
} }
CG_SWITCH_TO_MAIN(cg);
break; break;
} }
@ -5158,6 +5169,27 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
} }
js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top); js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top);
if (pn->pn_extra & PNX_FUNCDEFS) {
/*
* This block contains top-level function definitions. To ensure
* that we emit the bytecode defining them prior the rest of code
* in the block we use a separate pass over functions. During the
* main pass later the emitter will add JSOP_NOP with source notes
* for the function to preserve the original functions position
* when decompiling.
*
* Currently this is used only for functions, as compile-as-we go
* mode for scripts does not allow separate emitter passes.
*/
JS_ASSERT(cg->treeContext.flags & TCF_IN_FUNCTION);
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
if (pn2->pn_type == TOK_FUNCTION) {
JS_ASSERT(pn2->pn_op == JSOP_NOP);
if (!js_EmitTree(cx, cg, pn2))
return JS_FALSE;
}
}
}
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
if (!js_EmitTree(cx, cg, pn2)) if (!js_EmitTree(cx, cg, pn2))
return JS_FALSE; return JS_FALSE;

Просмотреть файл

@ -4717,51 +4717,17 @@ interrupt:
*/ */
slot = GET_VARNO(pc); slot = GET_VARNO(pc);
JS_ASSERT(!fp->blockChain); parent = js_GetScopeChain(cx, fp);
if (!(fp->flags & JSFRAME_POP_BLOCKS)) { if (!parent) {
/* ok = JS_FALSE;
* If the compiler-created function object (obj) is scoped by a goto out;
* let-induced body block, temporarily update fp->blockChain so
* that js_GetScopeChain will clone the block into the runtime
* scope needed to parent the function object's clone.
*/
parent = OBJ_GET_PARENT(cx, obj);
if (parent && OBJ_GET_CLASS(cx, parent) == &js_BlockClass)
fp->blockChain = parent;
parent = js_GetScopeChain(cx, fp);
} else {
/*
* We have already emulated JSOP_ENTERBLOCK for the enclosing
* body block, for a prior JSOP_DEFLOCALFUN in the prolog, so
* we just load fp->scopeChain into parent.
*
* In typical execution scenarios, the prolog bytecodes that
* include this JSOP_DEFLOCALFUN run, then come main bytecodes
* including JSOP_ENTERBLOCK for the outermost (body) block.
* JSOP_ENTERBLOCK will detect that it need not do anything if
* the body block was entered above due to a local function.
* Finally the matching JSOP_LEAVEBLOCK runs.
*
* If the matching JSOP_LEAVEBLOCK for the body block does not
* run for some reason, the body block will be properly "put"
* (via js_PutBlockObject) by the PutBlockObjects call at the
* bottom of js_Interpret.
*/
parent = fp->scopeChain;
JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass);
JS_ASSERT(OBJ_GET_PROTO(cx, parent) == OBJ_GET_PARENT(cx, obj));
JS_ASSERT(OBJ_GET_CLASS(cx, OBJ_GET_PARENT(cx, parent))
== &js_CallClass);
} }
/* If re-parenting, store a clone of the function object. */ SAVE_SP_AND_PC(fp);
if (OBJ_GET_PARENT(cx, obj) != parent) { obj = js_CloneFunctionObject(cx, obj, parent);
SAVE_SP_AND_PC(fp); if (!obj) {
obj = js_CloneFunctionObject(cx, obj, parent); ok = JS_FALSE;
if (!obj) { goto out;
ok = JS_FALSE;
goto out;
}
} }
fp->vars[slot] = OBJECT_TO_JSVAL(obj); fp->vars[slot] = OBJECT_TO_JSVAL(obj);
@ -5547,23 +5513,12 @@ interrupt:
*/ */
if (fp->flags & JSFRAME_POP_BLOCKS) { if (fp->flags & JSFRAME_POP_BLOCKS) {
JS_ASSERT(!fp->blockChain); JS_ASSERT(!fp->blockChain);
obj = js_CloneBlockObject(cx, obj, fp->scopeChain, fp);
/* if (!obj) {
* Check whether JSOP_DEFLOCALFUN emulated JSOP_ENTERBLOCK for ok = JS_FALSE;
* the body block in order to correctly scope the local cloned goto out;
* function object it creates.
*/
parent = fp->scopeChain;
if (OBJ_GET_PROTO(cx, parent) == obj) {
JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass);
} else {
obj = js_CloneBlockObject(cx, obj, parent, fp);
if (!obj) {
ok = JS_FALSE;
goto out;
}
fp->scopeChain = obj;
} }
fp->scopeChain = obj;
} else { } else {
JS_ASSERT(!fp->blockChain || JS_ASSERT(!fp->blockChain ||
OBJ_GET_PARENT(cx, obj) == fp->blockChain); OBJ_GET_PARENT(cx, obj) == fp->blockChain);

Просмотреть файл

@ -84,6 +84,14 @@
#include "jsdhash.h" #include "jsdhash.h"
#endif #endif
/*
* Asserts to verify assumptions behind pn_ macros.
*/
JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.atom) ==
offsetof(JSParseNode, pn_u.apair.atom));
JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.slot) ==
offsetof(JSParseNode, pn_u.lexical.slot));
/* /*
* JS parsers, from lowest to highest precedence. * JS parsers, from lowest to highest precedence.
* *
@ -1106,6 +1114,9 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
pn = NewParseNode(cx, ts, PN_FUNC, tc); pn = NewParseNode(cx, ts, PN_FUNC, tc);
if (!pn) if (!pn)
return NULL; return NULL;
#ifdef DEBUG
pn->pn_index = (uint32) -1;
#endif
/* Scan the optional function name into funAtom. */ /* Scan the optional function name into funAtom. */
ts->flags |= TSF_KEYWORD_IS_NAME; ts->flags |= TSF_KEYWORD_IS_NAME;
@ -1442,6 +1453,7 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
CHECK_RECURSION(); CHECK_RECURSION();
JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
pn = NewParseNode(cx, ts, PN_LIST, tc); pn = NewParseNode(cx, ts, PN_LIST, tc);
if (!pn) if (!pn)
return NULL; return NULL;
@ -1453,8 +1465,11 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
ts->flags |= TSF_OPERAND; ts->flags |= TSF_OPERAND;
tt = js_PeekToken(cx, ts); tt = js_PeekToken(cx, ts);
ts->flags &= ~TSF_OPERAND; ts->flags &= ~TSF_OPERAND;
if (tt <= TOK_EOF || tt == TOK_RC) if (tt <= TOK_EOF || tt == TOK_RC) {
if (tt == TOK_ERROR)
return NULL;
break; 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)
@ -1462,10 +1477,21 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
return NULL; return NULL;
} }
/* Detect a function statement for the TOK_LC case in Statement. */ if (pn2->pn_type == TOK_FUNCTION) {
if (pn2->pn_type == TOK_FUNCTION && !AT_TOP_LEVEL(tc)) /*
tc->flags |= TCF_HAS_FUNCTION_STMT; * PNX_FUNCDEFS notifies the emitter that the block contains top-
* level function definitions that should be processed before the
* rest of nodes.
*
* TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
* is relevant only for function definitions not at top-level,
* which we call function statements.
*/
if (AT_TOP_LEVEL(tc))
pn->pn_extra |= PNX_FUNCDEFS;
else
tc->flags |= TCF_HAS_FUNCTION_STMT;
}
PN_APPEND(pn, pn2); PN_APPEND(pn, pn2);
} }
@ -1478,10 +1504,6 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
pn = tc->blockNode; pn = tc->blockNode;
tc->blockNode = saveBlock; tc->blockNode = saveBlock;
ts->flags &= ~TSF_OPERAND;
if (tt == TOK_ERROR)
return NULL;
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
return pn; return pn;
} }

Просмотреть файл

@ -282,6 +282,7 @@ struct JSParseNode {
JSParseNode *body; /* TOK_LC list of statements */ JSParseNode *body; /* TOK_LC list of statements */
uint16 flags; /* accumulated tree context flags */ uint16 flags; /* accumulated tree context flags */
uint16 sclen; /* maximum scope chain length */ uint16 sclen; /* maximum scope chain length */
uint32 index; /* emitter's index */
} func; } func;
struct { /* list of next-linked nodes */ struct { /* list of next-linked nodes */
JSParseNode *head; /* first node in list */ JSParseNode *head; /* first node in list */
@ -331,6 +332,7 @@ struct JSParseNode {
#define pn_body pn_u.func.body #define pn_body pn_u.func.body
#define pn_flags pn_u.func.flags #define pn_flags pn_u.func.flags
#define pn_sclen pn_u.func.sclen #define pn_sclen pn_u.func.sclen
#define pn_index pn_u.func.index
#define pn_head pn_u.list.head #define pn_head pn_u.list.head
#define pn_tail pn_u.list.tail #define pn_tail pn_u.list.tail
#define pn_count pn_u.list.count #define pn_count pn_u.list.count
@ -362,7 +364,8 @@ struct JSParseNode {
#define PNX_XMLROOT 0x20 /* top-most node in XML literal tree */ #define PNX_XMLROOT 0x20 /* top-most node in XML literal tree */
#define PNX_GROUPINIT 0x40 /* var [a, b] = [c, d]; unit list */ #define PNX_GROUPINIT 0x40 /* var [a, b] = [c, d]; unit list */
#define PNX_NEEDBRACES 0x80 /* braces necessary due to closure */ #define PNX_NEEDBRACES 0x80 /* braces necessary due to closure */
#define PNX_FUNCDEFS 0x100 /* contains top-level function
statements */
/* /*
* Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off * Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off
* any kids in pn2->pn_u, by clearing pn2. * any kids in pn2->pn_u, by clearing pn2.

Просмотреть файл

@ -202,7 +202,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match * before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file. * the current version, abort deserialization and invalidate the file.
*/ */
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 17) #define JSXDR_BYTECODE_VERSION (0xb973c0de - 18)
/* /*
* Library-private functions. * Library-private functions.