- Shaver hacked this fix with advice from me, and I carried it to check-in. We now avoid a heavyweight outer function when the inner one is defined at top-level or in an expression (is not a JSOP_CLOSURE, IOW), and it doesn't refer to any non-local names. See bug 65308 for details on the win. (r=rogerl, sr=brendan)
- Fix scope chain for nested functions at top-level (JSOP_DEFFUN), in a part of another statement (JSOP_CLOSURE), and unnamed in an expression (JSOP_ANONFUNOBJ) to match ECMA-262 13.2. My bad: fp->varobj was used up till now, instead of fp->scopeChain; we still *bind* the name of a statement-level (top or not) nested function in fp->varobj. This fixes bug 69559. (r=rogerl, sr=jband) - Add an Intern command to the shell, for GC vs. intern'ed atom testing. git-svn-id: svn://10.0.0.236/trunk@87871 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
ce4e310532
commit
713ac17d77
@ -991,6 +991,24 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
if (!script)
|
||||
continue;
|
||||
|
||||
if (JSVAL_IS_FUNCTION(cx, argv[i])) {
|
||||
JSFunction *fun = JS_ValueToFunction(cx, argv[i]);
|
||||
if (fun && (fun->flags & JSFUN_FLAGS_MASK)) {
|
||||
uint8 flags = fun->flags;
|
||||
fputs("flags:", stderr);
|
||||
|
||||
#define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
|
||||
|
||||
SHOW_FLAG(SETTER);
|
||||
SHOW_FLAG(GETTER);
|
||||
SHOW_FLAG(BOUND_METHOD);
|
||||
SHOW_FLAG(HEAVYWEIGHT);
|
||||
|
||||
#undef SHOW_FLAG
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
js_Disassemble(cx, script, lines, stdout);
|
||||
SrcNotes(cx, script);
|
||||
TryNotes(cx, script);
|
||||
@ -999,7 +1017,8 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
}
|
||||
|
||||
static JSBool
|
||||
DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
{
|
||||
#define LINE_BUF_LEN 512
|
||||
uintN i, len, line1, line2, bupline;
|
||||
@ -1386,6 +1405,21 @@ Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
Intern(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
JSString *str;
|
||||
|
||||
str = JS_ValueToString(cx, argv[0]);
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
if (!JS_InternUCStringN(cx, JS_GetStringChars(str),
|
||||
JS_GetStringLength(str))) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSFunctionSpec shell_functions[] = {
|
||||
{"version", Version, 0},
|
||||
{"options", Options, 0},
|
||||
@ -1413,6 +1447,7 @@ static JSFunctionSpec shell_functions[] = {
|
||||
#endif
|
||||
{"build", BuildDate, 0},
|
||||
{"clear", Clear, 0},
|
||||
{"intern", Intern, 1},
|
||||
{0}
|
||||
};
|
||||
|
||||
|
||||
@ -499,10 +499,11 @@ LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
||||
return JS_TRUE;
|
||||
|
||||
/*
|
||||
* We can't optimize if var and function collide.
|
||||
* We can't optimize if var and closure (a local function not in a larger
|
||||
* expression and not at top-level within another's body) collide.
|
||||
* XXX suboptimal: keep track of colliding names and deoptimize only those
|
||||
*/
|
||||
if (tc->flags & TCF_FUN_VS_VAR)
|
||||
if (tc->flags & TCF_FUN_CLOSURE_VS_VAR)
|
||||
return JS_TRUE;
|
||||
|
||||
/*
|
||||
@ -587,6 +588,11 @@ LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
||||
}
|
||||
|
||||
if (pn->pn_slot < 0) {
|
||||
/* We couldn't optimize it, so it's not an arg or local var name. */
|
||||
tc->flags |= TCF_FUN_USES_NONLOCALS;
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -912,11 +918,15 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
cg->principals)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
cg2.treeContext.flags = pn->pn_flags;
|
||||
cg2.treeContext.flags = pn->pn_flags | TCF_IN_FUNCTION;
|
||||
cg2.treeContext.tryCount = pn->pn_tryCount;
|
||||
fun = pn->pn_fun;
|
||||
if (!js_EmitFunctionBody(cx, &cg2, pn2, fun))
|
||||
return JS_FALSE;
|
||||
|
||||
/* We need an activation object if an inner peeks out. */
|
||||
if (cg2.treeContext.flags & TCF_FUN_USES_NONLOCALS)
|
||||
cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
js_FinishCodeGenerator(cx, &cg2);
|
||||
|
||||
/* Make the function object a literal in the outer script's pool. */
|
||||
@ -943,10 +953,40 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* Top-levels also need a prolog op to predefine their names. */
|
||||
/*
|
||||
* Top-levels also need a prolog op to predefine their names in the
|
||||
* variable object, or if local, to fill their stack slots.
|
||||
*/
|
||||
CG_SWITCH_TO_PROLOG(cg);
|
||||
EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex);
|
||||
#if JS_HAS_LEXICAL_CLOSURE
|
||||
if (cg->treeContext.flags & TCF_IN_FUNCTION) {
|
||||
JSObject *obj, *pobj;
|
||||
JSScopeProperty *sprop;
|
||||
uintN slot;
|
||||
jsbytecode *pc;
|
||||
|
||||
obj = OBJ_GET_PARENT(cx, pn->pn_fun->object);
|
||||
if (!js_LookupProperty(cx, obj, (jsid)fun->atom, &pobj,
|
||||
(JSProperty **)&sprop)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
JS_ASSERT(sprop && pobj == obj);
|
||||
slot = (uintN) JSVAL_TO_INT(sprop->id);
|
||||
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
||||
|
||||
/* Emit [JSOP_DEFLOCALFUN, local variable slot, atomIndex]. */
|
||||
off = js_EmitN(cx, cg, JSOP_DEFLOCALFUN, VARNO_LEN+ATOM_INDEX_LEN);
|
||||
if (off < 0)
|
||||
return JS_FALSE;
|
||||
pc = CG_CODE(cg, off);
|
||||
SET_VARNO(pc, slot);
|
||||
pc += VARNO_LEN;
|
||||
SET_ATOM_INDEX(pc, atomIndex);
|
||||
} else
|
||||
#endif
|
||||
EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex);
|
||||
CG_SWITCH_TO_MAIN(cg);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -91,14 +91,15 @@ struct JSTreeContext { /* tree context for semantic checks */
|
||||
JSParseNode *nodeList; /* list of recyclable parse-node structs */
|
||||
};
|
||||
|
||||
#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_IN_FOR_INIT 0x10 /* parsing init expr of for; exclude 'in' */
|
||||
#define TCF_FUN_VS_VAR 0x20 /* function and var with same name */
|
||||
#define TCF_FUN_HEAVYWEIGHT 0x40 /* function needs Call object per call */
|
||||
#define TCF_FUN_FLAGS 0x60 /* flags to propagate from FunctionBody */
|
||||
#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_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_FLAGS 0xE0 /* flags to propagate from FunctionBody */
|
||||
|
||||
#define TREE_CONTEXT_INIT(tc) \
|
||||
((tc)->flags = 0, (tc)->tryCount = 0, (tc)->topStmt = NULL, \
|
||||
|
||||
@ -170,8 +170,6 @@ js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
|
||||
* elements of argsobj.
|
||||
*/
|
||||
argsobj = fp->argsobj;
|
||||
if (!argsobj)
|
||||
return JS_TRUE;
|
||||
ok = args_enumerate(cx, argsobj);
|
||||
|
||||
/*
|
||||
@ -380,10 +378,12 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
/*
|
||||
* Get the arguments object to snapshot fp's actual argument values.
|
||||
*/
|
||||
argsid = (jsid) cx->runtime->atomState.argumentsAtom;
|
||||
ok &= js_GetProperty(cx, callobj, argsid, &aval);
|
||||
ok &= js_SetProperty(cx, callobj, argsid, &aval);
|
||||
ok &= js_PutArgsObject(cx, fp);
|
||||
if (fp->argsobj) {
|
||||
argsid = (jsid) cx->runtime->atomState.argumentsAtom;
|
||||
ok &= js_GetProperty(cx, callobj, argsid, &aval);
|
||||
ok &= js_SetProperty(cx, callobj, argsid, &aval);
|
||||
ok &= js_PutArgsObject(cx, fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the private pointer to fp, which is about to go away (js_Invoke).
|
||||
|
||||
@ -3109,7 +3109,14 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
|
||||
/*
|
||||
* We must be at top-level (default "box", either function body or
|
||||
* global) scope, not inside a with or other compound statement.
|
||||
* global) scope, not inside a with or other compound statement in
|
||||
* the same compilation unit (ECMA Program).
|
||||
*
|
||||
* However, we could be in a Program being eval'd from inside a
|
||||
* with statement, so we need to distinguish variables object from
|
||||
* scope chain head. Hence the two assignments to parent below.
|
||||
* First we make sure the function object we're defining has the
|
||||
* right scope chain. Then we define its name in fp->varobj.
|
||||
*
|
||||
* If static link is not current scope, clone fun's object to link
|
||||
* to the current scope via parent. This clause exists to enable
|
||||
@ -3129,7 +3136,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
* promote compile-cost sharing and amortizing, and because Script
|
||||
* is not and will not be standardized.
|
||||
*/
|
||||
parent = fp->varobj;
|
||||
parent = fp->scopeChain;
|
||||
if (OBJ_GET_PARENT(cx, obj) != parent) {
|
||||
obj = js_CloneFunctionObject(cx, obj, parent);
|
||||
if (!obj) {
|
||||
@ -3148,6 +3155,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
* here at runtime as well as at compile-time, to handle eval
|
||||
* as well as multiple HTML script tags.
|
||||
*/
|
||||
parent = fp->varobj;
|
||||
ok = js_CheckRedeclaration(cx, parent, id, attrs, &cond);
|
||||
if (!ok)
|
||||
goto out;
|
||||
@ -3168,13 +3176,39 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
}
|
||||
|
||||
#if JS_HAS_LEXICAL_CLOSURE
|
||||
case JSOP_DEFLOCALFUN:
|
||||
/*
|
||||
* Define a local function (i.e., one nested at the top level of
|
||||
* another function), parented by the current scope chain, and
|
||||
* stored in a local variable slot that the compiler allocated.
|
||||
* This is an optimization over JSOP_DEFFUN that avoids requiring
|
||||
* a call object for the outer function's activation.
|
||||
*/
|
||||
pc2 = pc;
|
||||
slot = GET_VARNO(pc2);
|
||||
pc2 += VARNO_LEN;
|
||||
atom = GET_ATOM(cx, script, pc2);
|
||||
obj = ATOM_TO_OBJECT(atom);
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, obj);
|
||||
|
||||
parent = fp->scopeChain;
|
||||
if (OBJ_GET_PARENT(cx, obj) != parent) {
|
||||
obj = js_CloneFunctionObject(cx, obj, parent);
|
||||
if (!obj) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
fp->vars[slot] = OBJECT_TO_JSVAL(obj);
|
||||
break;
|
||||
|
||||
case JSOP_ANONFUNOBJ:
|
||||
/* Push the specified function object literal. */
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
obj = ATOM_TO_OBJECT(atom);
|
||||
|
||||
/* If re-parenting, push a clone of the function object. */
|
||||
parent = fp->varobj;
|
||||
parent = fp->scopeChain;
|
||||
if (OBJ_GET_PARENT(cx, obj) != parent) {
|
||||
obj = js_CloneFunctionObject(cx, obj, parent);
|
||||
if (!obj) {
|
||||
@ -3253,41 +3287,15 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
case JSOP_CLOSURE:
|
||||
/*
|
||||
* ECMA ed. 3 extension: a named function expression in a compound
|
||||
* statement (not at top-level or "box" scope, either global code
|
||||
* or in a function body).
|
||||
* statement (not at the top statement level of global code, or at
|
||||
* the top level of a function body).
|
||||
*
|
||||
* Name the closure in the object at the head of the scope chain,
|
||||
* referenced by parent. Even if it's a With object? Not for the
|
||||
* current version (1.5), which uses the object named by the with
|
||||
* statement's head.
|
||||
*/
|
||||
parent = fp->varobj;
|
||||
if (fp->scopeChain != parent) {
|
||||
parent = fp->scopeChain;
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_WithClass);
|
||||
#if JS_BUG_WITH_CLOSURE
|
||||
/*
|
||||
* If in a with statement, set parent to the With object's
|
||||
* prototype, i.e., the object specified in the head of
|
||||
* the with statement.
|
||||
*/
|
||||
while (OBJ_GET_CLASS(cx, parent) == &js_WithClass) {
|
||||
proto = OBJ_GET_PROTO(cx, parent);
|
||||
if (!proto)
|
||||
break;
|
||||
parent = proto;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Get immediate operand atom, which is a function object literal.
|
||||
* From it, get the function to close.
|
||||
*/
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
JS_ASSERT(JSVAL_IS_FUNCTION(cx, ATOM_KEY(atom)));
|
||||
obj = ATOM_TO_OBJECT(atom);
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, obj);
|
||||
|
||||
/*
|
||||
* Clone the function object with the current scope chain as the
|
||||
@ -3296,6 +3304,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
* have seen the right parent already and created a sufficiently
|
||||
* well-scoped function object.
|
||||
*/
|
||||
parent = fp->scopeChain;
|
||||
if (OBJ_GET_PARENT(cx, obj) != parent) {
|
||||
obj = js_CloneFunctionObject(cx, obj, parent);
|
||||
if (!obj) {
|
||||
@ -3305,12 +3314,13 @@ js_Interpret(JSContext *cx, jsval *result)
|
||||
}
|
||||
|
||||
/*
|
||||
* Define a property in parent with id fun->atom and value obj,
|
||||
* unless fun is a getter or setter (in which case, obj is cast
|
||||
* to a JSPropertyOp and passed accordingly).
|
||||
* Make a property in fp->varobj with id fun->atom and value obj,
|
||||
* unless fun is a getter or setter (in which case, obj is cast to
|
||||
* a JSPropertyOp and passed accordingly).
|
||||
*/
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, obj);
|
||||
attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);
|
||||
ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)fun->atom,
|
||||
ok = OBJ_DEFINE_PROPERTY(cx, fp->varobj, (jsid)fun->atom,
|
||||
attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj),
|
||||
(attrs & JSFUN_GETTER)
|
||||
? (JSPropertyOp) obj
|
||||
|
||||
@ -208,7 +208,6 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
{
|
||||
jsbytecode *pc2 = pc;
|
||||
jsint npairs;
|
||||
jsval key;
|
||||
|
||||
off = GET_JUMP_OFFSET(pc2);
|
||||
pc2 += JUMP_OFFSET_LEN;
|
||||
@ -221,8 +220,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
off = GET_JUMP_OFFSET(pc2);
|
||||
pc2 += JUMP_OFFSET_LEN;
|
||||
|
||||
key = ATOM_KEY(atom);
|
||||
str = js_ValueToSource(cx, key);
|
||||
str = js_ValueToSource(cx, ATOM_KEY(atom));
|
||||
if (!str)
|
||||
return 0;
|
||||
cstr = js_DeflateString(cx, str->chars, str->length);
|
||||
@ -245,6 +243,22 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
fprintf(fp, " %u", GET_VARNO(pc));
|
||||
break;
|
||||
|
||||
#if JS_HAS_LEXICAL_CLOSURE
|
||||
case JOF_DEFLOCALVAR:
|
||||
fprintf(fp, " %u", GET_VARNO(pc));
|
||||
pc += VARNO_LEN;
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
str = js_ValueToSource(cx, ATOM_KEY(atom));
|
||||
if (!str)
|
||||
return 0;
|
||||
cstr = js_DeflateString(cx, str->chars, str->length);
|
||||
if (!cstr)
|
||||
return 0;
|
||||
fprintf(fp, " %s", cstr);
|
||||
JS_free(cx, cstr);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default: {
|
||||
char numBuf[12];
|
||||
JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
|
||||
|
||||
@ -65,6 +65,7 @@ typedef enum JSOp {
|
||||
#define JOF_LOOKUPSWITCH 5 /* lookup switch */
|
||||
#define JOF_QARG 6 /* quickened get/set function argument ops */
|
||||
#define JOF_QVAR 7 /* quickened get/set local variable ops */
|
||||
#define JOF_DEFLOCALVAR 8 /* define local var with initial value */
|
||||
#define JOF_TYPEMASK 0x000f /* mask for above immediate types */
|
||||
#define JOF_NAME 0x0010 /* name operation */
|
||||
#define JOF_PROP 0x0020 /* obj.prop operation */
|
||||
@ -109,8 +110,10 @@ typedef enum JSOp {
|
||||
/* Synonyms for quick JOF_QARG and JOF_QVAR bytecodes. */
|
||||
#define GET_ARGNO(pc) GET_ARGC(pc)
|
||||
#define SET_ARGNO(pc,argno) SET_JUMP_OFFSET(pc,argno)
|
||||
#define ARGNO_LEN JUMP_OFFSET_LEN
|
||||
#define GET_VARNO(pc) GET_ARGC(pc)
|
||||
#define SET_VARNO(pc,varno) SET_JUMP_OFFSET(pc,varno)
|
||||
#define VARNO_LEN JUMP_OFFSET_LEN
|
||||
|
||||
struct JSCodeSpec {
|
||||
const char *name; /* JS bytecode name */
|
||||
|
||||
@ -204,7 +204,7 @@ OPDEF(JSOP_FORPROP, 105,"forprop", NULL, 3, 1, 1, 0, JOF_CONST|
|
||||
OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 2, 4, 0, JOF_BYTE |JOF_ELEM)
|
||||
OPDEF(JSOP_POP2, 107,"pop2", NULL, 1, 2, 0, 0, JOF_BYTE)
|
||||
|
||||
/* ECMA-complaint assignment ops. */
|
||||
/* ECMA-compliant assignment ops. */
|
||||
OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 0, JOF_CONST|JOF_NAME)
|
||||
OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 1, JOF_CONST|JOF_NAME|JOF_SET)
|
||||
|
||||
@ -297,3 +297,10 @@ OPDEF(JSOP_SWAP, 135,"swap", NULL, 1, 2, 2, 0, JOF_BYTE)
|
||||
*/
|
||||
OPDEF(JSOP_ARGSUB, 136,"argsub", NULL, 3, 0, 1, 12, JOF_QARG |JOF_NAME)
|
||||
OPDEF(JSOP_ARGCNT, 137,"argcnt", NULL, 1, 0, 1, 12, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Define a local function object as a local variable.
|
||||
* The local variable's slot number is the first immediate two-byte operand.
|
||||
* The function object's atom index is the second immediate operand.
|
||||
*/
|
||||
OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 5, 0, 0, 0, JOF_DEFLOCALVAR)
|
||||
|
||||
@ -719,16 +719,30 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
|
||||
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
|
||||
|
||||
#if JS_HAS_LEXICAL_CLOSURE
|
||||
/*
|
||||
* If we collected flags that indicate nested functions or with statements
|
||||
* while compiling our body, flag the function as heavyweight (requiring
|
||||
* a call object per invocation). Therefore, update tc->flags to reflect
|
||||
* the fact that the outer context contains this function, and may also be
|
||||
* heavy if it is a function body.
|
||||
* If we collected flags that indicate nested heavyweight functions, or
|
||||
* this function contains heavyweight-making statements (references to
|
||||
* __parent__ or __proto__; use of with, eval, import, or export; and
|
||||
* assignment to arguments), flag the function as heavyweight (requiring
|
||||
* a call object per invocation).
|
||||
*/
|
||||
if (funtc.flags & TCF_FUN_HEAVYWEIGHT)
|
||||
if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
|
||||
fun->flags |= JSFUN_HEAVYWEIGHT;
|
||||
tc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
tc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
} else {
|
||||
/*
|
||||
* If this function is a named statement function not at top-level
|
||||
* (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that
|
||||
* are not local args or vars (TCF_FUN_USES_NONLOCALS), then our
|
||||
* enclosing function, if any, must be heavyweight.
|
||||
*/
|
||||
if ((!lambda && funAtom && tc->topStmt) ||
|
||||
(funtc.flags & TCF_FUN_USES_NONLOCALS)) {
|
||||
tc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Record names for function statements in tc->decls so we know when to
|
||||
@ -744,7 +758,8 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
? JSREPORT_WARNING|JSREPORT_STRICT
|
||||
: JSREPORT_ERROR,
|
||||
JSMSG_REDECLARED_VAR,
|
||||
(prevop == JSOP_DEFFUN)
|
||||
(prevop == JSOP_DEFFUN ||
|
||||
prevop == JSOP_CLOSURE)
|
||||
? js_function_str
|
||||
: (prevop == JSOP_DEFCONST)
|
||||
? js_const_str
|
||||
@ -752,18 +767,57 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
ATOM_BYTES(funAtom))) {
|
||||
return NULL;
|
||||
}
|
||||
if (prevop == JSOP_DEFVAR)
|
||||
tc->flags |= TCF_FUN_VS_VAR;
|
||||
if (tc->topStmt && prevop == JSOP_DEFVAR)
|
||||
tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
|
||||
} else {
|
||||
ale = js_IndexAtom(cx, funAtom, &tc->decls);
|
||||
if (!ale)
|
||||
return NULL;
|
||||
}
|
||||
ALE_SET_JSOP(ale, JSOP_DEFFUN);
|
||||
ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN);
|
||||
|
||||
#if JS_HAS_LEXICAL_CLOSURE
|
||||
/*
|
||||
* A function nested at top level inside another's body needs only a
|
||||
* local variable to bind its name to its value, and not an activation
|
||||
* object property (it might also need the activation property, if the
|
||||
* outer function contains with statements, e.g., but the stack slot
|
||||
* wins when jsemit.c's LookupArgOrVar can optimize a JSOP_NAME into a
|
||||
* JSOP_GETVAR bytecode).
|
||||
*/
|
||||
if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) {
|
||||
JSStackFrame *fp;
|
||||
JSObject *varobj;
|
||||
|
||||
/*
|
||||
* Define a property on the outer function so that LookupArgOrVar
|
||||
* can properly optimize accesses.
|
||||
*
|
||||
* XXX Here and in Variables, we use the function object's scope,
|
||||
* XXX arguably polluting it, when we could use a compiler-private
|
||||
* XXX scope structure. Tradition!
|
||||
*/
|
||||
fp = cx->fp;
|
||||
varobj = fp->varobj;
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);
|
||||
JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));
|
||||
if (!js_DefineProperty(cx, varobj, (jsid)funAtom,
|
||||
OBJECT_TO_JSVAL(fun->object),
|
||||
js_GetLocalVariable, js_SetLocalVariable,
|
||||
JSPROP_ENUMERATE,
|
||||
(JSProperty **)&sprop)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate a slot for this property. */
|
||||
sprop->id = INT_TO_JSVAL(fp->fun->nvars++);
|
||||
OBJ_DROP_PROPERTY(cx, varobj, (JSProperty *)sprop);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if JS_HAS_LEXICAL_CLOSURE
|
||||
if (lambda || !fun->atom) {
|
||||
if (lambda || !funAtom) {
|
||||
/*
|
||||
* ECMA ed. 3 standard: function expression, possibly anonymous (even
|
||||
* if at top-level, an unnamed function is an expression statement, not
|
||||
@ -1871,7 +1925,8 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
? JSREPORT_WARNING|JSREPORT_STRICT
|
||||
: JSREPORT_ERROR,
|
||||
JSMSG_REDECLARED_VAR,
|
||||
(prevop == JSOP_DEFFUN)
|
||||
(prevop == JSOP_DEFFUN ||
|
||||
prevop == JSOP_CLOSURE)
|
||||
? js_function_str
|
||||
: (prevop == JSOP_DEFCONST)
|
||||
? js_const_str
|
||||
@ -1879,8 +1934,8 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
ATOM_BYTES(atom))) {
|
||||
return NULL;
|
||||
}
|
||||
if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_DEFFUN)
|
||||
tc->flags |= TCF_FUN_VS_VAR;
|
||||
if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)
|
||||
tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
|
||||
} else {
|
||||
ale = js_IndexAtom(cx, atom, &tc->decls);
|
||||
if (!ale)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user