From 837c04691215bf020d59f417b5aea5529310e959 Mon Sep 17 00:00:00 2001 From: "igor@mir2.org" Date: Tue, 13 Nov 2007 07:47:28 -0800 Subject: [PATCH] Bug 398609: Backing out due to mochi test failure. --- js/src/js.c | 2 +- js/src/jsapi.c | 36 +-- js/src/jsdbgapi.c | 13 +- js/src/jsemit.c | 287 ++++++++++++--------- js/src/jsfun.c | 635 +++++++++++++++++++++------------------------- js/src/jsfun.h | 51 ---- js/src/jsinterp.c | 49 +++- js/src/jsinterp.h | 12 + js/src/jsobj.c | 20 ++ js/src/jsobj.h | 22 +- js/src/jsopcode.c | 299 +++++++++++++--------- js/src/jsopcode.h | 15 +- js/src/jsparse.c | 323 +++++++++++++++++------ js/src/jsparse.h | 6 +- js/src/jsprvtd.h | 5 - js/src/jsscope.c | 18 +- js/src/jsscope.h | 4 +- js/src/jsscript.c | 2 +- js/src/jsxdrapi.h | 2 +- js/src/jsxml.c | 14 +- 20 files changed, 1024 insertions(+), 791 deletions(-) diff --git a/js/src/js.c b/js/src/js.c index 4dd8ae656de..3fa6853e933 100644 --- a/js/src/js.c +++ b/js/src/js.c @@ -1239,7 +1239,7 @@ SrcNotes(JSContext *cx, JSScript *script) index = js_GetSrcNoteOffset(sn, 0); JS_GET_SCRIPT_OBJECT(script, index, obj); - fun = (JSFunction *) JS_GetPrivate(cx, obj); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj); str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT); bytes = str ? JS_GetStringBytes(str) : "N/A"; fprintf(gOutFile, " function %u (%s)", index, bytes); diff --git a/js/src/jsapi.c b/js/src/jsapi.c index 5c41437be07..70580d6280d 100644 --- a/js/src/jsapi.c +++ b/js/src/jsapi.c @@ -4663,18 +4663,24 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, goto out; } } - fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom); + fun = js_NewFunction(cx, NULL, NULL, nargs, 0, obj, funAtom); if (!fun) goto out; - for (i = 0; i < nargs; i++) { - argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); - if (!argAtom) { - fun = NULL; - goto out; - } - if (!js_AddLocal(cx, fun, argAtom, JSLOCAL_ARG)) { - fun = NULL; - goto out; + if (nargs) { + for (i = 0; i < nargs; i++) { + argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); + if (!argAtom) { + fun = NULL; + goto out; + } + if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom), + js_GetArgument, js_SetArgument, + SPROP_INVALID_SLOT, + JSPROP_PERMANENT | JSPROP_SHARED, + SPROP_HAS_SHORTID, i)) { + fun = NULL; + goto out; + } } } @@ -4710,7 +4716,7 @@ JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, JSString *str; CHECK_REQUEST(cx); - jp = JS_NEW_PRINTER(cx, name, NULL, + jp = JS_NEW_PRINTER(cx, name, indent & ~JS_DONT_PRETTY_PRINT, !(indent & JS_DONT_PRETTY_PRINT)); if (!jp) @@ -4730,12 +4736,12 @@ JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent) JSString *str; CHECK_REQUEST(cx); - jp = JS_NEW_PRINTER(cx, "JS_DecompileFunction", fun, + jp = JS_NEW_PRINTER(cx, "JS_DecompileFunction", indent & ~JS_DONT_PRETTY_PRINT, !(indent & JS_DONT_PRETTY_PRINT)); if (!jp) return NULL; - if (js_DecompileFunction(jp)) + if (js_DecompileFunction(jp, fun)) str = js_GetPrinterOutput(jp); else str = NULL; @@ -4750,12 +4756,12 @@ JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) JSString *str; CHECK_REQUEST(cx); - jp = JS_NEW_PRINTER(cx, "JS_DecompileFunctionBody", fun, + jp = JS_NEW_PRINTER(cx, "JS_DecompileFunctionBody", indent & ~JS_DONT_PRETTY_PRINT, !(indent & JS_DONT_PRETTY_PRINT)); if (!jp) return NULL; - if (js_DecompileFunctionBody(jp)) + if (js_DecompileFunctionBody(jp, fun)) str = js_GetPrinterOutput(jp); else str = NULL; diff --git a/js/src/jsdbgapi.c b/js/src/jsdbgapi.c index 69162b852f4..0d9d70d69f9 100644 --- a/js/src/jsdbgapi.c +++ b/js/src/jsdbgapi.c @@ -563,7 +563,7 @@ js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) closure = (JSObject *) wp->closure; clasp = OBJ_GET_CLASS(cx, closure); if (clasp == &js_FunctionClass) { - fun = GET_FUNCTION_PRIVATE(cx, closure); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, closure); script = FUN_SCRIPT(fun); } else if (clasp == &js_ScriptClass) { fun = NULL; @@ -653,7 +653,7 @@ js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, funobj = JSVAL_TO_OBJECT(argv[-2]); JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass); - wrapper = GET_FUNCTION_PRIVATE(cx, funobj); + wrapper = (JSFunction *) OBJ_GET_PRIVATE(cx, funobj); userid = ATOM_KEY(wrapper->atom); *rval = argv[0]; return js_watch_set(cx, obj, userid, rval); @@ -1326,12 +1326,9 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0) | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0) | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0) - | ((getter == js_GetCallVariable) ? JSPD_VARIABLE : 0); - if (JSID_IS_HIDDEN(sprop->id)) { - pd->flags |= (getter == JS_HIDDEN_ARG_GETTER) - ? JSPD_ARGUMENT - : JSPD_VARIABLE; - } + | ((getter == js_GetCallVariable) ? JSPD_VARIABLE : 0) + | ((getter == js_GetArgument) ? JSPD_ARGUMENT : 0) + | ((getter == js_GetLocalVariable) ? JSPD_VARIABLE : 0); /* for Call Object 'real' getter isn't passed in to us */ if (OBJ_GET_CLASS(cx, obj) == &js_CallClass && diff --git a/js/src/jsemit.c b/js/src/jsemit.c index 58cfa933ddf..755720154b3 100644 --- a/js/src/jsemit.c +++ b/js/src/jsemit.c @@ -1233,7 +1233,8 @@ js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp) continue; } if (stmt->flags & SIF_SCOPE) { - JS_ASSERT(STOBJ_GET_CLASS(stmt->u.blockObj) == &js_BlockClass); + JS_ASSERT(LOCKED_OBJ_GET_CLASS(stmt->u.blockObj) == + &js_BlockClass); scope = OBJ_SCOPE(stmt->u.blockObj); if (SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom))) return JS_FALSE; @@ -1645,10 +1646,26 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, * with object or catch variable; nor can prop's value be changed, * nor can prop be deleted. */ + prop = NULL; if (OBJ_GET_CLASS(cx, obj) == &js_FunctionClass) { - JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, obj)); - if (js_LookupLocal(cx, fp->fun, atom, NULL) != JSLOCAL_NONE) + ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), + &pobj, &prop); + if (!ok) break; + if (prop) { +#ifdef DEBUG + JSScopeProperty *sprop = (JSScopeProperty *)prop; + + /* + * Any hidden property must be a formal arg or local var, + * which will shadow a global const of the same name. + */ + JS_ASSERT(sprop->getter == js_GetArgument || + sprop->getter == js_GetLocalVariable); +#endif + OBJ_DROP_PROPERTY(cx, pobj, prop); + break; + } } ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); @@ -1818,7 +1835,7 @@ EmitSlotIndexOp(JSContext *cx, JSOp op, uintN slot, uintN index, * pn->pn_op. If pn->pn_slot is still -1 on return, pn->pn_op nevertheless * may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS. Whether * or not pn->pn_op was modified, if this function finds an argument or local - * variable name, pn->pn_const will be true for const properties after a + * variable name, pn->pn_attrs will contain the property's attributes after a * successful return. * * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget @@ -1834,10 +1851,14 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, jsint slot; JSOp op; JSStackFrame *fp; + JSObject *obj, *pobj; JSClass *clasp; - JSLocalKind localKind; - uintN index; + JSBool optimizeGlobals; + JSPropertyOp getter; + uintN attrs; JSAtomListElement *ale; + JSProperty *prop; + JSScopeProperty *sprop; JS_ASSERT(pn->pn_type == TOK_NAME); if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS) @@ -1901,32 +1922,42 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, return JS_TRUE; /* - * We can't optimize if we are in an eval called inside a with statement. + * We can't optimize if we're not compiling a function body, whether via + * eval, or directly when compiling a function statement or expression. */ - if (fp->scopeChain != fp->varobj) - return JS_TRUE; - - clasp = OBJ_GET_CLASS(cx, fp->varobj); + obj = fp->varobj; + clasp = OBJ_GET_CLASS(cx, obj); if (clasp != &js_FunctionClass && clasp != &js_CallClass) { - /* - * We cannot optimize the name access when compiling with an eval or - * debugger frame. - */ + /* Check for an eval or debugger frame. */ if (fp->flags & JSFRAME_SPECIAL) return JS_TRUE; /* - * We are compiling a top-level script. Optimize global variable - * accesses if there are at least 100 uses in unambiguous contexts, - * or failing that, if least half of all the uses of global - * vars/consts/functions are in loops. + * Optimize global variable accesses if there are at least 100 uses + * in unambiguous contexts, or failing that, if least half of all the + * uses of global vars/consts/functions are in loops. */ - if (!(tc->globalUses >= 100 || - (tc->loopyGlobalUses && - tc->loopyGlobalUses >= tc->globalUses / 2))) { + optimizeGlobals = (tc->globalUses >= 100 || + (tc->loopyGlobalUses && + tc->loopyGlobalUses >= tc->globalUses / 2)); + if (!optimizeGlobals) return JS_TRUE; - } + } else { + optimizeGlobals = JS_FALSE; + } + /* + * We can't optimize if we are in an eval called inside a with statement. + */ + if (fp->scopeChain != obj) + return JS_TRUE; + + op = PN_OP(pn); + getter = NULL; +#ifdef __GNUC__ + attrs = slot = 0; /* quell GCC overwarning */ +#endif + if (optimizeGlobals) { /* * We are optimizing global variables, and there is no pre-existing * global property named atom. If atom was declared via const or var, @@ -1938,6 +1969,10 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, return JS_TRUE; } + attrs = (ALE_JSOP(ale) == JSOP_DEFCONST) + ? JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT + : JSPROP_ENUMERATE | JSPROP_PERMANENT; + /* 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); @@ -1951,90 +1986,93 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, if ((uint16)(slot + 1) > tc->ngvars) tc->ngvars = (uint16)(slot + 1); - - op = PN_OP(pn); - switch (op) { - case JSOP_NAME: op = JSOP_GETGVAR; break; - case JSOP_SETNAME: op = JSOP_SETGVAR; break; - case JSOP_SETCONST: /* NB: no change */ break; - case JSOP_INCNAME: op = JSOP_INCGVAR; break; - case JSOP_NAMEINC: op = JSOP_GVARINC; break; - case JSOP_DECNAME: op = JSOP_DECGVAR; break; - case JSOP_NAMEDEC: op = JSOP_GVARDEC; break; - case JSOP_FORNAME: /* NB: no change */ break; - case JSOP_DELNAME: /* NB: no change */ break; - default: JS_ASSERT(0); + } else { + /* + * We may be able to optimize name to stack slot. Look for an argument + * or variable property in the function, or its call object, not found + * in any prototype object. Rewrite pn_op and update pn accordingly. + * NB: We know that JSOP_DELNAME on an argument or variable evaluates + * to false, due to JSPROP_PERMANENT. + */ + if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) + return JS_FALSE; + sprop = (JSScopeProperty *) prop; + if (sprop) { + if (pobj == obj) { + getter = sprop->getter; + attrs = sprop->attrs; + slot = (sprop->flags & SPROP_HAS_SHORTID) ? sprop->shortid : -1; + } + OBJ_DROP_PROPERTY(cx, pobj, prop); + } + } + + if (optimizeGlobals || getter) { + if (optimizeGlobals) { + switch (op) { + case JSOP_NAME: op = JSOP_GETGVAR; break; + case JSOP_SETNAME: op = JSOP_SETGVAR; break; + case JSOP_SETCONST: /* NB: no change */ break; + case JSOP_INCNAME: op = JSOP_INCGVAR; break; + case JSOP_NAMEINC: op = JSOP_GVARINC; break; + case JSOP_DECNAME: op = JSOP_DECGVAR; break; + case JSOP_NAMEDEC: op = JSOP_GVARDEC; break; + case JSOP_FORNAME: /* NB: no change */ break; + case JSOP_DELNAME: /* NB: no change */ break; + default: JS_ASSERT(0); + } + } else if (getter == js_GetLocalVariable || + getter == js_GetCallVariable) { + switch (op) { + case JSOP_NAME: op = JSOP_GETVAR; break; + case JSOP_SETNAME: op = JSOP_SETVAR; break; + case JSOP_SETCONST: op = JSOP_SETVAR; break; + case JSOP_INCNAME: op = JSOP_INCVAR; break; + case JSOP_NAMEINC: op = JSOP_VARINC; break; + case JSOP_DECNAME: op = JSOP_DECVAR; break; + case JSOP_NAMEDEC: op = JSOP_VARDEC; break; + case JSOP_FORNAME: op = JSOP_FORVAR; break; + case JSOP_DELNAME: op = JSOP_FALSE; break; + default: JS_ASSERT(0); + } + } else if (getter == js_GetArgument || + (getter == js_CallClass.getProperty && + fp->fun && (uintN) slot < fp->fun->nargs)) { + switch (op) { + case JSOP_NAME: op = JSOP_GETARG; break; + case JSOP_SETNAME: op = JSOP_SETARG; break; + case JSOP_INCNAME: op = JSOP_INCARG; break; + case JSOP_NAMEINC: op = JSOP_ARGINC; break; + case JSOP_DECNAME: op = JSOP_DECARG; break; + case JSOP_NAMEDEC: op = JSOP_ARGDEC; break; + case JSOP_FORNAME: op = JSOP_FORARG; break; + case JSOP_DELNAME: op = JSOP_FALSE; break; + default: JS_ASSERT(0); + } } - pn->pn_const = (ALE_JSOP(ale) == JSOP_DEFCONST); if (op != pn->pn_op) { pn->pn_op = op; pn->pn_slot = slot; } - return JS_TRUE; + pn->pn_attrs = attrs; } - if (clasp == &js_FunctionClass) { + if (pn->pn_slot < 0) { /* - * We are compiling a function body and may be able to optimize name - * to stack slot. Look for an argument or variable in the function and - * rewrite pn_op and update pn accordingly. + * We couldn't optimize pn, so it's not a global or local slot name. + * Now we must check for the predefined arguments variable. It may be + * overridden by assignment, in which case the function is heavyweight + * and the interpreter will look up 'arguments' in the function's call + * object. */ - JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->varobj)); - localKind = js_LookupLocal(cx, fp->fun, atom, &index); - if (localKind != JSLOCAL_NONE) { - op = PN_OP(pn); - if (localKind == JSLOCAL_ARG) { - switch (op) { - case JSOP_NAME: op = JSOP_GETARG; break; - case JSOP_SETNAME: op = JSOP_SETARG; break; - case JSOP_INCNAME: op = JSOP_INCARG; break; - case JSOP_NAMEINC: op = JSOP_ARGINC; break; - case JSOP_DECNAME: op = JSOP_DECARG; break; - case JSOP_NAMEDEC: op = JSOP_ARGDEC; break; - case JSOP_FORNAME: op = JSOP_FORARG; break; - case JSOP_DELNAME: op = JSOP_FALSE; break; - default: JS_ASSERT(0); - } - pn->pn_const = JS_FALSE; - } else { - JS_ASSERT(localKind == JSLOCAL_VAR || - localKind == JSLOCAL_CONST); - switch (op) { - case JSOP_NAME: op = JSOP_GETVAR; break; - case JSOP_SETNAME: op = JSOP_SETVAR; break; - case JSOP_SETCONST: op = JSOP_SETVAR; break; - case JSOP_INCNAME: op = JSOP_INCVAR; break; - case JSOP_NAMEINC: op = JSOP_VARINC; break; - case JSOP_DECNAME: op = JSOP_DECVAR; break; - case JSOP_NAMEDEC: op = JSOP_VARDEC; break; - case JSOP_FORNAME: op = JSOP_FORVAR; break; - case JSOP_DELNAME: op = JSOP_FALSE; break; - default: JS_ASSERT(0); - } - pn->pn_const = (localKind == JSLOCAL_CONST); - } - pn->pn_op = op; - pn->pn_slot = index; + if (pn->pn_op == JSOP_NAME && + atom == cx->runtime->atomState.argumentsAtom) { + pn->pn_op = JSOP_ARGUMENTS; return JS_TRUE; } - } - /* - * Here we either compiling a function body or an eval script inside a - * function and couldn't optimize pn, so it's not a global or local slot - * name. - * - * Now we must check for the predefined arguments variable. It may be - * overridden by assignment, in which case the function is heavyweight - * and the interpreter will look up 'arguments' in the function's call - * object. - */ - if (pn->pn_op == JSOP_NAME && - atom == cx->runtime->atomState.argumentsAtom) { - pn->pn_op = JSOP_ARGUMENTS; - return JS_TRUE; + tc->flags |= TCF_FUN_USES_NONLOCALS; } - tc->flags |= TCF_FUN_USES_NONLOCALS; return JS_TRUE; } @@ -2071,7 +2109,7 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, * name in that scope object. See comments at case JSOP_NAMEDFUNOBJ: * in jsinterp.c. */ - fun = GET_FUNCTION_PRIVATE(cx, pn->pn_funpob->object); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, pn->pn_funpob->object); if (fun->atom) *answer = JS_TRUE; break; @@ -2132,7 +2170,7 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, if (!*answer && (pn->pn_op != JSOP_NOP || pn2->pn_slot < 0 || - !pn2->pn_const)) { + !(pn2->pn_attrs & JSPROP_READONLY))) { *answer = JS_TRUE; } } @@ -3996,7 +4034,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) pn->pn_pos.begin.lineno); cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION); cg2->parent = cg; - fun = GET_FUNCTION_PRIVATE(cx, pn->pn_funpob->object); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, pn->pn_funpob->object); if (!js_EmitFunctionBody(cx, cg2, pn->pn_body, fun)) return JS_FALSE; @@ -4049,22 +4087,31 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if ((cg->treeContext.flags & TCF_IN_FUNCTION) || ((cx->fp->flags & JSFRAME_SPECIAL) && cx->fp->fun)) { - JSFunction *parentFun; - JSLocalKind localKind; + JSObject *obj, *pobj; + JSProperty *prop; + JSScopeProperty *sprop; - if (cg->treeContext.flags & TCF_IN_FUNCTION) { - JS_ASSERT(OBJ_GET_CLASS(cx, OBJ_GET_PARENT(cx, fun->object)) == - &js_FunctionClass); - parentFun = - GET_FUNCTION_PRIVATE(cx, OBJ_GET_PARENT(cx, fun->object)); - } else { - parentFun = cx->fp->fun; + obj = (cg->treeContext.flags & TCF_IN_FUNCTION) + ? OBJ_GET_PARENT(cx, fun->object) + : cx->fp->fun->object; + if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(fun->atom), + &pobj, &prop)) { + return JS_FALSE; } - localKind = js_LookupLocal(cx, parentFun, fun->atom, &slot); - if (localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST) - op = JSOP_DEFLOCALFUN; - else - JS_ASSERT(!(cg->treeContext.flags & TCF_IN_FUNCTION)); + + if (prop) { + if (pobj == obj) { + sprop = (JSScopeProperty *) prop; + if (sprop->getter == js_GetLocalVariable) { + slot = sprop->shortid; + op = JSOP_DEFLOCALFUN; + } + } + OBJ_DROP_PROPERTY(cx, pobj, prop); + } + + JS_ASSERT(op == JSOP_DEFLOCALFUN || + !(cg->treeContext.flags & TCF_IN_FUNCTION)); /* * If this local function is declared in a body block induced @@ -4076,9 +4123,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) 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); + obj = stmt->down->u.blockObj; + JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); + OBJ_SET_PARENT(cx, fun->object, obj); } } @@ -4436,7 +4483,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) op = PN_OP(pn3); } if (pn3->pn_slot >= 0) { - if (pn3->pn_const) { + if (pn3->pn_attrs & JSPROP_READONLY) { JS_ASSERT(op == JSOP_FORVAR); op = JSOP_FORCONST; } @@ -5439,12 +5486,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) /* * Take care to avoid SRC_ASSIGNOP if the left-hand side is a * const declared in a function (i.e., with non-negative pn_slot - * and when pn_const is true), as in this case (just a bit further - * below) we will avoid emitting the assignment op. + * and JSPROP_READONLY in pn_attrs), as in this case (just a bit + * further below) we will avoid emitting the assignment op. */ if (pn2->pn_type != TOK_NAME || pn2->pn_slot < 0 || - !pn2->pn_const) { + !(pn2->pn_attrs & JSPROP_READONLY)) { if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0) return JS_FALSE; } @@ -5465,7 +5512,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) /* Finally, emit the specialized assignment bytecode. */ switch (pn2->pn_type) { case TOK_NAME: - if (pn2->pn_slot < 0 || !pn2->pn_const) { + if (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY)) { if (pn2->pn_slot >= 0) { EMIT_UINT16_IMM_OP(PN_OP(pn2), atomIndex); } else { @@ -5710,7 +5757,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; op = PN_OP(pn2); if (pn2->pn_slot >= 0) { - if (pn2->pn_const) { + if (pn2->pn_attrs & JSPROP_READONLY) { /* Incrementing a declared const: just get its value. */ op = ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_ATOM) ? JSOP_GETGVAR diff --git a/js/src/jsfun.c b/js/src/jsfun.c index d0ba7448e90..54376fd3da1 100644 --- a/js/src/jsfun.c +++ b/js/src/jsfun.c @@ -722,8 +722,9 @@ js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JS_ASSERT(JSVAL_IS_INT(id)); fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (fp) { - JS_ASSERT((uintN) JSVAL_TO_INT(id) < fp->nvars); - *vp = fp->vars[JSVAL_TO_INT(id)]; + /* XXX no jsint slot commoning here to avoid MSVC1.52 crashes */ + if ((uintN)JSVAL_TO_INT(id) < fp->nvars) + *vp = fp->vars[JSVAL_TO_INT(id)]; } return JS_TRUE; } @@ -736,8 +737,10 @@ js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JS_ASSERT(JSVAL_IS_INT(id)); fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (fp) { - JS_ASSERT((uintN) JSVAL_TO_INT(id) < fp->nvars); - fp->vars[JSVAL_TO_INT(id)] = *vp; + /* XXX jsint slot is block-local here to avoid MSVC1.52 crashes */ + jsint slot = JSVAL_TO_INT(id); + if ((uintN)slot < fp->nvars) + fp->vars[slot] = *vp; } return JS_TRUE; } @@ -749,6 +752,7 @@ call_enumerate(JSContext *cx, JSObject *obj) JSObject *funobj, *pobj; JSScope *scope; JSScopeProperty *sprop, *cprop; + JSPropertyOp getter; jsval *vec; jsid id; JSProperty *prop; @@ -780,9 +784,13 @@ call_enumerate(JSContext *cx, JSObject *obj) */ scope = OBJ_SCOPE(funobj); for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (!JSID_IS_HIDDEN(sprop->id)) + getter = sprop->getter; + if (getter == js_GetArgument) + vec = fp->argv; + else if (getter == js_GetLocalVariable) + vec = fp->vars; + else continue; - vec = (sprop->getter == JS_HIDDEN_ARG_GETTER) ? fp->argv : fp->vars; /* Trigger reflection by looking up the unhidden atom for sprop->id. */ id = JSID_UNHIDE_NAME(sprop->id); @@ -813,12 +821,16 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { JSStackFrame *fp; + JSObject *funobj; JSString *str; JSAtom *atom; - JSLocalKind localKind; + JSObject *obj2; + JSProperty *prop; + JSScopeProperty *sprop; JSPropertyOp getter, setter; - uintN slot, attrs, spflags; - jsval value; + uintN attrs, slot, nslots, spflags; + jsval *vp, value; + intN shortid; fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (!fp) @@ -828,42 +840,61 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, if (!JSVAL_IS_STRING(id)) return JS_TRUE; - if (!fp->callee) + funobj = fp->callee; + if (!funobj) return JS_TRUE; - JS_ASSERT(GET_FUNCTION_PRIVATE(cx, fp->callee) == fp->fun); + JS_ASSERT((JSFunction *) OBJ_GET_PRIVATE(cx, funobj) == fp->fun); str = JSVAL_TO_STRING(id); atom = js_AtomizeString(cx, str, 0); if (!atom) return JS_FALSE; + if (!js_LookupHiddenProperty(cx, funobj, ATOM_TO_JSID(atom), &obj2, &prop)) + return JS_FALSE; - localKind = js_LookupLocal(cx, fp->fun, atom, &slot); - if (localKind != JSLOCAL_NONE) { - if (localKind == JSLOCAL_ARG) { - JS_ASSERT(slot < fp->fun->nargs); - value = (slot < fp->argc) ? fp->argv[slot] : JSVAL_VOID; - getter = setter = NULL; - attrs = JSPROP_PERMANENT; - spflags = 0; - slot = 0; - } else { - JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); - JS_ASSERT(fp->fun->u.i.nvars == fp->nvars); - JS_ASSERT(slot < fp->nvars); - value = fp->vars[slot]; - getter = js_GetCallVariable; - setter = js_SetCallVariable; - attrs = (localKind == JSLOCAL_CONST) - ? JSPROP_PERMANENT | JSPROP_READONLY - : JSPROP_PERMANENT; - spflags = SPROP_HAS_SHORTID; + if (prop) { + if (!OBJ_IS_NATIVE(obj2)) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + return JS_TRUE; } - if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, - getter, setter, attrs, - spflags, (int) slot, NULL)) { - return JS_FALSE; + + sprop = (JSScopeProperty *) prop; + getter = sprop->getter; + attrs = sprop->attrs & ~JSPROP_SHARED; + slot = (uintN) sprop->shortid; + OBJ_DROP_PROPERTY(cx, obj2, prop); + + /* Ensure we found an arg or var property for the same function. */ + if ((sprop->flags & SPROP_IS_HIDDEN) && + (obj2 == funobj || + (JSFunction *) OBJ_GET_PRIVATE(cx, obj2) == fp->fun)) { + if (getter == js_GetArgument) { + vp = fp->argv; + nslots = JS_MAX(fp->argc, fp->fun->nargs); + getter = setter = NULL; + } else { + JS_ASSERT(getter == js_GetLocalVariable); + vp = fp->vars; + nslots = fp->nvars; + getter = js_GetCallVariable; + setter = js_SetCallVariable; + } + if (slot < nslots) { + value = vp[slot]; + spflags = SPROP_HAS_SHORTID; + shortid = (intN) slot; + } else { + value = JSVAL_VOID; + spflags = 0; + shortid = 0; + } + if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, + getter, setter, attrs, + spflags, shortid, NULL)) { + return JS_FALSE; + } + *objp = obj; } - *objp = obj; return JS_TRUE; } @@ -1075,20 +1106,39 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, if (!JSVAL_IS_STRING(id)) return JS_TRUE; - fun = GET_FUNCTION_PRIVATE(cx, obj); - JS_ASSERT(fun->object); + /* No valid function object should lack private data. */ + fun = (JSFunction *)JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL); + JS_ASSERT(fun && fun->object); /* - * No need to reflect fun.prototype in 'fun.prototype = ... '. - * - * This is not just an optimization, because we must not resolve when - * defining hidden properties during compilation. The setup code for the - * prototype and the lazy properties below eventually calls the property - * hooks for the function object. That in turn calls fun_reserveSlots to - * get the number of the reserved slots which is just the number of - * regular expressions literals in the function. When compiling, that - * number is not yet ready so we must make sure that fun_resolve does - * nothing until the code for the function is generated. + * Check for a hidden formal parameter or local variable binding in the + * clone-parent of obj, which would be a different, non-null fun->object. + */ + if (flags & JSRESOLVE_HIDDEN) { + if (fun->object != obj) { + JSObject *pobj; + JSProperty *prop; + + atom = js_AtomizeString(cx, JSVAL_TO_STRING(id), 0); + if (!atom) + return JS_FALSE; + if (!js_LookupHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom), + &pobj, &prop)) { + return JS_FALSE; + } + if (prop) { + JS_ASSERT(pobj == fun->object); + *objp = pobj; + OBJ_DROP_PROPERTY(cx, pobj, prop); + } + } + return JS_TRUE; + } + + /* + * No need to reflect fun.prototype in 'fun.prototype = ...'. This test + * must come after the JSRESOLVE_HIDDEN test, since call_resolve may look + * for a hidden function object property from an assignment bytecode. */ if (flags & JSRESOLVE_ASSIGNING) return JS_TRUE; @@ -1195,6 +1245,12 @@ fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) #include "jsxdrapi.h" +enum { + JSXDR_FUNARG = 1, + JSXDR_FUNVAR = 2, + JSXDR_FUNCONST = 3 +}; + /* XXX store parent and proto, if defined */ static JSBool fun_xdrObject(JSXDRState *xdr, JSObject **objp) @@ -1202,15 +1258,29 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) JSContext *cx; JSFunction *fun; uint32 nullAtom; /* flag to indicate if fun->atom is NULL */ - uintN nargs, nvars, n; - uint32 localsword; /* word to xdr argument and variable counts */ - uint32 flagsword; /* originally only flags was JS_XDRUint8'd */ JSTempValueRooter tvr; + uint32 flagsword; /* originally only flags was JS_XDRUint8'd */ + uint16 extraUnused; /* variable for no longer used field */ + JSAtom *propAtom; + JSScopeProperty *sprop; + uint32 userid; /* NB: holds a signed int-tagged jsval */ + uintN i, n, dupflag; + uint32 type; JSBool ok; +#ifdef DEBUG + uintN nvars = 0, nargs = 0; +#endif cx = xdr->cx; if (xdr->mode == JSXDR_ENCODE) { - fun = GET_FUNCTION_PRIVATE(cx, *objp); + /* + * No valid function object should lack private data, but fail soft + * (return true, no error report) in case one does due to API pilot + * or internal error. + */ + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, *objp); + if (!fun) + return JS_TRUE; if (!FUN_INTERPRETED(fun)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_SCRIPTED_FUNCTION, @@ -1218,17 +1288,12 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) return JS_FALSE; } nullAtom = !fun->atom; - nargs = fun->nargs; - nvars = fun->u.i.nvars; - localsword = (nargs << 16) | nvars; flagsword = fun->flags; + extraUnused = 0; } else { - fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL); + fun = js_NewFunction(cx, NULL, NULL, 0, 0, NULL, NULL); if (!fun) return JS_FALSE; -#ifdef __GNUC__ - nvars = nargs = 0; /* quell GCC uninitialized warning */ -#endif } /* From here on, control flow must flow through label out. */ @@ -1239,96 +1304,128 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) goto bad; if (!nullAtom && !js_XDRStringAtom(xdr, &fun->atom)) goto bad; - if (!JS_XDRUint32(xdr, &localsword) || + + if (!JS_XDRUint16(xdr, &fun->nargs) || + !JS_XDRUint16(xdr, &extraUnused) || + !JS_XDRUint16(xdr, &fun->u.i.nvars) || !JS_XDRUint32(xdr, &flagsword)) { goto bad; } - if (xdr->mode == JSXDR_DECODE) { - nargs = localsword >> 16; - nvars = localsword & JS_BITMASK(16); - JS_ASSERT(flagsword | JSFUN_INTERPRETED); - fun->flags = (uint16) flagsword; - } + /* Assert that all previous writes of extraUnused were writes of 0. */ + JS_ASSERT(extraUnused == 0); /* do arguments and local vars */ - if (fun->object && (n = nargs + nvars) != 0) { - void *mark; - uintN i; - uintN bitmapLength; - uint32 *bitmap; - JSAtom **names, *name; - uint32 localKind; - - mark = JS_ARENA_MARK(&xdr->cx->tempPool); - - /* From this point the control must flow through label release_mark. */ - bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32); + if (fun->object) { + n = fun->nargs + fun->u.i.nvars; if (xdr->mode == JSXDR_ENCODE) { - names = js_GetLocalNames(xdr->cx, fun, &xdr->cx->tempPool, &bitmap); - if (!names) { - ok = JS_FALSE; - goto release_mark; - } - } else { -#ifdef __GNUC__ - names = NULL; /* quell GCC uninitialized warning */ -#endif - JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool, - bitmapLength * sizeof *bitmap); - if (!bitmap) { - JS_ReportOutOfMemory(xdr->cx); - ok = JS_FALSE; - goto release_mark; - } - } - for (i = 0; i != bitmapLength; ++i) { - ok = JS_XDRUint32(xdr, &bitmap[i]); - if (!ok) - goto release_mark; - } - for (i = 0; i != n; ++i) { - if (i < nargs && - !(bitmap[i / JS_BITS_PER_UINT32] & - JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) { - if (xdr->mode == JSXDR_DECODE) { - ok = js_AddLocal(xdr->cx, fun, NULL, JSLOCAL_ARG); - if (!ok) - goto release_mark; - } else { - JS_ASSERT(!names[i]); - } - continue; - } - if (xdr->mode == JSXDR_ENCODE) - name = names[i]; - ok = js_XDRStringAtom(xdr, &name); - if (!ok) - goto release_mark; - if (xdr->mode == JSXDR_DECODE) { - localKind = (i < nargs) - ? JSLOCAL_ARG - : bitmap[i / JS_BITS_PER_UINT32] & - JS_BIT(i & (JS_BITS_PER_UINT32 - 1)) - ? JSLOCAL_CONST - : JSLOCAL_VAR; - ok = js_AddLocal(xdr->cx, fun, name, localKind); - if (!ok) - goto release_mark; - } - } - ok = JS_TRUE; + JSScope *scope; + JSScopeProperty **spvec, *auto_spvec[8]; + void *mark; - release_mark: - JS_ARENA_RELEASE(&xdr->cx->tempPool, mark); - if (!ok) - goto out; + if (n <= sizeof auto_spvec / sizeof auto_spvec[0]) { + spvec = auto_spvec; + mark = NULL; + } else { + mark = JS_ARENA_MARK(&cx->tempPool); + JS_ARENA_ALLOCATE_CAST(spvec, JSScopeProperty **, &cx->tempPool, + n * sizeof(JSScopeProperty *)); + if (!spvec) { + JS_ReportOutOfMemory(cx); + goto bad; + } + } + scope = OBJ_SCOPE(fun->object); + for (sprop = SCOPE_LAST_PROP(scope); sprop; + sprop = sprop->parent) { + if (sprop->getter == js_GetArgument) { + JS_ASSERT(nargs++ <= fun->nargs); + spvec[sprop->shortid] = sprop; + } else if (sprop->getter == js_GetLocalVariable) { + JS_ASSERT(nvars++ <= fun->u.i.nvars); + spvec[fun->nargs + sprop->shortid] = sprop; + } + } + for (i = 0; i < n; i++) { + sprop = spvec[i]; + JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); + type = (i < fun->nargs) + ? JSXDR_FUNARG + : (sprop->attrs & JSPROP_READONLY) + ? JSXDR_FUNCONST + : JSXDR_FUNVAR; + userid = INT_TO_JSVAL(sprop->shortid); + + /* + * sprop->id here represents hidden names so we unhide it and + * encode as an atom. During decoding we read the atom and use + * js_AddHiddenProperty to reconstruct sprop with the hidden + * id. + */ + propAtom = JSID_TO_ATOM(JSID_UNHIDE_NAME(sprop->id)); + if (!JS_XDRUint32(xdr, &type) || + !JS_XDRUint32(xdr, &userid) || + !js_XDRStringAtom(xdr, &propAtom)) { + if (mark) + JS_ARENA_RELEASE(&cx->tempPool, mark); + goto bad; + } + } + if (mark) + JS_ARENA_RELEASE(&cx->tempPool, mark); + } else { + JSPropertyOp getter, setter; + + for (i = n; i != 0; i--) { + uintN attrs = JSPROP_PERMANENT; + + if (!JS_XDRUint32(xdr, &type) || + !JS_XDRUint32(xdr, &userid) || + !js_XDRStringAtom(xdr, &propAtom)) { + goto bad; + } + JS_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR || + type == JSXDR_FUNCONST); + if (type == JSXDR_FUNARG) { + getter = js_GetArgument; + setter = js_SetArgument; + JS_ASSERT(nargs++ <= fun->nargs); + } else if (type == JSXDR_FUNVAR || type == JSXDR_FUNCONST) { + getter = js_GetLocalVariable; + setter = js_SetLocalVariable; + if (type == JSXDR_FUNCONST) + attrs |= JSPROP_READONLY; + JS_ASSERT(nvars++ <= fun->u.i.nvars); + } else { + getter = NULL; + setter = NULL; + } + + /* Flag duplicate argument if atom is bound in fun->object. */ + dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object), + JSID_HIDE_NAME( + ATOM_TO_JSID(propAtom))) + ? SPROP_IS_DUPLICATE + : 0; + + if (!js_AddHiddenProperty(cx, fun->object, + ATOM_TO_JSID(propAtom), + getter, setter, SPROP_INVALID_SLOT, + attrs | JSPROP_SHARED, + dupflag | SPROP_HAS_SHORTID, + JSVAL_TO_INT(userid))) { + goto bad; + } + } + } } if (!js_XDRScript(xdr, &fun->u.i.script, NULL)) goto bad; if (xdr->mode == JSXDR_DECODE) { + fun->flags = (uint16) flagsword | JSFUN_INTERPRETED; + *objp = fun->object; js_CallNewScriptHook(cx, fun->u.i.script, fun); } @@ -1383,7 +1480,6 @@ fun_trace(JSTracer *trc, JSObject *obj) { JSFunction *fun; - /* A newborn function object may have a not yet initialized private slot. */ fun = (JSFunction *) JS_GetPrivate(trc->context, obj); if (fun) { JS_CALL_TRACER(trc, fun, JSTRACE_FUNCTION, "private"); @@ -1401,11 +1497,6 @@ fun_reserveSlots(JSContext *cx, JSObject *obj) { JSFunction *fun; - /* - * We use JS_GetPrivate and not GET_FUNCTION_PRIVATE because during - * js_InitFunctionClass invocation the function is called before the - * private slot of the function object is set. - */ fun = (JSFunction *) JS_GetPrivate(cx, obj); return (fun && FUN_INTERPRETED(fun) && fun->u.i.script && fun->u.i.script->regexpsOffset != 0) @@ -1468,7 +1559,7 @@ fun_toStringHelper(JSContext *cx, uint32 indent, uintN argc, jsval *vp) return JS_FALSE; JS_ASSERT(JS_ObjectIsFunction(cx, obj)); - fun = GET_FUNCTION_PRIVATE(cx, obj); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj); if (!fun) return JS_TRUE; str = JS_DecompileFunction(cx, fun, (uintN)indent); @@ -1708,10 +1799,12 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSStackFrame *fp, *caller; JSFunction *fun; JSObject *parent; - uintN i, n, lineno; + uintN i, n, lineno, dupflag; JSAtom *atom; const char *filename; - JSBool ok; + JSObject *obj2; + JSProperty *prop; + JSScopeProperty *sprop; JSString *str, *arg; JSParseContext pc; JSPrincipals *principals; @@ -1719,6 +1812,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) void *mark; size_t arg_length, args_length, old_args_length; JSTokenType tt; + JSBool ok; fp = cx->fp; if (!(fp->flags & JSFRAME_CONSTRUCTING)) { @@ -1727,11 +1821,6 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); } - - /* - * The constructor is called before the private slot is initialized so we - * must use JS_GetPrivate, not GET_FUNCTION_PRIVATE here. - */ fun = (JSFunction *) JS_GetPrivate(cx, obj); if (fun) return JS_TRUE; @@ -1748,8 +1837,8 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) */ parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2])); - fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED, - parent, cx->runtime->atomState.anonymousAtom); + fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA, parent, + cx->runtime->atomState.anonymousAtom); if (!fun) return JS_FALSE; @@ -1873,24 +1962,54 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * we're assured at this point that it's a valid identifier. */ atom = CURRENT_TOKEN(&pc.tokenStream).t_atom; + if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), + &obj2, &prop)) { + goto after_args; + } + sprop = (JSScopeProperty *) prop; + dupflag = 0; + if (sprop) { + ok = JS_TRUE; + if (obj2 == obj) { + const char *name = js_AtomToPrintableString(cx, atom); - /* Check for a duplicate parameter name. */ - if (js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) { - const char *name; + /* + * A duplicate parameter name. We force a duplicate + * node on the SCOPE_LAST_PROP(scope) list with the + * same id, distinguished by the SPROP_IS_DUPLICATE + * flag, and not mapped by an entry in scope. + */ + JS_ASSERT(sprop->getter == js_GetArgument); + ok = name && + js_ReportCompileErrorNumber(cx, &pc.tokenStream, + JSREPORT_TS | + JSREPORT_WARNING | + JSREPORT_STRICT, + JSMSG_DUPLICATE_FORMAL, + name); - name = js_AtomToPrintableString(cx, atom); - ok = name && - js_ReportCompileErrorNumber(cx, &pc.tokenStream, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_DUPLICATE_FORMAL, - name); + dupflag = SPROP_IS_DUPLICATE; + } + OBJ_DROP_PROPERTY(cx, obj2, prop); if (!ok) goto after_args; + sprop = NULL; } - if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG)) + if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom), + js_GetArgument, js_SetArgument, + SPROP_INVALID_SLOT, + JSPROP_PERMANENT | JSPROP_SHARED, + dupflag | SPROP_HAS_SHORTID, + fun->nargs)) { goto after_args; + } + if (fun->nargs == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_ARGS); + state = BAD; + goto after_args; + } + fun->nargs++; /* * Get the next token. Stop on end of stream. Otherwise @@ -1955,13 +2074,14 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj) 0); if (!atom) goto bad; - fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL); + fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, NULL); if (!fun) goto bad; fun->u.i.script = js_NewScript(cx, 1, 0, 0, 0, 0, 0); if (!fun->u.i.script) goto bad; fun->u.i.script->code[0] = JSOP_STOP; + fun->flags |= JSFUN_INTERPRETED; return proto; bad: @@ -2017,18 +2137,10 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, /* Initialize all function members. */ fun->object = NULL; fun->nargs = nargs; - fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_INTERPRETED); - if (flags & JSFUN_INTERPRETED) { - JS_ASSERT(!native); - JS_ASSERT(nargs == 0); - fun->u.i.nvars = 0; - fun->u.i.spare = 0; - fun->u.i.script = NULL; - } else { - fun->u.n.native = native; - fun->u.n.extra = 0; - fun->u.n.minargs = 0; - } + fun->flags = flags & JSFUN_FLAGS_MASK; + fun->u.n.native = native; + fun->u.n.extra = 0; + fun->u.n.minargs = 0; fun->atom = atom; fun->clasp = NULL; @@ -2064,7 +2176,7 @@ js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) newfunobj = js_NewObject(cx, &js_FunctionClass, NULL, parent); if (!newfunobj) return NULL; - fun = GET_FUNCTION_PRIVATE(cx, funobj); + fun = (JSFunction *) JS_GetPrivate(cx, funobj); if (!js_LinkFunctionObject(cx, fun, newfunobj)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; @@ -2122,7 +2234,7 @@ js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags) js_ReportIsNotFunction(cx, vp, flags); return NULL; } - return GET_FUNCTION_PRIVATE(cx, obj); + return (JSFunction *) JS_GetPrivate(cx, obj); } JSObject * @@ -2208,170 +2320,3 @@ js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) *vp, NULL, name, source); } - -JSBool -js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind) -{ - uint16 *indexp; - JSPropertyOp getter; - uintN readonly; - - JS_ASSERT(FUN_INTERPRETED(fun)); - JS_ASSERT(OBJ_IS_NATIVE(fun->object)); - if (kind == JSLOCAL_ARG) { -#if JS_HAS_DESTRUCTURING - /* - * Destructuring parameter does not have name so we just update the - * number of arguments for it without adding a property. - */ - if (!atom) { - ++fun->nargs; - return JS_TRUE; - } -#endif - indexp = &fun->nargs; - getter = JS_HIDDEN_ARG_GETTER; - readonly = 0; - } else { - JS_ASSERT(kind == JSLOCAL_VAR || kind == JSLOCAL_CONST); - indexp = &fun->u.i.nvars; - getter = JS_HIDDEN_VAR_GETTER; - readonly = (kind == JSLOCAL_CONST) ? JSPROP_READONLY : 0; - } - - if (*indexp == JS_BITMASK(16)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - (kind == JSLOCAL_ARG) - ? JSMSG_TOO_MANY_FUN_ARGS - :JSMSG_TOO_MANY_FUN_VARS); - return JS_FALSE; - } - - /* - * To support duplicate parameter names we force a duplicate node on the - * SCOPE_LAST_PROP(scope) list with the same id, distinguished by the - * SPROP_ALLOW_DUPLICATE flag, and not mapped by an entry in scope. The - * flag is cleared when js_AddNativeProperty finds that the property is - * unique. - */ - if (!js_AddNativeProperty(cx, fun->object, - JSID_HIDE_NAME(ATOM_TO_JSID(atom)), - getter, NULL, SPROP_INVALID_SLOT, - JSPROP_PERMANENT | JSPROP_SHARED | readonly, - SPROP_HAS_SHORTID | SPROP_ALLOW_DUPLICATE, - *indexp)) { - return JS_FALSE; - } - - /* Update the argument of variable counter. */ - ++*indexp; - return JS_TRUE; -} - -JSLocalKind -js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp) -{ - jsid id; - JSScope *scope; - JSScopeProperty *sprop; - JSLocalKind kind; - - JS_ASSERT(FUN_INTERPRETED(fun)); - JS_ASSERT(OBJ_IS_NATIVE(fun->object)); - kind = JSLOCAL_NONE; - if (fun->nargs + fun->u.i.nvars != 0) { - id = JSID_HIDE_NAME(ATOM_TO_JSID(atom)); - - /* - * Inline js_LookupPropertyWithFlags to avoid prototype chain search - * and the resolve hook invocation. The former is just an optimization - * while the latter is necessary to avoid early creation of lazy slots - * in fun->objects. See comments in fun_resolve for details. - */ - JS_LOCK_OBJ(cx, fun->object); - scope = OBJ_SCOPE(fun->object); - JS_ASSERT(scope->object == fun->object); - sprop = SCOPE_GET_PROPERTY(scope, id); - if (sprop) { - JS_ASSERT(sprop->setter == NULL); - if (sprop->getter == JS_HIDDEN_ARG_GETTER) { - kind = JSLOCAL_ARG; - } else { - JS_ASSERT(sprop->getter == JS_HIDDEN_VAR_GETTER); - kind = (sprop->attrs & JSPROP_READONLY) - ? JSLOCAL_CONST - : JSLOCAL_VAR; - } - if (indexp) - *indexp = (uint16) sprop->shortid; - } - JS_UNLOCK_OBJ(cx, fun->object); - } - return kind; -} - -JSAtom ** -js_GetLocalNames(JSContext *cx, JSFunction *fun, JSArenaPool *pool, - uint32 **bitmap) -{ - uintN n, index; - size_t allocsize; - JSAtom **names; - JSScopeProperty *sprop; - JSBool setbit; - uint32 bit; -#ifdef DEBUG - uintN nvars = 0, nargs = 0; -#endif - - JS_ASSERT(FUN_INTERPRETED(fun)); - JS_ASSERT(OBJ_IS_NATIVE(fun->object)); - n = fun->nargs + fun->u.i.nvars; - JS_ASSERT(n != 0); - allocsize = n * sizeof *names; - if (bitmap) - allocsize += JS_HOWMANY(n, JS_BITS_PER_UINT32) * sizeof(uint32); - JS_ARENA_ALLOCATE_CAST(names, JSAtom **, pool, allocsize); - if (!names) { - JS_ReportOutOfMemory(cx); - return NULL; - } - -#if JS_HAS_DESTRUCTURING - /* Some parameter names can be NULL due to destructuring patterns. */ - memset(names, 0, fun->nargs * sizeof *names); -#endif - if (bitmap) { - *bitmap = (uint32 *) (names + n); - memset(*bitmap, 0, JS_HOWMANY(n, JS_BITS_PER_UINT32) * sizeof(uint32)); - } - for (sprop = SCOPE_LAST_PROP(OBJ_SCOPE(fun->object)); - sprop; sprop = sprop->parent) { - if (!JSID_IS_HIDDEN(sprop->id)) - continue; - index = (uint16) sprop->shortid; - if (sprop->getter == JS_HIDDEN_ARG_GETTER) { - JS_ASSERT(nargs++ < fun->nargs); - JS_ASSERT(index < fun->nargs); - setbit = JS_TRUE; - } else { - JS_ASSERT(sprop->getter == JS_HIDDEN_VAR_GETTER); - JS_ASSERT(nvars++ < fun->u.i.nvars); - JS_ASSERT(index < fun->u.i.nvars); - index += fun->nargs; - setbit = (sprop->attrs & JSPROP_READONLY); - } - names[index] = JSID_TO_ATOM(JSID_UNHIDE_NAME(sprop->id)); - if (bitmap && setbit) { - bit = JS_BIT(index & (JS_BITS_PER_UINT32 - 1)); - JS_ASSERT(((*bitmap)[index / JS_BITS_PER_UINT32] & bit) == 0); - (*bitmap)[index / JS_BITS_PER_UINT32] |= bit; - } - } -#if !JS_HAS_DESTRUCTURING - JS_ASSERT(nargs == fun->nargs); -#endif - JS_ASSERT(nvars == fun->u.i.nvars); - return names; -} - diff --git a/js/src/jsfun.h b/js/src/jsfun.h index af8f188a7f9..f2ff2445691 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -98,14 +98,6 @@ extern JS_FRIEND_DATA(JSClass) js_FunctionClass; (!JSVAL_IS_PRIMITIVE(v) && \ OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass) -/* - * Macro to access the private slot of the function object after the slot is - * initialized. - */ -#define GET_FUNCTION_PRIVATE(cx, funobj) \ - (JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass), \ - (JSFunction *) OBJ_GET_PRIVATE(cx, funobj)) - extern JSObject * js_InitFunctionClass(JSContext *cx, JSObject *obj); @@ -180,49 +172,6 @@ js_PutArgsObject(JSContext *cx, JSStackFrame *fp); extern JSBool js_XDRFunction(JSXDRState *xdr, JSObject **objp); -typedef enum JSLocalKind { - JSLOCAL_NONE, - JSLOCAL_ARG, - JSLOCAL_VAR, - JSLOCAL_CONST -} JSLocalKind; - -extern JSBool -js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind); - -/* - * Look up an argument or variable name returning its kind when found or - * JSLOCAL_NONE when no such name exists. When indexp is not null and the name - * exists, *indexp will receive the index of the corresponding argument or - * variable. - */ -extern JSLocalKind -js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp); - -/* - * Get names of arguments and variables for the interpreted function. - * - * The result is an array allocated from the given pool with - * fun->nargs + fun->u.i.nvars - * elements with the names of the arguments coming first. The argument - * name is null when it corresponds to a destructive pattern. - * - * When bitmap is not null, on successful return it will contain a bit array - * where for each index below fun->nargs the bit is set when the corresponding - * argument name is not null. For indexes greater or equal fun->nargs the bit - * is set when the corresponding var is really a const. - */ -extern JSAtom ** -js_GetLocalNames(JSContext *cx, JSFunction *fun, JSArenaPool *pool, - uint32 **bitmap); - -/* - * Pseudo-getter function pointers to distinguish the kind of the hidden - * property. - */ -#define JS_HIDDEN_ARG_GETTER ((JSPropertyOp) sizeof(jsuword)) -#define JS_HIDDEN_VAR_GETTER ((JSPropertyOp) (2 * sizeof(jsuword))) - JS_END_EXTERN_C #endif /* jsfun_h___ */ diff --git a/js/src/jsinterp.c b/js/src/jsinterp.c index 7446a72dfa5..46e685a33c5 100644 --- a/js/src/jsinterp.c +++ b/js/src/jsinterp.c @@ -388,6 +388,30 @@ js_FreeStack(JSContext *cx, void *mark) JS_ARENA_RELEASE(&cx->stackPool, mark); } +JSBool +js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +JSBool +js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +JSBool +js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +JSBool +js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + JSObject * js_GetScopeChain(JSContext *cx, JSStackFrame *fp) { @@ -892,7 +916,7 @@ LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv) key.lineno = 0; name = ""; if (VALUE_IS_FUNCTION(cx, callee)) { - fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(callee)); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, JSVAL_TO_OBJECT(callee)); if (fun->atom) name = js_AtomToPrintableString(cx, fun->atom); if (FUN_INTERPRETED(fun)) { @@ -982,8 +1006,8 @@ LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv) break; case JSTYPE_FUNCTION: if (VALUE_IS_FUNCTION(cx, argval)) { - fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(argval)); - if (fun->atom) { + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, JSVAL_TO_OBJECT(argval)); + if (fun && fun->atom) { str = ATOM_TO_STRING(fun->atom); break; } @@ -1149,7 +1173,7 @@ js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags) } else { have_fun: /* Get private data and set derived locals from it. */ - fun = GET_FUNCTION_PRIVATE(cx, funobj); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, funobj); nslots = FUN_MINARGS(fun); nslots = (nslots > argc) ? nslots - argc : 0; if (FUN_INTERPRETED(fun)) { @@ -1493,7 +1517,8 @@ js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE); if (cx->runtime->checkObjectAccess && VALUE_IS_FUNCTION(cx, fval) && - FUN_INTERPRETED(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(fval))) && + FUN_INTERPRETED((JSFunction *) + JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval))) && !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, &fval)) { return JS_FALSE; @@ -1913,7 +1938,7 @@ js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc) parent = OBJ_GET_PARENT(cx, obj2); if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { - funclasp = GET_FUNCTION_PRIVATE(cx, obj2)->clasp; + funclasp = ((JSFunction *) OBJ_GET_PRIVATE(cx, obj2))->clasp; if (funclasp) clasp = funclasp; } @@ -3887,9 +3912,9 @@ interrupt: SAVE_SP_AND_PC(fp); if (VALUE_IS_FUNCTION(cx, lval)) { obj = JSVAL_TO_OBJECT(lval); - fun = GET_FUNCTION_PRIVATE(cx, obj); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj); - if (FUN_INTERPRETED(fun)) { + if (fun->flags & JSFUN_INTERPRETED) { uintN nframeslots, nvars, nslots, missing; JSArena *a; jsuword avail, nbytes; @@ -4851,7 +4876,7 @@ interrupt: BEGIN_CASE(JSOP_DEFFUN) LOAD_FUNCTION(0); - fun = GET_FUNCTION_PRIVATE(cx, obj); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj); id = ATOM_TO_JSID(fun->atom); /* @@ -5092,7 +5117,7 @@ interrupt: * name is [fun->atom, the identifier parsed by the compiler], * value is Result(3), and attributes are { DontDelete, ReadOnly }. */ - fun = GET_FUNCTION_PRIVATE(cx, obj); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj); attrs = JSFUN_GSFLAG2ATTR(fun->flags); if (attrs) { attrs |= JSPROP_SHARED; @@ -5166,7 +5191,7 @@ interrupt: * unless fun is a getter or setter (in which case, obj is cast to * a JSPropertyOp and passed accordingly). */ - fun = GET_FUNCTION_PRIVATE(cx, obj); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj); attrs = JSFUN_GSFLAG2ATTR(fun->flags); if (attrs) { attrs |= JSPROP_SHARED; @@ -5845,7 +5870,7 @@ interrupt: /* Wrap primitive lval in object clothing if necessary. */ if (!VALUE_IS_FUNCTION(cx, rval) || (obj = JSVAL_TO_OBJECT(rval), - fun = GET_FUNCTION_PRIVATE(cx, obj), + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj), !PRIMITIVE_THIS_TEST(fun, lval))) { ok = js_PrimitiveToObject(cx, &sp[-1]); if (!ok) diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 9758dc6d55d..073d6da673b 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -120,6 +120,18 @@ js_AllocStack(JSContext *cx, uintN nslots, void **markp); extern JS_FRIEND_API(void) js_FreeStack(JSContext *cx, void *mark); +extern JSBool +js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + #ifdef DUMP_CALL_TABLE # define JSOPTION_LOGCALL_TOSOURCE JS_BIT(15) diff --git a/js/src/jsobj.c b/js/src/jsobj.c index bf8af597153..28368bae01b 100644 --- a/js/src/jsobj.c +++ b/js/src/jsobj.c @@ -2995,6 +2995,26 @@ CheckForStringIndex(jsid id, const jschar *cp, const jschar *end, return id; } +JSScopeProperty * +js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uint32 slot, + uintN attrs, uintN flags, intN shortid) +{ + id = JSID_HIDE_NAME(id); + flags |= SPROP_IS_HIDDEN; + return js_AddNativeProperty(cx, obj, id, getter, setter, slot, attrs, + flags, shortid); +} + +JSBool +js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + id = JSID_HIDE_NAME(id); + return js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_HIDDEN, + objp, propp); +} + JSScopeProperty * js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot, diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 90832419b56..783a2281c41 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -174,7 +174,7 @@ struct JSObject { #define STOBJ_SET_SYSTEM(obj) ((void)((obj)->fslots[JSSLOT_CLASS] |= 2)) -#define STOBJ_GET_PRIVATE(obj) \ +#define STOBJ_GET_PRIVATE(obj) \ (JS_ASSERT(JSVAL_IS_INT(STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE))), \ JSVAL_TO_PRIVATE(STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE))) @@ -443,6 +443,21 @@ js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp); extern void js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot); +/* + * Native property add and lookup variants that hide id in the hidden atom + * subspace, so as to avoid collisions between internal properties such as + * formal arguments and local variables in function objects, and externally + * set properties with the same ids. + */ +extern JSScopeProperty * +js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uint32 slot, + uintN attrs, uintN flags, intN shortid); + +extern JSBool +js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp); + /* * Find or create a property named by id in obj's scope, with the given getter * and setter, slot, attributes, and other members. @@ -492,11 +507,15 @@ js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, /* * Specialized subroutine that allows caller to preset JSRESOLVE_* flags. + * JSRESOLVE_HIDDEN flags hidden function param/local name lookups, just for + * internal use by fun_resolve and similar built-ins. */ extern JSBool js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp, JSProperty **propp); +#define JSRESOLVE_HIDDEN 0x8000 + extern JS_FRIEND_API(JSBool) js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, JSProperty **propp); @@ -625,7 +644,6 @@ js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller extern JSBool js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, JSPrincipals *principals, JSAtom *caller); - JS_END_EXTERN_C #endif /* jsobj_h___ */ diff --git a/js/src/jsopcode.c b/js/src/jsopcode.c index 42ea949a0e8..7d99f617146 100644 --- a/js/src/jsopcode.c +++ b/js/src/jsopcode.c @@ -649,8 +649,7 @@ struct JSPrinter { JSPackedBool grouped; /* in parenthesized expression context */ JSScript *script; /* script being printed */ jsbytecode *dvgfence; /* js_DecompileValueGenerator fencepost */ - JSFunction *fun; /* interpreted function */ - JSAtom **localNames; /* argument and variable names */ + JSObject *object; /* interpreted function object */ #if JS_HAS_BLOCK_SCOPE JSBraceState braceState; /* remove braces around let declaration */ ptrdiff_t spaceOffset; /* -1 or offset of space before maybe-{ */ @@ -666,8 +665,7 @@ struct JSPrinter { #define JS_IN_GROUP_CONTEXT 0x10000 JSPrinter * -JS_NEW_PRINTER(JSContext *cx, const char *name, JSFunction *fun, - uintN indent, JSBool pretty) +JS_NEW_PRINTER(JSContext *cx, const char *name, uintN indent, JSBool pretty) { JSPrinter *jp; @@ -681,20 +679,11 @@ JS_NEW_PRINTER(JSContext *cx, const char *name, JSFunction *fun, jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0; jp->script = NULL; jp->dvgfence = NULL; + jp->object = NULL; #if JS_HAS_BLOCK_SCOPE jp->braceState = ALWAYS_BRACE; jp->spaceOffset = -1; #endif - jp->fun = fun; - if (!fun || !FUN_INTERPRETED(fun) || fun->nargs + fun->u.i.nvars == 0) { - jp->localNames = NULL; - } else { - jp->localNames = js_GetLocalNames(cx, fun, &jp->pool, NULL); - if (!jp->localNames) { - js_DestroyPrinter(jp); - return NULL; - } - } return jp; } @@ -1217,35 +1206,50 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, return JS_TRUE; } +static JSAtom * +GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot) +{ + JSObject *obj; + JSScopeProperty *sprop; + + obj = jp->object; + while (obj) { + for (sprop = SCOPE_LAST_PROP(OBJ_SCOPE(obj)); sprop; + sprop = sprop->parent) { + if (sprop->getter != getter) + continue; + JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); + JS_ASSERT(JSID_IS_HIDDEN(sprop->id)); + if ((uintN) sprop->shortid == slot) + return JSID_TO_ATOM(JSID_UNHIDE_NAME(sprop->id)); + } + obj = OBJ_GET_PROTO(jp->sprinter.context, obj); + } + return NULL; +} + +static JSBool +PushSlotAtom(SprintStack *ss, JSPropertyOp getter, uintN slot, JSOp op) +{ + JSAtom *atom; + char *lval; + + atom = GetSlotAtom(ss->printer, getter, slot); + if (!atom) + return JS_FALSE; + JS_ASSERT(ATOM_IS_STRING(atom)); + lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!lval) + return JS_FALSE; + return PushOff(ss, STR2OFF(&ss->sprinter, lval), op); +} + #define LOCAL_ASSERT_RV(expr, rv) \ JS_BEGIN_MACRO \ JS_ASSERT(expr); \ if (!(expr)) return (rv); \ JS_END_MACRO -static JSAtom * -GetSlotAtom(JSPrinter *jp, JSBool argument, uintN slot) -{ - JSFunction *fun; - JSAtom *name; - - fun = jp->fun; - LOCAL_ASSERT_RV(jp->fun, NULL); - LOCAL_ASSERT_RV(jp->localNames, NULL); - if (argument) { - LOCAL_ASSERT_RV(slot < fun->nargs, NULL); - name = jp->localNames[slot]; -#if !JS_HAS_DESTRUCTURING - LOCAL_ASSERT_RV(name, NULL); -#endif - } else { - LOCAL_ASSERT_RV(slot < fun->u.i.nvars, NULL); - name = jp->localNames[fun->nargs + slot]; - LOCAL_ASSERT_RV(name, NULL); - } - return name; -} - const char * GetLocal(SprintStack *ss, jsint i) { @@ -1358,14 +1362,14 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, i = GET_UINT16(pc); atom = NULL; lval = NULL; - if (op == JSOP_SETARG || op == JSOP_SETVAR) { - atom = GetSlotAtom(jp, op == JSOP_SETARG, i); - LOCAL_ASSERT(atom); - } else if (op == JSOP_SETGVAR) { + if (op == JSOP_SETARG) + atom = GetSlotAtom(jp, js_GetArgument, i); + else if (op == JSOP_SETVAR) + atom = GetSlotAtom(jp, js_GetLocalVariable, i); + else if (op == JSOP_SETGVAR) GET_ATOM_FROM_BYTECODE(jp->script, pc, 0, atom); - } else { + else lval = GetLocal(ss, i); - } if (atom) lval = js_AtomToPrintableString(cx, atom); LOCAL_ASSERT(lval); @@ -2081,19 +2085,22 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) JS_GET_SCRIPT_OBJECT(jp->script, js_GetSrcNoteOffset(sn, 0), obj); do_function: - js_puts(jp, "\n"); - fun = GET_FUNCTION_PRIVATE(cx, obj); - jp2 = JS_NEW_PRINTER(cx, "nested_function", fun, + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj); + jp2 = JS_NEW_PRINTER(cx, "nested_function", jp->indent, jp->pretty); if (!jp2) return NULL; - ok = js_DecompileFunction(jp2); - if (ok && jp2->sprinter.base) + jp2->object = jp->object; + js_puts(jp2, "\n"); + ok = js_DecompileFunction(jp2, fun); + if (ok && jp2->sprinter.base) { js_puts(jp, jp2->sprinter.base); + js_puts(jp, "\n"); + } js_DestroyPrinter(jp2); if (!ok) return NULL; - js_puts(jp, "\n\n"); + js_puts(jp, "\n"); break; case SRC_BRACE: @@ -2735,8 +2742,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) break; case JSOP_RETURN: - LOCAL_ASSERT(jp->fun); - fun = jp->fun; + obj = jp->object; + LOCAL_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj); if (fun->flags & JSFUN_EXPR_CLOSURE) { rval = POP_STR(); js_printf(jp, (*rval == '{') ? "(%s)%s" : ss_format, @@ -3077,13 +3085,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) goto do_logical_connective; case JSOP_FORARG: - atom = GetSlotAtom(jp, JS_TRUE, GET_ARGNO(pc)); + atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); goto do_fornameinloop; case JSOP_FORVAR: case JSOP_FORCONST: - atom = GetSlotAtom(jp, JS_FALSE, GET_VARNO(pc)); + atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_fornameinloop; @@ -3313,12 +3321,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) break; case JSOP_SETARG: - atom = GetSlotAtom(jp, JS_TRUE, GET_ARGNO(pc)); + atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); goto do_setname; case JSOP_SETVAR: - atom = GetSlotAtom(jp, JS_FALSE, GET_VARNO(pc)); + atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_setname; @@ -3493,13 +3501,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_INCARG: case JSOP_DECARG: - atom = GetSlotAtom(jp, JS_TRUE, GET_ARGNO(pc)); + atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); goto do_incatom; case JSOP_INCVAR: case JSOP_DECVAR: - atom = GetSlotAtom(jp, JS_FALSE, GET_VARNO(pc)); + atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_incatom; @@ -3555,13 +3563,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_ARGINC: case JSOP_ARGDEC: - atom = GetSlotAtom(jp, JS_TRUE, GET_ARGNO(pc)); + atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); LOCAL_ASSERT(atom); goto do_atominc; case JSOP_VARINC: case JSOP_VARDEC: - atom = GetSlotAtom(jp, JS_FALSE, GET_VARNO(pc)); + atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_atominc; @@ -3638,13 +3646,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) END_LITOPX_CASE BEGIN_LITOPX_CASE(JSOP_GETARGPROP, ARGNO_LEN) + if (!PushSlotAtom(ss, js_GetArgument, GET_ARGNO(pc), op)) + return NULL; + goto do_getprop; + BEGIN_LITOPX_CASE(JSOP_GETVARPROP, VARNO_LEN) - atom = GetSlotAtom(ss->printer, op == JSOP_GETARGPROP, - GET_UINT16(pc)); - LOCAL_ASSERT(atom); - JS_ASSERT(ATOM_IS_STRING(atom)); - lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!lval || !PushOff(ss, STR2OFF(&ss->sprinter, lval), op)) + if (!PushSlotAtom(ss, js_GetLocalVariable, GET_VARNO(pc), op)) return NULL; goto do_getprop; @@ -3745,7 +3752,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_CALLARG: case JSOP_GETARG: i = GET_ARGNO(pc); - atom = GetSlotAtom(jp, JS_TRUE, i); + atom = GetSlotAtom(jp, js_GetArgument, i); #if JS_HAS_DESTRUCTURING if (!atom) { todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i); @@ -3758,7 +3765,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) case JSOP_CALLVAR: case JSOP_GETVAR: - atom = GetSlotAtom(jp, JS_FALSE, GET_VARNO(pc)); + atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); LOCAL_ASSERT(atom); goto do_name; @@ -3821,7 +3828,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) SprintStack ss2; LOAD_FUNCTION(0); - fun = GET_FUNCTION_PRIVATE(cx, obj); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj); LOCAL_ASSERT(FUN_INTERPRETED(fun)); inner = fun->u.i.script; @@ -3937,7 +3944,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * parenthesization without confusing getter/setter code * that checks for JSOP_ANONFUNOBJ and JSOP_NAMEDFUNOBJ. */ - fun = GET_FUNCTION_PRIVATE(cx, obj); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj); if (!(fun->flags & JSFUN_EXPR_CLOSURE)) indent |= JS_IN_GROUP_CONTEXT; str = JS_DecompileFunction(cx, fun, indent); @@ -4684,35 +4691,37 @@ js_DecompileScript(JSPrinter *jp, JSScript *script) static const char native_code_str[] = "\t[native code]\n"; JSBool -js_DecompileFunctionBody(JSPrinter *jp) +js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun) { JSScript *script; + JSObject *oldobject; + JSBool ok; - JS_ASSERT(jp->fun); - JS_ASSERT(!jp->script); - if (!FUN_INTERPRETED(jp->fun)) { + if (!FUN_INTERPRETED(fun)) { js_printf(jp, native_code_str); return JS_TRUE; } - - script = jp->fun->u.i.script; - return js_DecompileCode(jp, script, script->code, (uintN)script->length, 0); + script = fun->u.i.script; + oldobject = jp->object; + jp->object = fun->object; + ok = js_DecompileCode(jp, script, script->code, (uintN)script->length, 0); + jp->object = oldobject; + return ok; } JSBool -js_DecompileFunction(JSPrinter *jp) +js_DecompileFunction(JSPrinter *jp, JSFunction *fun) { - JSFunction *fun; - uintN i; - JSAtom *param; + JSContext *cx; + uintN i, nargs, indent; + void *mark; + JSAtom **params; + JSObject *oldobject; + JSScopeProperty *sprop; jsbytecode *pc, *endpc; ptrdiff_t len; JSBool ok; - fun = jp->fun; - JS_ASSERT(fun); - JS_ASSERT(!jp->script); - /* * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force @@ -4734,19 +4743,45 @@ js_DecompileFunction(JSPrinter *jp) return JS_FALSE; js_puts(jp, "("); - if (!FUN_INTERPRETED(fun)) { - js_printf(jp, ") {\n"); - jp->indent += 4; - js_printf(jp, native_code_str); - jp->indent -= 4; - js_printf(jp, "\t}"); - } else { + if (FUN_INTERPRETED(fun) && fun->object) { + size_t paramsize; #ifdef JS_HAS_DESTRUCTURING SprintStack ss; - void *mark; + JSScript *oldscript; #endif - /* Print the parameters. */ + /* + * Print the parameters. + * + * This code is complicated by the need to handle duplicate parameter + * names, as required by ECMA (bah!). A duplicate parameter is stored + * as another node with the same id (the parameter name) but different + * shortid (the argument index) along the property tree ancestor line + * starting at SCOPE_LAST_PROP(scope). Only the last duplicate param + * is mapped by the scope's hash table. + */ + cx = jp->sprinter.context; + nargs = fun->nargs; + mark = JS_ARENA_MARK(&cx->tempPool); + paramsize = nargs * sizeof(JSAtom *); + JS_ARENA_ALLOCATE_CAST(params, JSAtom **, &cx->tempPool, paramsize); + if (!params) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + memset(params, 0, paramsize); + for (sprop = SCOPE_LAST_PROP(OBJ_SCOPE(fun->object)); sprop; + sprop = sprop->parent) { + if (sprop->getter != js_GetArgument) + continue; + JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); + JS_ASSERT((uint16) sprop->shortid < nargs); + JS_ASSERT(JSID_IS_HIDDEN(sprop->id)); + params[(uint16) sprop->shortid] = + JSID_TO_ATOM(JSID_UNHIDE_NAME(sprop->id)); + } + pc = fun->u.i.script->main; endpc = pc + fun->u.i.script->length; ok = JS_TRUE; @@ -4757,20 +4792,20 @@ js_DecompileFunction(JSPrinter *jp) pc += JSOP_GENERATOR_LENGTH; ss.printer = NULL; + oldscript = jp->script; jp->script = fun->u.i.script; - mark = JS_ARENA_MARK(&jp->sprinter.context->tempPool); + oldobject = jp->object; + jp->object = fun->object; #endif - for (i = 0; i < fun->nargs; i++) { + for (i = 0; i < nargs; i++) { if (i > 0) js_puts(jp, ", "); - param = GetSlotAtom(jp, JS_TRUE, i); - #if JS_HAS_DESTRUCTURING #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE) - if (!param) { + if (!params[i]) { ptrdiff_t todo; const char *lval; @@ -4778,8 +4813,7 @@ js_DecompileFunction(JSPrinter *jp) pc += JSOP_GETARG_LENGTH; LOCAL_ASSERT(*pc == JSOP_DUP); if (!ss.printer) { - ok = InitSprintStack(jp->sprinter.context, &ss, jp, - fun->u.i.script->depth); + ok = InitSprintStack(cx, &ss, jp, fun->u.i.script->depth); if (!ok) break; } @@ -4802,39 +4836,56 @@ js_DecompileFunction(JSPrinter *jp) #undef LOCAL_ASSERT #endif - if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(param), 0)) { + if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(params[i]), 0)) { ok = JS_FALSE; break; } } #ifdef JS_HAS_DESTRUCTURING - jp->script = NULL; - JS_ARENA_RELEASE(&jp->sprinter.context->tempPool, mark); + jp->script = oldscript; + jp->object = oldobject; #endif + JS_ARENA_RELEASE(&cx->tempPool, mark); if (!ok) return JS_FALSE; - if (fun->flags & JSFUN_EXPR_CLOSURE) { - js_printf(jp, ") "); - } else { - js_printf(jp, ") {\n"); - jp->indent += 4; - } - - len = fun->u.i.script->code + fun->u.i.script->length - pc; - ok = js_DecompileCode(jp, fun->u.i.script, pc, (uintN)len, 0); - if (!ok) - return JS_FALSE; - - if (!(fun->flags & JSFUN_EXPR_CLOSURE)) { - jp->indent -= 4; - js_printf(jp, "\t}"); - } +#ifdef __GNUC__ + } else { + pc = NULL; +#endif } - if (!jp->pretty && !jp->grouped && (fun->flags & JSFUN_LAMBDA)) - js_puts(jp, ")"); + indent = jp->indent; + if (fun->flags & JSFUN_EXPR_CLOSURE) { + js_printf(jp, ") "); + } else { + js_printf(jp, ") {\n"); + jp->indent += 4; + } + if (FUN_INTERPRETED(fun) && fun->object) { + oldobject = jp->object; + jp->object = fun->object; + len = fun->u.i.script->code + fun->u.i.script->length - pc; + ok = js_DecompileCode(jp, fun->u.i.script, pc, (uintN)len, 0); + jp->object = oldobject; + if (!ok) { + jp->indent = indent; + return JS_FALSE; + } + } else { + js_printf(jp, native_code_str); + } + + if (!(fun->flags & JSFUN_EXPR_CLOSURE)) { + jp->indent -= 4; + js_printf(jp, "\t}"); + } + + if (!jp->pretty) { + if (!jp->grouped && (fun->flags & JSFUN_LAMBDA)) + js_puts(jp, ")"); + } return JS_TRUE; } @@ -5162,8 +5213,12 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, } name = NULL; - jp = JS_NEW_PRINTER(cx, "js_DecompileValueGenerator", fp->fun, 0, JS_FALSE); + jp = JS_NEW_PRINTER(cx, "js_DecompileValueGenerator", 0, JS_FALSE); if (jp) { + if (fp->fun && fp->fun->object) { + JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object)); + jp->object = fp->fun->object; + } jp->dvgfence = end; if (js_DecompileCode(jp, script, begin, (uintN)len, (uintN)pcdepth)) { name = (jp->sprinter.base) ? jp->sprinter.base : (char *) ""; diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 244203bde4d..7adb3842d98 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -273,16 +273,15 @@ js_QuoteString(JSContext *cx, JSString *str, jschar quote); */ #ifdef JS_ARENAMETER -# define JS_NEW_PRINTER(cx, name, fun, indent, pretty) \ - js_NewPrinter(cx, name, fun, indent, pretty) +# define JS_NEW_PRINTER(cx, name, indent, pretty) \ + js_NewPrinter(cx, name, indent, pretty) #else -# define JS_NEW_PRINTER(cx, name, fun, indent, pretty) \ - js_NewPrinter(cx, fun, indent, pretty) +# define JS_NEW_PRINTER(cx, name, indent, pretty) \ + js_NewPrinter(cx, indent, pretty) #endif extern JSPrinter * -JS_NEW_PRINTER(JSContext *cx, const char *name, JSFunction *fun, - uintN indent, JSBool pretty); +JS_NEW_PRINTER(JSContext *cx, const char *name, uintN indent, JSBool pretty); extern void js_DestroyPrinter(JSPrinter *jp); @@ -356,10 +355,10 @@ extern JSBool js_DecompileScript(JSPrinter *jp, JSScript *script); extern JSBool -js_DecompileFunctionBody(JSPrinter *jp); +js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun); extern JSBool -js_DecompileFunction(JSPrinter *jp); +js_DecompileFunction(JSPrinter *jp, JSFunction *fun); /* * Find the source expression that resulted in v, and return a newly allocated diff --git a/js/src/jsparse.c b/js/src/jsparse.c index fdbe3796c4a..ff73eb7657f 100644 --- a/js/src/jsparse.c +++ b/js/src/jsparse.c @@ -814,7 +814,6 @@ FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, uintN oldflags, firstLine; JSParseNode *pn; - JS_ASSERT(FUN_INTERPRETED(fun)); fp = cx->fp; funobj = fun->object; if (!fp || fp->fun != fun || fp->varobj != funobj || @@ -829,6 +828,12 @@ FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, cx->fp = &frame; } + /* + * Set interpreted early so js_EmitTree can test it to decide whether to + * eliminate useless expressions. + */ + fun->flags |= JSFUN_INTERPRETED; + js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); stmtInfo.flags = SIF_BODY_BLOCK; @@ -991,7 +996,9 @@ struct BindData { struct { JSFunction *fun; /* this overlays u.arg.fun */ JSClass *clasp; - JSLocalKind kind; + JSPropertyOp getter; + JSPropertyOp setter; + uintN attrs; } var; struct { jsuint index; @@ -1009,40 +1016,83 @@ struct BindData { (data)->pn ? (void *)(data)->pn : (void *)(data)->ts, \ ((data)->pn ? JSREPORT_PN : JSREPORT_TS) | (flags) +static JSBool +BumpFormalCount(JSContext *cx, JSFunction *fun) +{ + if (fun->nargs == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_ARGS); + return JS_FALSE; + } + fun->nargs++; + return JS_TRUE; +} + static JSBool BindArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) { + JSObject *obj, *pobj; + JSProperty *prop; + JSBool ok; + uintN dupflag; + JSFunction *fun; const char *name; - /* - * Check for a duplicate parameter name, a "feature" required by ECMA-262. - */ - if (js_LookupLocal(cx, data->u.arg.fun, atom, NULL) != JSLOCAL_NONE) { + obj = data->obj; + ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); + if (!ok) + return JS_FALSE; + + dupflag = 0; + if (prop) { + JS_ASSERT(pobj == obj); name = js_AtomToPrintableString(cx, atom); - if (!name || - !js_ReportCompileErrorNumber(cx, + + /* + * A duplicate parameter name, a "feature" required by ECMA-262. + * We force a duplicate node on the SCOPE_LAST_PROP(scope) list + * with the same id, distinguished by the SPROP_IS_DUPLICATE flag, + * and not mapped by an entry in scope. + */ + ok = name && + js_ReportCompileErrorNumber(cx, BIND_DATA_REPORT_ARGS(data, JSREPORT_WARNING | JSREPORT_STRICT), JSMSG_DUPLICATE_FORMAL, - name)) { + name); + + OBJ_DROP_PROPERTY(cx, pobj, prop); + if (!ok) return JS_FALSE; - } + + dupflag = SPROP_IS_DUPLICATE; } - return js_AddLocal(cx, data->u.arg.fun, atom, JSLOCAL_ARG); + fun = data->u.arg.fun; + if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom), + js_GetArgument, js_SetArgument, + SPROP_INVALID_SLOT, + JSPROP_PERMANENT | JSPROP_SHARED, + dupflag | SPROP_HAS_SHORTID, + fun->nargs)) { + return JS_FALSE; + } + + return BumpFormalCount(cx, fun); } static JSBool BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom) { + JSFunction *fun; + /* - * Can't increase fun->nvars in an active frame when kind is JSFL_NONE. + * Can't increase fun->nvars in an active frame, so insist that getter is + * js_GetLocalVariable, not js_GetCallVariable or anything else. */ - if (data->u.var.kind == JSLOCAL_NONE) + if (data->u.var.getter != js_GetLocalVariable) return JS_TRUE; - JS_ASSERT(data->u.var.kind == JSLOCAL_VAR || - data->u.var.kind == JSLOCAL_CONST); /* * Don't bind a variable with the hidden name 'arguments', per ECMA-262. @@ -1053,7 +1103,21 @@ BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom) if (atom == cx->runtime->atomState.argumentsAtom) return JS_TRUE; - return js_AddLocal(cx, data->u.var.fun, atom, data->u.var.kind); + fun = data->u.var.fun; + if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom), + data->u.var.getter, data->u.var.setter, + SPROP_INVALID_SLOT, + data->u.var.attrs | JSPROP_SHARED, + SPROP_HAS_SHORTID, fun->u.i.nvars)) { + return JS_FALSE; + } + if (fun->u.i.nvars == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_VARS); + return JS_FALSE; + } + fun->u.i.nvars++; + return JS_TRUE; } #if JS_HAS_DESTRUCTURING @@ -1069,6 +1133,9 @@ BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) { JSAtomListElement *ale; + JSFunction *fun; + JSObject *obj, *pobj; + JSProperty *prop; const char *name; ATOM_LIST_SEARCH(ale, &tc->decls, atom); @@ -1079,7 +1146,13 @@ BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, ALE_SET_JSOP(ale, data->op); } - if (js_LookupLocal(cx, data->u.var.fun, atom, NULL) != JSLOCAL_NONE) { + fun = data->u.var.fun; + obj = data->obj; + if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) + return JS_FALSE; + + if (prop) { + JS_ASSERT(pobj == obj && OBJ_IS_NATIVE(pobj)); name = js_AtomToPrintableString(cx, atom); if (!name || !js_ReportCompileErrorNumber(cx, @@ -1090,6 +1163,7 @@ BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, name)) { return JS_FALSE; } + OBJ_DROP_PROPERTY(cx, pobj, prop); } else { if (!BindLocalVariable(cx, data, atom)) return JS_FALSE; @@ -1108,8 +1182,9 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSAtom *funAtom; JSParsedObjectBox *funpob; JSStackFrame *fp; - JSObject *varobj; + JSObject *varobj, *pobj; JSAtomListElement *ale; + JSProperty *prop; JSFunction *fun; JSTreeContext funtc; #if JS_HAS_DESTRUCTURING @@ -1190,31 +1265,56 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * JSOP_GETVAR bytecode). */ if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) { - JSLocalKind localKind; + JSScopeProperty *sprop; /* * Define a property on the outer function so that BindNameToSlot - * can properly optimize accesses. Note that we need a variable, - * not an argument, for the function statement. Thus we add a - * variable even if the parameter with the given name already - * exists. + * can properly optimize accesses. */ JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass); - JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, varobj)); - localKind = js_LookupLocal(cx, fp->fun, funAtom, NULL); - if (localKind == JSLOCAL_NONE || localKind == JSLOCAL_ARG) { - if (!js_AddLocal(cx, fp->fun, funAtom, JSLOCAL_VAR)) + JS_ASSERT(fp->fun == (JSFunction *) OBJ_GET_PRIVATE(cx, varobj)); + if (!js_LookupHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom), + &pobj, &prop)) { + return NULL; + } + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + sprop = NULL; + if (!prop || + pobj != varobj || + (sprop = (JSScopeProperty *)prop, + sprop->getter != js_GetLocalVariable)) { + uintN sflags; + + /* + * Use SPROP_IS_DUPLICATE if there is a formal argument of the + * same name, so the decompiler can find the parameter name. + */ + sflags = (sprop && sprop->getter == js_GetArgument) + ? SPROP_IS_DUPLICATE | SPROP_HAS_SHORTID + : SPROP_HAS_SHORTID; + if (!js_AddHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom), + js_GetLocalVariable, + js_SetLocalVariable, + SPROP_INVALID_SLOT, + JSPROP_PERMANENT | JSPROP_SHARED, + sflags, fp->fun->u.i.nvars)) { return NULL; + } + if (fp->fun->u.i.nvars == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_TOO_MANY_FUN_VARS); + return NULL; + } + fp->fun->u.i.nvars++; } } } - fun = js_NewFunction(cx, NULL, NULL, 0, - JSFUN_INTERPRETED | (lambda ? JSFUN_LAMBDA : 0), - varobj, funAtom); + fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj, + funAtom); if (!fun) return NULL; - #if JS_HAS_GETTER_SETTER if (op != JSOP_NOP) fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER; @@ -1222,7 +1322,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, /* * Create wrapping box for fun->object early to protect against a - * last-ditch GC. + * last-ditch GC under js_LookupHiddenProperty. */ funpob = js_NewParsedObjectBox(cx, tc->parseContext, fun->object); if (!funpob) @@ -1262,7 +1362,10 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, data.op = JSOP_DEFVAR; data.binder = BindDestructuringArg; data.u.var.clasp = &js_FunctionClass; - data.u.var.kind = JSLOCAL_VAR; + data.u.var.getter = js_GetLocalVariable; + data.u.var.setter = js_SetLocalVariable; + data.u.var.attrs = JSPROP_PERMANENT; + lhs = DestructuringExpr(cx, &data, &funtc, tt); if (!lhs) return NULL; @@ -1278,7 +1381,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * parameter that is to be destructured. */ slot = fun->nargs; - if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG)) + if (!BumpFormalCount(cx, fun)) return NULL; /* @@ -1736,8 +1839,11 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) JSOp op, prevop; const char *name; JSFunction *fun; - JSObject *obj; - JSLocalKind localKind; + JSObject *obj, *pobj; + JSProperty *prop; + JSBool ok; + JSPropertyOp getter, setter; + JSScopeProperty *sprop; stmt = js_LexicalLookup(tc, atom, NULL, 0); ATOM_LIST_SEARCH(ale, &tc->decls, atom); @@ -1779,16 +1885,84 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) fun = data->u.var.fun; obj = data->obj; - if (!fun || OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) { - /* - * Don't lookup global variables or variables in an active frame at - * compile time. - */ - return JS_TRUE; + if (!fun) { + /* Don't lookup global variables at compile time. */ + prop = NULL; + } else { + JS_ASSERT(OBJ_IS_NATIVE(obj)); + if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), + &pobj, &prop)) { + return JS_FALSE; + } } - localKind = js_LookupLocal(cx, fun, atom, NULL); - if (localKind == JSLOCAL_NONE) { + ok = JS_TRUE; + getter = data->u.var.getter; + setter = data->u.var.setter; + + if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) { + sprop = (JSScopeProperty *)prop; + if (sprop->getter == js_GetArgument) { + name = js_AtomToPrintableString(cx, atom); + if (!name) { + ok = JS_FALSE; + } else if (op == JSOP_DEFCONST) { + js_ReportCompileErrorNumber(cx, + BIND_DATA_REPORT_ARGS(data, + JSREPORT_ERROR), + JSMSG_REDECLARED_PARAM, + name); + ok = JS_FALSE; + } else { + getter = js_GetArgument; + setter = js_SetArgument; + ok = js_ReportCompileErrorNumber(cx, + BIND_DATA_REPORT_ARGS(data, + JSREPORT_WARNING | + JSREPORT_STRICT), + JSMSG_VAR_HIDES_ARG, + name); + } + } else { + JS_ASSERT(getter == js_GetLocalVariable); + + if (fun) { + /* Not an argument, must be a redeclared local var. */ + if (data->u.var.clasp == &js_FunctionClass) { + JS_ASSERT(sprop->getter == js_GetLocalVariable); + JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && + (uint16) sprop->shortid < fun->u.i.nvars); + } else if (data->u.var.clasp == &js_CallClass) { + if (sprop->getter == js_GetCallVariable) { + /* + * Referencing a name introduced by a var statement in + * the enclosing function. Check that the slot number + * we have is in range. + */ + JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && + (uint16) sprop->shortid < fun->u.i.nvars); + } else { + /* + * A variable introduced through another eval: don't + * use the special getters and setters since we can't + * allocate a slot in the frame. + */ + getter = sprop->getter; + setter = sprop->setter; + } + } + + /* Override the old getter and setter, to handle eval. */ + sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, + 0, sprop->attrs, + getter, setter); + if (!sprop) + ok = JS_FALSE; + } + } + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + } else { /* * Property not found in current variable scope: we have not seen this * variable before. Define a new local variable by adding a property @@ -1797,37 +1971,19 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) * bodies are handled at runtime, by script prolog JSOP_DEFVAR opcodes * generated for slot-less vars. */ + sprop = NULL; + if (prop) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + prop = NULL; + } + if (cx->fp->scopeChain == obj && !js_InWithStatement(tc) && !BindLocalVariable(cx, data, atom)) { return JS_FALSE; } - } else if (localKind == JSLOCAL_ARG) { - name = js_AtomToPrintableString(cx, atom); - if (!name) - return JS_FALSE; - - if (op == JSOP_DEFCONST) { - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_ERROR), - JSMSG_REDECLARED_PARAM, - name); - return JS_FALSE; - } - if (!js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_WARNING | - JSREPORT_STRICT), - JSMSG_VAR_HIDES_ARG, - name)) { - return JS_FALSE; - } - } else { - /* Not an argument, must be a redeclared local var. */ - JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); } - return JS_TRUE; + return ok; } #if JS_HAS_DESTRUCTURING @@ -1862,7 +2018,7 @@ BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn, pn->pn_op = (data->op == JSOP_DEFCONST) ? JSOP_SETCONST : JSOP_SETNAME; - pn->pn_const = (data->u.var.kind == JSLOCAL_CONST); + pn->pn_attrs = data->u.var.attrs; return JS_TRUE; } @@ -3612,16 +3768,20 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) data.u.var.clasp = OBJ_GET_CLASS(cx, data.obj); if (data.u.var.fun && data.u.var.clasp == &js_FunctionClass) { /* We are compiling code inside a function */ - data.u.var.kind = (data.op == JSOP_DEFCONST) - ? JSLOCAL_CONST - : JSLOCAL_VAR; + data.u.var.getter = js_GetLocalVariable; + data.u.var.setter = js_SetLocalVariable; + } else if (data.u.var.fun && data.u.var.clasp == &js_CallClass) { + /* We are compiling code from an eval inside a function */ + data.u.var.getter = js_GetCallVariable; + data.u.var.setter = js_SetCallVariable; } else { - /* - * We are compiling global code or code from an eval inside a - * function - */ - data.u.var.kind = JSLOCAL_NONE; + data.u.var.getter = data.u.var.clasp->getProperty; + data.u.var.setter = data.u.var.clasp->setProperty; } + + data.u.var.attrs = (data.op == JSOP_DEFCONST) + ? JSPROP_PERMANENT | JSPROP_READONLY + : JSPROP_PERMANENT; } do { @@ -3674,7 +3834,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn2->pn_atom = atom; pn2->pn_slot = -1; if (!let) - pn2->pn_const = (data.u.var.kind == JSLOCAL_CONST); + pn2->pn_attrs = data.u.var.attrs; PN_APPEND(pn, pn2); if (js_MatchToken(cx, ts, TOK_ASSIGN)) { @@ -4385,10 +4545,11 @@ GeneratorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * Make the generator function and flag it as interpreted ASAP (see the * comment in FunctionBody). */ - fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED, - cx->fp->varobj, NULL); + fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_LAMBDA, cx->fp->varobj, + NULL); if (!fun) return NULL; + fun->flags |= JSFUN_INTERPRETED; /* * This generator function is referenced by an anonymous function object diff --git a/js/src/jsparse.h b/js/src/jsparse.h index e1a417965ef..c8334db4155 100644 --- a/js/src/jsparse.h +++ b/js/src/jsparse.h @@ -186,7 +186,7 @@ JS_BEGIN_EXTERN_C * TOK_STRING, pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT, or * JSOP_REGEXP * TOK_REGEXP If JSOP_NAME, pn_op may be JSOP_*ARG or JSOP_*VAR - * with pn_slot >= 0 and pn_const telling const-ness + * with pn_slot >= 0 and pn_attrs telling const-ness * TOK_NUMBER dval pn_dval: double value of numeric literal * TOK_PRIMARY nullary pn_op: JSOp bytecode * @@ -306,7 +306,7 @@ struct JSParseNode { JSAtom *atom; /* name or label atom, null if slot */ JSParseNode *expr; /* object or initializer */ jsint slot; /* -1 or arg or local var slot */ - JSBool constslot; /* true for const names */ + uintN attrs; /* attributes if local var or const */ } name; struct { /* lexical scope. */ JSParsedObjectBox *pob; /* block object */ @@ -345,7 +345,7 @@ struct JSParseNode { #define pn_atom pn_u.name.atom #define pn_expr pn_u.name.expr #define pn_slot pn_u.name.slot -#define pn_const pn_u.name.constslot +#define pn_attrs pn_u.name.attrs #define pn_dval pn_u.dval #define pn_atom2 pn_u.apair.atom2 #define pn_pob pn_u.object.pob diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 94eda25b5d9..162978d10e5 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -89,11 +89,6 @@ (JS_ASSERT(JSID_IS_HIDDEN(id)), \ (jsid)((jsval)(id) ^ (JSVAL_BOOLEAN ^ JSVAL_STRING))) -/* - * Convenience constants. - */ -#define JS_BITS_PER_UINT32 (sizeof(uint32) * JS_BITS_PER_BYTE) - /* Scalar typedefs. */ typedef uint8 jsbytecode; typedef uint8 jssrcnote; diff --git a/js/src/jsscope.c b/js/src/jsscope.c index b0f9bcbdced..614bb68556a 100644 --- a/js/src/jsscope.c +++ b/js/src/jsscope.c @@ -388,7 +388,7 @@ ChangeScope(JSContext *cx, JSScope *scope, int change) * the GC, or we are searching for a property that has not yet been flagged as * a duplicate when making a duplicate formal parameter. */ -#define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_ALLOW_DUPLICATE) +#define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_IS_DUPLICATE) JS_STATIC_DLL_CALLBACK(JSDHashNumber) js_HashScopeProperty(JSDHashTable *table, const void *key) @@ -955,7 +955,7 @@ CheckAncestorLine(JSScope *scope, JSBool sparse) for (sprop = ancestorLine; sprop; sprop = sprop->parent) { if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) { - JS_ASSERT(sparse || (sprop->flags & SPROP_ALLOW_DUPLICATE)); + JS_ASSERT(sparse || (sprop->flags & SPROP_IS_DUPLICATE)); continue; } ancestorCount++; @@ -1066,12 +1066,12 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, * Duplicate formal parameters require us to leave the old property * on the ancestor line, so the decompiler can find it, even though * its entry in scope->table is overwritten to point at a new property - * descending from the old one. The SPROP_ALLOW_DUPLICATE flag helps - * us cope with the consequent disparity between ancestor line height - * and scope->entryCount. + * descending from the old one. The SPROP_IS_DUPLICATE flag helps us + * cope with the consequent disparity between ancestor line height and + * scope->entryCount. */ - if (flags & SPROP_ALLOW_DUPLICATE) { - sprop->flags |= SPROP_ALLOW_DUPLICATE; + if (flags & SPROP_IS_DUPLICATE) { + sprop->flags |= SPROP_IS_DUPLICATE; } else { /* * If we are clearing sprop to force an existing property to be @@ -1131,7 +1131,7 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, * all deleted properties out of scope's ancestor line. Otherwise we * risk adding a node with the same id as a "middle" node, violating * the rule that properties along an ancestor line have distinct ids - * (unless flagged SPROP_ALLOW_DUPLICATE). + * (unless flagged SPROP_IS_DUPLICATE). */ if (SCOPE_HAD_MIDDLE_DELETE(scope)) { JS_ASSERT(scope->table); @@ -1274,7 +1274,7 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, child.setter = setter; child.slot = slot; child.attrs = attrs; - child.flags = flags & ~SPROP_ALLOW_DUPLICATE; + child.flags = flags; child.shortid = shortid; sprop = GetPropertyTreeChild(cx, scope->lastProp, &child); if (!sprop) diff --git a/js/src/jsscope.h b/js/src/jsscope.h index e997277cf67..5ee217914e0 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -288,9 +288,11 @@ struct JSScopeProperty { /* Bits stored in sprop->flags. */ #define SPROP_MARK 0x01 -#define SPROP_ALLOW_DUPLICATE 0x02 +#define SPROP_IS_DUPLICATE 0x02 #define SPROP_IS_ALIAS 0x04 #define SPROP_HAS_SHORTID 0x08 +#define SPROP_IS_HIDDEN 0x10 /* a normally-hidden property, + e.g., function arg or var */ /* * If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather diff --git a/js/src/jsscript.c b/js/src/jsscript.c index f6449281bac..d013ebf375f 100644 --- a/js/src/jsscript.c +++ b/js/src/jsscript.c @@ -1637,7 +1637,7 @@ js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) pc += js_CodeSpec[*pc].length; if (*pc == JSOP_DEFFUN) { GET_FUNCTION_FROM_BYTECODE(script, pc, 0, obj); - fun = GET_FUNCTION_PRIVATE(cx, obj); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, obj); JS_ASSERT(FUN_INTERPRETED(fun)); return fun->u.i.script->lineno; } diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index 598aff2fb72..c44f245b3b4 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -202,7 +202,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id); * before deserialization of bytecode. If the saved version does not match * the current version, abort deserialization and invalidate the file. */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 16) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 15) /* * Library-private functions. diff --git a/js/src/jsxml.c b/js/src/jsxml.c index ddcc55a6024..9de9086e8ca 100644 --- a/js/src/jsxml.c +++ b/js/src/jsxml.c @@ -5585,7 +5585,6 @@ StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp) { JSXML *xml; JSFunction *fun; - char numBuf[12]; JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp)); @@ -5605,11 +5604,14 @@ StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp) } } - fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp)); - JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NON_LIST_XML_METHOD, - JS_GetFunctionName(fun), numBuf); + fun = (JSFunction *) OBJ_GET_PRIVATE(cx, JSVAL_TO_OBJECT(*vp)); + if (fun) { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NON_LIST_XML_METHOD, + JS_GetFunctionName(fun), numBuf); + } return NULL; }