Statically bind global variables where possible (bug 561923, r=brendan).

This commit is contained in:
David Anderson 2010-05-22 15:38:04 -07:00
Родитель 9d857ed6ca
Коммит 26458364a5
15 изменённых файлов: 540 добавлений и 30 удалений

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

@ -936,6 +936,14 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_UNBRANDTHIS */
0, /* JSOP_SHARPINIT */
3, /* JSOP_OBJTOSTR */
0, /* JSOP_GETGLOBAL */
0, /* JSOP_SETGLOBAL */
0, /* JSOP_INCGLOBAL */
0, /* JSOP_DECGLOBAL */
0, /* JSOP_GLOBALINC */
0, /* JSOP_GLOBALDEC */
0, /* JSOP_CALLGLOBAL */
0, /* JSOP_FORGLOBAL */
};
#define JSOP_IS_IMACOP(x) (0 \
|| x == JSOP_BITOR \

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

@ -102,7 +102,8 @@ JSCodeGenerator::JSCodeGenerator(Parser *parser,
numSpanDeps(0), numJumpTargets(0), spanDepTodo(0),
arrayCompDepth(0),
emitLevel(0),
constMap(parser->context)
constMap(parser->context),
globalUses(ContextAllocPolicy(parser->context))
{
flags = TCF_COMPILING;
memset(&prolog, 0, sizeof prolog);
@ -2161,6 +2162,40 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
}
if (dn->pn_dflags & PND_GVAR) {
if (js_CodeSpec[dn->pn_op].type() == JOF_GLOBAL) {
switch (op) {
case JSOP_NAME: op = JSOP_GETGLOBAL; break;
case JSOP_SETNAME: op = JSOP_SETGLOBAL; break;
case JSOP_INCNAME: op = JSOP_INCGLOBAL; break;
case JSOP_NAMEINC: op = JSOP_GLOBALINC; break;
case JSOP_DECNAME: op = JSOP_DECGLOBAL; break;
case JSOP_NAMEDEC: op = JSOP_GLOBALDEC; break;
case JSOP_FORNAME: op = JSOP_FORGLOBAL; break;
case JSOP_SETCONST:
case JSOP_DELNAME:
/* Not supported. */
return JS_TRUE;
default: JS_NOT_REACHED("gvar");
}
JSCodeGenerator *globalCg = cg->compiler()->globalScope->cg;
if (globalCg != cg) {
uint32 slot = globalCg->globalUses[cookie].slot;
/* Fall back to NAME if we can't add a slot. */
if (!cg->addGlobalUse(atom, slot, &cookie))
return JS_FALSE;
if (cookie == FREE_UPVAR_COOKIE)
return JS_TRUE;
}
pn->pn_op = op;
pn->pn_cookie = cookie;
pn->pn_dflags |= PND_BOUND;
return JS_TRUE;
}
/*
* If this is a global reference from within a function, leave pn_op as
* JSOP_NAME, etc. We could emit JSOP_*GVAR ops within function code if
@ -2426,6 +2461,40 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_TRUE;
}
bool
JSCodeGenerator::addGlobalUse(JSAtom *atom, uint32 slot, uint32 *indexp)
{
JSAtomListElement *ale = globalMap.lookup(atom);
if (ale) {
*indexp = ALE_INDEX(ale);
return true;
}
/* Don't bother encoding indexes >= uint16 */
if (globalUses.length() >= UINT16_LIMIT) {
*indexp = FREE_UPVAR_COOKIE;
return true;
}
/* Find or add an existing atom table entry. */
ale = atomList.add(parser, atom);
if (!ale)
return false;
*indexp = uint32(globalUses.length());
GlobalSlotArray::Entry entry = { ALE_INDEX(ale), slot };
if (!globalUses.append(entry))
return false;
ale = globalMap.add(parser, atom);
if (!ale)
return false;
ALE_SET_INDEX(ale, *indexp);
return true;
}
/*
* If pn contains a useful expression, return true with *answer set to true.
* If pn contains a useless expression, return true with *answer set to false.
@ -2653,6 +2722,9 @@ EmitNameOp(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
JS_ASSERT(!cg->funbox);
op = JSOP_CALLGVAR;
break;
case JSOP_GETGLOBAL:
op = JSOP_CALLGLOBAL;
break;
case JSOP_GETARG:
op = JSOP_CALLARG;
break;
@ -3671,7 +3743,9 @@ MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
}
if (JOF_OPTYPE(pn->pn_op) == JOF_ATOM &&
(!cg->inFunction() || (cg->flags & TCF_FUN_HEAVYWEIGHT))) {
(!cg->inFunction() || (cg->flags & TCF_FUN_HEAVYWEIGHT)) &&
js_CodeSpec[pn->pn_op].type() != JOF_GLOBAL)
{
CG_SWITCH_TO_PROLOG(cg);
if (!UpdateLineNumberNotes(cx, cg, pn->pn_pos.begin.lineno))
return JS_FALSE;
@ -3783,6 +3857,7 @@ EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
case JSOP_SETARG:
case JSOP_SETGVAR:
case JSOP_SETGLOBAL:
slot = (jsuint) pn->pn_cookie;
EMIT_UINT16_IMM_OP(PN_OP(pn), slot);
if (js_Emit1(cx, cg, JSOP_POP) < 0)
@ -4822,6 +4897,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
case JSOP_SETGVAR: op = JSOP_FORNAME; break;
case JSOP_GETLOCAL: /* FALL THROUGH */
case JSOP_SETLOCAL: op = JSOP_FORLOCAL; break;
case JSOP_GETGLOBAL: /* FALL THROUGH */
case JSOP_SETGLOBAL: op = JSOP_FORGLOBAL; break;
default: JS_ASSERT(0);
}
} else {
@ -5826,6 +5903,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
JS_ASSERT(PN_OP(pn2) != JSOP_GETUPVAR);
EMIT_UINT16_IMM_OP((PN_OP(pn2) == JSOP_SETGVAR)
? JSOP_GETGVAR
: (PN_OP(pn2) == JSOP_SETGLOBAL)
? JSOP_GETGLOBAL
: (PN_OP(pn2) == JSOP_SETARG)
? JSOP_GETARG
: JSOP_GETLOCAL,

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

@ -245,6 +245,11 @@ struct JSStmtInfo {
*/
#define TCF_FUN_USES_EVAL 0x800000
/*
* Compiling an eval() script.
*/
#define TCF_COMPILE_FOR_EVAL 0x1000000
/*
* Flags to check for return; vs. return expr; in a function.
*/
@ -341,6 +346,8 @@ struct JSTreeContext { /* tree context for semantic checks */
int sharpSlotBase;
bool ensureSharpSlots();
js::Compiler *compiler() { return (js::Compiler *)parser; }
// Return true there is a generator function within |skip| lexical scopes
// (going upward) from this context's lexical scope. Always return true if
// this context is itself a generator.
@ -487,6 +494,11 @@ struct JSCodeGenerator : public JSTreeContext
JSAtomList upvarList; /* map of atoms to upvar indexes */
JSUpvarArray upvarMap; /* indexed upvar pairs (JS_realloc'ed) */
typedef js::Vector<js::GlobalSlotArray::Entry, 16, js::ContextAllocPolicy> GlobalUseVector;
GlobalUseVector globalUses; /* per-script global uses */
JSAtomList globalMap; /* per-script map of global name to globalUses vector */
/*
* Initialize cg to allocate bytecode space from codePool, source note
* space from notePool, and all other arena-allocated temporaries from
@ -506,6 +518,8 @@ struct JSCodeGenerator : public JSTreeContext
*/
~JSCodeGenerator();
bool addGlobalUse(JSAtom *atom, uint32 slot, uint32 *indexp);
bool hasSharps() {
bool rv = !!(flags & TCF_HAS_SHARPS);
JS_ASSERT((sharpSlotBase >= 0) == rv);
@ -515,6 +529,8 @@ struct JSCodeGenerator : public JSTreeContext
uintN sharpSlots() {
return hasSharps() ? SHARP_NSLOTS : 0;
}
bool compilingForEval() { return !!(flags & TCF_COMPILE_FOR_EVAL); }
};
#define CG_TS(cg) TS((cg)->parser)

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

@ -393,6 +393,9 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSObject *funobj, JSFunctio
: 0,
(script->trynotesOffset != 0)
? script->trynotes()->length
: 0,
(script->globalsOffset != 0)
? script->globals()->length
: 0);
if (!wscript)
return NULL;
@ -415,6 +418,10 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSObject *funobj, JSFunctio
memcpy(wscript->trynotes()->vector, script->trynotes()->vector,
wscript->trynotes()->length * sizeof(JSTryNote));
}
if (script->globalsOffset != 0) {
memcpy(wscript->globals()->vector, script->globals()->vector,
wscript->globals()->length * sizeof(GlobalSlotArray::Entry));
}
if (wfun->u.i.nupvars != 0) {
JS_ASSERT(wfun->u.i.nupvars == wscript->upvars()->length);

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

@ -1358,9 +1358,9 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
*/
JSStackFrame *callerFrame = (staticLevel != 0) ? caller : NULL;
if (!script) {
uint32 tcflags = TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT | TCF_COMPILE_FOR_EVAL;
script = Compiler::compileScript(cx, scopeobj, callerFrame,
principals,
TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT,
principals, tcflags,
str->chars(), str->length(),
NULL, file, line, str, staticLevel);
if (!script)

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

@ -386,6 +386,15 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
fprintf(fp, " %s", bytes);
break;
case JOF_GLOBAL:
atom = script->getGlobalAtom(GET_SLOTNO(pc));
v = ATOM_TO_JSVAL(atom);
bytes = ToDisassemblySource(cx, v);
if (!bytes)
return 0;
fprintf(fp, " %s", bytes);
break;
case JOF_UINT16PAIR:
i = (jsint)GET_UINT16(pc);
fprintf(fp, " %d", i);
@ -1411,6 +1420,7 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
LOCAL_ASSERT(*pc == JSOP_POP);
break;
case JSOP_SETGLOBAL:
case JSOP_SETARG:
case JSOP_SETGVAR:
case JSOP_SETLOCAL:
@ -1425,6 +1435,8 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
LOCAL_ASSERT(atom);
} else if (op == JSOP_SETGVAR) {
LOAD_ATOM(0);
} else if (op == JSOP_SETGLOBAL) {
atom = jp->script->getGlobalAtom(GET_SLOTNO(pc));
} else if (IsVarSlot(jp, pc, &i)) {
atom = GetArgOrVarAtom(jp, i);
LOCAL_ASSERT(atom);
@ -3365,6 +3377,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
xval = "&&";
goto do_logical_connective;
case JSOP_FORGLOBAL:
atom = jp->script->getGlobalAtom(GET_SLOTNO(pc));
goto do_forname;
case JSOP_FORARG:
sn = NULL;
i = GET_ARGNO(pc);
@ -3388,6 +3404,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
case JSOP_FORNAME:
LOAD_ATOM(0);
do_forname:
sn = js_GetSrcNote(jp->script, pc);
todo = SprintCString(&ss->sprinter, VarPrefix(sn));
if (todo < 0 || !QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
@ -3500,6 +3518,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
LOCAL_ASSERT(atom);
goto do_setname;
case JSOP_SETGLOBAL:
atom = jp->script->getGlobalAtom(GET_SLOTNO(pc));
goto do_setname;
case JSOP_SETCONST:
case JSOP_SETNAME:
case JSOP_SETGVAR:
@ -3716,6 +3738,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
LOCAL_ASSERT(atom);
goto do_incatom;
case JSOP_INCGLOBAL:
case JSOP_DECGLOBAL:
atom = jp->script->getGlobalAtom(GET_SLOTNO(pc));
goto do_incatom;
case JSOP_INCNAME:
case JSOP_DECNAME:
case JSOP_INCGVAR:
@ -3772,6 +3799,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
LOCAL_ASSERT(atom);
goto do_atominc;
case JSOP_GLOBALINC:
case JSOP_GLOBALDEC:
atom = jp->script->getGlobalAtom(GET_SLOTNO(pc));
goto do_atominc;
case JSOP_NAMEINC:
case JSOP_NAMEDEC:
case JSOP_GVARINC:
@ -3978,6 +4010,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
#endif
goto do_name;
case JSOP_CALLGLOBAL:
case JSOP_GETGLOBAL:
atom = jp->script->getGlobalAtom(GET_SLOTNO(pc));
goto do_name;
case JSOP_CALLNAME:
case JSOP_NAME:
case JSOP_GETGVAR:

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

@ -85,6 +85,7 @@ typedef enum JSOp {
#define JOF_INT8 18 /* int8 immediate operand */
#define JOF_ATOMOBJECT 19 /* uint16 constant index + object index */
#define JOF_UINT16PAIR 20 /* pair of uint16 immediates */
#define JOF_GLOBAL 21 /* uint16 global array index */
#define JOF_TYPEMASK 0x001f /* mask for above immediate types */
#define JOF_NAME (1U<<5) /* name operation */
@ -254,6 +255,8 @@ struct JSCodeSpec {
int8 ndefs; /* number of stack results */
uint8 prec; /* operator precedence */
uint32 format; /* immediate operand format */
uint32 type() const { return JOF_TYPE(format); }
};
extern const JSCodeSpec js_CodeSpec[];

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

@ -610,3 +610,14 @@ OPDEF(JSOP_SHARPINIT, 239,"sharpinit", NULL, 3, 0, 0, 0, JOF_UINT16
* If the argument is an object, perform toString conversion (E-262-3 section 9.8).
*/
OPDEF(JSOP_OBJTOSTR, 240,"objtostr", NULL, 1, 1, 1, 0, JOF_BYTE)
/* Static binding for globals. */
OPDEF(JSOP_GETGLOBAL, 241,"getglobal", NULL, 3, 0, 1, 19, JOF_GLOBAL|JOF_NAME)
OPDEF(JSOP_SETGLOBAL, 242,"setglobal", NULL, 3, 1, 1, 3, JOF_GLOBAL|JOF_NAME|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INCGLOBAL, 243,"incglobal", NULL, 3, 0, 1, 15, JOF_GLOBAL|JOF_NAME|JOF_INC|JOF_TMPSLOT2)
OPDEF(JSOP_DECGLOBAL, 244,"decglobal", NULL, 3, 0, 1, 15, JOF_GLOBAL|JOF_NAME|JOF_DEC|JOF_TMPSLOT2)
OPDEF(JSOP_GLOBALINC, 245,"globalinc", NULL, 3, 0, 1, 15, JOF_GLOBAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT2)
OPDEF(JSOP_GLOBALDEC, 246,"globaldec", NULL, 3, 0, 1, 15, JOF_GLOBAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT2)
OPDEF(JSOP_CALLGLOBAL, 247,"callglobal", NULL, 3, 0, 2, 19, JOF_GLOBAL|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_FORGLOBAL, 248,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL|JOF_NAME|JOF_FOR|JOF_TMPSLOT)

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

@ -1499,6 +1499,23 @@ do_incop:
int incr, incr2;
Value *vp;
BEGIN_CASE(JSOP_INCGLOBAL)
incr = 1; incr2 = 1; goto do_bound_global_incop;
BEGIN_CASE(JSOP_DECGLOBAL)
incr = -1; incr2 = -1; goto do_bound_global_incop;
BEGIN_CASE(JSOP_GLOBALINC)
incr = 1; incr2 = 0; goto do_bound_global_incop;
BEGIN_CASE(JSOP_GLOBALDEC)
incr = -1; incr2 = 0; goto do_bound_global_incop;
do_bound_global_incop:
uint32 slot = GET_SLOTNO(regs.pc);
slot = script->getGlobalSlot(slot);
JSObject *obj = fp->scopeChain->getGlobal();
vp = &obj->getSlotRef(slot);
goto do_int_fast_incop;
END_CASE(JSOP_INCGLOBAL)
/* Position cases so the most frequent i++ does not need a jump. */
BEGIN_CASE(JSOP_DECARG)
incr = -1; incr2 = -1; goto do_arg_incop;
@ -1510,7 +1527,7 @@ BEGIN_CASE(JSOP_ARGINC)
incr = 1; incr2 = 0;
do_arg_incop:
uint32 slot = GET_ARGNO(regs.pc);
slot = GET_ARGNO(regs.pc);
JS_ASSERT(slot < fp->fun->nargs);
METER_SLOT_OP(op, slot);
vp = fp->argv + slot;
@ -2965,6 +2982,62 @@ BEGIN_CASE(JSOP_CALLDSLOT)
}
END_CASE(JSOP_GETDSLOT)
BEGIN_CASE(JSOP_GETGLOBAL)
BEGIN_CASE(JSOP_CALLGLOBAL)
{
uint32 slot = GET_SLOTNO(regs.pc);
slot = script->getGlobalSlot(slot);
JSObject *obj = fp->scopeChain->getGlobal();
JS_ASSERT(slot < obj->scope()->freeslot);
PUSH_COPY(obj->getSlot(slot));
if (op == JSOP_CALLGLOBAL)
PUSH_NULL();
}
END_CASE(JSOP_GETGLOBAL)
BEGIN_CASE(JSOP_FORGLOBAL)
{
Value rval;
if (!IteratorNext(cx, &regs.sp[-1].asObject(), &rval))
goto error;
PUSH_COPY(rval);
uint32 slot = GET_SLOTNO(regs.pc);
slot = script->getGlobalSlot(slot);
JSObject *obj = fp->scopeChain->getGlobal();
JS_ASSERT(slot < obj->scope()->freeslot);
JS_LOCK_OBJ(cx, obj);
{
JSScope *scope = obj->scope();
if (!scope->methodWriteBarrier(cx, slot, rval)) {
JS_UNLOCK_SCOPE(cx, scope);
goto error;
}
obj->lockedSetSlot(slot, rval);
JS_UNLOCK_SCOPE(cx, scope);
}
regs.sp--;
}
END_CASE(JSOP_FORGLOBAL)
BEGIN_CASE(JSOP_SETGLOBAL)
{
uint32 slot = GET_SLOTNO(regs.pc);
slot = script->getGlobalSlot(slot);
JSObject *obj = fp->scopeChain->getGlobal();
JS_ASSERT(slot < obj->scope()->freeslot);
{
JS_LOCK_OBJ(cx, obj);
JSScope *scope = obj->scope();
if (!scope->methodWriteBarrier(cx, slot, regs.sp[-1])) {
JS_UNLOCK_SCOPE(cx, scope);
goto error;
}
obj->lockedSetSlot(slot, regs.sp[-1]);
JS_UNLOCK_SCOPE(cx, scope);
}
}
END_SET_CASE(JSOP_SETGLOBAL)
BEGIN_CASE(JSOP_GETGVAR)
BEGIN_CASE(JSOP_CALLGVAR)
{

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

@ -734,7 +734,8 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *calle
void *sbrk(ptrdiff_t), *before = sbrk(0);
#endif
JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT)));
JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT |
TCF_COMPILE_FOR_EVAL)));
/*
* The scripted callerFrame can only be given for compile-and-go scripts
@ -761,11 +762,21 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *calle
MUST_FLOW_THROUGH("out");
JSObject *globalObj = scopeChain ? scopeChain->getGlobal() : NULL;
js::GlobalScope globalScope(cx, globalObj, &cg);
if (globalObj) {
JS_ASSERT(globalObj->isNative());
JS_ASSERT((globalObj->getClass()->flags & JSCLASS_GLOBAL_FLAGS) == JSCLASS_GLOBAL_FLAGS);
globalScope.globalFreeSlot = globalObj->scope()->freeslot;
}
/* Null script early in case of error, to reduce our code footprint. */
script = NULL;
globalScope.cg = &cg;
cg.flags |= tcflags;
cg.scopeChain = scopeChain;
compiler.globalScope = &globalScope;
if (!SetStaticLevel(&cg, staticLevel))
goto out;
@ -926,6 +937,27 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *calle
}
}
if (globalScope.defs.length()) {
JS_ASSERT(globalObj->scope()->freeslot == globalScope.globalFreeSlot);
JS_ASSERT(!cg.compilingForEval());
for (size_t i = 0; i < globalScope.defs.length(); i++) {
JSAtom *atom = globalScope.defs[i];
jsid id = ATOM_TO_JSID(atom);
JSProperty *prop;
if (!js_DefineNativeProperty(cx, globalObj, id, Value(UndefinedTag()), PropertyStub,
PropertyStub, JSPROP_ENUMERATE | JSPROP_PERMANENT,
0, 0, &prop)) {
goto out;
}
JS_ASSERT(prop);
JS_ASSERT(((JSScopeProperty*)prop)->slot == globalScope.globalFreeSlot + i);
globalObj->dropProperty(cx, prop);
}
}
#ifdef METER_PARSENODES
printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
(char *)sbrk(0) - (char *)before,
@ -3253,7 +3285,70 @@ OuterLet(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *atom)
return false;
}
static bool
DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom)
{
GlobalScope *globalScope = cg->compiler()->globalScope;
JSObject *globalObj = globalScope->globalObj;
if (!cg->compileAndGo() || !globalObj || cg->compilingForEval())
return true;
JS_LOCK_OBJ(cg->parser->context, globalObj);
JSScope *scope = globalObj->scope();
if (JSScopeProperty *sprop = scope->lookup(ATOM_TO_JSID(atom))) {
/*
* If the property was found, bind the slot immediately if
* we can. If we can't, don't bother emitting a GVAR op,
* since it's unlikely that it will optimize either.
*/
uint32 index;
if (!sprop->configurable() &&
SPROP_HAS_VALID_SLOT(sprop, globalObj->scope()) &&
sprop->hasDefaultGetterOrIsMethod() &&
sprop->hasDefaultSetter() &&
cg->addGlobalUse(atom, sprop->slot, &index) &&
index != FREE_UPVAR_COOKIE)
{
pn->pn_op = JSOP_GETGLOBAL;
pn->pn_cookie = index;
pn->pn_dflags |= PND_BOUND | PND_GVAR;
}
JS_UNLOCK_SCOPE(cg->parser->context, scope);
return true;
}
JS_UNLOCK_SCOPE(cg->parser->context, scope);
/* Definitions from |var| are not redefined, like functions. */
JS_ASSERT(!cg->globalMap.lookup(atom));
uint32 slot = globalScope->globalFreeSlot + globalScope->defs.length();
if (!globalScope->defs.append(atom))
return false;
uint32 index;
if (!cg->addGlobalUse(atom, slot, &index))
return false;
if (index != FREE_UPVAR_COOKIE) {
pn->pn_op = JSOP_GETGLOBAL;
pn->pn_cookie = index;
pn->pn_dflags |= PND_BOUND | PND_GVAR;
}
return true;
}
/*
* If compile-and-go, and a global object is present, try to bake in either
* an already available slot or a predicted slot that will be defined after
* compiling is completed.
*
* If not compile-and-go, or compiling for eval, this optimization is invalid.
* The old path (explained below), which works for global references only, is
* thus preserved at the bottom of BindGvar().
*
* If we are generating global or eval-called-from-global code, bind a "gvar"
* here, as soon as possible. The JSOP_GETGVAR, etc., ops speed up interpreted
* global variable access by memoizing name-to-slot mappings during execution
@ -3274,27 +3369,38 @@ BindGvar(JSParseNode *pn, JSTreeContext *tc, bool inWith = false)
JS_ASSERT(pn->pn_op == JSOP_NAME);
JS_ASSERT(!tc->inFunction());
if (tc->compiling() && !tc->parser->callerFrame) {
JSCodeGenerator *cg = (JSCodeGenerator *) tc;
if (!tc->compiling() || tc->parser->callerFrame)
return true;
/* Index pn->pn_atom so we can map fast global number to name. */
JSAtomListElement *ale = cg->atomList.add(tc->parser, pn->pn_atom);
if (!ale)
JSCodeGenerator *cg = (JSCodeGenerator *) tc;
if (!(pn->pn_dflags & PND_CONST) && !inWith) {
if (!DefineGlobal(pn, cg, pn->pn_atom))
return false;
/* Defend against cg->ngvars 16-bit overflow. */
uintN slot = ALE_INDEX(ale);
if ((slot + 1) >> 16)
if (pn->pn_dflags & PND_BOUND)
return true;
}
if ((uint16)(slot + 1) > cg->ngvars)
cg->ngvars = (uint16)(slot + 1);
/* If direct binding failed, try the old gvar optimization. */
if (!inWith) {
pn->pn_op = JSOP_GETGVAR;
pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, slot);
pn->pn_dflags |= PND_BOUND | PND_GVAR;
}
/* Index pn->pn_atom so we can map fast global number to name. */
JSAtomListElement *ale = cg->atomList.add(tc->parser, pn->pn_atom);
if (!ale)
return false;
/* Defend against cg->ngvars 16-bit overflow. */
uintN slot = ALE_INDEX(ale);
if ((slot + 1) >> 16)
return true;
if ((uint16)(slot + 1) > cg->ngvars)
cg->ngvars = (uint16)(slot + 1);
/* See bug 561011; don't optimize, but slot must be reserved above. */
if (!inWith) {
pn->pn_op = JSOP_GETGVAR;
pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, slot);
pn->pn_dflags |= PND_BOUND | PND_GVAR;
}
return true;
@ -3572,10 +3678,12 @@ BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
* done by the data->binder function.
*/
if (pn->pn_dflags & PND_BOUND) {
JS_ASSERT_IF((pn->pn_dflags & PND_GVAR),
PN_OP(pn) == JSOP_GETGVAR || PN_OP(pn) == JSOP_GETGLOBAL);
pn->pn_op = (pn->pn_op == JSOP_ARGUMENTS)
? JSOP_SETNAME
: (pn->pn_dflags & PND_GVAR)
? JSOP_SETGVAR
? (PN_OP(pn) == JSOP_GETGVAR ? JSOP_SETGVAR : JSOP_SETGLOBAL)
: JSOP_SETLOCAL;
} else {
pn->pn_op = (data->op == JSOP_DEFCONST)
@ -5847,10 +5955,13 @@ Parser::variables(bool inLetHead)
pn2->pn_expr = init;
}
JS_ASSERT_IF((pn2->pn_dflags & PND_GVAR),
PN_OP(pn2) == JSOP_GETGVAR || PN_OP(pn2) == JSOP_GETGLOBAL);
pn2->pn_op = (PN_OP(pn2) == JSOP_ARGUMENTS)
? JSOP_SETNAME
: (pn2->pn_dflags & PND_GVAR)
? JSOP_SETGVAR
? (PN_OP(pn2) == JSOP_GETGVAR ? JSOP_SETGVAR : JSOP_SETGLOBAL)
: (pn2->pn_dflags & PND_BOUND)
? JSOP_SETLOCAL
: (data.op == JSOP_DEFCONST)

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

@ -287,6 +287,21 @@ typedef enum JSParseNodeArity {
struct JSDefinition;
namespace js {
struct GlobalScope {
GlobalScope(JSContext *cx, JSObject *globalObj, JSCodeGenerator *cg)
: globalObj(globalObj), cg(cg), defs(ContextAllocPolicy(cx))
{ }
JSObject *globalObj;
JSCodeGenerator *cg;
Vector<JSAtom *, 16, ContextAllocPolicy> defs;
uint32 globalFreeSlot;
};
} /* namespace js */
struct JSParseNode {
uint32 pn_type:16, /* TOK_* type, see jsscan.h */
pn_op:8, /* see JSOp enum and jsopcode.tbl */
@ -1060,6 +1075,7 @@ private:
struct Compiler
{
Parser parser;
GlobalScope *globalScope;
Compiler(JSContext *cx, JSPrincipals *prin = NULL, JSStackFrame *cfp = NULL)
: parser(cx, prin, cfp)

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

@ -75,7 +75,7 @@ static const jsbytecode emptyScriptCode[] = {JSOP_STOP, SRC_NULL};
/* static */ const JSScript JSScript::emptyScriptConst = {
const_cast<jsbytecode*>(emptyScriptCode),
1, JSVERSION_DEFAULT, 0, 0, 0, 0, 0, true, false, false, false,
1, JSVERSION_DEFAULT, 0, 0, 0, 0, 0, 0, true, false, false, false,
const_cast<jsbytecode*>(emptyScriptCode),
{0, NULL}, NULL, 0, 0, 0, NULL
};
@ -105,6 +105,9 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
filenameWasSaved = JS_FALSE;
notes = NULL;
/* Should not XDR scripts optimized for a single global object. */
JS_ASSERT_IF(script, !script->globalsOffset);
if (xdr->mode == JSXDR_ENCODE)
magic = JSXDR_MAGIC_SCRIPT_CURRENT;
if (!JS_XDRUint32(xdr, &magic))
@ -146,7 +149,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
* the shorthand (0 length word) for us. Make a new mutable empty
* script here and return it immediately.
*/
script = js_NewScript(cx, 1, 1, 0, 0, 0, 0, 0);
script = js_NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0);
if (!script)
return JS_FALSE;
@ -214,7 +217,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
if (xdr->mode == JSXDR_DECODE) {
script = js_NewScript(cx, length, nsrcnotes, natoms, nobjects, nupvars,
nregexps, ntrynotes);
nregexps, ntrynotes, 0);
if (!script)
return JS_FALSE;
@ -806,7 +809,7 @@ JS_STATIC_ASSERT(sizeof(JSScript) + 2 * sizeof(JSObjectArray) +
JSScript *
js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
uint32 nobjects, uint32 nupvars, uint32 nregexps,
uint32 ntrynotes)
uint32 ntrynotes, uint32 nglobals)
{
size_t size, vectorSize;
JSScript *script;
@ -824,6 +827,8 @@ js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *);
if (ntrynotes != 0)
size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote);
if (nglobals != 0)
size += sizeof(GlobalSlotArray) + nglobals * sizeof(GlobalSlotArray::Entry);
script = (JSScript *) cx->malloc(size);
if (!script)
@ -849,6 +854,11 @@ js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
script->trynotesOffset = (uint8)(cursor - (uint8 *)script);
cursor += sizeof(JSTryNoteArray);
}
if (nglobals != 0) {
JS_ASSERT((cursor - (uint8*)script) <= 0xFF);
script->globalsOffset = (uint8)(cursor - (uint8 *)script);
cursor += sizeof(GlobalSlotArray);
}
if (natoms != 0) {
script->atomMap.length = natoms;
@ -889,6 +899,13 @@ js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
cursor += vectorSize;
}
if (nglobals != 0) {
script->globals()->length = nglobals;
script->globals()->vector = (GlobalSlotArray::Entry *)cursor;
vectorSize = nglobals * sizeof(script->globals()->vector[0]);
cursor += vectorSize;
}
/*
* NB: We allocate the vector of uint32 upvar cookies after all vectors of
* pointers, to avoid misalignment on 64-bit platforms. See bug 514645.
@ -988,7 +1005,7 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
script = js_NewScript(cx, prologLength + mainLength, nsrcnotes,
cg->atomList.count, cg->objectList.length,
cg->upvarList.count, cg->regexpList.length,
cg->ntrynotes);
cg->ntrynotes, cg->globalUses.length());
if (!script)
return NULL;
@ -1044,6 +1061,11 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
cg->upvarMap.vector = NULL;
}
if (cg->globalUses.length()) {
memcpy(script->globals()->vector, &cg->globalUses[0],
cg->globalUses.length() * sizeof(GlobalSlotArray::Entry));
}
/*
* We initialize fun->u.script to be the script constructed above
* so that the debugger has a valid FUN_SCRIPT(fun).

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

@ -86,6 +86,19 @@ typedef struct JSUpvarArray {
uint32 length; /* count of indexed upvar cookies */
} JSUpvarArray;
namespace js {
struct GlobalSlotArray {
struct Entry {
uint32 atomIndex; /* index into atom table */
uint32 slot; /* global obj slot number */
};
Entry *vector;
uint32 length;
};
} /* namespace js */
#define CALLEE_UPVAR_SLOT 0xffff
#define FREE_STATIC_LEVEL 0x3fff
#define FREE_UPVAR_COOKIE 0xffffffff
@ -115,6 +128,8 @@ struct JSScript {
regexps or 0 if none. */
uint8 trynotesOffset; /* offset to the array of try notes or
0 if none */
uint8 globalsOffset; /* offset to the array of global slots or
0 if none */
bool noScriptRval:1; /* no need for result value of last
expression statement */
bool savedCallerFun:1; /* object 0 is caller function */
@ -159,6 +174,11 @@ struct JSScript {
return (JSTryNoteArray *) ((uint8 *) this + trynotesOffset);
}
js::GlobalSlotArray *globals() {
JS_ASSERT(globalsOffset != 0);
return (js::GlobalSlotArray *) ((uint8 *)this + globalsOffset);
}
JSAtom *getAtom(size_t index) {
JS_ASSERT(index < atomMap.length);
return atomMap.vector[index];
@ -170,6 +190,18 @@ struct JSScript {
return arr->vector[index];
}
uint32 getGlobalSlot(size_t index) {
js::GlobalSlotArray *arr = globals();
JS_ASSERT(index < arr->length);
return arr->vector[index].slot;
}
JSAtom *getGlobalAtom(size_t index) {
js::GlobalSlotArray *arr = globals();
JS_ASSERT(index < arr->length);
return getAtom(arr->vector[index].atomIndex);
}
inline JSFunction *getFunction(size_t index);
inline JSObject *getRegExp(size_t index);
@ -286,7 +318,7 @@ js_SweepScriptFilenames(JSRuntime *rt);
extern JSScript *
js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
uint32 nobjects, uint32 nupvars, uint32 nregexps,
uint32 ntrynotes);
uint32 ntrynotes, uint32 nglobals);
extern JSScript *
js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg);

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

@ -13760,6 +13760,21 @@ TraceRecorder::record_JSOP_FORLOCAL()
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_FORGLOBAL()
{
LIns* v_ins;
CHECK_STATUS_A(unboxNextValue(v_ins));
uint32 slot = cx->fp->script->getGlobalSlot(GET_SLOTNO(cx->regs->pc));
if (!lazilyImportGlobalSlot(slot))
RETURN_STOP_A("lazy import of global slot failed");
set(&globalObj->getSlotRef(slot), v_ins);
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_POPN()
{
@ -15296,6 +15311,81 @@ TraceRecorder::record_JSOP_SHARPINIT()
return ARECORD_STOP;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_GETGLOBAL()
{
uint32 slot = cx->fp->script->getGlobalSlot(GET_SLOTNO(cx->regs->pc));
if (!lazilyImportGlobalSlot(slot))
RETURN_STOP_A("lazy import of global slot failed");
stack(0, get(&globalObj->getSlotRef(slot)));
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_SETGLOBAL()
{
uint32 slot = cx->fp->script->getGlobalSlot(GET_SLOTNO(cx->regs->pc));
if (!lazilyImportGlobalSlot(slot))
RETURN_STOP_A("lazy import of global slot failed");
set(&globalObj->getSlotRef(slot), stack(-1));
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_CALLGLOBAL()
{
uint32 slot = cx->fp->script->getGlobalSlot(GET_SLOTNO(cx->regs->pc));
if (!lazilyImportGlobalSlot(slot))
RETURN_STOP_A("lazy import of global slot failed");
jsval& v = globalObj->getSlotRef(slot);
stack(0, get(&v));
stack(1, INS_NULL());
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_GLOBALDEC()
{
uint32 slot = cx->fp->script->getGlobalSlot(GET_SLOTNO(cx->regs->pc));
if (!lazilyImportGlobalSlot(slot))
RETURN_STOP_A("lazy import of global slot failed");
return InjectStatus(inc(globalObj->getSlotRef(slot), -1, false));
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_DECGLOBAL()
{
uint32 slot = cx->fp->script->getGlobalSlot(GET_SLOTNO(cx->regs->pc));
if (!lazilyImportGlobalSlot(slot))
RETURN_STOP_A("lazy import of global slot failed");
return InjectStatus(inc(globalObj->getSlotRef(slot), -1, true));
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_INCGLOBAL()
{
uint32 slot = cx->fp->script->getGlobalSlot(GET_SLOTNO(cx->regs->pc));
if (!lazilyImportGlobalSlot(slot))
RETURN_STOP_A("lazy import of global slot failed");
return InjectStatus(inc(globalObj->getSlotRef(slot), 1, true));
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_GLOBALINC()
{
uint32 slot = cx->fp->script->getGlobalSlot(GET_SLOTNO(cx->regs->pc));
if (!lazilyImportGlobalSlot(slot))
RETURN_STOP_A("lazy import of global slot failed");
return InjectStatus(inc(globalObj->getSlotRef(slot), 1, false));
}
#define DBG_STUB(OP) \
JS_REQUIRES_STACK AbortableRecordingStatus \
TraceRecorder::record_##OP() \

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

@ -0,0 +1,5 @@
/* Test that NaN does not trigger js_InitMathClass & constants while parsing. */
var NaN
var x = 2;