зеркало из https://github.com/mozilla/gecko-dev.git
Bug 753145 - Attach static scope nesting information to scripts (r=jimb)
--HG-- extra : rebase_source : c2608319ab0c6a1668c1ec130569867d2013e0a4
This commit is contained in:
Родитель
9c907e3113
Коммит
36eedc0d88
|
@ -65,6 +65,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call
|
|||
|
||||
bool savedCallerFun = compileAndGo && callerFrame && callerFrame->isFunctionFrame();
|
||||
Rooted<JSScript*> script(cx, JSScript::Create(cx,
|
||||
/* enclosingScope = */ NullPtr(),
|
||||
savedCallerFun,
|
||||
principals,
|
||||
originPrincipals,
|
||||
|
@ -231,6 +232,7 @@ frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun,
|
|||
return false;
|
||||
|
||||
Rooted<JSScript*> script(cx, JSScript::Create(cx,
|
||||
/* enclosingScope = */ NullPtr(),
|
||||
/* savedCallerFun = */ false,
|
||||
principals,
|
||||
originPrincipals,
|
||||
|
|
|
@ -673,12 +673,31 @@ PushStatementBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, StmtType type, ptrdiff
|
|||
PushStatement(bce, stmt, type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the enclosing lexical scope, which is the innermost enclosing static
|
||||
* block object or compiler created function.
|
||||
*/
|
||||
static JSObject *
|
||||
EnclosingStaticScope(BytecodeEmitter *bce)
|
||||
{
|
||||
if (bce->blockChain)
|
||||
return bce->blockChain;
|
||||
|
||||
if (!bce->sc->inFunction()) {
|
||||
JS_ASSERT(!bce->parent);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return bce->sc->fun();
|
||||
}
|
||||
|
||||
// Push a block scope statement and link blockObj into bce->blockChain.
|
||||
static void
|
||||
PushBlockScopeBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, StaticBlockObject &blockObj,
|
||||
ptrdiff_t top)
|
||||
{
|
||||
PushStatementBCE(bce, stmt, STMT_BLOCK, top);
|
||||
blockObj.initEnclosingStaticScope(EnclosingStaticScope(bce));
|
||||
FinishPushBlockScope(bce, stmt, blockObj);
|
||||
}
|
||||
|
||||
|
@ -4219,7 +4238,6 @@ EmitIf(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
* destructure y
|
||||
* pick 1
|
||||
* dup +1 SRC_DESTRUCTLET + offset to enterlet0
|
||||
* pick
|
||||
* destructure z
|
||||
* pick 1
|
||||
* pop -1
|
||||
|
@ -4827,7 +4845,9 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
|
||||
// Inherit most things (principals, version, etc) from the parent.
|
||||
Rooted<JSScript*> parent(cx, bce->script);
|
||||
Rooted<JSObject*> enclosingScope(cx, EnclosingStaticScope(bce));
|
||||
Rooted<JSScript*> script(cx, JSScript::Create(cx,
|
||||
enclosingScope,
|
||||
/* savedCallerFun = */ false,
|
||||
parent->principals,
|
||||
parent->originPrincipals,
|
||||
|
|
|
@ -102,14 +102,6 @@ PushStatementTC(TreeContext *tc, StmtInfoTC *stmt, StmtType type)
|
|||
stmt->isFunctionBodyBlock = false;
|
||||
}
|
||||
|
||||
// Push a block scope statement and link blockObj into tc->blockChain.
|
||||
static void
|
||||
PushBlockScopeTC(TreeContext *tc, StmtInfoTC *stmt, StaticBlockObject &blockObj)
|
||||
{
|
||||
PushStatementTC(tc, stmt, STMT_BLOCK);
|
||||
FinishPushBlockScope(tc, stmt, blockObj);
|
||||
}
|
||||
|
||||
Parser::Parser(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin,
|
||||
const jschar *chars, size_t length, const char *fn, unsigned ln, JSVersion v,
|
||||
bool foldConstants, bool compileAndGo)
|
||||
|
@ -2147,12 +2139,16 @@ struct RemoveDecl {
|
|||
static void
|
||||
PopStatementTC(TreeContext *tc)
|
||||
{
|
||||
if (tc->topStmt->isBlockScope) {
|
||||
StaticBlockObject &blockObj = *tc->topStmt->blockObj;
|
||||
JS_ASSERT(!blockObj.inDictionaryMode());
|
||||
ForEachLetDef(tc, blockObj, RemoveDecl());
|
||||
}
|
||||
StaticBlockObject *blockObj = tc->topStmt->blockObj;
|
||||
JS_ASSERT(!!blockObj == (tc->topStmt->isBlockScope));
|
||||
|
||||
FinishPopStatement(tc);
|
||||
|
||||
if (blockObj) {
|
||||
JS_ASSERT(!blockObj->inDictionaryMode());
|
||||
ForEachLetDef(tc, *blockObj, RemoveDecl());
|
||||
blockObj->resetPrevBlockChainFromParser();
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
|
@ -2761,22 +2757,27 @@ Parser::returnOrYield(bool useAssignExpr)
|
|||
}
|
||||
|
||||
static ParseNode *
|
||||
PushLexicalScope(JSContext *cx, Parser *parser, StaticBlockObject &obj, StmtInfoTC *stmt)
|
||||
PushLexicalScope(JSContext *cx, Parser *parser, StaticBlockObject &blockObj, StmtInfoTC *stmt)
|
||||
{
|
||||
ParseNode *pn = LexicalScopeNode::create(PNK_LEXICALSCOPE, parser);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
|
||||
ObjectBox *blockbox = parser->newObjectBox(&obj);
|
||||
ObjectBox *blockbox = parser->newObjectBox(&blockObj);
|
||||
if (!blockbox)
|
||||
return NULL;
|
||||
|
||||
PushBlockScopeTC(parser->tc, stmt, obj);
|
||||
TreeContext *tc = parser->tc;
|
||||
|
||||
PushStatementTC(tc, stmt, STMT_BLOCK);
|
||||
blockObj.initPrevBlockChainFromParser(tc->blockChain);
|
||||
FinishPushBlockScope(tc, stmt, blockObj);
|
||||
|
||||
pn->setOp(JSOP_LEAVEBLOCK);
|
||||
pn->pn_objbox = blockbox;
|
||||
pn->pn_cookie.makeFree();
|
||||
pn->pn_dflags = 0;
|
||||
if (!GenerateBlockId(parser->tc, stmt->blockid))
|
||||
if (!GenerateBlockId(tc, stmt->blockid))
|
||||
return NULL;
|
||||
pn->pn_blockid = stmt->blockid;
|
||||
return pn;
|
||||
|
@ -3778,7 +3779,7 @@ Parser::letStatement()
|
|||
stmt->downScope = tc->topScopeStmt;
|
||||
tc->topScopeStmt = stmt;
|
||||
|
||||
blockObj->setEnclosingBlock(tc->blockChain);
|
||||
blockObj->initPrevBlockChainFromParser(tc->blockChain);
|
||||
tc->blockChain = blockObj;
|
||||
stmt->blockObj = blockObj;
|
||||
|
||||
|
|
|
@ -145,7 +145,6 @@ frontend::FinishPushBlockScope(ContextT *ct, typename ContextT::StmtInfo *stmt,
|
|||
StaticBlockObject &blockObj)
|
||||
{
|
||||
stmt->isBlockScope = true;
|
||||
blockObj.setEnclosingBlock(ct->blockChain);
|
||||
stmt->downScope = ct->topScopeStmt;
|
||||
ct->topScopeStmt = stmt;
|
||||
ct->blockChain = &blockObj;
|
||||
|
|
|
@ -4662,8 +4662,15 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent_)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a function was compiled as compile-and-go or was compiled to be
|
||||
* lexically nested inside some other script, we cannot clone it without
|
||||
* breaking the compiler's assumptions.
|
||||
*/
|
||||
RootedFunction fun(cx, funobj->toFunction());
|
||||
if (fun->isInterpreted() && fun->script()->compileAndGo) {
|
||||
if (fun->isInterpreted() &&
|
||||
(fun->script()->compileAndGo || fun->script()->enclosingStaticScope()))
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
|
||||
return NULL;
|
||||
|
@ -5373,7 +5380,7 @@ JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *scriptArg_, jsval *rval
|
|||
* mozilla, but there doesn't seem to be one, so we handle it here.
|
||||
*/
|
||||
if (scriptArg->compartment() != obj->compartment()) {
|
||||
script = CloneScript(cx, scriptArg);
|
||||
script = CloneScript(cx, NullPtr(), NullPtr(), scriptArg);
|
||||
if (!script.get())
|
||||
return false;
|
||||
} else {
|
||||
|
|
|
@ -4645,6 +4645,10 @@ extern JS_PUBLIC_API(JSFunction *)
|
|||
JS_DefineFunctionById(JSContext *cx, JSObject *obj, jsid id, JSNative call,
|
||||
unsigned nargs, unsigned attrs);
|
||||
|
||||
/*
|
||||
* Clone a top-level function into a new scope. This function will dynamically
|
||||
* fail if funobj was lexically nested inside some other function.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent);
|
||||
|
||||
|
|
|
@ -336,7 +336,8 @@ fun_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
|
|||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
js::XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *parentScript)
|
||||
js::XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
|
||||
JSObject **objp)
|
||||
{
|
||||
/* NB: Keep this in sync with CloneInterpretedFunction. */
|
||||
JSAtom *atom;
|
||||
|
@ -382,7 +383,7 @@ js::XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *paren
|
|||
if (!xdr->codeUint32(&flagsword))
|
||||
return false;
|
||||
|
||||
if (!XDRScript(xdr, &script, parentScript))
|
||||
if (!XDRScript(xdr, enclosingScope, enclosingScript, fun, &script))
|
||||
return false;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
|
@ -403,13 +404,13 @@ js::XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *paren
|
|||
}
|
||||
|
||||
template bool
|
||||
js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *xdr, JSObject **objp, JSScript *parentScript);
|
||||
js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, JSObject **);
|
||||
|
||||
template bool
|
||||
js::XDRInterpretedFunction(XDRState<XDR_DECODE> *xdr, JSObject **objp, JSScript *parentScript);
|
||||
js::XDRInterpretedFunction(XDRState<XDR_DECODE> *, HandleObject, HandleScript, JSObject **);
|
||||
|
||||
JSObject *
|
||||
js::CloneInterpretedFunction(JSContext *cx, HandleFunction srcFun)
|
||||
js::CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun)
|
||||
{
|
||||
/* NB: Keep this in sync with XDRInterpretedFunction. */
|
||||
|
||||
|
@ -423,7 +424,7 @@ js::CloneInterpretedFunction(JSContext *cx, HandleFunction srcFun)
|
|||
return NULL;
|
||||
|
||||
Rooted<JSScript*> srcScript(cx, srcFun->script());
|
||||
JSScript *clonedScript = CloneScript(cx, srcScript);
|
||||
JSScript *clonedScript = CloneScript(cx, enclosingScope, clone, srcScript);
|
||||
if (!clonedScript)
|
||||
return NULL;
|
||||
|
||||
|
@ -1281,16 +1282,20 @@ js_CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
|
|||
} else {
|
||||
/*
|
||||
* Across compartments we have to clone the script for interpreted
|
||||
* functions.
|
||||
* functions. Cross-compartment cloning only happens via JSAPI
|
||||
* (JS_CloneFunctionObject) which dynamically ensures that 'script' has
|
||||
* no enclosing lexical scope (only the global scope).
|
||||
*/
|
||||
if (clone->isInterpreted()) {
|
||||
RootedScript script(cx, clone->script());
|
||||
JS_ASSERT(script);
|
||||
JS_ASSERT(script->compartment() == fun->compartment());
|
||||
JS_ASSERT(script->compartment() != cx->compartment);
|
||||
JS_ASSERT(!script->enclosingStaticScope());
|
||||
|
||||
clone->mutableScript().init(NULL);
|
||||
JSScript *cscript = CloneScript(cx, script);
|
||||
|
||||
JSScript *cscript = CloneScript(cx, NullPtr(), clone, script);
|
||||
if (!cscript)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ struct JSFunction : public JSObject
|
|||
bool isNullClosure() const { return kind() == JSFUN_NULL_CLOSURE; }
|
||||
bool isFunctionPrototype() const { return flags & JSFUN_PROTOTYPE; }
|
||||
bool isInterpretedConstructor() const { return isInterpreted() && !isFunctionPrototype(); }
|
||||
bool isNamedLambda() const { return (flags & JSFUN_LAMBDA) && atom; }
|
||||
|
||||
uint16_t kind() const { return flags & JSFUN_KINDMASK; }
|
||||
void setKind(uint16_t k) {
|
||||
|
@ -253,17 +254,15 @@ JSFunction::toExtended() const
|
|||
return static_cast<const js::FunctionExtended *>(this);
|
||||
}
|
||||
|
||||
inline bool
|
||||
js_IsNamedLambda(JSFunction *fun) { return (fun->flags & JSFUN_LAMBDA) && fun->atom; }
|
||||
|
||||
namespace js {
|
||||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *parentScript);
|
||||
XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope,
|
||||
HandleScript enclosingScript, JSObject **objp);
|
||||
|
||||
extern JSObject *
|
||||
CloneInterpretedFunction(JSContext *cx, HandleFunction fun);
|
||||
CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction fun);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
|
|
|
@ -51,8 +51,6 @@ using namespace js;
|
|||
using namespace js::gc;
|
||||
using namespace js::frontend;
|
||||
|
||||
namespace js {
|
||||
|
||||
BindingKind
|
||||
Bindings::lookup(JSContext *cx, JSAtom *name, unsigned *indexp) const
|
||||
{
|
||||
|
@ -247,8 +245,6 @@ Bindings::trace(JSTracer *trc)
|
|||
MarkShape(trc, &lastBinding, "shape");
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
template<XDRMode mode>
|
||||
static bool
|
||||
XDRScriptConst(XDRState<mode> *xdr, HeapValue *vp)
|
||||
|
@ -342,9 +338,25 @@ XDRScriptConst(XDRState<mode> *xdr, HeapValue *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
FindBlockIndex(JSScript *script, StaticBlockObject &block)
|
||||
{
|
||||
ObjectArray *objects = script->objects();
|
||||
HeapPtrObject *vector = objects->vector;
|
||||
unsigned length = objects->length;
|
||||
for (unsigned i = 0; i < length; ++i) {
|
||||
if (vector[i] == &block)
|
||||
return i;
|
||||
}
|
||||
|
||||
JS_NOT_REACHED("Block not found");
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
|
||||
js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
|
||||
HandleFunction fun, JSScript **scriptp)
|
||||
{
|
||||
/* NB: Keep this in sync with CloneScript. */
|
||||
|
||||
|
@ -372,16 +384,12 @@ js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
|
|||
nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = nClosedArgs = nClosedVars = 0;
|
||||
jssrcnote *notes = NULL;
|
||||
|
||||
/* XDR arguments, var vars, and upvars. */
|
||||
uint16_t nargs, nvars;
|
||||
#if defined(DEBUG) || defined(__GNUC__) /* quell GCC overwarning */
|
||||
script = NULL;
|
||||
nargs = nvars = Bindings::BINDING_COUNT_LIMIT;
|
||||
#endif
|
||||
uint32_t argsVars;
|
||||
/* XDR arguments and vars. */
|
||||
uint16_t nargs = 0, nvars = 0;
|
||||
uint32_t argsVars = 0;
|
||||
if (mode == XDR_ENCODE) {
|
||||
script = *scriptp;
|
||||
JS_ASSERT_IF(parentScript, parentScript->compartment() == script->compartment());
|
||||
JS_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment());
|
||||
|
||||
nargs = script->bindings.numArgs();
|
||||
nvars = script->bindings.numVars();
|
||||
|
@ -515,7 +523,7 @@ js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
|
|||
if (script->analyzedArgsUsage() && script->needsArgsObj())
|
||||
scriptBits |= (1 << NeedsArgsObj);
|
||||
if (script->filename) {
|
||||
scriptBits |= (parentScript && parentScript->filename == script->filename)
|
||||
scriptBits |= (enclosingScript && enclosingScript->filename == script->filename)
|
||||
? (1 << ParentFilename)
|
||||
: (1 << OwnFilename);
|
||||
}
|
||||
|
@ -564,6 +572,7 @@ js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
|
|||
// principals and originPrincipals are set with xdr->initScriptPrincipals(script) below.
|
||||
// staticLevel is set below.
|
||||
script = JSScript::Create(cx,
|
||||
enclosingScope,
|
||||
!!(scriptBits & (1 << SavedCallerFun)),
|
||||
/* principals = */ NULL,
|
||||
/* originPrincipals = */ NULL,
|
||||
|
@ -621,9 +630,9 @@ js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
|
|||
return false;
|
||||
}
|
||||
} else if (scriptBits & (1 << ParentFilename)) {
|
||||
JS_ASSERT(parentScript);
|
||||
JS_ASSERT(enclosingScript);
|
||||
if (mode == XDR_DECODE)
|
||||
script->filename = parentScript->filename;
|
||||
script->filename = enclosingScript->filename;
|
||||
}
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
|
@ -647,10 +656,9 @@ js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
|
|||
}
|
||||
|
||||
/*
|
||||
* Here looping from 0-to-length to xdr objects is essential. It ensures
|
||||
* that block objects from the script->objects array will be written and
|
||||
* restored in the outer-to-inner order. js_XDRBlockObject relies on this
|
||||
* to restore the parent chain.
|
||||
* Here looping from 0-to-length to xdr objects is essential to ensure that
|
||||
* all references to enclosing blocks (via FindBlockIndex below) happen
|
||||
* after the enclosing block has been XDR'd.
|
||||
*/
|
||||
for (i = 0; i != nobjects; ++i) {
|
||||
HeapPtr<JSObject> *objp = &script->objects()->vector[i];
|
||||
|
@ -663,14 +671,58 @@ js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
|
|||
if (!xdr->codeUint32(&isBlock))
|
||||
return false;
|
||||
if (isBlock == 0) {
|
||||
/* Code the nested function's enclosing scope. */
|
||||
uint32_t funEnclosingScopeIndex = 0;
|
||||
if (mode == XDR_ENCODE) {
|
||||
StaticScopeIter ssi((*objp)->toFunction()->script()->enclosingStaticScope());
|
||||
if (ssi.done() || ssi.type() == StaticScopeIter::FUNCTION) {
|
||||
JS_ASSERT(ssi.done() == !fun);
|
||||
funEnclosingScopeIndex = UINT32_MAX;
|
||||
} else {
|
||||
funEnclosingScopeIndex = FindBlockIndex(script, ssi.block());
|
||||
JS_ASSERT(funEnclosingScopeIndex < i);
|
||||
}
|
||||
}
|
||||
if (!xdr->codeUint32(&funEnclosingScopeIndex))
|
||||
return false;
|
||||
Rooted<JSObject*> funEnclosingScope(cx);
|
||||
if (mode == XDR_DECODE) {
|
||||
if (funEnclosingScopeIndex == UINT32_MAX) {
|
||||
funEnclosingScope = fun;
|
||||
} else {
|
||||
JS_ASSERT(funEnclosingScopeIndex < i);
|
||||
funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex];
|
||||
}
|
||||
}
|
||||
|
||||
JSObject *tmp = *objp;
|
||||
if (!XDRInterpretedFunction(xdr, &tmp, parentScript))
|
||||
if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
|
||||
return false;
|
||||
*objp = tmp;
|
||||
} else {
|
||||
/* Code the nested block's enclosing scope. */
|
||||
JS_ASSERT(isBlock == 1);
|
||||
uint32_t blockEnclosingScopeIndex = 0;
|
||||
if (mode == XDR_ENCODE) {
|
||||
if (StaticBlockObject *block = (*objp)->asStaticBlock().enclosingBlock())
|
||||
blockEnclosingScopeIndex = FindBlockIndex(script, *block);
|
||||
else
|
||||
blockEnclosingScopeIndex = UINT32_MAX;
|
||||
}
|
||||
if (!xdr->codeUint32(&blockEnclosingScopeIndex))
|
||||
return false;
|
||||
Rooted<JSObject*> blockEnclosingScope(cx);
|
||||
if (mode == XDR_DECODE) {
|
||||
if (blockEnclosingScopeIndex != UINT32_MAX) {
|
||||
JS_ASSERT(blockEnclosingScopeIndex < i);
|
||||
blockEnclosingScope = script->objects()->vector[blockEnclosingScopeIndex];
|
||||
} else {
|
||||
blockEnclosingScope = fun;
|
||||
}
|
||||
}
|
||||
|
||||
StaticBlockObject *tmp = static_cast<StaticBlockObject *>(objp->get());
|
||||
if (!XDRStaticBlockObject(xdr, script, &tmp))
|
||||
if (!XDRStaticBlockObject(xdr, blockEnclosingScope, script, &tmp))
|
||||
return false;
|
||||
*objp = tmp;
|
||||
}
|
||||
|
@ -737,10 +789,10 @@ js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
|
|||
}
|
||||
|
||||
template bool
|
||||
js::XDRScript(XDRState<XDR_ENCODE> *xdr, JSScript **scriptp, JSScript *parentScript);
|
||||
js::XDRScript(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, HandleFunction, JSScript **);
|
||||
|
||||
template bool
|
||||
js::XDRScript(XDRState<XDR_DECODE> *xdr, JSScript **scriptp, JSScript *parentScript);
|
||||
js::XDRScript(XDRState<XDR_DECODE> *, HandleObject, HandleScript, HandleFunction, JSScript **);
|
||||
|
||||
bool
|
||||
JSScript::initScriptCounts(JSContext *cx)
|
||||
|
@ -1073,9 +1125,9 @@ ScriptDataSize(uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
|
|||
}
|
||||
|
||||
JSScript *
|
||||
JSScript::Create(JSContext *cx, bool savedCallerFun, JSPrincipals *principals,
|
||||
JSPrincipals *originPrincipals, bool compileAndGo, bool noScriptRval,
|
||||
JSVersion version, unsigned staticLevel)
|
||||
JSScript::Create(JSContext *cx, HandleObject enclosingScope, bool savedCallerFun,
|
||||
JSPrincipals *principals, JSPrincipals *originPrincipals,
|
||||
bool compileAndGo, bool noScriptRval, JSVersion version, unsigned staticLevel)
|
||||
{
|
||||
JSScript *script = js_NewGCScript(cx);
|
||||
if (!script)
|
||||
|
@ -1083,6 +1135,7 @@ JSScript::Create(JSContext *cx, bool savedCallerFun, JSPrincipals *principals,
|
|||
|
||||
PodZero(script);
|
||||
|
||||
script->enclosingScope_ = enclosingScope;
|
||||
script->savedCallerFun = savedCallerFun;
|
||||
|
||||
/* Establish invariant: principals implies originPrincipals. */
|
||||
|
@ -1098,7 +1151,7 @@ JSScript::Create(JSContext *cx, bool savedCallerFun, JSPrincipals *principals,
|
|||
|
||||
script->compileAndGo = compileAndGo;
|
||||
script->noScriptRval = noScriptRval;
|
||||
|
||||
|
||||
script->version = version;
|
||||
JS_ASSERT(script->getVersion() == version); // assert that no overflow occurred
|
||||
|
||||
|
@ -1314,7 +1367,7 @@ JSScript::fullyInitFromEmitter(JSContext *cx, Handle<JSScript*> script, Bytecode
|
|||
if (bce->sc->funArgumentsHasLocalBinding()) {
|
||||
// This must precede the script->bindings.transfer() call below
|
||||
script->setArgumentsHasVarBinding();
|
||||
if (bce->sc->funDefinitelyNeedsArgsObj())
|
||||
if (bce->sc->funDefinitelyNeedsArgsObj())
|
||||
script->setNeedsArgsObj(true);
|
||||
} else {
|
||||
JS_ASSERT(!bce->sc->funDefinitelyNeedsArgsObj());
|
||||
|
@ -1646,7 +1699,7 @@ Rebase(JSScript *dst, JSScript *src, T *srcp)
|
|||
}
|
||||
|
||||
JSScript *
|
||||
js::CloneScript(JSContext *cx, HandleScript src)
|
||||
js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript src)
|
||||
{
|
||||
/* NB: Keep this in sync with XDRScript. */
|
||||
|
||||
|
@ -1695,13 +1748,29 @@ js::CloneScript(JSContext *cx, HandleScript src)
|
|||
if (nobjects != 0) {
|
||||
HeapPtrObject *vector = src->objects()->vector;
|
||||
for (unsigned i = 0; i < nobjects; i++) {
|
||||
JSObject &obj = *vector[i];
|
||||
JSObject *clone;
|
||||
if (vector[i]->isStaticBlock()) {
|
||||
Rooted<StaticBlockObject*> block(cx, &vector[i]->asStaticBlock());
|
||||
clone = CloneStaticBlockObject(cx, block, objects, src);
|
||||
if (obj.isStaticBlock()) {
|
||||
Rooted<StaticBlockObject*> innerBlock(cx, &obj.asStaticBlock());
|
||||
|
||||
Rooted<JSObject*> enclosingScope(cx);
|
||||
if (StaticBlockObject *enclosingBlock = innerBlock->enclosingBlock())
|
||||
enclosingScope = objects[FindBlockIndex(src, *enclosingBlock)];
|
||||
else
|
||||
enclosingScope = fun;
|
||||
|
||||
clone = CloneStaticBlockObject(cx, enclosingScope, innerBlock);
|
||||
} else {
|
||||
RootedFunction fun(cx, vector[i]->toFunction());
|
||||
clone = CloneInterpretedFunction(cx, fun);
|
||||
Rooted<JSFunction*> innerFun(cx, obj.toFunction());
|
||||
|
||||
StaticScopeIter ssi(innerFun->script()->enclosingStaticScope());
|
||||
Rooted<JSObject*> enclosingScope(cx);
|
||||
if (!ssi.done() && ssi.type() == StaticScopeIter::BLOCK)
|
||||
enclosingScope = objects[FindBlockIndex(src, ssi.block())];
|
||||
else
|
||||
enclosingScope = fun;
|
||||
|
||||
clone = CloneInterpretedFunction(cx, enclosingScope, innerFun);
|
||||
}
|
||||
if (!clone || !objects.append(clone))
|
||||
return NULL;
|
||||
|
@ -1722,7 +1791,7 @@ js::CloneScript(JSContext *cx, HandleScript src)
|
|||
|
||||
/* Now that all fallible allocation is complete, create the GC thing. */
|
||||
|
||||
JSScript *dst = JSScript::Create(cx, src->savedCallerFun,
|
||||
JSScript *dst = JSScript::Create(cx, enclosingScope, src->savedCallerFun,
|
||||
cx->compartment->principals, src->originPrincipals,
|
||||
src->compileAndGo, src->noScriptRval,
|
||||
src->getVersion(), src->staticLevel);
|
||||
|
@ -2049,6 +2118,9 @@ JSScript::markChildren(JSTracer *trc)
|
|||
if (function())
|
||||
MarkObject(trc, &function_, "function");
|
||||
|
||||
if (enclosingScope_)
|
||||
MarkObject(trc, &enclosingScope_, "enclosing");
|
||||
|
||||
if (IS_GC_MARKING_TRACER(trc) && filename)
|
||||
MarkScriptFilename(trc->runtime, filename);
|
||||
|
||||
|
|
|
@ -414,8 +414,8 @@ struct JSScript : public js::gc::Cell
|
|||
#ifdef JS_METHODJIT
|
||||
JITScriptSet *jitInfo;
|
||||
#endif
|
||||
|
||||
js::HeapPtrFunction function_;
|
||||
js::HeapPtrObject enclosingScope_;
|
||||
|
||||
// 32-bit fields.
|
||||
|
||||
|
@ -434,10 +434,6 @@ struct JSScript : public js::gc::Cell
|
|||
* or has had backedges taken. Reset if the
|
||||
* script's JIT code is forcibly discarded. */
|
||||
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
uint32_t pad32;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
// Unique identifier within the compartment for this script, used for
|
||||
// printing analysis information.
|
||||
|
@ -526,7 +522,7 @@ struct JSScript : public js::gc::Cell
|
|||
//
|
||||
|
||||
public:
|
||||
static JSScript *Create(JSContext *cx, bool savedCallerFun,
|
||||
static JSScript *Create(JSContext *cx, js::HandleObject enclosingScope, bool savedCallerFun,
|
||||
JSPrincipals *principals, JSPrincipals *originPrincipals,
|
||||
bool compileAndGo, bool noScriptRval,
|
||||
JSVersion version, unsigned staticLevel);
|
||||
|
@ -614,6 +610,9 @@ struct JSScript : public js::gc::Cell
|
|||
|
||||
inline js::GlobalObject &global() const;
|
||||
|
||||
/* See StaticScopeIter comment. */
|
||||
JSObject *enclosingStaticScope() const { return enclosingScope_; }
|
||||
|
||||
private:
|
||||
bool makeTypes(JSContext *cx);
|
||||
bool makeAnalysis(JSContext *cx);
|
||||
|
@ -1007,7 +1006,7 @@ inline void
|
|||
CurrentScriptFileLineOrigin(JSContext *cx, unsigned *linenop, LineOption = NOT_CALLED_FROM_JSOP_EVAL);
|
||||
|
||||
extern JSScript *
|
||||
CloneScript(JSContext *cx, HandleScript script);
|
||||
CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript script);
|
||||
|
||||
/*
|
||||
* NB: after a successful XDR_DECODE, XDRScript callers must do any required
|
||||
|
@ -1016,7 +1015,8 @@ CloneScript(JSContext *cx, HandleScript script);
|
|||
*/
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript);
|
||||
XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
|
||||
HandleFunction fun, JSScript **scriptp);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ function test()
|
|||
else {
|
||||
expect = 'PASSED';
|
||||
|
||||
f = Function("a", "return (function () { return a * a;});")();
|
||||
f = Function("return a * a;");
|
||||
g = clone(f, {a: 3});
|
||||
f = null;
|
||||
gc();
|
||||
|
|
|
@ -114,6 +114,7 @@ GlobalObject::initFunctionAndObjectClasses(JSContext *cx)
|
|||
functionProto->flags |= JSFUN_PROTOTYPE;
|
||||
|
||||
Rooted<JSScript*> script(cx, JSScript::Create(cx,
|
||||
/* enclosingScope = */ NullPtr(),
|
||||
/* savedCallerFun = */ false,
|
||||
/* principals = */ NULL,
|
||||
/* originPrincipals = */ NULL,
|
||||
|
|
|
@ -152,17 +152,36 @@ BlockObject::setSlotValue(unsigned i, const Value &v)
|
|||
setSlot(RESERVED_SLOTS + i, v);
|
||||
}
|
||||
|
||||
inline void
|
||||
StaticBlockObject::initPrevBlockChainFromParser(StaticBlockObject *prev)
|
||||
{
|
||||
setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(prev));
|
||||
}
|
||||
|
||||
inline void
|
||||
StaticBlockObject::resetPrevBlockChainFromParser()
|
||||
{
|
||||
setReservedSlot(SCOPE_CHAIN_SLOT, UndefinedValue());
|
||||
}
|
||||
|
||||
inline void
|
||||
StaticBlockObject::initEnclosingStaticScope(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(getReservedSlot(SCOPE_CHAIN_SLOT).isUndefined());
|
||||
setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(obj));
|
||||
}
|
||||
|
||||
inline StaticBlockObject *
|
||||
StaticBlockObject::enclosingBlock() const
|
||||
{
|
||||
JSObject *obj = getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
|
||||
return obj ? &obj->asStaticBlock() : NULL;
|
||||
return obj && obj->isStaticBlock() ? &obj->asStaticBlock() : NULL;
|
||||
}
|
||||
|
||||
inline void
|
||||
StaticBlockObject::setEnclosingBlock(StaticBlockObject *blockObj)
|
||||
inline JSObject *
|
||||
StaticBlockObject::enclosingStaticScope() const
|
||||
{
|
||||
setFixedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(blockObj));
|
||||
return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
|
||||
}
|
||||
|
||||
inline void
|
||||
|
|
|
@ -23,6 +23,65 @@ using namespace js::types;
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
StaticScopeIter::StaticScopeIter(JSObject *obj)
|
||||
: obj(obj), onNamedLambda(false)
|
||||
{
|
||||
JS_ASSERT_IF(obj, obj->isStaticBlock() || obj->isFunction());
|
||||
}
|
||||
|
||||
bool
|
||||
StaticScopeIter::done() const
|
||||
{
|
||||
return obj == NULL;
|
||||
}
|
||||
|
||||
void
|
||||
StaticScopeIter::operator++(int)
|
||||
{
|
||||
if (obj->isStaticBlock()) {
|
||||
obj = obj->asStaticBlock().enclosingStaticScope();
|
||||
} else if (onNamedLambda || !obj->toFunction()->isNamedLambda()) {
|
||||
onNamedLambda = false;
|
||||
obj = obj->toFunction()->script()->enclosingStaticScope();
|
||||
} else {
|
||||
onNamedLambda = true;
|
||||
}
|
||||
JS_ASSERT_IF(obj, obj->isStaticBlock() || obj->isFunction());
|
||||
JS_ASSERT_IF(onNamedLambda, obj->isFunction());
|
||||
}
|
||||
|
||||
bool
|
||||
StaticScopeIter::hasDynamicScopeObject() const
|
||||
{
|
||||
return obj->isStaticBlock()
|
||||
? obj->asStaticBlock().needsClone()
|
||||
: obj->toFunction()->isHeavyweight();
|
||||
}
|
||||
|
||||
StaticScopeIter::Type
|
||||
StaticScopeIter::type() const
|
||||
{
|
||||
if (onNamedLambda)
|
||||
return NAMED_LAMBDA;
|
||||
return obj->isStaticBlock() ? BLOCK : FUNCTION;
|
||||
}
|
||||
|
||||
StaticBlockObject &
|
||||
StaticScopeIter::block() const
|
||||
{
|
||||
JS_ASSERT(type() == BLOCK);
|
||||
return obj->asStaticBlock();
|
||||
}
|
||||
|
||||
JSScript *
|
||||
StaticScopeIter::funScript() const
|
||||
{
|
||||
JS_ASSERT(type() == FUNCTION);
|
||||
return obj->toFunction()->script();
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
StaticBlockObject *
|
||||
js::ScopeCoordinateBlockChain(JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
|
@ -140,7 +199,7 @@ CallObject::createForFunction(JSContext *cx, StackFrame *fp)
|
|||
* For a named function expression Call's parent points to an environment
|
||||
* object holding function's name.
|
||||
*/
|
||||
if (js_IsNamedLambda(fp->fun())) {
|
||||
if (fp->fun()->isNamedLambda()) {
|
||||
scopeChain = DeclEnvObject::create(cx, fp);
|
||||
if (!scopeChain)
|
||||
return NULL;
|
||||
|
@ -681,45 +740,21 @@ Class js::BlockClass = {
|
|||
JS_ConvertStub
|
||||
};
|
||||
|
||||
#define NO_PARENT_INDEX UINT32_MAX
|
||||
|
||||
/*
|
||||
* If there's a parent id, then get the parent out of our script's object
|
||||
* array. We know that we clone block objects in outer-to-inner order, which
|
||||
* means that getting the parent now will work.
|
||||
*/
|
||||
static uint32_t
|
||||
FindObjectIndex(JSScript *script, StaticBlockObject *maybeBlock)
|
||||
{
|
||||
if (!maybeBlock || !script->hasObjects())
|
||||
return NO_PARENT_INDEX;
|
||||
|
||||
ObjectArray *objects = script->objects();
|
||||
HeapPtrObject *vector = objects->vector;
|
||||
unsigned length = objects->length;
|
||||
for (unsigned i = 0; i < length; ++i) {
|
||||
if (vector[i] == maybeBlock)
|
||||
return i;
|
||||
}
|
||||
|
||||
return NO_PARENT_INDEX;
|
||||
}
|
||||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
js::XDRStaticBlockObject(XDRState<mode> *xdr, JSScript *script, StaticBlockObject **objp)
|
||||
js::XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript script,
|
||||
StaticBlockObject **objp)
|
||||
{
|
||||
/* NB: Keep this in sync with CloneStaticBlockObject. */
|
||||
|
||||
JSContext *cx = xdr->cx();
|
||||
|
||||
Rooted<StaticBlockObject*> obj(cx);
|
||||
uint32_t parentId = 0;
|
||||
uint32_t count = 0;
|
||||
uint32_t depthAndCount = 0;
|
||||
|
||||
if (mode == XDR_ENCODE) {
|
||||
obj = *objp;
|
||||
parentId = FindObjectIndex(script, obj->enclosingBlock());
|
||||
uint32_t depth = obj->stackDepth();
|
||||
JS_ASSERT(depth <= UINT16_MAX);
|
||||
count = obj->slotCount();
|
||||
|
@ -727,23 +762,14 @@ js::XDRStaticBlockObject(XDRState<mode> *xdr, JSScript *script, StaticBlockObjec
|
|||
depthAndCount = (depth << 16) | uint16_t(count);
|
||||
}
|
||||
|
||||
/* First, XDR the parent atomid. */
|
||||
if (!xdr->codeUint32(&parentId))
|
||||
return false;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
obj = StaticBlockObject::create(cx);
|
||||
if (!obj)
|
||||
return false;
|
||||
obj->initEnclosingStaticScope(enclosingScope);
|
||||
*objp = obj;
|
||||
|
||||
obj->setEnclosingBlock(parentId == NO_PARENT_INDEX
|
||||
? NULL
|
||||
: &script->getObject(parentId)->asStaticBlock());
|
||||
}
|
||||
|
||||
AutoObjectRooter tvr(cx, obj);
|
||||
|
||||
if (!xdr->codeUint32(&depthAndCount))
|
||||
return false;
|
||||
|
||||
|
@ -818,14 +844,13 @@ js::XDRStaticBlockObject(XDRState<mode> *xdr, JSScript *script, StaticBlockObjec
|
|||
}
|
||||
|
||||
template bool
|
||||
js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *xdr, JSScript *script, StaticBlockObject **objp);
|
||||
js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, StaticBlockObject **);
|
||||
|
||||
template bool
|
||||
js::XDRStaticBlockObject(XDRState<XDR_DECODE> *xdr, JSScript *script, StaticBlockObject **objp);
|
||||
js::XDRStaticBlockObject(XDRState<XDR_DECODE> *, HandleObject, HandleScript, StaticBlockObject **);
|
||||
|
||||
JSObject *
|
||||
js::CloneStaticBlockObject(JSContext *cx, Handle<StaticBlockObject*> srcBlock,
|
||||
const AutoObjectVector &objects, JSScript *src)
|
||||
js::CloneStaticBlockObject(JSContext *cx, HandleObject enclosingScope, Handle<StaticBlockObject*> srcBlock)
|
||||
{
|
||||
/* NB: Keep this in sync with XDRStaticBlockObject. */
|
||||
|
||||
|
@ -833,11 +858,7 @@ js::CloneStaticBlockObject(JSContext *cx, Handle<StaticBlockObject*> srcBlock,
|
|||
if (!clone)
|
||||
return NULL;
|
||||
|
||||
uint32_t parentId = FindObjectIndex(src, srcBlock->enclosingBlock());
|
||||
clone->setEnclosingBlock(parentId == NO_PARENT_INDEX
|
||||
? NULL
|
||||
: &objects[parentId]->asStaticBlock());
|
||||
|
||||
clone->initEnclosingStaticScope(enclosingScope);
|
||||
clone->setStackDepth(srcBlock->stackDepth());
|
||||
|
||||
/* Shape::Range is reverse order, so build a list in forward order. */
|
||||
|
|
|
@ -18,14 +18,71 @@ namespace js {
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* All function scripts have an "enclosing static scope" that refers to the
|
||||
* innermost enclosing let or function in the program text. This allows full
|
||||
* reconstruction of the lexical scope for debugging or compiling efficient
|
||||
* access to variables in enclosing scopes. The static scope is represented at
|
||||
* runtime by a tree of compiler-created objects representing each scope:
|
||||
* - a StaticBlockObject is created for 'let' and 'catch' scopes
|
||||
* - a JSFunction+JSScript+Bindings trio is created for function scopes
|
||||
* (These objects are primarily used to clone objects scopes for the
|
||||
* dynamic scope chain.)
|
||||
*
|
||||
* There is an additional scope for named lambdas. E.g., in:
|
||||
*
|
||||
* (function f() { var x; function g() { } })
|
||||
*
|
||||
* g's innermost enclosing scope will first be the function scope containing
|
||||
* 'x', enclosed by a scope containing only the name 'f'. (This separate scope
|
||||
* is necessary due to the fact that declarations in the function scope shadow
|
||||
* (dynamically, in the case of 'eval') the lambda name.)
|
||||
*
|
||||
* There are two limitations to the current lexical nesting information:
|
||||
*
|
||||
* - 'with' is completely absent; this isn't a problem for the current use
|
||||
* cases since 'with' causes every static scope to be on the dynamic scope
|
||||
* chain (so the debugger can find everything) and inhibits all upvar
|
||||
* optimization.
|
||||
*
|
||||
* - The "enclosing static scope" chain stops at 'eval'. For example in:
|
||||
* let (x) { eval("function f() {}") }
|
||||
* f does not have an enclosing static scope. This is fine for current uses
|
||||
* for the same reason as 'with'.
|
||||
*
|
||||
* (See also AssertDynamicScopeMatchesStaticScope.)
|
||||
*/
|
||||
class StaticScopeIter
|
||||
{
|
||||
JSObject *obj;
|
||||
bool onNamedLambda;
|
||||
|
||||
public:
|
||||
explicit StaticScopeIter(JSObject *obj);
|
||||
|
||||
bool done() const;
|
||||
void operator++(int);
|
||||
|
||||
/* Return whether this static scope will be on the dynamic scope chain. */
|
||||
bool hasDynamicScopeObject() const;
|
||||
|
||||
enum Type { BLOCK, FUNCTION, NAMED_LAMBDA };
|
||||
Type type() const;
|
||||
|
||||
StaticBlockObject &block() const;
|
||||
JSScript *funScript() const;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* A "scope coordinate" describes how to get from head of the scope chain to a
|
||||
* given lexically-enclosing variable. A scope coordinate has two dimensions:
|
||||
* - hops: the number of scope objects on the scope chain to skip
|
||||
* - binding: which binding on the scope object
|
||||
* - slot: the slot on the scope object holding the variable's value
|
||||
* Additionally (as described in jsopcode.tbl) there is a 'block' index, but
|
||||
* this is only needed for decompilation/inference so it is not included in the
|
||||
* main ScopeCoordinate struct: use ScopeCoordinate{BlockChain,Atom} instead.
|
||||
* main ScopeCoordinate struct: use ScopeCoordinate{BlockChain,Name} instead.
|
||||
*/
|
||||
struct ScopeCoordinate
|
||||
{
|
||||
|
@ -228,12 +285,40 @@ class StaticBlockObject : public BlockObject
|
|||
public:
|
||||
static StaticBlockObject *create(JSContext *cx);
|
||||
|
||||
inline StaticBlockObject *enclosingBlock() const;
|
||||
inline void setEnclosingBlock(StaticBlockObject *blockObj);
|
||||
/* See StaticScopeIter comment. */
|
||||
inline JSObject *enclosingStaticScope() const;
|
||||
|
||||
void setStackDepth(uint32_t depth);
|
||||
/*
|
||||
* A refinement of enclosingStaticScope that returns NULL if the enclosing
|
||||
* static scope is a JSFunction.
|
||||
*/
|
||||
inline StaticBlockObject *enclosingBlock() const;
|
||||
|
||||
/*
|
||||
* Return whether this StaticBlockObject contains a variable stored at
|
||||
* the given stack depth (i.e., fp->base()[depth]).
|
||||
*/
|
||||
bool containsVarAtDepth(uint32_t depth);
|
||||
|
||||
/*
|
||||
* A let binding is aliased if accessed lexically by nested functions or
|
||||
* dynamically through dynamic name lookup (eval, with, function::, etc).
|
||||
*/
|
||||
bool isAliased(unsigned i);
|
||||
|
||||
/*
|
||||
* A static block object is cloned (when entering the block) iff some
|
||||
* variable of the block isAliased.
|
||||
*/
|
||||
bool needsClone();
|
||||
|
||||
/* Frontend-only functions ***********************************************/
|
||||
|
||||
/* Initialization functions for above fields. */
|
||||
void setAliased(unsigned i, bool aliased);
|
||||
void setStackDepth(uint32_t depth);
|
||||
void initEnclosingStaticScope(JSObject *obj);
|
||||
|
||||
/*
|
||||
* Frontend compilation temporarily uses the object's slots to link
|
||||
* a let var to its associated Definition parse node.
|
||||
|
@ -242,17 +327,13 @@ class StaticBlockObject : public BlockObject
|
|||
Definition *maybeDefinitionParseNode(unsigned i);
|
||||
|
||||
/*
|
||||
* A let binding is aliased is accessed lexically by nested functions or
|
||||
* dynamically through dynamic name lookup (eval, with, function::, etc).
|
||||
* The parser uses 'enclosingBlock' as the prev-link in the tc->blockChain
|
||||
* stack. Note: in the case of hoisting, this prev-link will not ultimately
|
||||
* be the same as enclosingBlock, initEnclosingStaticScope must be called
|
||||
* separately in the emitter. 'reset' is just for asserting stackiness.
|
||||
*/
|
||||
void setAliased(unsigned i, bool aliased);
|
||||
bool isAliased(unsigned i);
|
||||
|
||||
/*
|
||||
* A static block object is cloned (when entering the block) iff some
|
||||
* variable of the block isAliased.
|
||||
*/
|
||||
bool needsClone();
|
||||
void initPrevBlockChainFromParser(StaticBlockObject *prev);
|
||||
void resetPrevBlockChainFromParser();
|
||||
|
||||
static Shape *addVar(JSContext *cx, Handle<StaticBlockObject*> block, HandleId id,
|
||||
int index, bool *redeclared);
|
||||
|
@ -277,11 +358,11 @@ class ClonedBlockObject : public BlockObject
|
|||
|
||||
template<XDRMode mode>
|
||||
bool
|
||||
XDRStaticBlockObject(XDRState<mode> *xdr, JSScript *script, StaticBlockObject **objp);
|
||||
XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript script,
|
||||
StaticBlockObject **objp);
|
||||
|
||||
extern JSObject *
|
||||
CloneStaticBlockObject(JSContext *cx, Handle<StaticBlockObject*> srcBlock,
|
||||
const AutoObjectVector &objects, JSScript *src);
|
||||
CloneStaticBlockObject(JSContext *cx, HandleObject enclosingScope, Handle<StaticBlockObject*> src);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
|
@ -222,6 +222,42 @@ StackFrame::pcQuadratic(const ContextStack &stack, size_t maxDepth)
|
|||
return regs.fp()->script()->code;
|
||||
}
|
||||
|
||||
static inline void
|
||||
AssertDynamicScopeMatchesStaticScope(JSScript *script, JSObject *scope)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
for (StaticScopeIter i(script->enclosingStaticScope()); !i.done(); i++) {
|
||||
if (i.hasDynamicScopeObject()) {
|
||||
/*
|
||||
* 'with' does not participate in the static scope of the script,
|
||||
* but it does in the dynamic scope, so skip them here.
|
||||
*/
|
||||
while (scope->isWith())
|
||||
scope = &scope->asWith().enclosingScope();
|
||||
|
||||
switch (i.type()) {
|
||||
case StaticScopeIter::BLOCK:
|
||||
JS_ASSERT(i.block() == scope->asClonedBlock().staticBlock());
|
||||
scope = &scope->asClonedBlock().enclosingScope();
|
||||
break;
|
||||
case StaticScopeIter::FUNCTION:
|
||||
JS_ASSERT(i.funScript() == scope->asCall().callee().script());
|
||||
scope = &scope->asCall().enclosingScope();
|
||||
break;
|
||||
case StaticScopeIter::NAMED_LAMBDA:
|
||||
scope = &scope->asDeclEnv().enclosingScope();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ideally, we'd JS_ASSERT(!scope->isScope()) but the enclosing lexical
|
||||
* scope chain stops at eval() boundaries. See StaticScopeIter comment.
|
||||
*/
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
StackFrame::prologue(JSContext *cx, bool newType)
|
||||
{
|
||||
|
@ -247,6 +283,7 @@ StackFrame::prologue(JSContext *cx, bool newType)
|
|||
}
|
||||
|
||||
JS_ASSERT(isNonEvalFunctionFrame());
|
||||
AssertDynamicScopeMatchesStaticScope(script(), scopeChain());
|
||||
|
||||
if (fun()->isHeavyweight()) {
|
||||
CallObject *callobj = CallObject::createForFunction(cx, this);
|
||||
|
@ -299,12 +336,11 @@ StackFrame::epilogue(JSContext *cx)
|
|||
}
|
||||
|
||||
JS_ASSERT(isNonEvalFunctionFrame());
|
||||
if (fun()->isHeavyweight()) {
|
||||
|
||||
if (fun()->isHeavyweight())
|
||||
JS_ASSERT_IF(hasCallObj(), scopeChain()->asCall().callee().script() == script());
|
||||
} else {
|
||||
JS_ASSERT(!scopeChain()->isCall() || scopeChain()->asCall().isForEval() ||
|
||||
scopeChain()->asCall().callee().script() != script());
|
||||
}
|
||||
else
|
||||
AssertDynamicScopeMatchesStaticScope(script(), scopeChain());
|
||||
|
||||
if (cx->compartment->debugMode())
|
||||
cx->runtime->debugScopes->onPopCall(this, cx);
|
||||
|
|
|
@ -123,7 +123,10 @@ XDRState<mode>::codeFunction(JSObject **objp)
|
|||
if (mode == XDR_DECODE)
|
||||
*objp = NULL;
|
||||
|
||||
return VersionCheck(this) && XDRInterpretedFunction(this, objp, NULL);
|
||||
if (!VersionCheck(this))
|
||||
return false;
|
||||
|
||||
return XDRInterpretedFunction(this, NullPtr(), NullPtr(), objp);
|
||||
}
|
||||
|
||||
template<XDRMode mode>
|
||||
|
@ -138,7 +141,10 @@ XDRState<mode>::codeScript(JSScript **scriptp)
|
|||
script = *scriptp;
|
||||
}
|
||||
|
||||
if (!VersionCheck(this) || !XDRScript(this, &script, NULL))
|
||||
if (!VersionCheck(this))
|
||||
return false;
|
||||
|
||||
if (!XDRScript(this, NullPtr(), NullPtr(), NullPtr(), &script))
|
||||
return false;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace js {
|
|||
* and saved versions. If deserialization fails, the data should be
|
||||
* invalidated if possible.
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 119);
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 120);
|
||||
|
||||
class XDRBuffer {
|
||||
public:
|
||||
|
|
Загрузка…
Ссылка в новой задаче