Bug 966912 - Part 2: Add StaticWithObject to the static scope chain r=luke

--HG--
extra : rebase_source : c0ccbca2166c953dcac37e32aba9b30175399d25
This commit is contained in:
Andy Wingo 2014-02-04 18:18:19 +01:00
parent 10a99f426e
commit 17707f6384
22 changed files with 447 additions and 223 deletions

View File

@ -497,7 +497,7 @@ class NonLocalExitScope {
StmtInfoBCE *stmt = bce->topStmt;
while (1) {
JS_ASSERT(stmt);
if (stmt->isBlockScope) {
if (stmt->isNestedScope) {
openScopeIndex = stmt->blockScopeIndex;
break;
}
@ -512,18 +512,12 @@ class NonLocalExitScope {
bce->stackDepth = savedDepth;
}
bool popScopeForNonLocalExit(StaticBlockObject &blockObj, uint32_t blockScopeIndex) {
bool popScopeForNonLocalExit(uint32_t blockScopeIndex) {
uint32_t scopeObjectIndex = bce->blockScopeList.findEnclosingScope(blockScopeIndex);
uint32_t parent = openScopeIndex;
if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
return false;
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
return false;
if (blockObj.needsClone()) {
if (Emit1(cx, bce, JSOP_POPBLOCKSCOPE) < 0)
return false;
}
openScopeIndex = bce->blockScopeList.length() - 1;
return true;
}
@ -554,6 +548,9 @@ NonLocalExitScope::prepareForNonLocalJump(StmtInfoBCE *toStmt)
FLUSH_POPS();
if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
return false;
JS_ASSERT(stmt->isNestedScope);
if (!popScopeForNonLocalExit(stmt->blockScopeIndex))
return false;
break;
case STMT_FOR_OF_LOOP:
@ -578,9 +575,16 @@ NonLocalExitScope::prepareForNonLocalJump(StmtInfoBCE *toStmt)
}
if (stmt->isBlockScope) {
JS_ASSERT(stmt->isNestedScope);
StaticBlockObject &blockObj = stmt->staticBlock();
if (!popScopeForNonLocalExit(blockObj, stmt->blockScopeIndex))
if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
return false;
if (!popScopeForNonLocalExit(stmt->blockScopeIndex))
return false;
if (blockObj.needsClone()) {
if (Emit1(cx, bce, JSOP_POPBLOCKSCOPE) < 0)
return false;
}
npops += blockObj.slotCount();
}
}
@ -728,6 +732,55 @@ ComputeAliasedSlots(ExclusiveContext *cx, BytecodeEmitter *bce, Handle<StaticBlo
static bool
EmitInternedObjectOp(ExclusiveContext *cx, uint32_t index, JSOp op, BytecodeEmitter *bce);
static bool
EnterNestedScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, ObjectBox *objbox,
StmtType stmtType)
{
Rooted<NestedScopeObject *> scopeObj(cx, &objbox->object->as<NestedScopeObject>());
uint32_t scopeObjectIndex = bce->objectList.add(objbox);
switch (stmtType) {
case STMT_BLOCK: {
Rooted<StaticBlockObject *> blockObj(cx, &scopeObj->as<StaticBlockObject>());
if (!ComputeAliasedSlots(cx, bce, blockObj))
return false;
if (blockObj->needsClone()) {
if (!EmitInternedObjectOp(cx, scopeObjectIndex, JSOP_PUSHBLOCKSCOPE, bce))
return false;
}
break;
}
case STMT_WITH:
JS_ASSERT(scopeObj->is<StaticWithObject>());
if (!EmitInternedObjectOp(cx, scopeObjectIndex, JSOP_ENTERWITH, bce))
return false;
break;
default:
MOZ_ASSUME_UNREACHABLE();
}
uint32_t parent = BlockScopeNote::NoBlockScopeIndex;
if (bce->staticScope) {
StmtInfoBCE *stmt = bce->topScopeStmt;
for (; stmt->staticScope != bce->staticScope; stmt = stmt->down) {}
parent = stmt->blockScopeIndex;
}
stmt->blockScopeIndex = bce->blockScopeList.length();
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
return false;
PushStatementBCE(bce, stmt, stmtType, bce->offset());
scopeObj->initEnclosingNestedScope(EnclosingStaticScope(bce));
FinishPushNestedScope(bce, stmt, *scopeObj);
JS_ASSERT(stmt->isNestedScope);
stmt->isBlockScope = (stmtType == STMT_BLOCK);
return true;
}
// ~ Block Scopes ~
//
// A block scope is a region of a script with an additional set of named
@ -771,50 +824,26 @@ EmitInternedObjectOp(ExclusiveContext *cx, uint32_t index, JSOp op, BytecodeEmit
// for-in loops.
//
// Summary: Enter block scopes with EnterBlockScope. It will emit
// PUSHBLOCKSCOPE if needed. Leave them with LeaveBlockScope, which will emit
// PUSHBLOCKSCOPE if needed. Leave them with LeaveNestedScope, which will emit
// DEBUGLEAVEBLOCK and may emit POPBLOCKSCOPE. Pass EnterBlockScope a fresh
// StmtInfoBCE object, and pass that same object to the corresponding
// LeaveBlockScope. Push locals before entering a scope, and pop them
// LeaveNestedScope. Push locals before entering a scope, and pop them
// afterwards. Brush your teeth, and clean behind your ears!
//
static bool
EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, ObjectBox *objbox,
unsigned extraSlots)
{
uint32_t parent = BlockScopeNote::NoBlockScopeIndex;
if (bce->staticScope) {
StmtInfoBCE *stmt = bce->topScopeStmt;
for (; stmt->staticScope != bce->staticScope; stmt = stmt->down) {}
parent = stmt->blockScopeIndex;
}
Rooted<StaticBlockObject *> blockObj(cx, &objbox->object->as<StaticBlockObject>());
uint32_t scopeObjectIndex = bce->objectList.add(objbox);
// FIXME: Once bug 962599 lands, we won't care about the stack depth, so we
// won't have extraSlots and thus invocations of EnterBlockScope can become
// invocations of EnterNestedScope.
int depth = bce->stackDepth - (blockObj->slotCount() + extraSlots);
JS_ASSERT(depth >= 0);
blockObj->setStackDepth(depth);
if (!ComputeAliasedSlots(cx, bce, blockObj))
return false;
if (blockObj->needsClone()) {
if (!EmitInternedObjectOp(cx, scopeObjectIndex, JSOP_PUSHBLOCKSCOPE, bce))
return false;
}
stmt->blockScopeIndex = bce->blockScopeList.length();
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
return false;
PushStatementBCE(bce, stmt, STMT_BLOCK, bce->offset());
blockObj->initEnclosingNestedScope(EnclosingStaticScope(bce));
FinishPushBlockScope(bce, stmt, *blockObj);
JS_ASSERT(stmt->isBlockScope);
return true;
return EnterNestedScope(cx, bce, stmt, objbox, STMT_BLOCK);
}
// Patches |breaks| and |continues| unless the top statement info record
@ -835,10 +864,11 @@ PopStatementBCE(ExclusiveContext *cx, BytecodeEmitter *bce)
}
static bool
LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce)
LeaveNestedScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt)
{
StmtInfoBCE *stmt = bce->topStmt;
JS_ASSERT(stmt->isBlockScope);
JS_ASSERT(stmt == bce->topStmt);
JS_ASSERT(stmt->isNestedScope);
JS_ASSERT(stmt->isBlockScope == !(stmt->type == STMT_WITH));
uint32_t blockScopeIndex = stmt->blockScopeIndex;
#ifdef DEBUG
@ -848,20 +878,18 @@ LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce)
NestedScopeObject *staticScope = &blockObjBox->object->as<NestedScopeObject>();
JS_ASSERT(stmt->staticScope == staticScope);
JS_ASSERT(staticScope == bce->staticScope);
JS_ASSERT_IF(!stmt->isBlockScope, staticScope->is<StaticWithObject>());
#endif
JS_ASSERT(bce->staticScope->is<StaticBlockObject>());
bool blockOnChain = bce->staticScope->as<StaticBlockObject>().needsClone();
if (!PopStatementBCE(cx, bce))
return false;
if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
if (Emit1(cx, bce, stmt->isBlockScope ? JSOP_DEBUGLEAVEBLOCK : JSOP_LEAVEWITH) < 0)
return false;
bce->blockScopeList.recordEnd(blockScopeIndex, bce->offset());
if (blockOnChain) {
if (stmt->isBlockScope && stmt->staticScope->as<StaticBlockObject>().needsClone()) {
if (Emit1(cx, bce, JSOP_POPBLOCKSCOPE) < 0)
return false;
}
@ -1120,8 +1148,7 @@ EmitAliasedVarOp(ExclusiveContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *
while (!b->containsVarAtDepth(depth)) {
if (b->needsClone())
skippedScopes++;
b = b->enclosingBlock();
JS_ASSERT(b);
b = &b->enclosingNestedScope()->as<StaticBlockObject>();
}
if (!AssignHops(bce, pn, skippedScopes, &sc))
return false;
@ -1736,6 +1763,7 @@ CheckSideEffects(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool
CheckSideEffects(cx, bce, pn->pn_kid3, answer);
case PN_BINARY:
case PN_BINARY_OBJ:
if (pn->isAssignment()) {
/*
* Assignment is presumed to be useful, even if the next operation
@ -1888,7 +1916,7 @@ BytecodeEmitter::needsImplicitThis()
} else {
JSObject *scope = sc->asGlobalSharedContext()->scopeChain();
while (scope) {
if (scope->is<WithObject>())
if (scope->is<DynamicWithObject>())
return true;
scope = scope->enclosingScope();
}
@ -2719,7 +2747,7 @@ EmitSwitch(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
}
if (pn->pn_right->isKind(PNK_LEXICALSCOPE)) {
if (!LeaveBlockScope(cx, bce))
if (!LeaveNestedScope(cx, bce, &stmtInfo))
return false;
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObj->slotCount());
} else {
@ -4269,7 +4297,7 @@ EmitLet(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
if (!EmitTree(cx, bce, letBody->pn_expr))
return false;
if (!LeaveBlockScope(cx, bce))
if (!LeaveNestedScope(cx, bce, &stmtInfo))
return false;
JSOp leaveOp = letBody->getOp();
@ -4305,7 +4333,7 @@ EmitLexicalScope(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (!EmitTree(cx, bce, pn->pn_expr))
return false;
if (!LeaveBlockScope(cx, bce))
if (!LeaveNestedScope(cx, bce, &stmtInfo))
return false;
EMIT_UINT16_IMM_OP(JSOP_POPN, slots);
@ -4319,15 +4347,13 @@ EmitWith(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
StmtInfoBCE stmtInfo(cx);
if (!EmitTree(cx, bce, pn->pn_left))
return false;
PushStatementBCE(bce, &stmtInfo, STMT_WITH, bce->offset());
if (Emit1(cx, bce, JSOP_ENTERWITH) < 0)
if (!EnterNestedScope(cx, bce, &stmtInfo, pn->pn_binary_obj, STMT_WITH))
return false;
if (!EmitTree(cx, bce, pn->pn_right))
return false;
if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
if (!LeaveNestedScope(cx, bce, &stmtInfo))
return false;
return PopStatementBCE(cx, bce);
return true;
}
static bool
@ -4474,7 +4500,7 @@ EmitForOf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
return false;
if (letDecl) {
if (!LeaveBlockScope(cx, bce))
if (!LeaveNestedScope(cx, bce, &letStmt))
return false;
}
@ -4637,7 +4663,7 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
return false;
if (letDecl) {
if (!LeaveBlockScope(cx, bce))
if (!LeaveNestedScope(cx, bce, &letStmt))
return false;
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount);
}

View File

@ -49,6 +49,7 @@ ContainsVarOrConst(ParseNode *pn)
return pnt;
return ContainsVarOrConst(pn->pn_kid3);
case PN_BINARY:
case PN_BINARY_OBJ:
/*
* Limit recursion if pn is a binary expression, which can't contain a
* var statement.
@ -327,6 +328,7 @@ Fold(ExclusiveContext *cx, ParseNode **pnp,
break;
case PN_BINARY:
case PN_BINARY_OBJ:
if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) {
// Propagate Condition context through logical connectives.
SyntacticContext kidsc = SyntacticContext::Other;

View File

@ -385,8 +385,10 @@ class FullParseHandler
return new_<UnaryNode>(PNK_RETURN, JSOP_RETURN, pos, expr);
}
ParseNode *newWithStatement(uint32_t begin, ParseNode *expr, ParseNode *body) {
return new_<BinaryNode>(PNK_WITH, JSOP_NOP, TokenPos(begin, body->pn_pos.end), expr, body);
ParseNode *newWithStatement(uint32_t begin, ParseNode *expr, ParseNode *body,
ObjectBox *staticWith) {
return new_<BinaryObjNode>(PNK_WITH, JSOP_NOP, TokenPos(begin, body->pn_pos.end),
expr, body, staticWith);
}
ParseNode *newLabeledStatement(PropertyName *label, ParseNode *stmt, uint32_t begin) {

View File

@ -310,6 +310,7 @@ class NameResolver
return false;
break;
case PN_BINARY:
case PN_BINARY_OBJ:
if (!resolve(cur->pn_left, prefix))
return false;

View File

@ -170,6 +170,7 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
stack->pushUnlessNull(pn->pn_kid3);
break;
case PN_BINARY:
case PN_BINARY_OBJ:
if (pn->pn_left != pn->pn_right)
stack->pushUnlessNull(pn->pn_left);
stack->pushUnlessNull(pn->pn_right);
@ -382,6 +383,15 @@ Parser<FullParseHandler>::cloneParseTree(ParseNode *opn)
pn->pn_iflags = opn->pn_iflags;
break;
case PN_BINARY_OBJ:
NULLCHECK(pn->pn_left = cloneParseTree(opn->pn_left));
if (opn->pn_right != opn->pn_left)
NULLCHECK(pn->pn_right = cloneParseTree(opn->pn_right));
else
pn->pn_right = pn->pn_left;
pn->pn_binary_obj = opn->pn_binary_obj;
break;
case PN_UNARY:
NULLCHECK(pn->pn_kid = cloneParseTree(opn->pn_kid));
break;
@ -548,6 +558,9 @@ ParseNode::dump(int indent)
case PN_BINARY:
((BinaryNode *) this)->dump(indent);
break;
case PN_BINARY_OBJ:
((BinaryObjNode *) this)->dump(indent);
break;
case PN_TERNARY:
((TernaryNode *) this)->dump(indent);
break;
@ -618,6 +631,18 @@ BinaryNode::dump(int indent)
fprintf(stderr, ")");
}
void
BinaryObjNode::dump(int indent)
{
const char *name = parseNodeNames[getKind()];
fprintf(stderr, "(%s ", name);
indent += strlen(name) + 2;
DumpParseTree(pn_left, indent);
IndentNewLine(indent);
DumpParseTree(pn_right, indent);
fprintf(stderr, ")");
}
void
TernaryNode::dump(int indent)
{

View File

@ -288,7 +288,7 @@ enum ParseNodeKind
* pn_kid3: catch block statements
* PNK_BREAK name pn_atom: label or null
* PNK_CONTINUE name pn_atom: label or null
* PNK_WITH binary pn_left: head expr, pn_right: body
* PNK_WITH binary-obj pn_left: head expr; pn_right: body; pn_binary_obj: StaticWithObject
* PNK_VAR, list pn_head: list of PNK_NAME or PNK_ASSIGN nodes
* PNK_CONST each name node has either
* pn_used: false
@ -420,6 +420,7 @@ enum ParseNodeArity
PN_NULLARY, /* 0 kids, only pn_atom/pn_dval/etc. */
PN_UNARY, /* one kid, plus a couple of scalars */
PN_BINARY, /* two kids, plus a couple of scalars */
PN_BINARY_OBJ, /* two kids, plus an objbox */
PN_TERNARY, /* three kids */
PN_CODE, /* module or function definition node */
PN_LIST, /* generic singly linked list */
@ -516,7 +517,10 @@ class ParseNode
struct { /* two kids if binary */
ParseNode *left;
ParseNode *right;
unsigned iflags; /* JSITER_* flags for PNK_FOR node */
union {
unsigned iflags; /* JSITER_* flags for PNK_FOR node */
ObjectBox *objbox; /* Only for PN_BINARY_OBJ */
};
} binary;
struct { /* one kid if unary */
ParseNode *kid;
@ -570,6 +574,7 @@ class ParseNode
#define pn_right pn_u.binary.right
#define pn_pval pn_u.binary.pval
#define pn_iflags pn_u.binary.iflags
#define pn_binary_obj pn_u.binary.objbox
#define pn_kid pn_u.unary.kid
#define pn_prologue pn_u.unary.prologue
#define pn_atom pn_u.name.atom
@ -905,6 +910,30 @@ struct BinaryNode : public ParseNode
#endif
};
struct BinaryObjNode : public ParseNode
{
BinaryObjNode(ParseNodeKind kind, JSOp op, const TokenPos &pos, ParseNode *left, ParseNode *right,
ObjectBox *objbox)
: ParseNode(kind, op, PN_BINARY_OBJ, pos)
{
pn_left = left;
pn_right = right;
pn_binary_obj = objbox;
}
static inline BinaryObjNode *create(ParseNodeKind kind, FullParseHandler *handler) {
return (BinaryObjNode *) ParseNode::create(kind, PN_BINARY_OBJ, handler);
}
static bool test(const ParseNode &node) {
return node.isArity(PN_BINARY_OBJ);
}
#ifdef DEBUG
void dump(int indent);
#endif
};
struct TernaryNode : public ParseNode
{
TernaryNode(ParseNodeKind kind, JSOp op, ParseNode *kid1, ParseNode *kid2, ParseNode *kid3)

View File

@ -535,7 +535,7 @@ FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunct
//
JSObject *scope = outerpc->sc->asGlobalSharedContext()->scopeChain();
while (scope) {
if (scope->is<WithObject>())
if (scope->is<DynamicWithObject>())
inWith = true;
scope = scope->enclosingScope();
}
@ -2774,7 +2774,7 @@ static void
PopStatementPC(TokenStream &ts, ParseContext<ParseHandler> *pc)
{
RootedNestedScopeObject scopeObj(ts.context(), pc->topStmt->staticScope);
JS_ASSERT(!!scopeObj == (pc->topStmt->isBlockScope));
JS_ASSERT(!!scopeObj == pc->topStmt->isNestedScope);
FinishPopStatement(pc);
@ -3196,7 +3196,8 @@ Parser<ParseHandler>::pushLexicalScope(HandleStaticBlockObject blockObj, StmtInf
PushStatementPC(pc, stmt, STMT_BLOCK);
blockObj->initEnclosingNestedScopeFromParser(pc->staticScope);
FinishPushBlockScope(pc, stmt, *blockObj.get());
FinishPushNestedScope(pc, stmt, *blockObj.get());
stmt->isBlockScope = true;
Node pn = handler.newLexicalScope(blockbox);
if (!pn)
@ -3593,7 +3594,7 @@ Parser<FullParseHandler>::letDeclaration()
* lacks the SIF_SCOPE flag, it must be a try, catch, or finally
* block.
*/
stmt->isBlockScope = true;
stmt->isBlockScope = stmt->isNestedScope = true;
stmt->downScope = pc->topScopeStmt;
pc->topScopeStmt = stmt;
@ -4896,9 +4897,16 @@ Parser<FullParseHandler>::withStatement()
StmtInfoPC stmtInfo(context);
PushStatementPC(pc, &stmtInfo, STMT_WITH);
Rooted<StaticWithObject *> staticWith(context, StaticWithObject::create(context));
if (!staticWith)
return null();
staticWith->initEnclosingNestedScopeFromParser(pc->staticScope);
FinishPushNestedScope(pc, &stmtInfo, *staticWith);
Node innerBlock = statement();
if (!innerBlock)
return null();
PopStatementPC(tokenStream, pc);
pc->sc->setBindingsAccessedDynamically();
@ -4914,7 +4922,10 @@ Parser<FullParseHandler>::withStatement()
handler.deoptimizeUsesWithin(lexdep, TokenPos(begin, pos().begin));
}
return handler.newWithStatement(begin, objectExpr, innerBlock);
ObjectBox *staticWithBox = newObjectBox(staticWith);
if (!staticWithBox)
return null();
return handler.newWithStatement(begin, objectExpr, innerBlock, staticWithBox);
}
template <>
@ -5860,6 +5871,7 @@ CompExprTransplanter::transplant(ParseNode *pn)
break;
case PN_BINARY:
case PN_BINARY_OBJ:
if (!transplant(pn->pn_left))
return false;

View File

@ -389,22 +389,30 @@ enum StmtType {
// work with both types.
struct StmtInfoBase {
uint16_t type; /* statement type */
// Statement type (StmtType).
uint16_t type;
/*
* True if type is STMT_BLOCK, STMT_TRY, STMT_SWITCH, or
* STMT_FINALLY and the block contains at least one let-declaration.
*/
// True if type is STMT_BLOCK, STMT_TRY, STMT_SWITCH, or STMT_FINALLY and
// the block contains at least one let-declaration, or if type is
// STMT_CATCH.
bool isBlockScope:1;
/* for (let ...) induced block scope */
// True if isBlockScope or type == STMT_WITH.
bool isNestedScope:1;
// for (let ...) induced block scope
bool isForLetBlock:1;
RootedAtom label; /* name of LABEL */
Rooted<NestedScopeObject *> staticScope; /* scope object */
// Block label.
RootedAtom label;
// Compile-time scope chain node for this scope. Only set if
// isNestedScope.
Rooted<NestedScopeObject *> staticScope;
StmtInfoBase(ExclusiveContext *cx)
: isBlockScope(false), isForLetBlock(false), label(cx), staticScope(cx)
: isBlockScope(false), isNestedScope(false), isForLetBlock(false),
label(cx), staticScope(cx)
{}
bool maybeScope() const {
@ -412,12 +420,12 @@ struct StmtInfoBase {
}
bool linksScope() const {
return (STMT_WITH <= type && type <= STMT_CATCH) || isBlockScope;
return isNestedScope;
}
StaticBlockObject& staticBlock() const {
JS_ASSERT(isNestedScope);
JS_ASSERT(isBlockScope);
JS_ASSERT(staticScope);
return staticScope->as<StaticBlockObject>();
}
@ -437,6 +445,7 @@ PushStatement(ContextT *ct, typename ContextT::StmtInfo *stmt, StmtType type)
{
stmt->type = type;
stmt->isBlockScope = false;
stmt->isNestedScope = false;
stmt->isForLetBlock = false;
stmt->label = nullptr;
stmt->staticScope = nullptr;
@ -452,9 +461,9 @@ PushStatement(ContextT *ct, typename ContextT::StmtInfo *stmt, StmtType type)
template <class ContextT>
void
FinishPushBlockScope(ContextT *ct, typename ContextT::StmtInfo *stmt, NestedScopeObject &staticScope)
FinishPushNestedScope(ContextT *ct, typename ContextT::StmtInfo *stmt, NestedScopeObject &staticScope)
{
stmt->isBlockScope = true;
stmt->isNestedScope = true;
stmt->downScope = ct->topScopeStmt;
ct->topScopeStmt = stmt;
ct->staticScope = &staticScope;
@ -472,8 +481,10 @@ FinishPopStatement(ContextT *ct)
ct->topStmt = stmt->down;
if (stmt->linksScope()) {
ct->topScopeStmt = stmt->downScope;
if (stmt->isBlockScope)
ct->staticScope = stmt->staticBlock().enclosingBlock();
if (stmt->isNestedScope) {
JS_ASSERT(stmt->staticScope);
ct->staticScope = stmt->staticScope->enclosingNestedScope();
}
}
}

View File

@ -5663,7 +5663,7 @@ TryAttachScopeNameStub(JSContext *cx, HandleScript script, ICGetName_Fallback *s
return true;
}
if (!scopeChain->is<ScopeObject>() || scopeChain->is<WithObject>())
if (!scopeChain->is<ScopeObject>() || scopeChain->is<DynamicWithObject>())
return true;
// Check for an 'own' property on the scope. There is no need to

View File

@ -5952,4 +5952,3 @@ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Objects
#endif
}
}

View File

@ -433,8 +433,9 @@ SaveSharedScriptData(ExclusiveContext *, Handle<JSScript *>, SharedScriptData *,
enum XDRClassKind {
CK_BlockObject = 0,
CK_JSFunction = 1,
CK_JSObject = 2
CK_WithObject = 1,
CK_JSFunction = 2,
CK_JSObject = 3
};
template<XDRMode mode>
@ -766,6 +767,8 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
JSObject *obj = *objp;
if (obj->is<BlockObject>())
classk = CK_BlockObject;
else if (obj->is<StaticWithObject>())
classk = CK_WithObject;
else if (obj->is<JSFunction>())
classk = CK_JSFunction;
else if (obj->is<JSObject>() || obj->is<ArrayObject>())
@ -778,32 +781,40 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
return false;
switch (classk) {
case CK_BlockObject: {
case CK_BlockObject:
case CK_WithObject: {
/* Code the nested block's enclosing scope. */
uint32_t blockEnclosingScopeIndex = 0;
uint32_t enclosingStaticScopeIndex = 0;
if (mode == XDR_ENCODE) {
NestedScopeObject &scope = (*objp)->as<NestedScopeObject>();
if (NestedScopeObject *enclosing = scope.enclosingNestedScope())
blockEnclosingScopeIndex = FindScopeObjectIndex(script, *enclosing);
enclosingStaticScopeIndex = FindScopeObjectIndex(script, *enclosing);
else
blockEnclosingScopeIndex = UINT32_MAX;
enclosingStaticScopeIndex = UINT32_MAX;
}
if (!xdr->codeUint32(&blockEnclosingScopeIndex))
if (!xdr->codeUint32(&enclosingStaticScopeIndex))
return false;
Rooted<JSObject*> blockEnclosingScope(cx);
Rooted<JSObject*> enclosingStaticScope(cx);
if (mode == XDR_DECODE) {
if (blockEnclosingScopeIndex != UINT32_MAX) {
JS_ASSERT(blockEnclosingScopeIndex < i);
blockEnclosingScope = script->objects()->vector[blockEnclosingScopeIndex];
if (enclosingStaticScopeIndex != UINT32_MAX) {
JS_ASSERT(enclosingStaticScopeIndex < i);
enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex];
} else {
blockEnclosingScope = fun;
enclosingStaticScope = fun;
}
}
Rooted<StaticBlockObject*> tmp(cx, static_cast<StaticBlockObject *>(objp->get()));
if (!XDRStaticBlockObject(xdr, blockEnclosingScope, tmp.address()))
return false;
*objp = tmp;
if (classk == CK_BlockObject) {
Rooted<StaticBlockObject*> tmp(cx, static_cast<StaticBlockObject *>(objp->get()));
if (!XDRStaticBlockObject(xdr, enclosingStaticScope, tmp.address()))
return false;
*objp = tmp;
} else {
Rooted<StaticWithObject*> tmp(cx, static_cast<StaticWithObject *>(objp->get()));
if (!XDRStaticWithObject(xdr, enclosingStaticScope, tmp.address()))
return false;
*objp = tmp;
}
break;
}

View File

@ -396,7 +396,8 @@ class ReferenceFinder {
/* Certain classes of object are for internal use only. */
if (object->is<BlockObject>() ||
object->is<CallObject>() ||
object->is<WithObject>() ||
object->is<StaticWithObject>() ||
object->is<DynamicWithObject>() ||
object->is<DeclEnvObject>()) {
return JSVAL_VOID;
}

View File

@ -5591,7 +5591,8 @@ IsDeclarative(Env *env)
static bool
IsWith(Env *env)
{
return env->is<DebugScopeObject>() && env->as<DebugScopeObject>().scope().is<WithObject>();
return env->is<DebugScopeObject>() &&
env->as<DebugScopeObject>().scope().is<DynamicWithObject>();
}
static bool
@ -5641,7 +5642,7 @@ DebuggerEnv_getObject(JSContext *cx, unsigned argc, Value *vp)
JSObject *obj;
if (IsWith(env)) {
obj = &env->as<DebugScopeObject>().scope().as<WithObject>().object();
obj = &env->as<DebugScopeObject>().scope().as<DynamicWithObject>().object();
} else {
obj = env;
JS_ASSERT(!obj->is<DebugScopeObject>());

View File

@ -183,8 +183,8 @@ FetchName(JSContext *cx, HandleObject obj, HandleObject obj2, HandlePropertyName
return false;
} else {
Rooted<JSObject*> normalized(cx, obj);
if (normalized->getClass() == &WithObject::class_ && !shape->hasDefaultGetter())
normalized = &normalized->as<WithObject>().object();
if (normalized->is<DynamicWithObject>() && !shape->hasDefaultGetter())
normalized = &normalized->as<DynamicWithObject>().object();
if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
/* Fast path for Object instance properties. */
JS_ASSERT(shape->hasSlot());

View File

@ -832,8 +832,10 @@ js::TypeOfValue(const Value &v)
* of the with block with sp + stackIndex.
*/
static bool
EnterWith(JSContext *cx, AbstractFramePtr frame, HandleValue val, uint32_t stackDepth)
EnterWith(JSContext *cx, AbstractFramePtr frame, HandleValue val, uint32_t stackDepth,
HandleObject staticWith)
{
JS_ASSERT(staticWith->is<StaticWithObject>());
RootedObject obj(cx);
if (val.isObject()) {
obj = &val.toObject();
@ -844,7 +846,8 @@ EnterWith(JSContext *cx, AbstractFramePtr frame, HandleValue val, uint32_t stack
}
RootedObject scopeChain(cx, frame.scopeChain());
WithObject *withobj = WithObject::create(cx, obj, scopeChain, stackDepth);
DynamicWithObject *withobj = DynamicWithObject::create(cx, obj, scopeChain, stackDepth,
staticWith);
if (!withobj)
return false;
@ -867,7 +870,7 @@ js::UnwindScope(JSContext *cx, ScopeIter &si, uint32_t stackDepth)
si.frame().popBlock(cx);
break;
case ScopeIter::With:
if (si.scope().as<WithObject>().stackDepth() < stackDepth)
if (si.scope().as<DynamicWithObject>().stackDepth() < stackDepth)
return;
si.frame().popWith(cx);
break;
@ -1727,13 +1730,6 @@ END_CASE(JSOP_POP)
CASE(JSOP_POPN)
JS_ASSERT(GET_UINT16(REGS.pc) <= REGS.stackDepth());
REGS.sp -= GET_UINT16(REGS.pc);
#ifdef DEBUG
if (NestedScopeObject *scope = script->getStaticScope(REGS.pc + JSOP_POPN_LENGTH)) {
JS_ASSERT(scope->is<StaticBlockObject>());
StaticBlockObject &blockObj = scope->as<StaticBlockObject>();
JS_ASSERT(REGS.stackDepth() >= blockObj.stackDepth() + blockObj.slotCount());
}
#endif
END_CASE(JSOP_POPN)
CASE(JSOP_POPNV)
@ -1742,13 +1738,6 @@ CASE(JSOP_POPNV)
Value val = REGS.sp[-1];
REGS.sp -= GET_UINT16(REGS.pc);
REGS.sp[-1] = val;
#ifdef DEBUG
if (NestedScopeObject *scope = script->getStaticScope(REGS.pc + JSOP_POPNV_LENGTH)) {
JS_ASSERT(scope->is<StaticBlockObject>());
StaticBlockObject &blockObj = scope->as<StaticBlockObject>();
JS_ASSERT(REGS.stackDepth() >= blockObj.stackDepth() + blockObj.slotCount());
}
#endif
}
END_CASE(JSOP_POPNV)
@ -1759,9 +1748,11 @@ END_CASE(JSOP_SETRVAL)
CASE(JSOP_ENTERWITH)
{
RootedValue &val = rootValue0;
RootedObject &staticWith = rootObject0;
val = REGS.sp[-1];
staticWith = script->getObject(REGS.pc);
if (!EnterWith(cx, REGS.fp(), val, REGS.stackDepth() - 1))
if (!EnterWith(cx, REGS.fp(), val, REGS.stackDepth() - 1, staticWith))
goto error;
/*

View File

@ -49,7 +49,7 @@
/* Long-standing JavaScript bytecodes. */ \
macro(JSOP_UNDEFINED, 1, js_undefined_str, "", 1, 0, 1, JOF_BYTE) \
macro(JSOP_UNUSED2, 2, "unused2", NULL, 1, 1, 0, JOF_BYTE) \
macro(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, JOF_BYTE) \
macro(JSOP_ENTERWITH, 3, "enterwith", NULL, 5, 1, 1, JOF_OBJECT) \
macro(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, JOF_BYTE) \
macro(JSOP_RETURN, 5, "return", NULL, 1, 1, 0, JOF_BYTE) \
macro(JSOP_GOTO, 6, "goto", NULL, 5, 0, 0, JOF_JUMP) \

View File

@ -56,7 +56,7 @@ StaticScopeIter<allowGC>::operator++(int)
} else {
onNamedLambda = true;
}
JS_ASSERT_IF(obj, obj->template is<StaticBlockObject>() || obj->template is<JSFunction>());
JS_ASSERT_IF(obj, obj->template is<NestedScopeObject>() || obj->template is<JSFunction>());
JS_ASSERT_IF(onNamedLambda, obj->template is<JSFunction>());
}
@ -66,7 +66,8 @@ StaticScopeIter<allowGC>::hasDynamicScopeObject() const
{
return obj->template is<StaticBlockObject>()
? obj->template as<StaticBlockObject>().needsClone()
: obj->template as<JSFunction>().isHeavyweight();
: (obj->template is<StaticWithObject>() ||
obj->template as<JSFunction>().isHeavyweight());
}
template <AllowGC allowGC>
@ -88,7 +89,9 @@ StaticScopeIter<allowGC>::type() const
{
if (onNamedLambda)
return NAMED_LAMBDA;
return obj->template is<StaticBlockObject>() ? BLOCK : FUNCTION;
return obj->template is<StaticBlockObject>()
? BLOCK
: (obj->template is<StaticWithObject>() ? WITH : FUNCTION);
}
template <AllowGC allowGC>
@ -99,6 +102,14 @@ StaticScopeIter<allowGC>::block() const
return obj->template as<StaticBlockObject>();
}
template <AllowGC allowGC>
inline StaticWithObject &
StaticScopeIter<allowGC>::staticWith() const
{
JS_ASSERT(type() == WITH);
return obj->template as<StaticWithObject>();
}
template <AllowGC allowGC>
inline JSScript *
StaticScopeIter<allowGC>::funScript() const

View File

@ -346,14 +346,72 @@ DeclEnvObject::create(JSContext *cx, HandleObject enclosing, HandleFunction call
return &obj->as<DeclEnvObject>();
}
WithObject *
WithObject::create(JSContext *cx, HandleObject proto, HandleObject enclosing, uint32_t depth)
template<XDRMode mode>
bool
js::XDRStaticWithObject(XDRState<mode> *xdr, HandleObject enclosingScope, StaticWithObject **objp)
{
RootedTypeObject type(cx, cx->getNewType(&class_, proto.get()));
if (mode == XDR_DECODE) {
JSContext *cx = xdr->cx();
Rooted<StaticWithObject*> obj(cx, StaticWithObject::create(cx));
if (!obj)
return false;
obj->initEnclosingNestedScope(enclosingScope);
*objp = obj;
}
// For encoding, there is nothing to do. The only information that is
// encoded by a StaticWithObject is its presence on the scope chain, and the
// script XDR handler already takes care of that.
return true;
}
template bool
js::XDRStaticWithObject(XDRState<XDR_ENCODE> *, HandleObject, StaticWithObject **);
template bool
js::XDRStaticWithObject(XDRState<XDR_DECODE> *, HandleObject, StaticWithObject **);
StaticWithObject *
StaticWithObject::create(ExclusiveContext *cx)
{
RootedTypeObject type(cx, cx->getNewType(&class_, nullptr));
if (!type)
return nullptr;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(proto),
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr),
nullptr, nullptr, FINALIZE_KIND));
if (!shape)
return nullptr;
RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, type));
if (!obj)
return nullptr;
return &obj->as<StaticWithObject>();
}
static JSObject *
CloneStaticWithObject(JSContext *cx, HandleObject enclosingScope, Handle<StaticWithObject*> srcWith)
{
Rooted<StaticWithObject*> clone(cx, StaticWithObject::create(cx));
if (!clone)
return nullptr;
clone->initEnclosingNestedScope(enclosingScope);
return clone;
}
DynamicWithObject *
DynamicWithObject::create(JSContext *cx, HandleObject object, HandleObject enclosing, uint32_t depth,
HandleObject staticWith)
{
JS_ASSERT(staticWith->is<StaticWithObject>());
RootedTypeObject type(cx, cx->getNewType(&class_, staticWith.get()));
if (!type)
return nullptr;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(staticWith),
&enclosing->global(), nullptr,
FINALIZE_KIND));
if (!shape)
@ -363,23 +421,23 @@ WithObject::create(JSContext *cx, HandleObject proto, HandleObject enclosing, ui
if (!obj)
return nullptr;
obj->as<ScopeObject>().setEnclosingScope(enclosing);
obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(depth));
JSObject *thisp = JSObject::thisObject(cx, proto);
JSObject *thisp = JSObject::thisObject(cx, object);
if (!thisp)
return nullptr;
obj->as<ScopeObject>().setEnclosingScope(enclosing);
obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(depth));
obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object));
obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp));
return &obj->as<WithObject>();
return &obj->as<DynamicWithObject>();
}
static bool
with_LookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
MutableHandleObject objp, MutableHandleShape propp)
{
RootedObject actual(cx, &obj->as<WithObject>().object());
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
return JSObject::lookupGeneric(cx, actual, id, objp, propp);
}
@ -413,7 +471,7 @@ static bool
with_GetGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
MutableHandleValue vp)
{
RootedObject actual(cx, &obj->as<WithObject>().object());
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
return JSObject::getGeneric(cx, actual, actual, id, vp);
}
@ -447,7 +505,7 @@ static bool
with_SetGeneric(JSContext *cx, HandleObject obj, HandleId id,
MutableHandleValue vp, bool strict)
{
RootedObject actual(cx, &obj->as<WithObject>().object());
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
return JSObject::setGeneric(cx, actual, actual, id, vp, strict);
}
@ -455,7 +513,7 @@ static bool
with_SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
MutableHandleValue vp, bool strict)
{
RootedObject actual(cx, &obj->as<WithObject>().object());
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
return JSObject::setProperty(cx, actual, actual, name, vp, strict);
}
@ -463,7 +521,7 @@ static bool
with_SetElement(JSContext *cx, HandleObject obj, uint32_t index,
MutableHandleValue vp, bool strict)
{
RootedObject actual(cx, &obj->as<WithObject>().object());
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
return JSObject::setElement(cx, actual, actual, index, vp, strict);
}
@ -471,21 +529,21 @@ static bool
with_SetSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
MutableHandleValue vp, bool strict)
{
RootedObject actual(cx, &obj->as<WithObject>().object());
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
return JSObject::setSpecial(cx, actual, actual, sid, vp, strict);
}
static bool
with_GetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
{
RootedObject actual(cx, &obj->as<WithObject>().object());
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
return JSObject::getGenericAttributes(cx, actual, id, attrsp);
}
static bool
with_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
{
RootedObject actual(cx, &obj->as<WithObject>().object());
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
return JSObject::setGenericAttributes(cx, actual, id, attrsp);
}
@ -493,7 +551,7 @@ static bool
with_DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
bool *succeeded)
{
RootedObject actual(cx, &obj->as<WithObject>().object());
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
return JSObject::deleteProperty(cx, actual, name, succeeded);
}
@ -501,7 +559,7 @@ static bool
with_DeleteElement(JSContext *cx, HandleObject obj, uint32_t index,
bool *succeeded)
{
RootedObject actual(cx, &obj->as<WithObject>().object());
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
return JSObject::deleteElement(cx, actual, index, succeeded);
}
@ -509,27 +567,33 @@ static bool
with_DeleteSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
bool *succeeded)
{
RootedObject actual(cx, &obj->as<WithObject>().object());
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
return JSObject::deleteSpecial(cx, actual, sid, succeeded);
}
static bool
with_Enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
MutableHandleValue statep, MutableHandleId idp)
{
RootedObject actual(cx, &obj->as<WithObject>().object());
return JSObject::enumerate(cx, actual, enum_op, statep, idp);
}
static JSObject *
with_ThisObject(JSContext *cx, HandleObject obj)
{
return &obj->as<WithObject>().withThis();
return &obj->as<DynamicWithObject>().withThis();
}
const Class WithObject::class_ = {
const Class StaticWithObject::class_ = {
"WithTemplate",
JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(StaticWithObject::RESERVED_SLOTS) |
JSCLASS_IS_ANONYMOUS,
JS_PropertyStub, /* addProperty */
JS_DeletePropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub
};
const Class DynamicWithObject::class_ = {
"With",
JSCLASS_HAS_RESERVED_SLOTS(WithObject::RESERVED_SLOTS) |
JSCLASS_HAS_RESERVED_SLOTS(DynamicWithObject::RESERVED_SLOTS) |
JSCLASS_IS_ANONYMOUS,
JS_PropertyStub, /* addProperty */
JS_DeletePropertyStub, /* delProperty */
@ -569,7 +633,7 @@ const Class WithObject::class_ = {
with_DeleteSpecial,
nullptr, nullptr, /* watch/unwatch */
nullptr, /* slice */
with_Enumerate,
nullptr, /* enumerate (native enumeration of target doesn't work) */
with_ThisObject,
}
};
@ -845,9 +909,13 @@ CloneStaticBlockObject(JSContext *cx, HandleObject enclosingScope, Handle<Static
JSObject *
js::CloneNestedScopeObject(JSContext *cx, HandleObject enclosingScope, Handle<NestedScopeObject*> srcBlock)
{
JS_ASSERT(srcBlock->is<StaticBlockObject>());
Rooted<StaticBlockObject *> blockObj(cx, &srcBlock->as<StaticBlockObject>());
return CloneStaticBlockObject(cx, enclosingScope, blockObj);
if (srcBlock->is<StaticBlockObject>()) {
Rooted<StaticBlockObject *> blockObj(cx, &srcBlock->as<StaticBlockObject>());
return CloneStaticBlockObject(cx, enclosingScope, blockObj);
} else {
Rooted<StaticWithObject *> withObj(cx, &srcBlock->as<StaticWithObject>());
return CloneStaticWithObject(cx, enclosingScope, withObj);
}
}
/*****************************************************************************/
@ -929,14 +997,17 @@ ScopeIter::operator++()
frame_ = NullFramePtr();
break;
case Block:
staticScope_ = staticScope_->as<StaticBlockObject>().enclosingBlock();
JS_ASSERT(staticScope_ && staticScope_->is<StaticBlockObject>());
staticScope_ = staticScope_->as<StaticBlockObject>().enclosingNestedScope();
if (hasScopeObject_)
cur_ = &cur_->as<ClonedBlockObject>().enclosingScope();
settle();
break;
case With:
JS_ASSERT(staticScope_ && staticScope_->is<StaticWithObject>());
JS_ASSERT(hasScopeObject_);
cur_ = &cur_->as<WithObject>().enclosingScope();
staticScope_ = staticScope_->as<StaticWithObject>().enclosingNestedScope();
cur_ = &cur_->as<DynamicWithObject>().enclosingScope();
settle();
break;
case StrictEvalScope:
@ -976,6 +1047,8 @@ ScopeIter::settle()
*/
if (frame_.isNonEvalFunctionFrame() && !frame_.fun()->isHeavyweight()) {
if (staticScope_) {
// If staticScope_ were a StaticWithObject, the function would be
// heavyweight.
JS_ASSERT(staticScope_->is<StaticBlockObject>());
type_ = Block;
hasScopeObject_ = staticScope_->as<StaticBlockObject>().needsClone();
@ -998,18 +1071,18 @@ ScopeIter::settle()
} else if (frame_.isStrictEvalFrame() && !frame_.hasCallObj()) {
JS_ASSERT(cur_ == frame_.evalPrevScopeChain(cx));
frame_ = NullFramePtr();
} else if (cur_->is<WithObject>()) {
JS_ASSERT_IF(frame_.isFunctionFrame(), frame_.fun()->isHeavyweight());
JS_ASSERT_IF(staticScope_, staticScope_->as<StaticBlockObject>().needsClone());
JS_ASSERT_IF(staticScope_,
staticScope_->as<StaticBlockObject>().stackDepth() <
cur_->as<WithObject>().stackDepth());
type_ = With;
hasScopeObject_ = true;
} else if (staticScope_) {
type_ = Block;
hasScopeObject_ = staticScope_->as<StaticBlockObject>().needsClone();
JS_ASSERT_IF(hasScopeObject_, cur_->as<ClonedBlockObject>().staticBlock() == *staticScope_);
if (staticScope_->is<StaticWithObject>()) {
JS_ASSERT(cur_);
JS_ASSERT(cur_->as<DynamicWithObject>().staticScope() == staticScope_);
type_ = With;
hasScopeObject_ = true;
} else {
type_ = Block;
hasScopeObject_ = staticScope_->as<StaticBlockObject>().needsClone();
JS_ASSERT_IF(hasScopeObject_,
cur_->as<ClonedBlockObject>().staticBlock() == *staticScope_);
}
} else if (cur_->is<CallObject>()) {
CallObject &callobj = cur_->as<CallObject>();
type_ = callobj.isForEval() ? StrictEvalScope : Call;
@ -1211,7 +1284,7 @@ class DebugScopeProxy : public BaseProxyHandler
}
/* The rest of the internal scopes do not have unaliased vars. */
JS_ASSERT(scope->is<DeclEnvObject>() || scope->is<WithObject>() ||
JS_ASSERT(scope->is<DeclEnvObject>() || scope->is<DynamicWithObject>() ||
scope->as<CallObject>().isForEval());
return false;
}
@ -1385,7 +1458,15 @@ class DebugScopeProxy : public BaseProxyHandler
return false;
}
if (!GetPropertyNames(cx, scope, flags, &props))
// DynamicWithObject isn't a very good proxy. It doesn't have a
// JSNewEnumerateOp implementation, because if it just delegated to the
// target object, the object would indicate that native enumeration is
// the thing to do, but native enumeration over the DynamicWithObject
// wrapper yields no properties. So instead here we hack around the
// issue, and punch a hole through to the with object target.
Rooted<JSObject*> target(cx, (scope->is<DynamicWithObject>()
? &scope->as<DynamicWithObject>().object() : scope));
if (!GetPropertyNames(cx, target, flags, &props))
return false;
/*
@ -1928,7 +2009,7 @@ DebugScopes::onPopWith(AbstractFramePtr frame)
{
DebugScopes *scopes = frame.compartment()->debugScopes;
if (scopes)
scopes->liveScopes.remove(&frame.scopeChain()->as<WithObject>());
scopes->liveScopes.remove(&frame.scopeChain()->as<DynamicWithObject>());
}
void

View File

@ -18,6 +18,8 @@ namespace js {
namespace frontend { struct Definition; }
class StaticWithObject;
/*****************************************************************************/
/*
@ -65,14 +67,16 @@ class StaticScopeIter
: obj(cx, obj), onNamedLambda(false)
{
JS_STATIC_ASSERT(allowGC == CanGC);
JS_ASSERT_IF(obj, obj->is<StaticBlockObject>() || obj->is<JSFunction>());
JS_ASSERT_IF(obj, obj->is<StaticBlockObject>() || obj->is<StaticWithObject>() ||
obj->is<JSFunction>());
}
StaticScopeIter(JSObject *obj)
: obj((ExclusiveContext *) nullptr, obj), onNamedLambda(false)
{
JS_STATIC_ASSERT(allowGC == NoGC);
JS_ASSERT_IF(obj, obj->is<StaticBlockObject>() || obj->is<JSFunction>());
JS_ASSERT_IF(obj, obj->is<StaticBlockObject>() || obj->is<StaticWithObject>() ||
obj->is<JSFunction>());
}
bool done() const;
@ -82,10 +86,11 @@ class StaticScopeIter
bool hasDynamicScopeObject() const;
Shape *scopeShape() const;
enum Type { BLOCK, FUNCTION, NAMED_LAMBDA };
enum Type { WITH, BLOCK, FUNCTION, NAMED_LAMBDA };
Type type() const;
StaticBlockObject &block() const;
StaticWithObject &staticWith() const;
JSScript *funScript() const;
};
@ -159,9 +164,11 @@ ScopeCoordinateFunctionScript(JSScript *script, jsbytecode *pc);
* \ CallObject Scope of entire function or strict eval
* \
* NestedScopeObject Scope created for a statement
* \ \
* \ WithObject with
* \
* \ \ \
* \ \ StaticWithObject Template for "with" object in static scope chain
* \ \
* \ DynamicWithObject Run-time "with" object on scope chain
* \
* BlockObject Shared interface of cloned/static block objects
* \ \
* \ ClonedBlockObject let, switch, catch, for
@ -316,6 +323,15 @@ class NestedScopeObject : public ScopeObject
*/
inline NestedScopeObject *enclosingNestedScope() const;
// Return true if this object is a compile-time scope template.
inline bool isStatic() { return !getProto(); }
// Return the static scope corresponding to this scope chain object.
inline NestedScopeObject* staticScope() {
JS_ASSERT(!isStatic());
return &getProto()->as<NestedScopeObject>();
}
// At compile-time it's possible for the scope chain to be null.
JSObject *enclosingScopeForStaticScopeIter() {
return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
@ -342,30 +358,46 @@ class NestedScopeObject : public ScopeObject
}
};
class WithObject : public NestedScopeObject
// With scope template objects on the static scope chain.
class StaticWithObject : public NestedScopeObject
{
static const unsigned THIS_SLOT = 2;
public:
static const unsigned RESERVED_SLOTS = 2;
static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND;
/* Use WithObject::object() instead. */
JSObject *getProto() const;
static const Class class_;
static StaticWithObject *create(ExclusiveContext *cx);
};
// With scope objects on the run-time scope chain.
class DynamicWithObject : public NestedScopeObject
{
static const unsigned OBJECT_SLOT = 2;
static const unsigned THIS_SLOT = 3;
public:
static const unsigned RESERVED_SLOTS = 3;
static const unsigned RESERVED_SLOTS = 4;
static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4_BACKGROUND;
static const Class class_;
static WithObject *
create(JSContext *cx, HandleObject proto, HandleObject enclosing, uint32_t depth);
static DynamicWithObject *
create(JSContext *cx, HandleObject object, HandleObject enclosing, uint32_t depth,
HandleObject staticWith);
/* Return object for the 'this' class hook. */
JSObject &withThis() const {
return getReservedSlot(THIS_SLOT).toObject();
StaticWithObject& staticWith() const {
return getProto()->as<StaticWithObject>();
}
/* Return the 'o' in 'with (o)'. */
JSObject &object() const {
return *JSObject::getProto();
return getReservedSlot(OBJECT_SLOT).toObject();
}
/* Return object for the 'this' class hook. */
JSObject &withThis() const {
return getReservedSlot(THIS_SLOT).toObject();
}
};
@ -412,12 +444,6 @@ class StaticBlockObject : public BlockObject
public:
static StaticBlockObject *create(ExclusiveContext *cx);
/*
* A refinement of enclosingScope that returns nullptr 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]).
@ -520,6 +546,11 @@ bool
XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope,
StaticBlockObject **objp);
template<XDRMode mode>
bool
XDRStaticWithObject(XDRState<mode> *xdr, HandleObject enclosingScope,
StaticWithObject **objp);
extern JSObject *
CloneNestedScopeObject(JSContext *cx, HandleObject enclosingScope, Handle<NestedScopeObject*> src);
@ -806,7 +837,7 @@ template<>
inline bool
JSObject::is<js::NestedScopeObject>() const
{
return is<js::BlockObject>() || is<js::WithObject>();
return is<js::BlockObject>() || is<js::StaticWithObject>() || is<js::DynamicWithObject>();
}
template<>
@ -867,13 +898,6 @@ NestedScopeObject::enclosingNestedScope() const
return obj && obj->is<NestedScopeObject>() ? &obj->as<NestedScopeObject>() : nullptr;
}
inline StaticBlockObject *
StaticBlockObject::enclosingBlock() const
{
JSObject *obj = getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
return obj && obj->is<StaticBlockObject>() ? &obj->as<StaticBlockObject>() : nullptr;
}
#ifdef DEBUG
bool
AnalyzeEntrainedVariables(JSContext *cx, HandleScript script);

View File

@ -133,8 +133,8 @@ Shape::set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict,
* |with (it) color='red';| ends up here.
* Avoid exposing the With object to native setters.
*/
if (obj->is<WithObject>()) {
RootedObject nobj(cx, &obj->as<WithObject>().object());
if (obj->is<DynamicWithObject>()) {
RootedObject nobj(cx, &obj->as<DynamicWithObject>().object());
return CallJSPropertyOpSetter(cx, self->setterOp(), nobj, id, strict, vp);
}

View File

@ -182,18 +182,15 @@ AssertDynamicScopeMatchesStaticScope(JSContext *cx, JSScript *script, JSObject *
RootedObject enclosingScope(cx, script->enclosingStaticScope());
for (StaticScopeIter<NoGC> i(enclosingScope); !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->is<WithObject>())
scope = &scope->as<WithObject>().enclosingScope();
switch (i.type()) {
case StaticScopeIter<NoGC>::BLOCK:
JS_ASSERT(i.block() == scope->as<ClonedBlockObject>().staticBlock());
JS_ASSERT(&i.block() == scope->as<ClonedBlockObject>().staticScope());
scope = &scope->as<ClonedBlockObject>().enclosingScope();
break;
case StaticScopeIter<NoGC>::WITH:
JS_ASSERT(&i.staticWith() == scope->as<DynamicWithObject>().staticScope());
scope = &scope->as<DynamicWithObject>().enclosingScope();
break;
case StaticScopeIter<NoGC>::FUNCTION:
JS_ASSERT(scope->as<CallObject>().callee().nonLazyScript() == i.funScript());
scope = &scope->as<CallObject>().enclosingScope();
@ -350,7 +347,7 @@ StackFrame::popWith(JSContext *cx)
if (MOZ_UNLIKELY(cx->compartment()->debugMode()))
DebugScopes::onPopWith(this);
JS_ASSERT(scopeChain()->is<WithObject>());
JS_ASSERT(scopeChain()->is<DynamicWithObject>());
popOffScopeChain();
}

View File

@ -23,7 +23,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 - 165);
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 166);
class XDRBuffer {
public: