Bug 932276 - Bytecode emitter records static scope extents. r=jorendorff

This commit is contained in:
Andy Wingo 2013-10-29 14:42:10 +01:00
parent 380ff683ba
commit ffb3919733
3 changed files with 126 additions and 51 deletions

View File

@ -59,6 +59,7 @@ struct frontend::StmtInfoBCE : public StmtInfoBase
ptrdiff_t update; /* loop update offset (top if none) */
ptrdiff_t breaks; /* offset of last break in loop */
ptrdiff_t continues; /* offset of last continue in loop */
uint32_t blockScopeIndex; /* index of scope in BlockScopeArray */
StmtInfoBCE(ExclusiveContext *cx) : StmtInfoBase(cx) {}
@ -100,10 +101,11 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
atomIndices(sc->context),
firstLine(lineNum),
stackDepth(0), maxStackDepth(0),
tryNoteList(sc->context),
arrayCompDepth(0),
emitLevel(0),
constList(sc->context),
tryNoteList(sc->context),
blockScopeList(sc->context),
typesetCount(0),
hasSingletons(false),
emittingForInit(false),
@ -686,13 +688,23 @@ EnclosingStaticScope(BytecodeEmitter *bce)
}
// Push a block scope statement and link blockObj into bce->blockChain.
static void
PushBlockScopeBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, StaticBlockObject &blockObj,
static bool
PushBlockScopeBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, ObjectBox *objbox,
ptrdiff_t top)
{
StaticBlockObject &blockObj = objbox->object->as<StaticBlockObject>();
PushStatementBCE(bce, stmt, STMT_BLOCK, top);
unsigned scopeObjectIndex = bce->objectList.add(objbox);
stmt->blockScopeIndex = bce->blockScopeList.length();
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset()))
return false;
blockObj.initEnclosingStaticScope(EnclosingStaticScope(bce));
FinishPushBlockScope(bce, stmt, blockObj);
return true;
}
// Patches |breaks| and |continues| unless the top statement info record
@ -707,6 +719,10 @@ PopStatementBCE(ExclusiveContext *cx, BytecodeEmitter *bce)
{
return false;
}
if (stmt->isBlockScope)
bce->blockScopeList.recordEnd(stmt->blockScopeIndex, bce->offset());
FinishPopStatement(bce);
return true;
}
@ -770,10 +786,16 @@ EmitAtomOp(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
}
static bool
EmitObjectOp(ExclusiveContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce)
EmitInternedObjectOp(ExclusiveContext *cx, uint32_t index, JSOp op, BytecodeEmitter *bce)
{
JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT);
return EmitIndex32(cx, op, bce->objectList.add(objbox), bce);
return EmitIndex32(cx, op, index, bce);
}
static bool
EmitObjectOp(ExclusiveContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce)
{
return EmitInternedObjectOp(cx, bce->objectList.add(objbox), op, bce);
}
static bool
@ -1061,7 +1083,14 @@ static bool
EmitEnterBlock(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
{
JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
if (!EmitObjectOp(cx, pn->pn_objbox, op, bce))
StmtInfoBCE *stmt = bce->topStmt;
JS_ASSERT(stmt->type == STMT_BLOCK || stmt->type == STMT_SWITCH);
JS_ASSERT(stmt->isBlockScope);
JS_ASSERT(stmt->blockScopeIndex == bce->blockScopeList.length() - 1);
JS_ASSERT(bce->blockScopeList.list[stmt->blockScopeIndex].length == 0);
uint32_t scopeObjectIndex = bce->blockScopeList.list[stmt->blockScopeIndex].index;
JS_ASSERT(scopeObjectIndex == bce->objectList.length - 1);
if (!EmitInternedObjectOp(cx, scopeObjectIndex, op, bce))
return false;
Rooted<StaticBlockObject*> blockObj(cx, &pn->pn_objbox->object->as<StaticBlockObject>());
@ -2305,7 +2334,8 @@ EmitSwitch(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
return false;
if (pn2->isKind(PNK_LEXICALSCOPE)) {
PushBlockScopeBCE(bce, &stmtInfo, pn2->pn_objbox->object->as<StaticBlockObject>(), -1);
if (!PushBlockScopeBCE(bce, &stmtInfo, pn2->pn_objbox, -1))
return false;
stmtInfo.type = STMT_SWITCH;
if (!EmitEnterBlock(cx, bce, pn2, JSOP_ENTERLET1))
return false;
@ -4193,7 +4223,8 @@ EmitLet(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
}
StmtInfoBCE stmtInfo(cx);
PushBlockScopeBCE(bce, &stmtInfo, *blockObj, bce->offset());
if (!PushBlockScopeBCE(bce, &stmtInfo, letBody->pn_objbox, bce->offset()))
return false;
DebugOnly<ptrdiff_t> bodyBegin = bce->offset();
if (!EmitEnterBlock(cx, bce, letBody, JSOP_ENTERLET0))
@ -4226,7 +4257,8 @@ EmitLexicalScope(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
ObjectBox *objbox = pn->pn_objbox;
StaticBlockObject &blockObj = objbox->object->as<StaticBlockObject>();
size_t slots = blockObj.slotCount();
PushBlockScopeBCE(bce, &stmtInfo, blockObj, bce->offset());
if (!PushBlockScopeBCE(bce, &stmtInfo, objbox, bce->offset()))
return false;
if (!EmitEnterBlock(cx, bce, pn, JSOP_ENTERBLOCK))
return false;
@ -4316,7 +4348,8 @@ EmitForOf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
// Enter the block before the loop body, after evaluating the obj.
StmtInfoBCE letStmt(cx);
if (letDecl) {
PushBlockScopeBCE(bce, &letStmt, *blockObj, bce->offset());
if (!PushBlockScopeBCE(bce, &letStmt, pn1->pn_objbox, bce->offset()))
return false;
letStmt.isForLetBlock = true;
if (!EmitEnterBlock(cx, bce, pn1, JSOP_ENTERLET2))
return false;
@ -4492,7 +4525,8 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
/* Enter the block before the loop body, after evaluating the obj. */
StmtInfoBCE letStmt(cx);
if (letDecl) {
PushBlockScopeBCE(bce, &letStmt, *blockObj, bce->offset());
if (!PushBlockScopeBCE(bce, &letStmt, pn1->pn_objbox, bce->offset()))
return false;
letStmt.isForLetBlock = true;
if (!EmitEnterBlock(cx, bce, pn1, JSOP_ENTERLET1))
return false;
@ -6775,25 +6809,8 @@ frontend::FinishTakingSrcNotes(ExclusiveContext *cx, BytecodeEmitter *bce, jssrc
return true;
}
bool
CGTryNoteList::append(JSTryNoteKind kind, unsigned stackDepth, size_t start, size_t end)
{
JS_ASSERT(unsigned(uint16_t(stackDepth)) == stackDepth);
JS_ASSERT(start <= end);
JS_ASSERT(size_t(uint32_t(start)) == start);
JS_ASSERT(size_t(uint32_t(end)) == end);
JSTryNote note;
note.kind = kind;
note.stackDepth = uint16_t(stackDepth);
note.start = uint32_t(start);
note.length = uint32_t(end - start);
return list.append(note);
}
void
CGTryNoteList::finish(TryNoteArray *array)
CGConstList::finish(ConstArray *array)
{
JS_ASSERT(length() == array->length);
@ -6878,8 +6895,56 @@ CGObjectList::finish(ObjectArray *array)
JS_ASSERT(cursor == array->vector);
}
bool
CGTryNoteList::append(JSTryNoteKind kind, unsigned stackDepth, size_t start, size_t end)
{
JS_ASSERT(unsigned(uint16_t(stackDepth)) == stackDepth);
JS_ASSERT(start <= end);
JS_ASSERT(size_t(uint32_t(start)) == start);
JS_ASSERT(size_t(uint32_t(end)) == end);
JSTryNote note;
note.kind = kind;
note.stackDepth = uint16_t(stackDepth);
note.start = uint32_t(start);
note.length = uint32_t(end - start);
return list.append(note);
}
void
CGConstList::finish(ConstArray *array)
CGTryNoteList::finish(TryNoteArray *array)
{
JS_ASSERT(length() == array->length);
for (unsigned i = 0; i < length(); i++)
array->vector[i] = list[i];
}
bool
CGBlockScopeList::append(uint32_t scopeObject, uint32_t offset)
{
BlockScopeNote note;
mozilla::PodZero(&note);
note.index = scopeObject;
note.start = offset;
return list.append(note);
}
void
CGBlockScopeList::recordEnd(uint32_t index, uint32_t offset)
{
JS_ASSERT(index < length());
JS_ASSERT(offset >= list[index].start);
JS_ASSERT(list[index].length == 0);
list[index].length = offset - list[index].start;
}
void
CGBlockScopeList::finish(BlockScopeArray *array)
{
JS_ASSERT(length() == array->length);

View File

@ -28,13 +28,13 @@ template <typename ParseHandler> class Parser;
class SharedContext;
class TokenStream;
struct CGTryNoteList {
Vector<JSTryNote> list;
CGTryNoteList(ExclusiveContext *cx) : list(cx) {}
bool append(JSTryNoteKind kind, unsigned stackDepth, size_t start, size_t end);
class CGConstList {
Vector<Value> list;
public:
CGConstList(ExclusiveContext *cx) : list(cx) {}
bool append(Value v) { JS_ASSERT_IF(v.isString(), v.toString()->isAtom()); return list.append(v); }
size_t length() const { return list.length(); }
void finish(TryNoteArray *array);
void finish(ConstArray *array);
};
struct CGObjectList {
@ -48,13 +48,23 @@ struct CGObjectList {
void finish(ObjectArray *array);
};
class CGConstList {
Vector<Value> list;
public:
CGConstList(ExclusiveContext *cx) : list(cx) {}
bool append(Value v) { JS_ASSERT_IF(v.isString(), v.toString()->isAtom()); return list.append(v); }
struct CGTryNoteList {
Vector<JSTryNote> list;
CGTryNoteList(ExclusiveContext *cx) : list(cx) {}
bool append(JSTryNoteKind kind, unsigned stackDepth, size_t start, size_t end);
size_t length() const { return list.length(); }
void finish(ConstArray *array);
void finish(TryNoteArray *array);
};
struct CGBlockScopeList {
Vector<BlockScopeNote> list;
CGBlockScopeList(ExclusiveContext *cx) : list(cx) {}
bool append(uint32_t scopeObject, uint32_t offset);
void recordEnd(uint32_t index, uint32_t offset);
size_t length() const { return list.length(); }
void finish(BlockScopeArray *array);
};
struct StmtInfoBCE;
@ -104,8 +114,6 @@ struct BytecodeEmitter
int stackDepth; /* current stack depth in script frame */
unsigned maxStackDepth; /* maximum stack depth so far */
CGTryNoteList tryNoteList; /* list of emitted try notes */
unsigned arrayCompDepth; /* stack depth of array in comprehension */
unsigned emitLevel; /* js::frontend::EmitTree recursion level */
@ -115,6 +123,8 @@ struct BytecodeEmitter
CGObjectList objectList; /* list of emitted objects */
CGObjectList regexpList; /* list of emitted regexp that will be
cloned during execution */
CGTryNoteList tryNoteList; /* list of emitted try notes */
CGBlockScopeList blockScopeList;/* list of emitted block scope notes */
uint16_t typesetCount; /* Number of JOF_TYPESET opcodes generated */

View File

@ -1878,10 +1878,9 @@ JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, Byteco
uint32_t prologLength = bce->prologOffset();
uint32_t nsrcnotes = uint32_t(bce->countFinalSourceNotes());
uint32_t natoms = bce->atomIndices->count();
uint32_t nblockscopes = 0;
if (!partiallyInit(cx, script,
bce->constList.length(), bce->objectList.length, bce->regexpList.length,
bce->tryNoteList.length(), nblockscopes, bce->typesetCount))
bce->tryNoteList.length(), bce->blockScopeList.length(), bce->typesetCount))
{
return false;
}
@ -1918,15 +1917,16 @@ JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, Byteco
FunctionBox *funbox = bce->sc->isFunctionBox() ? bce->sc->asFunctionBox() : nullptr;
// FIXME: Initialize blockScopes here.
if (bce->tryNoteList.length() != 0)
bce->tryNoteList.finish(script->trynotes());
if (bce->constList.length() != 0)
bce->constList.finish(script->consts());
if (bce->objectList.length != 0)
bce->objectList.finish(script->objects());
if (bce->regexpList.length != 0)
bce->regexpList.finish(script->regexps());
if (bce->constList.length() != 0)
bce->constList.finish(script->consts());
if (bce->tryNoteList.length() != 0)
bce->tryNoteList.finish(script->trynotes());
if (bce->blockScopeList.length() != 0)
bce->blockScopeList.finish(script->blockScopes());
script->strict = bce->sc->strict;
script->explicitUseStrict = bce->sc->hasExplicitUseStrict();
script->bindingsAccessedDynamically = bce->sc->bindingsAccessedDynamically();