mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-14 05:45:37 +00:00
Bug 614493 - Move top-level binding storage (and argument, variable, and upvar counts) out of JSFunction and into js::Bindings, itself stored in JSScript, anticipating the time when strict mode eval scripts will need it. r=brendan
This commit is contained in:
parent
c12bc82879
commit
1fe4d7a5ad
@ -96,6 +96,7 @@
|
||||
#include "jsscopeinlines.h"
|
||||
#include "jscntxtinlines.h"
|
||||
#include "jsregexpinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
#include "jsstrinlines.h"
|
||||
#include "assembler/wtf/Platform.h"
|
||||
|
||||
@ -4241,7 +4242,7 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
|
||||
uint32 i = uva->length;
|
||||
JS_ASSERT(i != 0);
|
||||
|
||||
for (Shape::Range r(fun->lastUpvar()); i-- != 0; r.popFront()) {
|
||||
for (Shape::Range r(fun->script()->bindings.lastUpvar()); i-- != 0; r.popFront()) {
|
||||
JSObject *obj = parent;
|
||||
int skip = uva->vector[i].level();
|
||||
while (--skip > 0) {
|
||||
@ -4784,6 +4785,7 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj,
|
||||
goto out2;
|
||||
}
|
||||
}
|
||||
|
||||
fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom);
|
||||
if (!fun)
|
||||
goto out2;
|
||||
@ -4792,19 +4794,22 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj,
|
||||
AutoObjectRooter tvr(cx, FUN_OBJECT(fun));
|
||||
MUST_FLOW_THROUGH("out");
|
||||
|
||||
Bindings bindings(cx);
|
||||
for (i = 0; i < nargs; i++) {
|
||||
argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0);
|
||||
if (!argAtom) {
|
||||
fun = NULL;
|
||||
goto out2;
|
||||
}
|
||||
if (!fun->addLocal(cx, argAtom, JSLOCAL_ARG)) {
|
||||
|
||||
uint16 dummy;
|
||||
if (!bindings.addArgument(cx, argAtom, &dummy)) {
|
||||
fun = NULL;
|
||||
goto out2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Compiler::compileFunctionBody(cx, fun, principals,
|
||||
if (!Compiler::compileFunctionBody(cx, fun, principals, &bindings,
|
||||
chars, length, filename, lineno)) {
|
||||
fun = NULL;
|
||||
goto out2;
|
||||
|
@ -2729,8 +2729,8 @@ class AutoLocalNameArray {
|
||||
JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: context(cx),
|
||||
mark(JS_ARENA_MARK(&cx->tempPool)),
|
||||
names(fun->getLocalNameArray(cx, &cx->tempPool)),
|
||||
count(fun->countLocalNames())
|
||||
names(fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool)),
|
||||
count(fun->script()->bindings.countLocalNames())
|
||||
{
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
@ -69,6 +69,7 @@
|
||||
#include "jsinterpinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsscopeinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
#include "jsautooplen.h"
|
||||
|
||||
@ -1126,14 +1127,14 @@ JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
|
||||
{
|
||||
return fun->hasLocalNames();
|
||||
return fun->script()->bindings.hasLocalNames();
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(jsuword *)
|
||||
JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
|
||||
{
|
||||
*markp = JS_ARENA_MARK(&cx->tempPool);
|
||||
return fun->getLocalNameArray(cx, &cx->tempPool);
|
||||
return fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool);
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(JSAtom *)
|
||||
|
@ -72,6 +72,7 @@
|
||||
#include "jsatominlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsscopeinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
/* Allocation chunk counts, must be powers of two in general. */
|
||||
#define BYTECODE_CHUNK 256 /* code allocation increment */
|
||||
@ -96,6 +97,12 @@ EmitIndexOp(JSContext *cx, JSOp op, uintN index, JSCodeGenerator *cg);
|
||||
static JSBool
|
||||
EmitLeaveBlock(JSContext *cx, JSCodeGenerator *cg, JSOp op, JSObjectBox *box);
|
||||
|
||||
void
|
||||
JSTreeContext::trace(JSTracer *trc)
|
||||
{
|
||||
bindings.trace(trc);
|
||||
}
|
||||
|
||||
JSCodeGenerator::JSCodeGenerator(Parser *parser,
|
||||
JSArenaPool *cpool, JSArenaPool *npool,
|
||||
uintN lineno)
|
||||
@ -1323,10 +1330,10 @@ JSTreeContext::ensureSharpSlots()
|
||||
if (!sharpArrayAtom || !sharpDepthAtom)
|
||||
return false;
|
||||
|
||||
sharpSlotBase = fun()->u.i.nvars;
|
||||
if (!fun()->addLocal(cx, sharpArrayAtom, JSLOCAL_VAR))
|
||||
sharpSlotBase = bindings.countVars();
|
||||
if (!bindings.addVariable(cx, sharpArrayAtom))
|
||||
return false;
|
||||
if (!fun()->addLocal(cx, sharpDepthAtom, JSLOCAL_VAR))
|
||||
if (!bindings.addVariable(cx, sharpDepthAtom))
|
||||
return false;
|
||||
} else {
|
||||
/*
|
||||
@ -1714,7 +1721,7 @@ LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
|
||||
* nor can prop be deleted.
|
||||
*/
|
||||
if (cg->inFunction()) {
|
||||
if (cg->fun()->lookupLocal(cx, atom, NULL) != JSLOCAL_NONE)
|
||||
if (cg->bindings.hasBinding(atom))
|
||||
break;
|
||||
} else {
|
||||
JS_ASSERT(cg->compileAndGo());
|
||||
@ -1897,7 +1904,7 @@ AdjustBlockSlot(JSContext *cx, JSCodeGenerator *cg, jsint slot)
|
||||
{
|
||||
JS_ASSERT((jsuint) slot < cg->maxStackDepth);
|
||||
if (cg->inFunction()) {
|
||||
slot += cg->fun()->u.i.nvars;
|
||||
slot += cg->bindings.countVars();
|
||||
if ((uintN) slot >= SLOTNO_LIMIT) {
|
||||
ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
|
||||
slot = -1;
|
||||
@ -2015,8 +2022,8 @@ MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg)
|
||||
JSAtom *atom = pn->pn_atom;
|
||||
|
||||
uintN index;
|
||||
JSLocalKind localKind = fun->lookupLocal(cx, atom, &index);
|
||||
if (localKind == JSLOCAL_NONE)
|
||||
BindingKind kind = fun->script()->bindings.lookup(atom, &index);
|
||||
if (kind == NONE)
|
||||
return true;
|
||||
|
||||
JS_ASSERT(cg->staticLevel > upvarLevel);
|
||||
@ -2025,7 +2032,7 @@ MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg)
|
||||
|
||||
JSAtomListElement *ale = cg->upvarList.lookup(atom);
|
||||
if (!ale) {
|
||||
if (cg->inFunction() && !cg->fun()->addLocal(cx, atom, JSLOCAL_UPVAR))
|
||||
if (cg->inFunction() && !cg->bindings.addUpvar(cx, atom))
|
||||
return false;
|
||||
|
||||
ale = cg->upvarList.add(cg->parser, atom);
|
||||
@ -2046,7 +2053,7 @@ MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg)
|
||||
cg->upvarMap.length = length;
|
||||
}
|
||||
|
||||
if (localKind != JSLOCAL_ARG)
|
||||
if (kind != ARGUMENT)
|
||||
index += fun->nargs;
|
||||
JS_ASSERT(index < JS_BIT(16));
|
||||
|
||||
@ -2417,7 +2424,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
if (ale) {
|
||||
index = ALE_INDEX(ale);
|
||||
} else {
|
||||
if (!cg->fun()->addLocal(cx, atom, JSLOCAL_UPVAR))
|
||||
if (!cg->bindings.addUpvar(cx, atom))
|
||||
return JS_FALSE;
|
||||
|
||||
ale = cg->upvarList.add(cg->parser, atom);
|
||||
@ -3869,7 +3876,7 @@ MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
|
||||
|
||||
if (cg->inFunction() &&
|
||||
JOF_OPTYPE(pn->pn_op) == JOF_LOCAL &&
|
||||
pn->pn_cookie.slot() < cg->fun()->u.i.nvars &&
|
||||
pn->pn_cookie.slot() < cg->bindings.countVars() &&
|
||||
cg->shouldNoteClosedName(pn))
|
||||
{
|
||||
if (!cg->closedVars.append(pn->pn_cookie.slot()))
|
||||
@ -4740,9 +4747,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
|
||||
cg2->flags = pn->pn_funbox->tcflags | TCF_COMPILING | TCF_IN_FUNCTION |
|
||||
(cg->flags & TCF_FUN_MIGHT_ALIAS_LOCALS);
|
||||
cg2->bindings.transfer(cx, &pn->pn_funbox->bindings);
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (cg2->flags & TCF_HAS_SHARPS) {
|
||||
cg2->sharpSlotBase = fun->sharpSlotBase(cx);
|
||||
cg2->sharpSlotBase = cg2->bindings.sharpSlotBase(cx);
|
||||
if (cg2->sharpSlotBase < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -4815,13 +4823,13 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
JSLocalKind localKind =
|
||||
BindingKind kind =
|
||||
#endif
|
||||
cg->fun()->lookupLocal(cx, fun->atom, &slot);
|
||||
JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
|
||||
cg->bindings.lookup(fun->atom, &slot);
|
||||
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
|
||||
JS_ASSERT(index < JS_BIT(20));
|
||||
pn->pn_index = index;
|
||||
op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN;
|
||||
op = fun->isFlatClosure() ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN;
|
||||
if (pn->isClosed() &&
|
||||
!cg->callsEval() &&
|
||||
!cg->closedVars.append(pn->pn_cookie.slot())) {
|
||||
|
@ -332,16 +332,21 @@ struct JSTreeContext { /* tree context for semantic checks */
|
||||
|
||||
JSParseNode *innermostWith; /* innermost WITH parse node */
|
||||
|
||||
js::Bindings bindings; /* bindings in this code, including
|
||||
arguments if we're compiling a function */
|
||||
|
||||
#ifdef JS_SCOPE_DEPTH_METER
|
||||
uint16 scopeDepth; /* current lexical scope chain depth */
|
||||
uint16 maxScopeDepth; /* maximum lexical scope chain depth */
|
||||
#endif
|
||||
|
||||
void trace(JSTracer *trc);
|
||||
|
||||
JSTreeContext(js::Parser *prs)
|
||||
: flags(0), bodyid(0), blockidGen(0),
|
||||
topStmt(NULL), topScopeStmt(NULL), blockChainBox(NULL), blockNode(NULL),
|
||||
parser(prs), scopeChain_(NULL), parent(prs->tc), staticLevel(0),
|
||||
funbox(NULL), functionList(NULL), innermostWith(NULL), sharpSlotBase(-1)
|
||||
: flags(0), bodyid(0), blockidGen(0), topStmt(NULL), topScopeStmt(NULL),
|
||||
blockChainBox(NULL), blockNode(NULL), parser(prs), scopeChain_(NULL), parent(prs->tc),
|
||||
staticLevel(0), funbox(NULL), functionList(NULL), innermostWith(NULL), bindings(prs->context),
|
||||
sharpSlotBase(-1)
|
||||
{
|
||||
prs->tc = this;
|
||||
JS_SCOPE_DEPTH_METERING(scopeDepth = maxScopeDepth = 0);
|
||||
|
482
js/src/jsfun.cpp
482
js/src/jsfun.cpp
@ -90,6 +90,7 @@
|
||||
#include "jsfuninlines.h"
|
||||
#include "jsinterpinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
@ -371,15 +372,12 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
|
||||
wfunobj->setPrivate(wfun);
|
||||
wfun->nargs = fun->nargs;
|
||||
wfun->flags = fun->flags | JSFUN_HEAVYWEIGHT;
|
||||
wfun->u.i.nvars = fun->u.i.nvars;
|
||||
wfun->u.i.nupvars = fun->u.i.nupvars;
|
||||
wfun->u.i.skipmin = fun->u.i.skipmin;
|
||||
wfun->u.i.wrapper = true;
|
||||
wfun->u.i.script = NULL;
|
||||
wfun->u.i.names = fun->u.i.names;
|
||||
wfun->atom = fun->atom;
|
||||
|
||||
JSScript *script = fun->u.i.script;
|
||||
JSScript *script = fun->script();
|
||||
jssrcnote *snbase = script->notes();
|
||||
jssrcnote *sn = snbase;
|
||||
while (!SN_IS_TERMINATOR(sn))
|
||||
@ -392,7 +390,7 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
|
||||
JSScript::isValidOffset(script->objectsOffset)
|
||||
? script->objects()->length
|
||||
: 0,
|
||||
fun->u.i.nupvars,
|
||||
script->bindings.countUpvars(),
|
||||
JSScript::isValidOffset(script->regexpsOffset)
|
||||
? script->regexps()->length
|
||||
: 0,
|
||||
@ -435,10 +433,10 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
|
||||
if (script->nClosedArgs + script->nClosedVars != 0)
|
||||
script->copyClosedSlotsTo(wscript);
|
||||
|
||||
if (wfun->u.i.nupvars != 0) {
|
||||
JS_ASSERT(wfun->u.i.nupvars == wscript->upvars()->length);
|
||||
if (script->bindings.hasUpvars()) {
|
||||
JS_ASSERT(script->bindings.countUpvars() == wscript->upvars()->length);
|
||||
memcpy(wscript->upvars()->vector, script->upvars()->vector,
|
||||
wfun->u.i.nupvars * sizeof(uint32));
|
||||
script->bindings.countUpvars() * sizeof(uint32));
|
||||
}
|
||||
|
||||
jsbytecode *pc = wscript->code;
|
||||
@ -494,6 +492,8 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
|
||||
wscript->owner = script->owner;
|
||||
#endif
|
||||
|
||||
wscript->bindings.clone(cx, &script->bindings);
|
||||
|
||||
/* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
|
||||
FUN_SET_KIND(wfun, JSFUN_INTERPRETED);
|
||||
wfun->u.i.script = wscript;
|
||||
@ -944,8 +944,10 @@ CalleeGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||
static JSObject *
|
||||
NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &callee)
|
||||
{
|
||||
size_t vars = fun->countArgsAndVars();
|
||||
size_t slots = JSObject::CALL_RESERVED_SLOTS + vars;
|
||||
Bindings &bindings = fun->script()->bindings;
|
||||
|
||||
size_t argsVars = bindings.countArgsAndVars();
|
||||
size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars;
|
||||
gc::FinalizeKind kind = gc::GetGCObjectKind(slots);
|
||||
|
||||
JSObject *callobj = js_NewGCObject(cx, kind);
|
||||
@ -954,10 +956,10 @@ NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &ca
|
||||
|
||||
/* Init immediately to avoid GC seeing a half-init'ed object. */
|
||||
callobj->init(cx, &js_CallClass, NULL, &scopeChain, NULL, false);
|
||||
callobj->setMap(fun->u.i.names);
|
||||
callobj->setMap(bindings.lastShape());
|
||||
|
||||
/* This must come after callobj->lastProp has been set. */
|
||||
if (!callobj->ensureInstanceReservedSlots(cx, vars))
|
||||
if (!callobj->ensureInstanceReservedSlots(cx, argsVars))
|
||||
return NULL;
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -1076,15 +1078,19 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
|
||||
JSFunction *fun = fp->fun();
|
||||
JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
|
||||
uintN n = fun->countArgsAndVars();
|
||||
|
||||
Bindings &bindings = fun->script()->bindings;
|
||||
uintN n = bindings.countArgsAndVars();
|
||||
|
||||
if (n != 0) {
|
||||
JS_ASSERT(JSFunction::CLASS_RESERVED_SLOTS + n <= callobj.numSlots());
|
||||
|
||||
uint32 nargs = fun->nargs;
|
||||
uint32 nvars = fun->u.i.nvars;
|
||||
uint32 nvars = bindings.countVars();
|
||||
uint32 nargs = bindings.countArgs();
|
||||
JS_ASSERT(fun->nargs == nargs);
|
||||
JS_ASSERT(nvars + nargs == n);
|
||||
|
||||
JSScript *script = fun->u.i.script;
|
||||
JSScript *script = fun->script();
|
||||
if (script->usesEval
|
||||
#ifdef JS_METHODJIT
|
||||
|| script->debugMode
|
||||
@ -1096,7 +1102,6 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
* For each arg & var that is closed over, copy it from the stack
|
||||
* into the call object.
|
||||
*/
|
||||
JSScript *script = fun->u.i.script;
|
||||
uint32 nclosed = script->nClosedArgs;
|
||||
for (uint32 i = 0; i < nclosed; i++) {
|
||||
uint32 e = script->getClosedArg(i);
|
||||
@ -1165,16 +1170,18 @@ CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, Value *vp,
|
||||
JSObject &callee = obj->getCallObjCallee();
|
||||
|
||||
#ifdef DEBUG
|
||||
JSFunction *callee_fun = (JSFunction *) callee.getPrivate();
|
||||
JS_ASSERT(FUN_FLAT_CLOSURE(callee_fun));
|
||||
JS_ASSERT(i < callee_fun->u.i.nupvars);
|
||||
JSFunction *calleeFun = callee.getFunctionPrivate();
|
||||
JS_ASSERT(calleeFun->isFlatClosure());
|
||||
JS_ASSERT(calleeFun->script()->bindings.countUpvars() == calleeFun->script()->upvars()->length);
|
||||
JS_ASSERT(i < calleeFun->script()->bindings.countUpvars());
|
||||
#endif
|
||||
|
||||
array = callee.getFlatClosureUpvars();
|
||||
} else {
|
||||
JSFunction *fun = obj->getCallObjCalleeFunction();
|
||||
JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
|
||||
JS_ASSERT_IF(kind == JSCPK_ARG, i < fun->nargs);
|
||||
JS_ASSERT_IF(kind == JSCPK_VAR, i < fun->u.i.nvars);
|
||||
JS_ASSERT_IF(kind == JSCPK_VAR, i < fun->script()->bindings.countVars());
|
||||
|
||||
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
|
||||
|
||||
@ -1314,17 +1321,14 @@ call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
||||
if (!JSID_IS_ATOM(id))
|
||||
return JS_TRUE;
|
||||
|
||||
#ifdef DEBUG
|
||||
JSFunction *fun = obj->getCallObjCalleeFunction();
|
||||
JS_ASSERT(fun->lookupLocal(cx, JSID_TO_ATOM(id), NULL) == JSLOCAL_NONE);
|
||||
#endif
|
||||
JS_ASSERT(!obj->getCallObjCalleeFunction()->script()->bindings.hasBinding(JSID_TO_ATOM(id)));
|
||||
|
||||
/*
|
||||
* Resolve arguments so that we never store a particular Call object's
|
||||
* arguments object reference in a Call prototype's |arguments| slot.
|
||||
*
|
||||
* Include JSPROP_ENUMERATE for consistency with all other Call object
|
||||
* properties; see JSFunction::addLocal and js::Interpret's JSOP_DEFFUN
|
||||
* properties; see js::Bindings::add and js::Interpret's JSOP_DEFFUN
|
||||
* rebinding-Call-property logic.
|
||||
*/
|
||||
if (JSID_IS_ATOM(id, cx->runtime->atomState.argumentsAtom)) {
|
||||
@ -1356,7 +1360,7 @@ call_trace(JSTracer *trc, JSObject *obj)
|
||||
* hiding hack.
|
||||
*/
|
||||
uintN first = JSObject::CALL_RESERVED_SLOTS;
|
||||
uintN count = fp->fun()->countArgsAndVars();
|
||||
uintN count = fp->fun()->script()->bindings.countArgsAndVars();
|
||||
|
||||
JS_ASSERT(obj->numSlots() >= first + count);
|
||||
SetValueRangeToUndefined(obj->getSlots() + first, count);
|
||||
@ -1792,9 +1796,7 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
|
||||
uint32 firstword; /* flag telling whether fun->atom is non-null,
|
||||
plus for fun->u.i.skipmin, fun->u.i.wrapper,
|
||||
and 14 bits reserved for future use */
|
||||
uintN nargs, nvars, nupvars, n;
|
||||
uint32 localsword; /* word for argument and variable counts */
|
||||
uint32 flagsword; /* word for fun->u.i.nupvars and fun->flags */
|
||||
uint32 flagsword; /* word for argument count and fun->flags */
|
||||
|
||||
cx = xdr->cx;
|
||||
if (xdr->mode == JSXDR_ENCODE) {
|
||||
@ -1815,20 +1817,13 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
|
||||
}
|
||||
JS_ASSERT((fun->u.i.wrapper & ~1U) == 0);
|
||||
firstword = (fun->u.i.skipmin << 2) | (fun->u.i.wrapper << 1) | !!fun->atom;
|
||||
nargs = fun->nargs;
|
||||
nvars = fun->u.i.nvars;
|
||||
nupvars = fun->u.i.nupvars;
|
||||
localsword = (nargs << 16) | nvars;
|
||||
flagsword = (nupvars << 16) | fun->flags;
|
||||
flagsword = (fun->nargs << 16) | fun->flags;
|
||||
} else {
|
||||
fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
|
||||
if (!fun)
|
||||
return false;
|
||||
FUN_OBJECT(fun)->clearParent();
|
||||
FUN_OBJECT(fun)->clearProto();
|
||||
#ifdef __GNUC__
|
||||
nvars = nargs = nupvars = 0; /* quell GCC uninitialized warning */
|
||||
#endif
|
||||
}
|
||||
|
||||
AutoObjectRooter tvr(cx, FUN_OBJECT(fun));
|
||||
@ -1837,122 +1832,17 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
|
||||
return false;
|
||||
if ((firstword & 1U) && !js_XDRAtom(xdr, &fun->atom))
|
||||
return false;
|
||||
if (!JS_XDRUint32(xdr, &localsword) ||
|
||||
!JS_XDRUint32(xdr, &flagsword)) {
|
||||
if (!JS_XDRUint32(xdr, &flagsword))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
nargs = localsword >> 16;
|
||||
nvars = uint16(localsword);
|
||||
fun->nargs = flagsword >> 16;
|
||||
JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
|
||||
nupvars = flagsword >> 16;
|
||||
fun->flags = uint16(flagsword);
|
||||
fun->u.i.skipmin = uint16(firstword >> 2);
|
||||
fun->u.i.wrapper = JSPackedBool((firstword >> 1) & 1);
|
||||
}
|
||||
|
||||
/* do arguments and local vars */
|
||||
n = nargs + nvars + nupvars;
|
||||
if (n != 0) {
|
||||
void *mark;
|
||||
uintN i;
|
||||
uintN bitmapLength;
|
||||
uint32 *bitmap;
|
||||
jsuword *names;
|
||||
JSAtom *name;
|
||||
JSLocalKind localKind;
|
||||
|
||||
bool ok = true;
|
||||
mark = JS_ARENA_MARK(&xdr->cx->tempPool);
|
||||
|
||||
/*
|
||||
* From this point the control must flow via the label release_mark.
|
||||
*
|
||||
* To xdr the names we prefix the names with a bitmap descriptor and
|
||||
* then xdr the names as strings. For argument names (indexes below
|
||||
* nargs) the corresponding bit in the bitmap is unset when the name
|
||||
* is null. Such null names are not encoded or decoded. For variable
|
||||
* names (indexes starting from nargs) bitmap's bit is set when the
|
||||
* name is declared as const, not as ordinary var.
|
||||
* */
|
||||
MUST_FLOW_THROUGH("release_mark");
|
||||
bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32);
|
||||
JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool,
|
||||
bitmapLength * sizeof *bitmap);
|
||||
if (!bitmap) {
|
||||
js_ReportOutOfScriptQuota(xdr->cx);
|
||||
ok = false;
|
||||
goto release_mark;
|
||||
}
|
||||
if (xdr->mode == JSXDR_ENCODE) {
|
||||
names = fun->getLocalNameArray(xdr->cx, &xdr->cx->tempPool);
|
||||
if (!names) {
|
||||
ok = false;
|
||||
goto release_mark;
|
||||
}
|
||||
PodZero(bitmap, bitmapLength);
|
||||
for (i = 0; i != n; ++i) {
|
||||
if (i < fun->nargs
|
||||
? JS_LOCAL_NAME_TO_ATOM(names[i]) != NULL
|
||||
: JS_LOCAL_NAME_IS_CONST(names[i])) {
|
||||
bitmap[i >> JS_BITS_PER_UINT32_LOG2] |=
|
||||
JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
else {
|
||||
names = NULL; /* quell GCC uninitialized warning */
|
||||
}
|
||||
#endif
|
||||
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_LOG2] &
|
||||
JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) {
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
ok = !!fun->addLocal(xdr->cx, NULL, JSLOCAL_ARG);
|
||||
if (!ok)
|
||||
goto release_mark;
|
||||
} else {
|
||||
JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names[i]));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (xdr->mode == JSXDR_ENCODE)
|
||||
name = JS_LOCAL_NAME_TO_ATOM(names[i]);
|
||||
ok = !!js_XDRAtom(xdr, &name);
|
||||
if (!ok)
|
||||
goto release_mark;
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
localKind = (i < nargs)
|
||||
? JSLOCAL_ARG
|
||||
: (i < nargs + nvars)
|
||||
? (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
|
||||
JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
|
||||
? JSLOCAL_CONST
|
||||
: JSLOCAL_VAR)
|
||||
: JSLOCAL_UPVAR;
|
||||
ok = !!fun->addLocal(xdr->cx, name, localKind);
|
||||
if (!ok)
|
||||
goto release_mark;
|
||||
}
|
||||
}
|
||||
|
||||
release_mark:
|
||||
JS_ARENA_RELEASE(&xdr->cx->tempPool, mark);
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
if (xdr->mode == JSXDR_DECODE)
|
||||
fun->freezeLocalNames(cx);
|
||||
}
|
||||
|
||||
if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
|
||||
return false;
|
||||
|
||||
@ -1961,6 +1851,7 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
|
||||
#ifdef CHECK_SCRIPT_OWNER
|
||||
fun->script()->owner = NULL;
|
||||
#endif
|
||||
JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
|
||||
js_CallNewScriptHook(cx, fun->script(), fun);
|
||||
}
|
||||
|
||||
@ -2018,69 +1909,40 @@ fun_trace(JSTracer *trc, JSObject *obj)
|
||||
MarkObject(trc, *fun, "private");
|
||||
|
||||
/* The function could be a flat closure with upvar copies in the clone. */
|
||||
if (FUN_FLAT_CLOSURE(fun) && fun->u.i.nupvars)
|
||||
MarkValueRange(trc, fun->u.i.nupvars, obj->getFlatClosureUpvars(), "upvars");
|
||||
if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars()) {
|
||||
MarkValueRange(trc, fun->script()->bindings.countUpvars(),
|
||||
obj->getFlatClosureUpvars(), "upvars");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (fun->atom)
|
||||
MarkString(trc, ATOM_TO_STRING(fun->atom), "atom");
|
||||
|
||||
if (FUN_INTERPRETED(fun)) {
|
||||
if (fun->u.i.script)
|
||||
js_TraceScript(trc, fun->u.i.script);
|
||||
for (const Shape *shape = fun->u.i.names; shape; shape = shape->previous())
|
||||
shape->trace(trc);
|
||||
}
|
||||
if (fun->isInterpreted() && fun->script())
|
||||
js_TraceScript(trc, fun->script());
|
||||
}
|
||||
|
||||
static void
|
||||
fun_finalize(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
/* Ignore newborn function objects. */
|
||||
JSFunction *fun = (JSFunction *) obj->getPrivate();
|
||||
JSFunction *fun = obj->getFunctionPrivate();
|
||||
if (!fun)
|
||||
return;
|
||||
|
||||
/* Cloned function objects may be flat closures with upvars to free. */
|
||||
if (fun != obj) {
|
||||
if (FUN_FLAT_CLOSURE(fun) && fun->u.i.nupvars != 0)
|
||||
if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars())
|
||||
cx->free((void *) obj->getFlatClosureUpvars());
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Null-check of u.i.script is required since the parser sets interpreted
|
||||
* very early.
|
||||
* Null-check fun->script() because the parser sets interpreted very early.
|
||||
*/
|
||||
if (FUN_INTERPRETED(fun) && fun->u.i.script)
|
||||
js_DestroyScriptFromGC(cx, fun->u.i.script);
|
||||
}
|
||||
|
||||
int
|
||||
JSFunction::sharpSlotBase(JSContext *cx)
|
||||
{
|
||||
#if JS_HAS_SHARP_VARS
|
||||
JSAtom *name = js_Atomize(cx, "#array", 6, 0);
|
||||
if (name) {
|
||||
uintN index = uintN(-1);
|
||||
#ifdef DEBUG
|
||||
JSLocalKind kind =
|
||||
#endif
|
||||
lookupLocal(cx, name, &index);
|
||||
JS_ASSERT(kind == JSLOCAL_VAR);
|
||||
return int(index);
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32
|
||||
JSFunction::countUpvarSlots() const
|
||||
{
|
||||
JS_ASSERT(FUN_INTERPRETED(this));
|
||||
|
||||
return (u.i.nupvars == 0) ? 0 : u.i.script->upvars()->length;
|
||||
if (fun->isInterpreted() && fun->script())
|
||||
js_DestroyScriptFromGC(cx, fun->script());
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2531,6 +2393,8 @@ Function(JSContext *cx, uintN argc, Value *vp)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
Bindings bindings(cx);
|
||||
|
||||
Value *argv = vp + 2;
|
||||
uintN n = argc ? argc - 1 : 0;
|
||||
if (n > 0) {
|
||||
@ -2635,7 +2499,7 @@ Function(JSContext *cx, uintN argc, Value *vp)
|
||||
JSAtom *atom = ts.currentToken().t_atom;
|
||||
|
||||
/* Check for a duplicate parameter name. */
|
||||
if (fun->lookupLocal(cx, atom, NULL) != JSLOCAL_NONE) {
|
||||
if (bindings.hasBinding(atom)) {
|
||||
JSAutoByteString name;
|
||||
if (!js_AtomToPrintableString(cx, atom, &name)) {
|
||||
state = BAD;
|
||||
@ -2644,11 +2508,16 @@ Function(JSContext *cx, uintN argc, Value *vp)
|
||||
if (!ReportCompileErrorNumber(cx, &ts, NULL,
|
||||
JSREPORT_WARNING | JSREPORT_STRICT,
|
||||
JSMSG_DUPLICATE_FORMAL, name.ptr())) {
|
||||
state = BAD;
|
||||
goto after_args;
|
||||
}
|
||||
}
|
||||
if (!fun->addLocal(cx, atom, JSLOCAL_ARG))
|
||||
|
||||
uint16 dummy;
|
||||
if (!bindings.addArgument(cx, atom, &dummy)) {
|
||||
state = BAD;
|
||||
goto after_args;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the next token. Stop on end of stream. Otherwise
|
||||
@ -2681,10 +2550,10 @@ Function(JSContext *cx, uintN argc, Value *vp)
|
||||
|
||||
JSString *str;
|
||||
if (argc) {
|
||||
str = js_ValueToString(cx, argv[argc-1]);
|
||||
str = js_ValueToString(cx, argv[argc - 1]);
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
argv[argc-1].setString(str);
|
||||
argv[argc - 1].setString(str);
|
||||
} else {
|
||||
str = cx->runtime->emptyString;
|
||||
}
|
||||
@ -2693,8 +2562,9 @@ Function(JSContext *cx, uintN argc, Value *vp)
|
||||
const jschar *chars = str->getChars(cx);
|
||||
if (!chars)
|
||||
return JS_FALSE;
|
||||
return Compiler::compileFunctionBody(cx, fun, principals, chars, length,
|
||||
filename, lineno);
|
||||
|
||||
return Compiler::compileFunctionBody(cx, fun, principals, &bindings,
|
||||
chars, length, filename, lineno);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -2768,12 +2638,9 @@ js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs,
|
||||
if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) {
|
||||
JS_ASSERT(!native);
|
||||
JS_ASSERT(nargs == 0);
|
||||
fun->u.i.nvars = 0;
|
||||
fun->u.i.nupvars = 0;
|
||||
fun->u.i.skipmin = 0;
|
||||
fun->u.i.wrapper = false;
|
||||
fun->u.i.script = NULL;
|
||||
fun->u.i.names = cx->runtime->emptyCallShape;
|
||||
} else {
|
||||
fun->u.n.clasp = NULL;
|
||||
if (flags & JSFUN_TRCINFO) {
|
||||
@ -2860,16 +2727,17 @@ JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CloneFunctionObject, CONTEXT, FUNCTION,
|
||||
JSObject * JS_FASTCALL
|
||||
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
|
||||
{
|
||||
JS_ASSERT(FUN_FLAT_CLOSURE(fun));
|
||||
JS_ASSERT((JSScript::isValidOffset(fun->u.i.script->upvarsOffset)
|
||||
? fun->u.i.script->upvars()->length
|
||||
: 0) == fun->u.i.nupvars);
|
||||
JS_ASSERT(fun->isFlatClosure());
|
||||
JS_ASSERT(JSScript::isValidOffset(fun->script()->upvarsOffset) ==
|
||||
fun->script()->bindings.hasUpvars());
|
||||
JS_ASSERT_IF(JSScript::isValidOffset(fun->script()->upvarsOffset),
|
||||
fun->script()->upvars()->length == fun->script()->bindings.countUpvars());
|
||||
|
||||
JSObject *closure = CloneFunctionObject(cx, fun, scopeChain);
|
||||
if (!closure)
|
||||
return closure;
|
||||
|
||||
uint32 nslots = fun->countUpvarSlots();
|
||||
uint32 nslots = fun->script()->bindings.countUpvars();
|
||||
if (nslots == 0)
|
||||
return closure;
|
||||
|
||||
@ -2896,12 +2764,12 @@ js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
|
||||
return NULL;
|
||||
|
||||
JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
|
||||
if (!closure || fun->u.i.nupvars == 0)
|
||||
if (!closure || !fun->script()->bindings.hasUpvars())
|
||||
return closure;
|
||||
|
||||
Value *upvars = closure->getFlatClosureUpvars();
|
||||
uintN level = fun->u.i.script->staticLevel;
|
||||
JSUpvarArray *uva = fun->u.i.script->upvars();
|
||||
JSUpvarArray *uva = fun->script()->upvars();
|
||||
|
||||
for (uint32 i = 0, n = uva->length; i < n; i++)
|
||||
upvars[i] = GetUpvar(cx, level, uva->vector[i]);
|
||||
@ -3073,215 +2941,3 @@ js_ReportIsNotFunction(JSContext *cx, const Value *vp, uintN flags)
|
||||
|
||||
js_ReportValueError3(cx, error, spindex, *vp, NULL, name, source);
|
||||
}
|
||||
|
||||
const Shape *
|
||||
JSFunction::lastArg() const
|
||||
{
|
||||
const Shape *shape = lastVar();
|
||||
if (u.i.nvars != 0) {
|
||||
while (shape->previous() && shape->getter() != GetCallArg)
|
||||
shape = shape->previous();
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
const Shape *
|
||||
JSFunction::lastVar() const
|
||||
{
|
||||
const Shape *shape = u.i.names;
|
||||
if (u.i.nupvars != 0) {
|
||||
while (shape->getter() == GetFlatUpvar)
|
||||
shape = shape->previous();
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
bool
|
||||
JSFunction::addLocal(JSContext *cx, JSAtom *atom, JSLocalKind kind)
|
||||
{
|
||||
JS_ASSERT(FUN_INTERPRETED(this));
|
||||
JS_ASSERT(!u.i.script);
|
||||
|
||||
/*
|
||||
* We still follow 10.2.3 of ES3 and make argument and variable properties
|
||||
* of the Call objects enumerable. ES5 reformulated all of its Clause 10 to
|
||||
* avoid objects as activations, something we should do too.
|
||||
*/
|
||||
uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED;
|
||||
uint16 *indexp;
|
||||
PropertyOp getter, setter;
|
||||
uint32 slot = JSObject::CALL_RESERVED_SLOTS;
|
||||
|
||||
if (kind == JSLOCAL_ARG) {
|
||||
JS_ASSERT(u.i.nupvars == 0);
|
||||
|
||||
indexp = &nargs;
|
||||
getter = GetCallArg;
|
||||
setter = SetCallArg;
|
||||
slot += nargs;
|
||||
} else if (kind == JSLOCAL_UPVAR) {
|
||||
indexp = &u.i.nupvars;
|
||||
getter = GetFlatUpvar;
|
||||
setter = SetFlatUpvar;
|
||||
slot = SHAPE_INVALID_SLOT;
|
||||
} else {
|
||||
JS_ASSERT(u.i.nupvars == 0);
|
||||
|
||||
indexp = &u.i.nvars;
|
||||
getter = GetCallVar;
|
||||
setter = SetCallVar;
|
||||
if (kind == JSLOCAL_CONST)
|
||||
attrs |= JSPROP_READONLY;
|
||||
else
|
||||
JS_ASSERT(kind == JSLOCAL_VAR);
|
||||
slot += nargs + u.i.nvars;
|
||||
}
|
||||
|
||||
if (*indexp == JS_BITMASK(16)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
(kind == JSLOCAL_ARG)
|
||||
? JSMSG_TOO_MANY_FUN_ARGS
|
||||
: JSMSG_TOO_MANY_LOCALS);
|
||||
return false;
|
||||
}
|
||||
|
||||
jsid id;
|
||||
if (!atom) {
|
||||
/* Destructuring formal parameter: use argument index as id. */
|
||||
JS_ASSERT(kind == JSLOCAL_ARG);
|
||||
id = INT_TO_JSID(nargs);
|
||||
} else {
|
||||
id = ATOM_TO_JSID(atom);
|
||||
}
|
||||
|
||||
Shape child(id, getter, setter, slot, attrs, Shape::HAS_SHORTID, *indexp);
|
||||
|
||||
Shape *shape = u.i.names->getChild(cx, child, &u.i.names);
|
||||
if (!shape)
|
||||
return false;
|
||||
|
||||
JS_ASSERT(u.i.names == shape);
|
||||
++*indexp;
|
||||
return true;
|
||||
}
|
||||
|
||||
JSLocalKind
|
||||
JSFunction::lookupLocal(JSContext *cx, JSAtom *atom, uintN *indexp)
|
||||
{
|
||||
JS_ASSERT(FUN_INTERPRETED(this));
|
||||
|
||||
Shape *shape = SHAPE_FETCH(Shape::search(&u.i.names, ATOM_TO_JSID(atom)));
|
||||
if (shape) {
|
||||
JSLocalKind localKind;
|
||||
|
||||
if (shape->getter() == GetCallArg)
|
||||
localKind = JSLOCAL_ARG;
|
||||
else if (shape->getter() == GetFlatUpvar)
|
||||
localKind = JSLOCAL_UPVAR;
|
||||
else if (!shape->writable())
|
||||
localKind = JSLOCAL_CONST;
|
||||
else
|
||||
localKind = JSLOCAL_VAR;
|
||||
|
||||
if (indexp)
|
||||
*indexp = shape->shortid;
|
||||
return localKind;
|
||||
}
|
||||
return JSLOCAL_NONE;
|
||||
}
|
||||
|
||||
jsuword *
|
||||
JSFunction::getLocalNameArray(JSContext *cx, JSArenaPool *pool)
|
||||
{
|
||||
JS_ASSERT(hasLocalNames());
|
||||
|
||||
uintN n = countLocalNames();
|
||||
jsuword *names;
|
||||
|
||||
/*
|
||||
* No need to check for overflow of the allocation size as we are making a
|
||||
* copy of already allocated data. As such it must fit size_t.
|
||||
*/
|
||||
JS_ARENA_ALLOCATE_CAST(names, jsuword *, pool, size_t(n) * sizeof *names);
|
||||
if (!names) {
|
||||
js_ReportOutOfScriptQuota(cx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for (uintN i = 0; i != n; i++)
|
||||
names[i] = 0xdeadbeef;
|
||||
#endif
|
||||
|
||||
for (Shape::Range r = u.i.names; !r.empty(); r.popFront()) {
|
||||
const Shape &shape = r.front();
|
||||
uintN index = uint16(shape.shortid);
|
||||
jsuword constFlag = 0;
|
||||
|
||||
if (shape.getter() == GetCallArg) {
|
||||
JS_ASSERT(index < nargs);
|
||||
} else if (shape.getter() == GetFlatUpvar) {
|
||||
JS_ASSERT(index < u.i.nupvars);
|
||||
index += nargs + u.i.nvars;
|
||||
} else {
|
||||
JS_ASSERT(index < u.i.nvars);
|
||||
index += nargs;
|
||||
if (!shape.writable())
|
||||
constFlag = 1;
|
||||
}
|
||||
|
||||
JSAtom *atom;
|
||||
if (JSID_IS_ATOM(shape.id)) {
|
||||
atom = JSID_TO_ATOM(shape.id);
|
||||
} else {
|
||||
JS_ASSERT(JSID_IS_INT(shape.id));
|
||||
JS_ASSERT(shape.getter() == GetCallArg);
|
||||
atom = NULL;
|
||||
}
|
||||
|
||||
names[index] = jsuword(atom);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for (uintN i = 0; i != n; i++)
|
||||
JS_ASSERT(names[i] != 0xdeadbeef);
|
||||
#endif
|
||||
return names;
|
||||
}
|
||||
|
||||
void
|
||||
JSFunction::freezeLocalNames(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(FUN_INTERPRETED(this));
|
||||
|
||||
Shape *shape = u.i.names;
|
||||
if (shape->inDictionary()) {
|
||||
do {
|
||||
JS_ASSERT(!shape->frozen());
|
||||
shape->setFrozen();
|
||||
} while ((shape = shape->parent) != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called only if we parsed a duplicate formal. Let's use the
|
||||
* simplest possible algorithm, risking O(n^2) pain -- anyone dup'ing formals
|
||||
* is asking for it!
|
||||
*/
|
||||
JSAtom *
|
||||
JSFunction::findDuplicateFormal() const
|
||||
{
|
||||
JS_ASSERT(isInterpreted());
|
||||
|
||||
if (nargs <= 1)
|
||||
return NULL;
|
||||
|
||||
for (Shape::Range r = lastArg(); !r.empty(); r.popFront()) {
|
||||
const Shape &shape = r.front();
|
||||
for (Shape::Range r2 = shape.previous(); !r2.empty(); r2.popFront()) {
|
||||
if (r2.front().id == shape.id)
|
||||
return JSID_TO_ATOM(shape.id);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
106
js/src/jsfun.h
106
js/src/jsfun.h
@ -46,6 +46,7 @@
|
||||
#include "jspubtd.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsatom.h"
|
||||
#include "jsscript.h"
|
||||
#include "jsstr.h"
|
||||
#include "jsopcode.h"
|
||||
|
||||
@ -113,29 +114,6 @@
|
||||
JS_ASSERT((fun)->flags & JSFUN_TRCINFO), \
|
||||
fun->u.n.trcinfo)
|
||||
|
||||
/*
|
||||
* Formal parameters, local variables, and upvars are stored in a shape tree
|
||||
* path with its latest node at fun->u.i.names. The addLocal, lookupLocal, and
|
||||
* getLocalNameArray methods abstract away this detail.
|
||||
*
|
||||
* The lastArg, lastVar, and lastUpvar JSFunction methods provide more direct
|
||||
* access to the shape path. These methods may be used to make a Shape::Range
|
||||
* for iterating over the relevant shapes from youngest to oldest (i.e., last
|
||||
* or right-most to first or left-most in source order).
|
||||
*
|
||||
* Sometimes iteration order must be from oldest to youngest, however. For such
|
||||
* cases, use getLocalNameArray. The RAII helper class js::AutoLocalNameArray,
|
||||
* defined in jscntxt.h, should be used where possible instead of direct calls
|
||||
* to getLocalNameArray.
|
||||
*/
|
||||
enum JSLocalKind {
|
||||
JSLOCAL_NONE,
|
||||
JSLOCAL_ARG,
|
||||
JSLOCAL_VAR,
|
||||
JSLOCAL_CONST,
|
||||
JSLOCAL_UPVAR
|
||||
};
|
||||
|
||||
struct JSFunction : public JSObject_Slots2
|
||||
{
|
||||
/* Functions always have two fixed slots (FUN_CLASS_RESERVED_SLOTS). */
|
||||
@ -152,9 +130,6 @@ struct JSFunction : public JSObject_Slots2
|
||||
} n;
|
||||
struct Scripted {
|
||||
JSScript *script; /* interpreted bytecode descriptor or null */
|
||||
uint16 nvars; /* number of local variables */
|
||||
uint16 nupvars; /* number of upvars (computable from script
|
||||
but here for faster access) */
|
||||
uint16 skipmin; /* net skip amount up (toward zero) from
|
||||
script->staticLevel to nearest upvar,
|
||||
including upvars in nested functions */
|
||||
@ -183,84 +158,19 @@ struct JSFunction : public JSObject_Slots2
|
||||
/* Returns the strictness of this function, which must be interpreted. */
|
||||
inline bool inStrictMode() const;
|
||||
|
||||
uintN countVars() const {
|
||||
JS_ASSERT(FUN_INTERPRETED(this));
|
||||
return u.i.nvars;
|
||||
void setArgCount(uint16 nargs) {
|
||||
JS_ASSERT(this->nargs == 0);
|
||||
this->nargs = nargs;
|
||||
}
|
||||
|
||||
/* uint16 representation bounds number of call object dynamic slots. */
|
||||
enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
|
||||
|
||||
uintN countArgsAndVars() const {
|
||||
JS_ASSERT(FUN_INTERPRETED(this));
|
||||
return nargs + u.i.nvars;
|
||||
}
|
||||
|
||||
uintN countLocalNames() const {
|
||||
JS_ASSERT(FUN_INTERPRETED(this));
|
||||
return countArgsAndVars() + u.i.nupvars;
|
||||
}
|
||||
|
||||
bool hasLocalNames() const {
|
||||
JS_ASSERT(FUN_INTERPRETED(this));
|
||||
return countLocalNames() != 0;
|
||||
}
|
||||
|
||||
int sharpSlotBase(JSContext *cx);
|
||||
|
||||
uint32 countUpvarSlots() const;
|
||||
|
||||
const js::Shape *lastArg() const;
|
||||
const js::Shape *lastVar() const;
|
||||
const js::Shape *lastUpvar() const { return u.i.names; }
|
||||
|
||||
/*
|
||||
* The parser builds shape paths for functions, usable by Call objects at
|
||||
* runtime, by calling addLocal. All locals of ARG kind must be addLocal'ed
|
||||
* before any VAR kind, and VAR before UPVAR.
|
||||
*/
|
||||
bool addLocal(JSContext *cx, 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.
|
||||
*/
|
||||
JSLocalKind lookupLocal(JSContext *cx, JSAtom *atom, uintN *indexp);
|
||||
|
||||
/*
|
||||
* Function and macros to work with local names as an array of words.
|
||||
* getLocalNameArray returns the array, or null if we are out of memory.
|
||||
* This function must be called only when fun->hasLocalNames().
|
||||
*
|
||||
* The supplied pool is used to allocate the returned array, so the caller
|
||||
* is obligated to mark and release to free it.
|
||||
*
|
||||
* The elements of the array with index less than fun->nargs correspond to
|
||||
* the names of function formal parameters. An index >= fun->nargs
|
||||
* addresses a var binding. Use JS_LOCAL_NAME_TO_ATOM to convert array's
|
||||
* element to an atom pointer. This pointer can be null when the element is
|
||||
* for a formal parameter corresponding to a destructuring pattern.
|
||||
*
|
||||
* If nameWord does not name a formal parameter, use JS_LOCAL_NAME_IS_CONST
|
||||
* to check if nameWord corresponds to the const declaration.
|
||||
*/
|
||||
jsuword *getLocalNameArray(JSContext *cx, struct JSArenaPool *pool);
|
||||
|
||||
void freezeLocalNames(JSContext *cx);
|
||||
|
||||
/*
|
||||
* If fun's formal parameters include any duplicate names, return one
|
||||
* of them (chosen arbitrarily). If they are all unique, return NULL.
|
||||
*/
|
||||
JSAtom *findDuplicateFormal() const;
|
||||
|
||||
#define JS_LOCAL_NAME_TO_ATOM(nameWord) ((JSAtom *) ((nameWord) & ~(jsuword) 1))
|
||||
#define JS_LOCAL_NAME_IS_CONST(nameWord) ((((nameWord) & (jsuword) 1)) != 0)
|
||||
|
||||
bool mightEscape() const {
|
||||
return FUN_INTERPRETED(this) && (FUN_FLAT_CLOSURE(this) || u.i.nupvars == 0);
|
||||
return isInterpreted() && (isFlatClosure() || !script()->bindings.hasUpvars());
|
||||
}
|
||||
|
||||
bool joinable() const {
|
||||
@ -606,12 +516,18 @@ GetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
||||
extern JSBool
|
||||
GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
||||
|
||||
extern JSBool
|
||||
GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
||||
|
||||
extern JSBool
|
||||
SetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
||||
|
||||
extern JSBool
|
||||
SetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
||||
|
||||
extern JSBool
|
||||
SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
|
||||
|
||||
} // namespace js
|
||||
|
||||
extern JSBool
|
||||
|
@ -974,7 +974,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
||||
if (prev && prev->script()->hasSharps) {
|
||||
JS_ASSERT(prev->numFixed() >= SHARP_NSLOTS);
|
||||
int base = (prev->isFunctionFrame() && !prev->isEvalOrDebuggerFrame())
|
||||
? prev->fun()->sharpSlotBase(cx)
|
||||
? prev->fun()->script()->bindings.sharpSlotBase(cx)
|
||||
: prev->numFixed() - SHARP_NSLOTS;
|
||||
if (base < 0)
|
||||
return false;
|
||||
@ -5301,7 +5301,7 @@ BEGIN_CASE(JSOP_CALLUPVAR_DBG)
|
||||
if (!names)
|
||||
goto error;
|
||||
|
||||
uintN index = fun->countArgsAndVars() + GET_UINT16(regs.pc);
|
||||
uintN index = fun->script()->bindings.countArgsAndVars() + GET_UINT16(regs.pc);
|
||||
atom = JS_LOCAL_NAME_TO_ATOM(names[index]);
|
||||
id = ATOM_TO_JSID(atom);
|
||||
|
||||
@ -5332,7 +5332,7 @@ BEGIN_CASE(JSOP_CALLFCSLOT)
|
||||
uintN index = GET_UINT16(regs.pc);
|
||||
JSObject *obj = &argv[-2].toObject();
|
||||
|
||||
JS_ASSERT(index < obj->getFunctionPrivate()->u.i.nupvars);
|
||||
JS_ASSERT(index < obj->getFunctionPrivate()->script()->bindings.countUpvars());
|
||||
PUSH_COPY(obj->getFlatClosureUpvar(index));
|
||||
if (op == JSOP_CALLFCSLOT)
|
||||
PUSH_UNDEFINED();
|
||||
|
@ -465,14 +465,14 @@ struct JSObject : js::gc::Cell {
|
||||
|
||||
bool hasOwnShape() const { return !!(flags & OWN_SHAPE); }
|
||||
|
||||
void setMap(JSObjectMap *amap) {
|
||||
void setMap(const JSObjectMap *amap) {
|
||||
JS_ASSERT(!hasOwnShape());
|
||||
map = amap;
|
||||
map = const_cast<JSObjectMap *>(amap);
|
||||
objShape = map->shape;
|
||||
}
|
||||
|
||||
void setSharedNonNativeMap() {
|
||||
setMap(const_cast<JSObjectMap *>(&JSObjectMap::sharedNonNative));
|
||||
setMap(&JSObjectMap::sharedNonNative);
|
||||
}
|
||||
|
||||
void deletingShapeChange(JSContext *cx, const js::Shape &shape);
|
||||
|
@ -493,6 +493,7 @@ JSObject::getFlatClosureUpvars() const
|
||||
inline js::Value
|
||||
JSObject::getFlatClosureUpvar(uint32 i) const
|
||||
{
|
||||
JS_ASSERT(i < getFunctionPrivate()->script()->bindings.countUpvars());
|
||||
return getFlatClosureUpvars()[i];
|
||||
}
|
||||
|
||||
|
@ -832,8 +832,8 @@ js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun,
|
||||
jp->pcstack = NULL;
|
||||
jp->fun = fun;
|
||||
jp->localNames = NULL;
|
||||
if (fun && FUN_INTERPRETED(fun) && fun->hasLocalNames()) {
|
||||
jp->localNames = fun->getLocalNameArray(cx, &jp->pool);
|
||||
if (fun && fun->isInterpreted() && fun->script()->bindings.hasLocalNames()) {
|
||||
jp->localNames = fun->script()->bindings.getLocalNameArray(cx, &jp->pool);
|
||||
if (!jp->localNames) {
|
||||
js_DestroyPrinter(jp);
|
||||
return NULL;
|
||||
@ -1340,7 +1340,7 @@ GetArgOrVarAtom(JSPrinter *jp, uintN slot)
|
||||
JSAtom *name;
|
||||
|
||||
LOCAL_ASSERT_RV(jp->fun, NULL);
|
||||
LOCAL_ASSERT_RV(slot < jp->fun->countLocalNames(), NULL);
|
||||
LOCAL_ASSERT_RV(slot < jp->fun->script()->bindings.countLocalNames(), NULL);
|
||||
name = JS_LOCAL_NAME_TO_ATOM(jp->localNames[slot]);
|
||||
#if !JS_HAS_DESTRUCTURING
|
||||
LOCAL_ASSERT_RV(name, NULL);
|
||||
@ -2895,12 +2895,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
jp->fun = jp->script->getFunction(0);
|
||||
}
|
||||
|
||||
if (!jp->localNames)
|
||||
jp->localNames = jp->fun->getLocalNameArray(cx, &jp->pool);
|
||||
if (!jp->localNames) {
|
||||
JS_ASSERT(fun == jp->fun);
|
||||
jp->localNames =
|
||||
jp->fun->script()->bindings.getLocalNameArray(cx, &jp->pool);
|
||||
}
|
||||
|
||||
uintN index = GET_UINT16(pc);
|
||||
if (index < jp->fun->u.i.nupvars) {
|
||||
index += jp->fun->countArgsAndVars();
|
||||
if (index < jp->fun->script()->bindings.countUpvars()) {
|
||||
index += jp->fun->script()->bindings.countArgsAndVars();
|
||||
} else {
|
||||
JSUpvarArray *uva;
|
||||
#ifdef DEBUG
|
||||
@ -2919,8 +2922,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
fp = fp->prev();
|
||||
JS_ASSERT(fp->script() == jp->script);
|
||||
JS_ASSERT(fp->prev()->fun() == jp->fun);
|
||||
JS_ASSERT(FUN_INTERPRETED(jp->fun));
|
||||
JS_ASSERT(jp->script != jp->fun->u.i.script);
|
||||
JS_ASSERT(jp->fun->isInterpreted());
|
||||
JS_ASSERT(jp->script != jp->fun->script());
|
||||
JS_ASSERT(JSScript::isValidOffset(jp->script->upvarsOffset));
|
||||
}
|
||||
#endif
|
||||
@ -4097,14 +4100,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
* release to mark before returning.
|
||||
*/
|
||||
mark = JS_ARENA_MARK(&cx->tempPool);
|
||||
if (!fun->hasLocalNames()) {
|
||||
innerLocalNames = NULL;
|
||||
} else {
|
||||
innerLocalNames = fun->getLocalNameArray(cx, &cx->tempPool);
|
||||
if (fun->script()->bindings.hasLocalNames()) {
|
||||
innerLocalNames =
|
||||
fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool);
|
||||
if (!innerLocalNames)
|
||||
return NULL;
|
||||
} else {
|
||||
innerLocalNames = NULL;
|
||||
}
|
||||
inner = fun->u.i.script;
|
||||
inner = fun->script();
|
||||
if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) {
|
||||
JS_ARENA_RELEASE(&cx->tempPool, mark);
|
||||
return NULL;
|
||||
|
@ -92,6 +92,7 @@
|
||||
#include "jsinterpinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsregexpinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
// Grr, windows.h or something under it #defines CONST...
|
||||
#ifdef CONST
|
||||
@ -243,6 +244,7 @@ Parser::newObjectBox(JSObject *obj)
|
||||
traceListHead = objbox;
|
||||
objbox->emitLink = NULL;
|
||||
objbox->object = obj;
|
||||
objbox->isFunctionBox = false;
|
||||
return objbox;
|
||||
}
|
||||
|
||||
@ -268,6 +270,7 @@ Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
|
||||
traceListHead = funbox;
|
||||
funbox->emitLink = NULL;
|
||||
funbox->object = obj;
|
||||
funbox->isFunctionBox = true;
|
||||
funbox->node = fn;
|
||||
funbox->siblings = tc->functionList;
|
||||
tc->functionList = funbox;
|
||||
@ -275,6 +278,7 @@ Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
|
||||
funbox->kids = NULL;
|
||||
funbox->parent = tc->funbox;
|
||||
funbox->methods = NULL;
|
||||
new (&funbox->bindings) Bindings(context);
|
||||
funbox->queued = false;
|
||||
funbox->inLoop = false;
|
||||
for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
|
||||
@ -315,8 +319,13 @@ Parser::trace(JSTracer *trc)
|
||||
JSObjectBox *objbox = traceListHead;
|
||||
while (objbox) {
|
||||
MarkObject(trc, *objbox->object, "parser.object");
|
||||
if (objbox->isFunctionBox)
|
||||
static_cast<JSFunctionBox *>(objbox)->bindings.trace(trc);
|
||||
objbox = objbox->traceLink;
|
||||
}
|
||||
|
||||
for (JSTreeContext *tc = this->tc; tc; tc = tc->parent)
|
||||
tc->trace(trc);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1314,13 +1323,10 @@ static bool
|
||||
CheckStrictFormals(JSContext *cx, JSTreeContext *tc, JSFunction *fun,
|
||||
JSParseNode *pn)
|
||||
{
|
||||
JSAtom *atom;
|
||||
|
||||
if (!tc->needStrictChecks())
|
||||
return true;
|
||||
|
||||
atom = fun->findDuplicateFormal();
|
||||
if (atom) {
|
||||
if (JSAtom *atom = tc->bindings.findDuplicateArgument()) {
|
||||
/*
|
||||
* We have found a duplicate parameter name. If we can find the
|
||||
* JSDefinition for the argument, that will have a more accurate source
|
||||
@ -1339,7 +1345,9 @@ CheckStrictFormals(JSContext *cx, JSTreeContext *tc, JSFunction *fun,
|
||||
|
||||
if (tc->flags & (TCF_FUN_PARAM_ARGUMENTS | TCF_FUN_PARAM_EVAL)) {
|
||||
JSAtomState *atoms = &cx->runtime->atomState;
|
||||
atom = (tc->flags & TCF_FUN_PARAM_ARGUMENTS) ? atoms->argumentsAtom : atoms->evalAtom;
|
||||
JSAtom *atom = (tc->flags & TCF_FUN_PARAM_ARGUMENTS)
|
||||
? atoms->argumentsAtom
|
||||
: atoms->evalAtom;
|
||||
|
||||
/* The definition's source position will be more precise. */
|
||||
JSDefinition *dn = ALE_DEFN(tc->decls.lookup(atom));
|
||||
@ -1643,7 +1651,7 @@ DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
|
||||
*/
|
||||
bool
|
||||
Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
|
||||
const jschar *chars, size_t length,
|
||||
Bindings *bindings, const jschar *chars, size_t length,
|
||||
const char *filename, uintN lineno)
|
||||
{
|
||||
Compiler compiler(cx, principals);
|
||||
@ -1667,6 +1675,8 @@ Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *prin
|
||||
|
||||
funcg.flags |= TCF_IN_FUNCTION;
|
||||
funcg.setFunction(fun);
|
||||
funcg.bindings.transfer(cx, bindings);
|
||||
fun->setArgCount(funcg.bindings.countArgs());
|
||||
if (!GenerateBlockId(&funcg, funcg.bodyid))
|
||||
return NULL;
|
||||
|
||||
@ -1683,7 +1693,7 @@ Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *prin
|
||||
* NB: do not use AutoLocalNameArray because it will release space
|
||||
* allocated from cx->tempPool by DefineArg.
|
||||
*/
|
||||
jsuword *names = fun->getLocalNameArray(cx, &cx->tempPool);
|
||||
jsuword *names = funcg.bindings.getLocalNameArray(cx, &cx->tempPool);
|
||||
if (!names) {
|
||||
fn = NULL;
|
||||
} else {
|
||||
@ -1762,11 +1772,10 @@ struct BindData {
|
||||
bool fresh;
|
||||
};
|
||||
|
||||
static JSBool
|
||||
BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
|
||||
JSLocalKind localKind, bool isArg)
|
||||
static bool
|
||||
BindLocalVariable(JSContext *cx, JSTreeContext *tc, JSAtom *atom, BindingKind kind, bool isArg)
|
||||
{
|
||||
JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
|
||||
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
|
||||
|
||||
/*
|
||||
* Don't bind a variable with the hidden name 'arguments', per ECMA-262.
|
||||
@ -1778,15 +1787,14 @@ BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
|
||||
* arguments property.
|
||||
*/
|
||||
if (atom == cx->runtime->atomState.argumentsAtom && !isArg)
|
||||
return JS_TRUE;
|
||||
return true;
|
||||
|
||||
return fun->addLocal(cx, atom, localKind);
|
||||
return tc->bindings.add(cx, atom, kind);
|
||||
}
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
static JSBool
|
||||
BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
|
||||
JSTreeContext *tc)
|
||||
BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
|
||||
{
|
||||
/* Flag tc so we don't have to lookup arguments on every use. */
|
||||
if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
|
||||
@ -1796,6 +1804,11 @@ BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
|
||||
|
||||
JS_ASSERT(tc->inFunction());
|
||||
|
||||
/*
|
||||
* NB: Check tc->decls rather than tc->bindings, because destructuring
|
||||
* bindings aren't added to tc->bindings until after all arguments have
|
||||
* been parsed.
|
||||
*/
|
||||
if (tc->decls.lookup(atom)) {
|
||||
ReportCompileErrorNumber(cx, TS(tc->parser), NULL, JSREPORT_ERROR,
|
||||
JSMSG_DESTRUCT_DUP_ARG);
|
||||
@ -2725,6 +2738,8 @@ LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
|
||||
}
|
||||
}
|
||||
|
||||
funbox->bindings.transfer(funtc->parser->context, &funtc->bindings);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2733,13 +2748,12 @@ DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom);
|
||||
|
||||
/*
|
||||
* FIXME? this Parser method was factored from Parser::functionDef with minimal
|
||||
* change, hence the funtc ref param, funbox, and fun. It probably should match
|
||||
* functionBody, etc., and use tc, tc->funbox, and tc->fun() instead of taking
|
||||
* explicit parameters.
|
||||
* change, hence the funtc ref param and funbox. It probably should match
|
||||
* functionBody, etc., and use tc and tc->funbox instead of taking explicit
|
||||
* parameters.
|
||||
*/
|
||||
bool
|
||||
Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun,
|
||||
JSParseNode **listp)
|
||||
Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSParseNode **listp)
|
||||
{
|
||||
if (tokenStream.getToken() != TOK_LP) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_BEFORE_FORMAL);
|
||||
@ -2782,8 +2796,8 @@ Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunctio
|
||||
* Adjust fun->nargs to count the single anonymous positional
|
||||
* parameter that is to be destructured.
|
||||
*/
|
||||
uintN slot = fun->nargs;
|
||||
if (!fun->addLocal(context, NULL, JSLOCAL_ARG))
|
||||
uint16 slot;
|
||||
if (!funtc.bindings.addDestructuring(context, &slot))
|
||||
return false;
|
||||
|
||||
/*
|
||||
@ -2822,14 +2836,20 @@ Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunctio
|
||||
|
||||
#ifdef JS_HAS_DESTRUCTURING
|
||||
/*
|
||||
* ECMA-262 requires us to support duplicate parameter names, but if the
|
||||
* parameter list includes destructuring, we consider the code to have
|
||||
* "opted in" to higher standards, and forbid duplicates. We may see a
|
||||
* destructuring parameter later, so always note duplicates now.
|
||||
* ECMA-262 requires us to support duplicate parameter names,
|
||||
* but if the parameter list includes destructuring, we
|
||||
* consider the code to have "opted in" to higher standards and
|
||||
* forbid duplicates. We may see a destructuring parameter
|
||||
* later, so always note duplicates now.
|
||||
*
|
||||
* Duplicates are warned about (strict option) or cause errors (strict
|
||||
* mode code), but we do those tests in one place below, after having
|
||||
* parsed the body in case it begins with a "use strict"; directive.
|
||||
* Duplicates are warned about (strict option) or cause errors
|
||||
* (strict mode code), but we do those tests in one place
|
||||
* below, after having parsed the body in case it begins with a
|
||||
* "use strict"; directive.
|
||||
*
|
||||
* NB: Check funtc.decls rather than funtc.bindings, because
|
||||
* destructuring bindings aren't added to funtc.bindings
|
||||
* until after all arguments have been parsed.
|
||||
*/
|
||||
if (funtc.decls.lookup(atom)) {
|
||||
duplicatedArg = atom;
|
||||
@ -2838,10 +2858,10 @@ Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunctio
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!DefineArg(funbox->node, atom, fun->nargs, &funtc))
|
||||
uint16 slot;
|
||||
if (!funtc.bindings.addArgument(context, atom, &slot))
|
||||
return false;
|
||||
|
||||
if (!fun->addLocal(context, atom, JSLOCAL_ARG))
|
||||
if (!DefineArg(funbox->node, atom, slot, &funtc))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@ -2971,15 +2991,15 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
|
||||
* already exists.
|
||||
*/
|
||||
uintN index;
|
||||
switch (tc->fun()->lookupLocal(context, funAtom, &index)) {
|
||||
case JSLOCAL_NONE:
|
||||
case JSLOCAL_ARG:
|
||||
index = tc->fun()->u.i.nvars;
|
||||
if (!tc->fun()->addLocal(context, funAtom, JSLOCAL_VAR))
|
||||
switch (tc->bindings.lookup(funAtom, &index)) {
|
||||
case NONE:
|
||||
case ARGUMENT:
|
||||
index = tc->bindings.countVars();
|
||||
if (!tc->bindings.addVariable(context, funAtom))
|
||||
return NULL;
|
||||
/* FALL THROUGH */
|
||||
|
||||
case JSLOCAL_VAR:
|
||||
case VARIABLE:
|
||||
pn->pn_cookie.set(tc->staticLevel, index);
|
||||
pn->pn_dflags |= PND_BOUND;
|
||||
break;
|
||||
@ -3002,17 +3022,19 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
|
||||
|
||||
/* Now parse formal argument list and compute fun->nargs. */
|
||||
JSParseNode *prelude = NULL;
|
||||
if (!functionArguments(funtc, funbox, fun, &prelude))
|
||||
if (!functionArguments(funtc, funbox, &prelude))
|
||||
return NULL;
|
||||
|
||||
fun->setArgCount(funtc.bindings.countArgs());
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
/*
|
||||
* If there were destructuring formal parameters, bind the destructured-to
|
||||
* local variables now that we've parsed all the regular and destructuring
|
||||
* formal parameters. Because JSFunction::addLocal must be called first for
|
||||
* all ARGs, then all VARs, finally all UPVARs, we can't bind vars induced
|
||||
* by formal parameter destructuring until after Parser::functionArguments
|
||||
* has returned.
|
||||
* formal parameters. Because js::Bindings::add must be called first for
|
||||
* all ARGUMENTs, then all VARIABLEs and CONSTANTs, and finally all UPVARs,
|
||||
* we can't bind vars induced by formal parameter destructuring until after
|
||||
* Parser::functionArguments has returned.
|
||||
*/
|
||||
if (prelude) {
|
||||
JSAtomListIterator iter(&funtc.decls);
|
||||
@ -3024,8 +3046,8 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
|
||||
if (apn->pn_op != JSOP_SETLOCAL)
|
||||
continue;
|
||||
|
||||
uintN index = fun->u.i.nvars;
|
||||
if (!BindLocalVariable(context, fun, apn->pn_atom, JSLOCAL_VAR, true))
|
||||
uint16 index = funtc.bindings.countVars();
|
||||
if (!BindLocalVariable(context, &funtc, apn->pn_atom, VARIABLE, true))
|
||||
return NULL;
|
||||
apn->pn_cookie.set(funtc.staticLevel, index);
|
||||
}
|
||||
@ -3802,8 +3824,8 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSLocalKind localKind = tc->fun()->lookupLocal(cx, atom, NULL);
|
||||
if (localKind == JSLOCAL_NONE) {
|
||||
BindingKind kind = tc->bindings.lookup(atom, NULL);
|
||||
if (kind == NONE) {
|
||||
/*
|
||||
* Property not found in current variable scope: we have not seen this
|
||||
* variable before. Define a new local variable by adding a property to
|
||||
@ -3812,10 +3834,10 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
|
||||
* runtime, by script prolog JSOP_DEFVAR opcodes generated for global
|
||||
* and heavyweight-function-local vars.
|
||||
*/
|
||||
localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
|
||||
kind = (data->op == JSOP_DEFCONST) ? CONSTANT : VARIABLE;
|
||||
|
||||
uintN index = tc->fun()->u.i.nvars;
|
||||
if (!BindLocalVariable(cx, tc->fun(), atom, localKind, false))
|
||||
uintN index = tc->bindings.countVars();
|
||||
if (!BindLocalVariable(cx, tc, atom, kind, false))
|
||||
return JS_FALSE;
|
||||
pn->pn_op = JSOP_GETLOCAL;
|
||||
pn->pn_cookie.set(tc->staticLevel, index);
|
||||
@ -3823,12 +3845,12 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
if (localKind == JSLOCAL_ARG) {
|
||||
if (kind == ARGUMENT) {
|
||||
/* We checked errors and strict warnings earlier -- see above. */
|
||||
JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
|
||||
} else {
|
||||
/* Not an argument, must be a redeclared local var. */
|
||||
JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
|
||||
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
@ -932,8 +932,9 @@ struct JSObjectBox {
|
||||
JSObjectBox *traceLink;
|
||||
JSObjectBox *emitLink;
|
||||
JSObject *object;
|
||||
uintN index;
|
||||
JSObjectBox *parent;
|
||||
uintN index;
|
||||
bool isFunctionBox;
|
||||
};
|
||||
|
||||
#define JSFB_LEVEL_BITS 14
|
||||
@ -949,6 +950,7 @@ struct JSFunctionBox : public JSObjectBox
|
||||
pn_link, since lambdas are
|
||||
neither definitions nor uses
|
||||
of a binding */
|
||||
js::Bindings bindings; /* bindings for this function */
|
||||
uint32 queued:1,
|
||||
inLoop:1, /* in a loop in parent function */
|
||||
level:JSFB_LEVEL_BITS;
|
||||
@ -1138,8 +1140,7 @@ private:
|
||||
bool recognizeDirectivePrologue(JSParseNode *pn, bool *isDirectivePrologueMember);
|
||||
|
||||
enum FunctionType { GETTER, SETTER, GENERAL };
|
||||
bool functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun,
|
||||
JSParseNode **list);
|
||||
bool functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSParseNode **list);
|
||||
JSParseNode *functionBody();
|
||||
JSParseNode *functionDef(JSAtom *name, FunctionType type, uintN lambda);
|
||||
|
||||
@ -1199,7 +1200,7 @@ struct Compiler
|
||||
|
||||
static bool
|
||||
compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
|
||||
const jschar *chars, size_t length,
|
||||
js::Bindings *bindings, const jschar *chars, size_t length,
|
||||
const char *filename, uintN lineno);
|
||||
|
||||
static JSScript *
|
||||
|
@ -65,6 +65,8 @@
|
||||
#include "jsarray.h"
|
||||
#include "jsnum.h"
|
||||
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
namespace js {
|
||||
|
@ -72,6 +72,8 @@
|
||||
#include "jsstaticcheck.h"
|
||||
#include "jsvector.h"
|
||||
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
#include "jsxml.h"
|
||||
#endif
|
||||
|
@ -295,6 +295,7 @@ struct Shape : public JSObjectMap
|
||||
friend struct ::JSObject;
|
||||
friend struct ::JSFunction;
|
||||
friend class js::PropertyTree;
|
||||
friend class js::Bindings;
|
||||
friend bool HasUnreachableGCThings(TreeFragment *f);
|
||||
|
||||
protected:
|
||||
@ -494,7 +495,7 @@ struct Shape : public JSObjectMap
|
||||
/* Property stored in per-object dictionary, not shared property tree. */
|
||||
IN_DICTIONARY = 0x08,
|
||||
|
||||
/* Prevent unwanted mutation of shared JSFunction::u.i.names nodes. */
|
||||
/* Prevent unwanted mutation of shared Bindings::lastBinding nodes. */
|
||||
FROZEN = 0x10
|
||||
};
|
||||
|
||||
@ -879,7 +880,7 @@ Shape::isSharedPermanent() const
|
||||
return (~attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace js
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
|
@ -73,6 +73,252 @@
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
namespace js {
|
||||
|
||||
BindingKind
|
||||
Bindings::lookup(JSAtom *name, uintN *indexp) const
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
|
||||
Shape *shape =
|
||||
SHAPE_FETCH(Shape::search(const_cast<Shape **>(&lastBinding), ATOM_TO_JSID(name)));
|
||||
if (!shape)
|
||||
return NONE;
|
||||
|
||||
if (indexp)
|
||||
*indexp = shape->shortid;
|
||||
|
||||
if (shape->getter() == GetCallArg)
|
||||
return ARGUMENT;
|
||||
if (shape->getter() == GetFlatUpvar)
|
||||
return UPVAR;
|
||||
|
||||
return shape->writable() ? VARIABLE : CONSTANT;
|
||||
}
|
||||
|
||||
bool
|
||||
Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
|
||||
/*
|
||||
* We still follow 10.2.3 of ES3 and make argument and variable properties
|
||||
* of the Call objects enumerable. ES5 reformulated all of its Clause 10 to
|
||||
* avoid objects as activations, something we should do too.
|
||||
*/
|
||||
uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED;
|
||||
|
||||
uint16 *indexp;
|
||||
PropertyOp getter, setter;
|
||||
uint32 slot = JSObject::CALL_RESERVED_SLOTS;
|
||||
|
||||
if (kind == ARGUMENT) {
|
||||
JS_ASSERT(nvars == 0);
|
||||
JS_ASSERT(nupvars == 0);
|
||||
indexp = &nargs;
|
||||
getter = GetCallArg;
|
||||
setter = SetCallArg;
|
||||
slot += nargs;
|
||||
} else if (kind == UPVAR) {
|
||||
indexp = &nupvars;
|
||||
getter = GetFlatUpvar;
|
||||
setter = SetFlatUpvar;
|
||||
slot = SHAPE_INVALID_SLOT;
|
||||
} else {
|
||||
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
|
||||
JS_ASSERT(nupvars == 0);
|
||||
|
||||
indexp = &nvars;
|
||||
getter = GetCallVar;
|
||||
setter = SetCallVar;
|
||||
if (kind == CONSTANT)
|
||||
attrs |= JSPROP_READONLY;
|
||||
slot += nargs + nvars;
|
||||
}
|
||||
|
||||
if (*indexp == JS_BITMASK(16)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
(kind == ARGUMENT)
|
||||
? JSMSG_TOO_MANY_FUN_ARGS
|
||||
: JSMSG_TOO_MANY_LOCALS);
|
||||
return false;
|
||||
}
|
||||
|
||||
jsid id;
|
||||
if (!name) {
|
||||
JS_ASSERT(kind == ARGUMENT); /* destructuring */
|
||||
id = INT_TO_JSID(nargs);
|
||||
} else {
|
||||
id = ATOM_TO_JSID(name);
|
||||
}
|
||||
|
||||
Shape child(id, getter, setter, slot, attrs, Shape::HAS_SHORTID, *indexp);
|
||||
|
||||
Shape *shape = lastBinding->getChild(cx, child, &lastBinding);
|
||||
if (!shape)
|
||||
return false;
|
||||
|
||||
JS_ASSERT(lastBinding == shape);
|
||||
++*indexp;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This algorithm is O(n^2)! But this method is only called if the function is
|
||||
* strict mode code or if JSOPTION_STRICT is set, so for now we'll tolerate the
|
||||
* quadratic blowup.
|
||||
*/
|
||||
JSAtom *
|
||||
Bindings::findDuplicateArgument() const
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
|
||||
if (nargs <= 1)
|
||||
return NULL;
|
||||
|
||||
for (Shape::Range r = lastArgument(); !r.empty(); r.popFront()) {
|
||||
const Shape &shape = r.front();
|
||||
for (Shape::Range r2 = shape.previous(); !r2.empty(); r2.popFront()) {
|
||||
if (r2.front().id == shape.id)
|
||||
return JSID_TO_ATOM(shape.id);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
jsuword *
|
||||
Bindings::getLocalNameArray(JSContext *cx, JSArenaPool *pool)
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
|
||||
JS_ASSERT(hasLocalNames());
|
||||
|
||||
uintN n = countLocalNames();
|
||||
jsuword *names;
|
||||
|
||||
JS_ASSERT(SIZE_MAX / size_t(n) > sizeof *names);
|
||||
JS_ARENA_ALLOCATE_CAST(names, jsuword *, pool, size_t(n) * sizeof *names);
|
||||
if (!names) {
|
||||
js_ReportOutOfScriptQuota(cx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for (uintN i = 0; i != n; i++)
|
||||
names[i] = 0xdeadbeef;
|
||||
#endif
|
||||
|
||||
for (Shape::Range r = lastBinding; !r.empty(); r.popFront()) {
|
||||
const Shape &shape = r.front();
|
||||
uintN index = uint16(shape.shortid);
|
||||
jsuword constFlag = 0;
|
||||
|
||||
if (shape.getter() == GetCallArg) {
|
||||
JS_ASSERT(index < nargs);
|
||||
} else if (shape.getter() == GetFlatUpvar) {
|
||||
JS_ASSERT(index < nupvars);
|
||||
index += nargs + nvars;
|
||||
} else {
|
||||
JS_ASSERT(index < nvars);
|
||||
index += nargs;
|
||||
if (!shape.writable())
|
||||
constFlag = 1;
|
||||
}
|
||||
|
||||
JSAtom *atom;
|
||||
if (JSID_IS_ATOM(shape.id)) {
|
||||
atom = JSID_TO_ATOM(shape.id);
|
||||
} else {
|
||||
JS_ASSERT(JSID_IS_INT(shape.id));
|
||||
JS_ASSERT(shape.getter() == GetCallArg);
|
||||
atom = NULL;
|
||||
}
|
||||
|
||||
names[index] = jsuword(atom);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for (uintN i = 0; i != n; i++)
|
||||
JS_ASSERT(names[i] != 0xdeadbeef);
|
||||
#endif
|
||||
return names;
|
||||
}
|
||||
|
||||
const Shape *
|
||||
Bindings::lastArgument() const
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
|
||||
const js::Shape *shape = lastVariable();
|
||||
if (nvars > 0) {
|
||||
while (shape->previous() && shape->getter() != GetCallArg)
|
||||
shape = shape->previous();
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
const Shape *
|
||||
Bindings::lastVariable() const
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
|
||||
const js::Shape *shape = lastUpvar();
|
||||
if (nupvars > 0) {
|
||||
while (shape->getter() == GetFlatUpvar)
|
||||
shape = shape->previous();
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
const Shape *
|
||||
Bindings::lastUpvar() const
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
return lastBinding;
|
||||
}
|
||||
|
||||
int
|
||||
Bindings::sharpSlotBase(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
#if JS_HAS_SHARP_VARS
|
||||
if (JSAtom *name = js_Atomize(cx, "#array", 6, 0)) {
|
||||
uintN index = uintN(-1);
|
||||
#ifdef DEBUG
|
||||
BindingKind kind =
|
||||
#endif
|
||||
lookup(name, &index);
|
||||
JS_ASSERT(kind == VARIABLE);
|
||||
return int(index);
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
Bindings::makeImmutable()
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
Shape *shape = lastBinding;
|
||||
if (shape->inDictionary()) {
|
||||
do {
|
||||
JS_ASSERT(!shape->frozen());
|
||||
shape->setFrozen();
|
||||
} while ((shape = shape->parent) != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Bindings::trace(JSTracer *trc)
|
||||
{
|
||||
for (const Shape *shape = lastBinding; shape; shape = shape->previous())
|
||||
shape->trace(trc);
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
#if JS_HAS_XDR
|
||||
|
||||
enum ScriptBits {
|
||||
@ -87,30 +333,30 @@ enum ScriptBits {
|
||||
JSBool
|
||||
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
{
|
||||
JSContext *cx;
|
||||
JSScript *script, *oldscript;
|
||||
JSScript *oldscript;
|
||||
JSBool ok;
|
||||
jsbytecode *code;
|
||||
uint32 length, lineno, nslots, magic;
|
||||
uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, nconsts, i;
|
||||
uint32 length, lineno, nslots;
|
||||
uint32 natoms, nsrcnotes, ntrynotes, nobjects, nregexps, nconsts, i;
|
||||
uint32 prologLength, version, encodedClosedCount;
|
||||
uint16 nClosedArgs = 0, nClosedVars = 0;
|
||||
JSPrincipals *principals;
|
||||
uint32 encodeable;
|
||||
JSBool filenameWasSaved;
|
||||
jssrcnote *notes, *sn;
|
||||
jssrcnote *sn;
|
||||
JSSecurityCallbacks *callbacks;
|
||||
uint32 scriptBits = 0;
|
||||
|
||||
cx = xdr->cx;
|
||||
script = *scriptp;
|
||||
nsrcnotes = ntrynotes = natoms = nobjects = nupvars = nregexps = nconsts = 0;
|
||||
JSContext *cx = xdr->cx;
|
||||
JSScript *script = *scriptp;
|
||||
nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = 0;
|
||||
filenameWasSaved = JS_FALSE;
|
||||
notes = NULL;
|
||||
jssrcnote *notes = NULL;
|
||||
|
||||
/* Should not XDR scripts optimized for a single global object. */
|
||||
JS_ASSERT_IF(script, !JSScript::isValidOffset(script->globalsOffset));
|
||||
|
||||
uint32 magic;
|
||||
if (xdr->mode == JSXDR_ENCODE)
|
||||
magic = JSXDR_MAGIC_SCRIPT_CURRENT;
|
||||
if (!JS_XDRUint32(xdr, &magic))
|
||||
@ -128,6 +374,116 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
if (hasMagic)
|
||||
*hasMagic = JS_TRUE;
|
||||
|
||||
/* XDR arguments, local vars, and upvars. */
|
||||
uint16 nargs, nvars, nupvars;
|
||||
uint32 argsVars, paddingUpvars;
|
||||
if (xdr->mode == JSXDR_ENCODE) {
|
||||
nargs = script->bindings.countArgs();
|
||||
nvars = script->bindings.countVars();
|
||||
nupvars = script->bindings.countUpvars();
|
||||
argsVars = (nargs << 16) | nvars;
|
||||
paddingUpvars = nupvars;
|
||||
}
|
||||
if (!JS_XDRUint32(xdr, &argsVars) || !JS_XDRUint32(xdr, &paddingUpvars))
|
||||
return false;
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
nargs = argsVars >> 16;
|
||||
nvars = argsVars & 0xFFFF;
|
||||
JS_ASSERT((paddingUpvars >> 16) == 0);
|
||||
nupvars = paddingUpvars & 0xFFFF;
|
||||
}
|
||||
|
||||
Bindings bindings(cx);
|
||||
uint32 nameCount = nargs + nvars + nupvars;
|
||||
if (nameCount > 0) {
|
||||
struct AutoMark {
|
||||
JSArenaPool * const pool;
|
||||
void * const mark;
|
||||
AutoMark(JSArenaPool *pool) : pool(pool), mark(JS_ARENA_MARK(pool)) { }
|
||||
~AutoMark() {
|
||||
JS_ARENA_RELEASE(pool, mark);
|
||||
}
|
||||
} automark(&cx->tempPool);
|
||||
|
||||
/*
|
||||
* To xdr the names we prefix the names with a bitmap descriptor and
|
||||
* then xdr the names as strings. For argument names (indexes below
|
||||
* nargs) the corresponding bit in the bitmap is unset when the name
|
||||
* is null. Such null names are not encoded or decoded. For variable
|
||||
* names (indexes starting from nargs) bitmap's bit is set when the
|
||||
* name is declared as const, not as ordinary var.
|
||||
* */
|
||||
uintN bitmapLength = JS_HOWMANY(nameCount, JS_BITS_PER_UINT32);
|
||||
uint32 *bitmap;
|
||||
JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &cx->tempPool,
|
||||
bitmapLength * sizeof *bitmap);
|
||||
if (!bitmap) {
|
||||
js_ReportOutOfScriptQuota(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
jsuword *names;
|
||||
if (xdr->mode == JSXDR_ENCODE) {
|
||||
names = script->bindings.getLocalNameArray(cx, &cx->tempPool);
|
||||
if (!names)
|
||||
return false;
|
||||
PodZero(bitmap, bitmapLength);
|
||||
for (uintN i = 0; i < nameCount; i++) {
|
||||
if (i < nargs
|
||||
? JS_LOCAL_NAME_TO_ATOM(names[i]) != NULL
|
||||
: JS_LOCAL_NAME_IS_CONST(names[i]))
|
||||
{
|
||||
bitmap[i >> JS_BITS_PER_UINT32_LOG2] |= JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
else {
|
||||
names = NULL; /* quell GCC uninitialized warning */
|
||||
}
|
||||
#endif
|
||||
for (uintN i = 0; i < bitmapLength; ++i) {
|
||||
if (!JS_XDRUint32(xdr, &bitmap[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uintN i = 0; i < nameCount; i++) {
|
||||
if (i < nargs &&
|
||||
!(bitmap[i >> JS_BITS_PER_UINT32_LOG2] & JS_BIT(i & (JS_BITS_PER_UINT32 - 1))))
|
||||
{
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
uint16 dummy;
|
||||
if (!bindings.addDestructuring(cx, &dummy))
|
||||
return false;
|
||||
} else {
|
||||
JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names[i]));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
JSAtom *name;
|
||||
if (xdr->mode == JSXDR_ENCODE)
|
||||
name = JS_LOCAL_NAME_TO_ATOM(names[i]);
|
||||
if (!js_XDRAtom(xdr, &name))
|
||||
return false;
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
BindingKind kind = (i < nargs)
|
||||
? ARGUMENT
|
||||
: (i < nargs + nvars)
|
||||
? (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
|
||||
JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
|
||||
? CONSTANT
|
||||
: VARIABLE)
|
||||
: UPVAR;
|
||||
if (!bindings.add(cx, name, kind))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (xdr->mode == JSXDR_DECODE)
|
||||
bindings.makeImmutable();
|
||||
}
|
||||
|
||||
if (xdr->mode == JSXDR_ENCODE)
|
||||
length = script->length;
|
||||
if (!JS_XDRUint32(xdr, &length))
|
||||
@ -152,7 +508,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
if (JSScript::isValidOffset(script->objectsOffset))
|
||||
nobjects = script->objects()->length;
|
||||
if (JSScript::isValidOffset(script->upvarsOffset))
|
||||
nupvars = script->upvars()->length;
|
||||
JS_ASSERT(script->bindings.countUpvars() == script->upvars()->length);
|
||||
if (JSScript::isValidOffset(script->regexpsOffset))
|
||||
nregexps = script->regexps()->length;
|
||||
if (JSScript::isValidOffset(script->trynotesOffset))
|
||||
@ -186,8 +542,8 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
return JS_FALSE;
|
||||
|
||||
/*
|
||||
* To fuse allocations, we need srcnote, atom, objects, upvar, regexp,
|
||||
* and trynote counts early.
|
||||
* To fuse allocations, we need srcnote, atom, objects, regexp, and trynote
|
||||
* counts early.
|
||||
*/
|
||||
if (!JS_XDRUint32(xdr, &natoms))
|
||||
return JS_FALSE;
|
||||
@ -197,8 +553,6 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
return JS_FALSE;
|
||||
if (!JS_XDRUint32(xdr, &nobjects))
|
||||
return JS_FALSE;
|
||||
if (!JS_XDRUint32(xdr, &nupvars))
|
||||
return JS_FALSE;
|
||||
if (!JS_XDRUint32(xdr, &nregexps))
|
||||
return JS_FALSE;
|
||||
if (!JS_XDRUint32(xdr, &nconsts))
|
||||
@ -220,6 +574,8 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
if (!script)
|
||||
return JS_FALSE;
|
||||
|
||||
script->bindings.transfer(cx, &bindings);
|
||||
|
||||
script->main += prologLength;
|
||||
script->setVersion(JSVersion(version & 0xffff));
|
||||
script->nfixed = uint16(version >> 16);
|
||||
@ -894,10 +1250,11 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
|
||||
PodZero(script);
|
||||
script->length = length;
|
||||
script->setVersion(cx->findVersion());
|
||||
new (&script->bindings) Bindings(cx);
|
||||
|
||||
uint8 *scriptEnd = reinterpret_cast<uint8 *>(script + 1);
|
||||
|
||||
cursor = (uint8 *)script + sizeof(JSScript);
|
||||
cursor = scriptEnd;
|
||||
if (nobjects != 0) {
|
||||
script->objectsOffset = (uint8)(cursor - scriptEnd);
|
||||
cursor += sizeof(JSObjectArray);
|
||||
@ -928,7 +1285,7 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
|
||||
} else {
|
||||
script->globalsOffset = JSScript::INVALID_OFFSET;
|
||||
}
|
||||
JS_ASSERT((cursor - (uint8 *)script) < 0xFF);
|
||||
JS_ASSERT(cursor - scriptEnd < 0xFF);
|
||||
if (nconsts != 0) {
|
||||
script->constOffset = (uint8)(cursor - scriptEnd);
|
||||
cursor += sizeof(JSConstArray);
|
||||
@ -1067,7 +1424,7 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
||||
memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
|
||||
memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
|
||||
nfixed = cg->inFunction()
|
||||
? cg->fun()->u.i.nvars
|
||||
? cg->bindings.countVars()
|
||||
: cg->sharpSlots();
|
||||
JS_ASSERT(nfixed < SLOTNO_LIMIT);
|
||||
script->nfixed = (uint16) nfixed;
|
||||
@ -1136,6 +1493,9 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
||||
script->nClosedVars * sizeof(uint32));
|
||||
}
|
||||
|
||||
cg->bindings.makeImmutable();
|
||||
script->bindings.transfer(cx, &cg->bindings);
|
||||
|
||||
/*
|
||||
* We initialize fun->u.script to be the script constructed above
|
||||
* so that the debugger has a valid FUN_SCRIPT(fun).
|
||||
@ -1143,13 +1503,14 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
||||
fun = NULL;
|
||||
if (cg->inFunction()) {
|
||||
fun = cg->fun();
|
||||
JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
|
||||
JS_ASSERT(fun->isInterpreted());
|
||||
JS_ASSERT(!fun->script());
|
||||
#ifdef DEBUG
|
||||
if (JSScript::isValidOffset(script->upvarsOffset))
|
||||
JS_ASSERT(script->upvars()->length == fun->u.i.nupvars);
|
||||
JS_ASSERT(script->upvars()->length == script->bindings.countUpvars());
|
||||
else
|
||||
fun->u.i.nupvars = 0;
|
||||
|
||||
fun->freezeLocalNames(cx);
|
||||
JS_ASSERT(script->bindings.countUpvars() == 0);
|
||||
#endif
|
||||
fun->u.i.script = script;
|
||||
#ifdef CHECK_SCRIPT_OWNER
|
||||
script->owner = NULL;
|
||||
@ -1343,6 +1704,8 @@ js_TraceScript(JSTracer *trc, JSScript *script)
|
||||
|
||||
if (IS_GC_MARKING_TRACER(trc) && script->filename)
|
||||
js_MarkScriptFilename(script->filename);
|
||||
|
||||
script->bindings.trace(trc);
|
||||
}
|
||||
|
||||
JSBool
|
||||
|
@ -145,6 +145,8 @@ typedef struct JSConstArray {
|
||||
uint32 length;
|
||||
} JSConstArray;
|
||||
|
||||
struct JSArenaPool;
|
||||
|
||||
namespace js {
|
||||
|
||||
struct GlobalSlotArray {
|
||||
@ -156,6 +158,162 @@ struct GlobalSlotArray {
|
||||
uint32 length;
|
||||
};
|
||||
|
||||
class Shape;
|
||||
|
||||
enum BindingKind { NONE, ARGUMENT, VARIABLE, CONSTANT, UPVAR };
|
||||
|
||||
/*
|
||||
* Formal parameters, local variables, and upvars are stored in a shape tree
|
||||
* path encapsulated within this class. This class represents bindings for
|
||||
* both function and top-level scripts (the latter is needed to track names in
|
||||
* strict mode eval code, to give such code its own lexical environment).
|
||||
*/
|
||||
class Bindings {
|
||||
js::Shape *lastBinding;
|
||||
uint16 nargs;
|
||||
uint16 nvars;
|
||||
uint16 nupvars;
|
||||
|
||||
public:
|
||||
inline Bindings(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Transfers ownership of bindings data from bindings into this fresh
|
||||
* Bindings instance. Once such a transfer occurs, the old bindings must
|
||||
* not be used again.
|
||||
*/
|
||||
inline void transfer(JSContext *cx, Bindings *bindings);
|
||||
|
||||
/*
|
||||
* Clones bindings data from bindings, which must be immutable, into this
|
||||
* fresh Bindings instance. A Bindings instance may be cloned multiple
|
||||
* times.
|
||||
*/
|
||||
inline void clone(JSContext *cx, Bindings *bindings);
|
||||
|
||||
uint16 countArgs() const { return nargs; }
|
||||
uint16 countVars() const { return nvars; }
|
||||
uint16 countUpvars() const { return nupvars; }
|
||||
|
||||
uintN countArgsAndVars() const { return nargs + nvars; }
|
||||
|
||||
uintN countLocalNames() const { return nargs + nvars + nupvars; }
|
||||
|
||||
bool hasUpvars() const { return nupvars > 0; }
|
||||
bool hasLocalNames() const { return countLocalNames() > 0; }
|
||||
|
||||
/* Returns the shape lineage generated for these bindings. */
|
||||
inline const js::Shape *lastShape() const;
|
||||
|
||||
/*
|
||||
* Add a local binding for the given name, of the given type, for the code
|
||||
* being compiled. If fun is non-null, this binding set is being created
|
||||
* for that function, so adjust corresponding metadata in that function
|
||||
* while adding. Otherwise this set must correspond to a top-level script.
|
||||
*
|
||||
* A binding may be added twice with different kinds; the last one for a
|
||||
* given name prevails. (We preserve both bindings for the decompiler,
|
||||
* which must deal with such cases.) Pass null for name when indicating a
|
||||
* destructuring argument. Return true on success.
|
||||
*
|
||||
*
|
||||
* The parser builds shape paths for functions, usable by Call objects at
|
||||
* runtime, by calling addLocal. All ARGUMENT bindings must be added before
|
||||
* before any VARIABLE or CONSTANT bindings, which themselves must be added
|
||||
* before all UPVAR bindings.
|
||||
*/
|
||||
bool add(JSContext *cx, JSAtom *name, BindingKind kind);
|
||||
|
||||
/* Convenience specializations. */
|
||||
bool addVariable(JSContext *cx, JSAtom *name) {
|
||||
return add(cx, name, VARIABLE);
|
||||
}
|
||||
bool addConstant(JSContext *cx, JSAtom *name) {
|
||||
return add(cx, name, CONSTANT);
|
||||
}
|
||||
bool addUpvar(JSContext *cx, JSAtom *name) {
|
||||
return add(cx, name, UPVAR);
|
||||
}
|
||||
bool addArgument(JSContext *cx, JSAtom *name, uint16 *slotp) {
|
||||
JS_ASSERT(name != NULL); /* not destructuring */
|
||||
*slotp = nargs;
|
||||
return add(cx, name, ARGUMENT);
|
||||
}
|
||||
bool addDestructuring(JSContext *cx, uint16 *slotp) {
|
||||
*slotp = nargs;
|
||||
return add(cx, NULL, ARGUMENT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up an argument or variable name, returning its kind when found or
|
||||
* 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.
|
||||
*/
|
||||
BindingKind lookup(JSAtom *name, uintN *indexp) const;
|
||||
|
||||
/* Convenience method to check for any binding for a name. */
|
||||
bool hasBinding(JSAtom *name) const {
|
||||
return lookup(name, NULL) != NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this binding set for the given function includes duplicated argument
|
||||
* names, return an arbitrary duplicate name. Otherwise, return NULL.
|
||||
*/
|
||||
JSAtom *findDuplicateArgument() const;
|
||||
|
||||
/*
|
||||
* Function and macros to work with local names as an array of words.
|
||||
* getLocalNameArray returns the array, or null if we are out of memory.
|
||||
* This function must be called only when hasLocalNames().
|
||||
*
|
||||
* The supplied pool is used to allocate the returned array, so the caller
|
||||
* is obligated to mark and release to free it.
|
||||
*
|
||||
* The elements of the array with index less than nargs correspond to the
|
||||
* the names of arguments. An index >= nargs addresses a var binding. Use
|
||||
* JS_LOCAL_NAME_TO_ATOM to convert array's element to an atom pointer.
|
||||
* This pointer can be null when the element is for an argument
|
||||
* corresponding to a destructuring pattern.
|
||||
*
|
||||
* If nameWord does not name an argument, use JS_LOCAL_NAME_IS_CONST to
|
||||
* check if nameWord corresponds to the const declaration.
|
||||
*/
|
||||
jsuword *
|
||||
getLocalNameArray(JSContext *cx, JSArenaPool *pool);
|
||||
|
||||
/*
|
||||
* Returns the slot where the sharp array is stored, or a value < 0 if no
|
||||
* sharps are present or in case of failure.
|
||||
*/
|
||||
int sharpSlotBase(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Protect stored bindings from mutation. Subsequent attempts to add
|
||||
* bindings will copy the existing bindings before adding to them, allowing
|
||||
* the original bindings to be safely shared.
|
||||
*/
|
||||
void makeImmutable();
|
||||
|
||||
/*
|
||||
* These methods provide direct access to the shape path normally
|
||||
* encapsulated by js::Bindings. These methods may be used to make a
|
||||
* Shape::Range for iterating over the relevant shapes from youngest to
|
||||
* oldest (i.e., last or right-most to first or left-most in source order).
|
||||
*
|
||||
* Sometimes iteration order must be from oldest to youngest, however. For
|
||||
* such cases, use js::Bindings::getLocalNameArray. The RAII class
|
||||
* js::AutoLocalNameArray, defined in jscntxt.h, should be used where
|
||||
* possible instead of direct calls to getLocalNameArray.
|
||||
*/
|
||||
const js::Shape *lastArgument() const;
|
||||
const js::Shape *lastVariable() const;
|
||||
const js::Shape *lastUpvar() const;
|
||||
|
||||
void trace(JSTracer *trc);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#define JS_OBJECT_ARRAY_SIZE(length) \
|
||||
@ -255,6 +413,8 @@ struct JSScript {
|
||||
uint16 staticLevel;/* static level for display maintenance */
|
||||
uint16 nClosedArgs; /* number of args which are closed over. */
|
||||
uint16 nClosedVars; /* number of vars which are closed over. */
|
||||
js::Bindings bindings; /* names of top-level variables in this script
|
||||
(and arguments if this is a function script) */
|
||||
JSPrincipals *principals;/* principals for this script */
|
||||
union {
|
||||
/*
|
||||
|
@ -41,10 +41,59 @@
|
||||
#ifndef jsscriptinlines_h___
|
||||
#define jsscriptinlines_h___
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jsfun.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsregexp.h"
|
||||
#include "jsscript.h"
|
||||
#include "jsscope.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
inline
|
||||
Bindings::Bindings(JSContext *cx)
|
||||
: lastBinding(cx->runtime->emptyCallShape), nargs(0), nvars(0), nupvars(0)
|
||||
{
|
||||
}
|
||||
|
||||
inline void
|
||||
Bindings::transfer(JSContext *cx, Bindings *bindings)
|
||||
{
|
||||
JS_ASSERT(lastBinding == cx->runtime->emptyCallShape);
|
||||
|
||||
*this = *bindings;
|
||||
#ifdef DEBUG
|
||||
bindings->lastBinding = NULL;
|
||||
#endif
|
||||
|
||||
/* Preserve back-pointer invariants across the lastBinding transfer. */
|
||||
if (lastBinding->inDictionary())
|
||||
lastBinding->listp = &this->lastBinding;
|
||||
}
|
||||
|
||||
inline void
|
||||
Bindings::clone(JSContext *cx, Bindings *bindings)
|
||||
{
|
||||
JS_ASSERT(lastBinding == cx->runtime->emptyCallShape);
|
||||
|
||||
/*
|
||||
* Non-dictionary bindings are fine to share, as are dictionary bindings if
|
||||
* they're copy-on-modification.
|
||||
*/
|
||||
JS_ASSERT(!bindings->lastBinding->inDictionary() || bindings->lastBinding->frozen());
|
||||
|
||||
*this = *bindings;
|
||||
}
|
||||
|
||||
const Shape *
|
||||
Bindings::lastShape() const
|
||||
{
|
||||
JS_ASSERT(lastBinding);
|
||||
JS_ASSERT_IF(lastBinding->inDictionary(), lastBinding->frozen());
|
||||
return lastBinding;
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
inline JSFunction *
|
||||
JSScript::getFunction(size_t index)
|
||||
|
@ -3377,7 +3377,7 @@ struct VarClosureTraits
|
||||
{
|
||||
// See also UpvarVarTraits.
|
||||
static inline Value get_slot(JSStackFrame* fp, unsigned slot) {
|
||||
JS_ASSERT(slot < fp->fun()->u.i.nvars);
|
||||
JS_ASSERT(slot < fp->fun()->script()->bindings.countVars());
|
||||
return fp->slots()[slot];
|
||||
}
|
||||
|
||||
@ -3391,7 +3391,7 @@ struct VarClosureTraits
|
||||
}
|
||||
|
||||
static inline uint16 slot_count(JSObject* obj) {
|
||||
return obj->getCallObjCalleeFunction()->u.i.nvars;
|
||||
return obj->getCallObjCalleeFunction()->script()->bindings.countVars();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -3472,11 +3472,13 @@ TraceRecorder::importImpl(Address addr, const void* p, JSValueType t,
|
||||
JSAutoByteString funNameBytes;
|
||||
if (*prefix == 'a' || *prefix == 'v') {
|
||||
mark = JS_ARENA_MARK(&cx->tempPool);
|
||||
if (fp->fun()->hasLocalNames())
|
||||
localNames = fp->fun()->getLocalNameArray(cx, &cx->tempPool);
|
||||
funName = fp->fun()->atom
|
||||
? js_AtomToPrintableString(cx, fp->fun()->atom, &funNameBytes)
|
||||
: "<anonymous>";
|
||||
JSFunction *fun = fp->fun();
|
||||
Bindings &bindings = fun->script()->bindings;
|
||||
if (bindings.hasLocalNames())
|
||||
localNames = bindings.getLocalNameArray(cx, &cx->tempPool);
|
||||
funName = fun->atom
|
||||
? js_AtomToPrintableString(cx, fun->atom, &funNameBytes)
|
||||
: "<anonymous>";
|
||||
}
|
||||
if (!strcmp(prefix, "argv")) {
|
||||
if (index < fp->numFormalArgs()) {
|
||||
@ -10224,7 +10226,7 @@ TraceRecorder::putActivationObjects()
|
||||
}
|
||||
|
||||
if (have_call) {
|
||||
int nslots = fp->fun()->countVars();
|
||||
int nslots = fp->fun()->script()->bindings.countVars();
|
||||
LIns* slots_ins;
|
||||
if (nslots) {
|
||||
slots_ins = w.allocp(sizeof(Value) * nslots);
|
||||
@ -13328,8 +13330,8 @@ TraceRecorder::guardCallee(Value& callee)
|
||||
* we wish to optimize for the particular deactivated stack frame (null
|
||||
* private data) case as noted above.
|
||||
*/
|
||||
if (FUN_INTERPRETED(callee_fun) &&
|
||||
(!FUN_NULL_CLOSURE(callee_fun) || callee_fun->u.i.nupvars != 0)) {
|
||||
if (callee_fun->isInterpreted() &&
|
||||
(!FUN_NULL_CLOSURE(callee_fun) || callee_fun->script()->bindings.hasUpvars())) {
|
||||
JSObject* parent = callee_obj.getParent();
|
||||
|
||||
if (parent != globalObj) {
|
||||
@ -15270,14 +15272,15 @@ TraceRecorder::record_JSOP_LAMBDA_FC()
|
||||
w.name(w.eqp(closure_ins, w.immpNull()), "guard(js_AllocFlatClosure)"),
|
||||
OOM_EXIT);
|
||||
|
||||
if (fun->u.i.nupvars) {
|
||||
JSUpvarArray *uva = fun->u.i.script->upvars();
|
||||
JSScript *script = fun->script();
|
||||
if (script->bindings.hasUpvars()) {
|
||||
JSUpvarArray *uva = script->upvars();
|
||||
LIns* upvars_ins = w.getObjPrivatizedSlot(closure_ins,
|
||||
JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
|
||||
|
||||
for (uint32 i = 0, n = uva->length; i < n; i++) {
|
||||
Value v;
|
||||
LIns* v_ins = upvar(fun->u.i.script, uva, i, v);
|
||||
LIns* v_ins = upvar(script, uva, i, v);
|
||||
if (!v_ins)
|
||||
return ARECORD_STOP;
|
||||
|
||||
|
@ -194,7 +194,8 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
|
||||
#define JSXDR_MAGIC_SCRIPT_8 0xdead0008
|
||||
#define JSXDR_MAGIC_SCRIPT_9 0xdead0009
|
||||
#define JSXDR_MAGIC_SCRIPT_10 0xdead000a
|
||||
#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_10
|
||||
#define JSXDR_MAGIC_SCRIPT_11 0xdead000b
|
||||
#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_11
|
||||
|
||||
/*
|
||||
* Bytecode version number. Increment the subtrahend whenever JS bytecode
|
||||
@ -205,7 +206,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 - 79)
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 80)
|
||||
|
||||
/*
|
||||
* Library-private functions.
|
||||
|
@ -1742,16 +1742,13 @@ mjit::Compiler::generateMethod()
|
||||
BEGIN_CASE(JSOP_DEFFUN)
|
||||
{
|
||||
uint32 index = fullAtomIndex(PC);
|
||||
JSFunction *inner = script->getFunction(index);
|
||||
JSFunction *innerFun = script->getFunction(index);
|
||||
|
||||
if (fun) {
|
||||
JSLocalKind localKind = fun->lookupLocal(cx, inner->atom, NULL);
|
||||
if (localKind != JSLOCAL_NONE)
|
||||
frame.syncAndForgetEverything();
|
||||
}
|
||||
if (fun && script->bindings.hasBinding(innerFun->atom))
|
||||
frame.syncAndForgetEverything();
|
||||
|
||||
prepareStubCall(Uses(0));
|
||||
masm.move(ImmPtr(inner), Registers::ArgReg1);
|
||||
masm.move(ImmPtr(innerFun), Registers::ArgReg1);
|
||||
INLINE_STUBCALL(STRICT_VARIANT(stubs::DefFun));
|
||||
}
|
||||
END_CASE(JSOP_DEFFUN)
|
||||
@ -1773,11 +1770,8 @@ mjit::Compiler::generateMethod()
|
||||
uint32 index = fullAtomIndex(PC);
|
||||
JSAtom *atom = script->getAtom(index);
|
||||
|
||||
if (fun) {
|
||||
JSLocalKind localKind = fun->lookupLocal(cx, atom, NULL);
|
||||
if (localKind != JSLOCAL_NONE)
|
||||
frame.syncAndForgetEverything();
|
||||
}
|
||||
if (fun && script->bindings.hasBinding(atom))
|
||||
frame.syncAndForgetEverything();
|
||||
|
||||
prepareStubCall(Uses(1));
|
||||
masm.move(ImmPtr(atom), Registers::ArgReg1);
|
||||
|
@ -44,6 +44,8 @@
|
||||
#include "jsnum.h"
|
||||
#include "jsscope.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
#include "methodjit/MethodJIT.h"
|
||||
#include "methodjit/Compiler.h"
|
||||
#include "methodjit/StubCalls.h"
|
||||
|
@ -2033,16 +2033,17 @@ DisassembleValue(JSContext *cx, jsval v, bool lines, bool recursive)
|
||||
else if (FUN_FLAT_CLOSURE(fun))
|
||||
fputs(" FLAT_CLOSURE", stdout);
|
||||
|
||||
if (fun->u.i.nupvars) {
|
||||
JSScript *script = fun->script();
|
||||
if (script->bindings.hasUpvars()) {
|
||||
fputs("\nupvars: {\n", stdout);
|
||||
|
||||
void *mark = JS_ARENA_MARK(&cx->tempPool);
|
||||
jsuword *localNames = fun->getLocalNameArray(cx, &cx->tempPool);
|
||||
jsuword *localNames = script->bindings.getLocalNameArray(cx, &cx->tempPool);
|
||||
if (!localNames)
|
||||
return false;
|
||||
|
||||
JSUpvarArray *uva = fun->u.i.script->upvars();
|
||||
uintN upvar_base = fun->countArgsAndVars();
|
||||
JSUpvarArray *uva = script->upvars();
|
||||
uintN upvar_base = script->bindings.countArgsAndVars();
|
||||
|
||||
for (uint32 i = 0, n = uva->length; i < n; i++) {
|
||||
JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[upvar_base + i]);
|
||||
|
Loading…
Reference in New Issue
Block a user