mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-29 18:47:31 +09:00
Issue #2155 - Split TDZCheckCache, IfEmitter/InternalIfEmitter, JumpList from BytecodeEmitter
This commit is contained in:
parent
05d29a53db
commit
e8fee20566
9 changed files with 740 additions and 333 deletions
|
|
@ -27,7 +27,9 @@
|
||||||
#include "jstypes.h"
|
#include "jstypes.h"
|
||||||
#include "jsutil.h"
|
#include "jsutil.h"
|
||||||
|
|
||||||
|
#include "frontend/IfEmitter.h"
|
||||||
#include "frontend/Parser.h"
|
#include "frontend/Parser.h"
|
||||||
|
#include "frontend/TDZCheckCache.h"
|
||||||
#include "frontend/TokenStream.h"
|
#include "frontend/TokenStream.h"
|
||||||
#include "vm/Debugger.h"
|
#include "vm/Debugger.h"
|
||||||
#include "vm/GeneratorObject.h"
|
#include "vm/GeneratorObject.h"
|
||||||
|
|
@ -67,28 +69,6 @@ ParseNodeRequiresSpecialLineNumberNotes(ParseNode* pn)
|
||||||
return pn->getKind() == PNK_WHILE || pn->getKind() == PNK_FOR;
|
return pn->getKind() == PNK_WHILE || pn->getKind() == PNK_FOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A cache that tracks superfluous TDZ checks.
|
|
||||||
//
|
|
||||||
// Each basic block should have a TDZCheckCache in scope. Some NestableControl
|
|
||||||
// subclasses contain a TDZCheckCache.
|
|
||||||
class BytecodeEmitter::TDZCheckCache : public Nestable<BytecodeEmitter::TDZCheckCache>
|
|
||||||
{
|
|
||||||
PooledMapPtr<CheckTDZMap> cache_;
|
|
||||||
|
|
||||||
MOZ_MUST_USE bool ensureCache(BytecodeEmitter* bce) {
|
|
||||||
return cache_ || cache_.acquire(bce->cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit TDZCheckCache(BytecodeEmitter* bce)
|
|
||||||
: Nestable<TDZCheckCache>(&bce->innermostTDZCheckCache),
|
|
||||||
cache_(bce->cx->frontendCollectionPool())
|
|
||||||
{ }
|
|
||||||
|
|
||||||
Maybe<MaybeCheckTDZ> needsTDZCheck(BytecodeEmitter* bce, JSAtom* name);
|
|
||||||
MOZ_MUST_USE bool noteTDZCheck(BytecodeEmitter* bce, JSAtom* name, MaybeCheckTDZ check);
|
|
||||||
};
|
|
||||||
|
|
||||||
class BytecodeEmitter::NestableControl : public Nestable<BytecodeEmitter::NestableControl>
|
class BytecodeEmitter::NestableControl : public Nestable<BytecodeEmitter::NestableControl>
|
||||||
{
|
{
|
||||||
StatementKind kind_;
|
StatementKind kind_;
|
||||||
|
|
@ -212,7 +192,7 @@ class LoopControl : public BreakableControl
|
||||||
{
|
{
|
||||||
// Loops' children are emitted in dominance order, so they can always
|
// Loops' children are emitted in dominance order, so they can always
|
||||||
// have a TDZCheckCache.
|
// have a TDZCheckCache.
|
||||||
BytecodeEmitter::TDZCheckCache tdzCache_;
|
TDZCheckCache tdzCache_;
|
||||||
|
|
||||||
// Stack depth when this loop was pushed on the control stack.
|
// Stack depth when this loop was pushed on the control stack.
|
||||||
int32_t stackDepth_;
|
int32_t stackDepth_;
|
||||||
|
|
@ -1461,53 +1441,6 @@ BytecodeEmitter::EmitterScope::leave(BytecodeEmitter* bce, bool nonLocal)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<MaybeCheckTDZ>
|
|
||||||
BytecodeEmitter::TDZCheckCache::needsTDZCheck(BytecodeEmitter* bce, JSAtom* name)
|
|
||||||
{
|
|
||||||
if (!ensureCache(bce))
|
|
||||||
return Nothing();
|
|
||||||
|
|
||||||
CheckTDZMap::AddPtr p = cache_->lookupForAdd(name);
|
|
||||||
if (p)
|
|
||||||
return Some(p->value().wrapped);
|
|
||||||
|
|
||||||
MaybeCheckTDZ rv = CheckTDZ;
|
|
||||||
for (TDZCheckCache* it = enclosing(); it; it = it->enclosing()) {
|
|
||||||
if (it->cache_) {
|
|
||||||
if (CheckTDZMap::Ptr p2 = it->cache_->lookup(name)) {
|
|
||||||
rv = p2->value();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cache_->add(p, name, rv)) {
|
|
||||||
ReportOutOfMemory(bce->cx);
|
|
||||||
return Nothing();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Some(rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
BytecodeEmitter::TDZCheckCache::noteTDZCheck(BytecodeEmitter* bce, JSAtom* name,
|
|
||||||
MaybeCheckTDZ check)
|
|
||||||
{
|
|
||||||
if (!ensureCache(bce))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
CheckTDZMap::AddPtr p = cache_->lookupForAdd(name);
|
|
||||||
if (p) {
|
|
||||||
MOZ_ASSERT(!check, "TDZ only needs to be checked once per binding per basic block.");
|
|
||||||
p->value() = check;
|
|
||||||
} else {
|
|
||||||
if (!cache_->add(p, name, check))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MOZ_STACK_CLASS TryEmitter
|
class MOZ_STACK_CLASS TryEmitter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -1836,156 +1769,6 @@ class MOZ_STACK_CLASS TryEmitter
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class MOZ_STACK_CLASS IfThenElseEmitter
|
|
||||||
{
|
|
||||||
BytecodeEmitter* bce_;
|
|
||||||
JumpList jumpAroundThen_;
|
|
||||||
JumpList jumpsAroundElse_;
|
|
||||||
unsigned noteIndex_;
|
|
||||||
int32_t thenDepth_;
|
|
||||||
#ifdef DEBUG
|
|
||||||
int32_t pushed_;
|
|
||||||
bool calculatedPushed_;
|
|
||||||
#endif
|
|
||||||
enum State {
|
|
||||||
Start,
|
|
||||||
If,
|
|
||||||
Cond,
|
|
||||||
IfElse,
|
|
||||||
Else,
|
|
||||||
End
|
|
||||||
};
|
|
||||||
State state_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit IfThenElseEmitter(BytecodeEmitter* bce)
|
|
||||||
: bce_(bce),
|
|
||||||
noteIndex_(-1),
|
|
||||||
thenDepth_(0),
|
|
||||||
#ifdef DEBUG
|
|
||||||
pushed_(0),
|
|
||||||
calculatedPushed_(false),
|
|
||||||
#endif
|
|
||||||
state_(Start)
|
|
||||||
{}
|
|
||||||
|
|
||||||
~IfThenElseEmitter()
|
|
||||||
{}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool emitIf(State nextState) {
|
|
||||||
MOZ_ASSERT(state_ == Start || state_ == Else);
|
|
||||||
MOZ_ASSERT(nextState == If || nextState == IfElse || nextState == Cond);
|
|
||||||
|
|
||||||
// Clear jumpAroundThen_ offset that points previous JSOP_IFEQ.
|
|
||||||
if (state_ == Else)
|
|
||||||
jumpAroundThen_ = JumpList();
|
|
||||||
|
|
||||||
// Emit an annotated branch-if-false around the then part.
|
|
||||||
SrcNoteType type = nextState == If ? SRC_IF : nextState == IfElse ? SRC_IF_ELSE : SRC_COND;
|
|
||||||
if (!bce_->newSrcNote(type, ¬eIndex_))
|
|
||||||
return false;
|
|
||||||
if (!bce_->emitJump(JSOP_IFEQ, &jumpAroundThen_))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// To restore stack depth in else part, save depth of the then part.
|
|
||||||
#ifdef DEBUG
|
|
||||||
// If DEBUG, this is also necessary to calculate |pushed_|.
|
|
||||||
thenDepth_ = bce_->stackDepth;
|
|
||||||
#else
|
|
||||||
if (nextState == IfElse || nextState == Cond)
|
|
||||||
thenDepth_ = bce_->stackDepth;
|
|
||||||
#endif
|
|
||||||
state_ = nextState;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool emitIf() {
|
|
||||||
return emitIf(If);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool emitCond() {
|
|
||||||
return emitIf(Cond);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool emitIfElse() {
|
|
||||||
return emitIf(IfElse);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool emitElse() {
|
|
||||||
MOZ_ASSERT(state_ == IfElse || state_ == Cond);
|
|
||||||
|
|
||||||
calculateOrCheckPushed();
|
|
||||||
|
|
||||||
// Emit a jump from the end of our then part around the else part. The
|
|
||||||
// patchJumpsToTarget call at the bottom of this function will fix up
|
|
||||||
// the offset with jumpsAroundElse value.
|
|
||||||
if (!bce_->emitJump(JSOP_GOTO, &jumpsAroundElse_))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Ensure the branch-if-false comes here, then emit the else.
|
|
||||||
if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Annotate SRC_IF_ELSE or SRC_COND with the offset from branch to
|
|
||||||
// jump, for IonMonkey's benefit. We can't just "back up" from the pc
|
|
||||||
// of the else clause, because we don't know whether an extended
|
|
||||||
// jump was required to leap from the end of the then clause over
|
|
||||||
// the else clause.
|
|
||||||
if (!bce_->setSrcNoteOffset(noteIndex_, 0,
|
|
||||||
jumpsAroundElse_.offset - jumpAroundThen_.offset))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore stack depth of the then part.
|
|
||||||
bce_->stackDepth = thenDepth_;
|
|
||||||
state_ = Else;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool emitEnd() {
|
|
||||||
MOZ_ASSERT(state_ == If || state_ == Else);
|
|
||||||
|
|
||||||
calculateOrCheckPushed();
|
|
||||||
|
|
||||||
if (state_ == If) {
|
|
||||||
// No else part, fixup the branch-if-false to come here.
|
|
||||||
if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patch all the jumps around else parts.
|
|
||||||
if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
state_ = End;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void calculateOrCheckPushed() {
|
|
||||||
#ifdef DEBUG
|
|
||||||
if (!calculatedPushed_) {
|
|
||||||
pushed_ = bce_->stackDepth - thenDepth_;
|
|
||||||
calculatedPushed_ = true;
|
|
||||||
} else {
|
|
||||||
MOZ_ASSERT(pushed_ == bce_->stackDepth - thenDepth_);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
int32_t pushed() const {
|
|
||||||
return pushed_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t popped() const {
|
|
||||||
return -pushed_;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
// Class for emitting bytecode for optional expressions.
|
// Class for emitting bytecode for optional expressions.
|
||||||
class MOZ_RAII OptionalEmitter
|
class MOZ_RAII OptionalEmitter
|
||||||
{
|
{
|
||||||
|
|
@ -1995,7 +1778,7 @@ class MOZ_RAII OptionalEmitter
|
||||||
private:
|
private:
|
||||||
BytecodeEmitter* bce_;
|
BytecodeEmitter* bce_;
|
||||||
|
|
||||||
BytecodeEmitter::TDZCheckCache tdzCache_;
|
TDZCheckCache tdzCache_;
|
||||||
|
|
||||||
// Jump target for short circuiting code, which has null or undefined values.
|
// Jump target for short circuiting code, which has null or undefined values.
|
||||||
JumpList jumpShortCircuit_;
|
JumpList jumpShortCircuit_;
|
||||||
|
|
@ -2150,8 +1933,8 @@ class ForOfLoopControl : public LoopControl
|
||||||
if (!bce->emit1(JSOP_STRICTNE)) // ITER ... EXCEPTION NE
|
if (!bce->emit1(JSOP_STRICTNE)) // ITER ... EXCEPTION NE
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
IfThenElseEmitter ifIteratorIsNotClosed(bce);
|
InternalIfEmitter ifIteratorIsNotClosed(bce);
|
||||||
if (!ifIteratorIsNotClosed.emitIf()) // ITER ... EXCEPTION
|
if (!ifIteratorIsNotClosed.emitThen()) // ITER ... EXCEPTION
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MOZ_ASSERT(slotFromTop == unsigned(bce->stackDepth - iterDepth_));
|
MOZ_ASSERT(slotFromTop == unsigned(bce->stackDepth - iterDepth_));
|
||||||
|
|
@ -2174,10 +1957,10 @@ class ForOfLoopControl : public LoopControl
|
||||||
if (!tryCatch_->emitFinally())
|
if (!tryCatch_->emitFinally())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
IfThenElseEmitter ifGeneratorClosing(bce);
|
InternalIfEmitter ifGeneratorClosing(bce);
|
||||||
if (!bce->emit1(JSOP_ISGENCLOSING)) // ITER ... FTYPE FVALUE CLOSING
|
if (!bce->emit1(JSOP_ISGENCLOSING)) // ITER ... FTYPE FVALUE CLOSING
|
||||||
return false;
|
return false;
|
||||||
if (!ifGeneratorClosing.emitIf()) // ITER ... FTYPE FVALUE
|
if (!ifGeneratorClosing.emitThen()) // ITER ... FTYPE FVALUE
|
||||||
return false;
|
return false;
|
||||||
if (!bce->emitDupAt(slotFromTop + 1)) // ITER ... FTYPE FVALUE ITER
|
if (!bce->emitDupAt(slotFromTop + 1)) // ITER ... FTYPE FVALUE ITER
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2498,27 +2281,6 @@ BytecodeEmitter::emitJumpTarget(JumpTarget* target)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
JumpList::push(jsbytecode* code, ptrdiff_t jumpOffset)
|
|
||||||
{
|
|
||||||
SET_JUMP_OFFSET(&code[jumpOffset], offset - jumpOffset);
|
|
||||||
offset = jumpOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
JumpList::patchAll(jsbytecode* code, JumpTarget target)
|
|
||||||
{
|
|
||||||
ptrdiff_t delta;
|
|
||||||
for (ptrdiff_t jumpOffset = offset; jumpOffset != -1; jumpOffset += delta) {
|
|
||||||
jsbytecode* pc = &code[jumpOffset];
|
|
||||||
MOZ_ASSERT(IsJumpOpcode(JSOp(*pc)) || JSOp(*pc) == JSOP_LABEL);
|
|
||||||
delta = GET_JUMP_OFFSET(pc);
|
|
||||||
MOZ_ASSERT(delta < 0);
|
|
||||||
ptrdiff_t span = target.offset - jumpOffset;
|
|
||||||
SET_JUMP_OFFSET(pc, span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BytecodeEmitter::emitJumpNoFallthrough(JSOp op, JumpList* jump)
|
BytecodeEmitter::emitJumpNoFallthrough(JSOp op, JumpList* jump)
|
||||||
{
|
{
|
||||||
|
|
@ -5467,14 +5229,14 @@ BytecodeEmitter::emitIteratorCloseInScope(EmitterScope& currentScope,
|
||||||
// Step 4.
|
// Step 4.
|
||||||
//
|
//
|
||||||
// Do nothing if "return" is null or undefined.
|
// Do nothing if "return" is null or undefined.
|
||||||
IfThenElseEmitter ifReturnMethodIsDefined(this);
|
InternalIfEmitter ifReturnMethodIsDefined(this);
|
||||||
if (!emit1(JSOP_DUP)) // ... ITER RET RET
|
if (!emit1(JSOP_DUP)) // ... ITER RET RET
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_UNDEFINED)) // ... ITER RET RET UNDEFINED
|
if (!emit1(JSOP_UNDEFINED)) // ... ITER RET RET UNDEFINED
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_NE)) // ... ITER RET ?NEQL
|
if (!emit1(JSOP_NE)) // ... ITER RET ?NEQL
|
||||||
return false;
|
return false;
|
||||||
if (!ifReturnMethodIsDefined.emitIfElse())
|
if (!ifReturnMethodIsDefined.emitThenElse())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (completionKind == CompletionKind::Throw) {
|
if (completionKind == CompletionKind::Throw) {
|
||||||
|
|
@ -5847,12 +5609,12 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
|
||||||
}
|
}
|
||||||
|
|
||||||
if (member->isKind(PNK_SPREAD)) {
|
if (member->isKind(PNK_SPREAD)) {
|
||||||
IfThenElseEmitter ifThenElse(this);
|
InternalIfEmitter ifThenElse(this);
|
||||||
if (!isFirst) {
|
if (!isFirst) {
|
||||||
// If spread is not the first element of the pattern,
|
// If spread is not the first element of the pattern,
|
||||||
// iterator can already be completed.
|
// iterator can already be completed.
|
||||||
// ... OBJ ITER *LREF DONE
|
// ... OBJ ITER *LREF DONE
|
||||||
if (!ifThenElse.emitIfElse()) // ... OBJ ITER *LREF
|
if (!ifThenElse.emitThenElse()) // ... OBJ ITER *LREF
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ ITER *LREF ARRAY
|
if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ ITER *LREF ARRAY
|
||||||
|
|
@ -5902,10 +5664,10 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
|
||||||
|
|
||||||
MOZ_ASSERT(!member->isKind(PNK_SPREAD));
|
MOZ_ASSERT(!member->isKind(PNK_SPREAD));
|
||||||
|
|
||||||
IfThenElseEmitter ifAlreadyDone(this);
|
InternalIfEmitter ifAlreadyDone(this);
|
||||||
if (!isFirst) {
|
if (!isFirst) {
|
||||||
// ... OBJ ITER *LREF DONE
|
// ... OBJ ITER *LREF DONE
|
||||||
if (!ifAlreadyDone.emitIfElse()) // ... OBJ ITER *LREF
|
if (!ifAlreadyDone.emitThenElse()) // ... OBJ ITER *LREF
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!emit1(JSOP_UNDEFINED)) // ... OBJ ITER *LREF UNDEF
|
if (!emit1(JSOP_UNDEFINED)) // ... OBJ ITER *LREF UNDEF
|
||||||
|
|
@ -5942,8 +5704,8 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
|
||||||
if (!emit2(JSOP_UNPICK, emitted + 2)) // ... OBJ ITER DONE *LREF RESULT DONE
|
if (!emit2(JSOP_UNPICK, emitted + 2)) // ... OBJ ITER DONE *LREF RESULT DONE
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
IfThenElseEmitter ifDone(this);
|
InternalIfEmitter ifDone(this);
|
||||||
if (!ifDone.emitIfElse()) // ... OBJ ITER DONE *LREF RESULT
|
if (!ifDone.emitThenElse()) // ... OBJ ITER DONE *LREF RESULT
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!emit1(JSOP_POP)) // ... OBJ ITER DONE *LREF
|
if (!emit1(JSOP_POP)) // ... OBJ ITER DONE *LREF
|
||||||
|
|
@ -5994,8 +5756,8 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
|
||||||
// The last DONE value is on top of the stack. If not DONE, call
|
// The last DONE value is on top of the stack. If not DONE, call
|
||||||
// IteratorClose.
|
// IteratorClose.
|
||||||
// ... OBJ ITER DONE
|
// ... OBJ ITER DONE
|
||||||
IfThenElseEmitter ifDone(this);
|
InternalIfEmitter ifDone(this);
|
||||||
if (!ifDone.emitIfElse()) // ... OBJ ITER
|
if (!ifDone.emitThenElse()) // ... OBJ ITER
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_POP)) // ... OBJ
|
if (!emit1(JSOP_POP)) // ... OBJ
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -6931,7 +6693,7 @@ BytecodeEmitter::emitTry(ParseNode* pn)
|
||||||
bool
|
bool
|
||||||
BytecodeEmitter::emitIf(ParseNode* pn)
|
BytecodeEmitter::emitIf(ParseNode* pn)
|
||||||
{
|
{
|
||||||
IfThenElseEmitter ifThenElse(this);
|
IfEmitter ifThenElse(this);
|
||||||
|
|
||||||
if_again:
|
if_again:
|
||||||
/* Emit code for the condition before pushing stmtInfo. */
|
/* Emit code for the condition before pushing stmtInfo. */
|
||||||
|
|
@ -6940,10 +6702,10 @@ BytecodeEmitter::emitIf(ParseNode* pn)
|
||||||
|
|
||||||
ParseNode* elseNode = pn->pn_kid3;
|
ParseNode* elseNode = pn->pn_kid3;
|
||||||
if (elseNode) {
|
if (elseNode) {
|
||||||
if (!ifThenElse.emitIfElse())
|
if (!ifThenElse.emitThenElse())
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!ifThenElse.emitIf())
|
if (!ifThenElse.emitThen())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6952,14 +6714,18 @@ BytecodeEmitter::emitIf(ParseNode* pn)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (elseNode) {
|
if (elseNode) {
|
||||||
if (!ifThenElse.emitElse())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (elseNode->isKind(PNK_IF)) {
|
if (elseNode->isKind(PNK_IF)) {
|
||||||
pn = elseNode;
|
pn = elseNode;
|
||||||
|
|
||||||
|
if (!ifThenElse.emitElseIf())
|
||||||
|
return false;
|
||||||
|
|
||||||
goto if_again;
|
goto if_again;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ifThenElse.emitElse())
|
||||||
|
return false;
|
||||||
|
|
||||||
/* Emit code for the else part. */
|
/* Emit code for the else part. */
|
||||||
if (!emitTreeInBranch(elseNode))
|
if (!emitTreeInBranch(elseNode))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -7198,14 +6964,14 @@ BytecodeEmitter::emitAsyncIterator()
|
||||||
if (!emitElemOpBase(JSOP_CALLELEM)) // OBJ ITERFN
|
if (!emitElemOpBase(JSOP_CALLELEM)) // OBJ ITERFN
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
IfThenElseEmitter ifAsyncIterIsUndefined(this);
|
InternalIfEmitter ifAsyncIterIsUndefined(this);
|
||||||
if (!emit1(JSOP_DUP)) // OBJ ITERFN ITERFN
|
if (!emit1(JSOP_DUP)) // OBJ ITERFN ITERFN
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_UNDEFINED)) // OBJ ITERFN ITERFN UNDEF
|
if (!emit1(JSOP_UNDEFINED)) // OBJ ITERFN ITERFN UNDEF
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_EQ)) // OBJ ITERFN EQ
|
if (!emit1(JSOP_EQ)) // OBJ ITERFN EQ
|
||||||
return false;
|
return false;
|
||||||
if (!ifAsyncIterIsUndefined.emitIfElse()) // OBJ ITERFN
|
if (!ifAsyncIterIsUndefined.emitThenElse()) // OBJ ITERFN
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!emit1(JSOP_POP)) // OBJ
|
if (!emit1(JSOP_POP)) // OBJ
|
||||||
|
|
@ -8983,8 +8749,8 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter)
|
||||||
if (!emit1(JSOP_EQ)) // ITER RESULT EXCEPTION ITER THROW ?EQL
|
if (!emit1(JSOP_EQ)) // ITER RESULT EXCEPTION ITER THROW ?EQL
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
IfThenElseEmitter ifThrowMethodIsNotDefined(this);
|
InternalIfEmitter ifThrowMethodIsNotDefined(this);
|
||||||
if (!ifThrowMethodIsNotDefined.emitIf()) // ITER RESULT EXCEPTION ITER THROW
|
if (!ifThrowMethodIsNotDefined.emitThen()) // ITER RESULT EXCEPTION ITER THROW
|
||||||
return false;
|
return false;
|
||||||
savedDepthTemp = stackDepth;
|
savedDepthTemp = stackDepth;
|
||||||
if (!emit1(JSOP_POP)) // ITER RESULT EXCEPTION ITER
|
if (!emit1(JSOP_POP)) // ITER RESULT EXCEPTION ITER
|
||||||
|
|
@ -9039,10 +8805,10 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter)
|
||||||
// Call iterator.return() for receiving a "forced return" completion from
|
// Call iterator.return() for receiving a "forced return" completion from
|
||||||
// the generator.
|
// the generator.
|
||||||
|
|
||||||
IfThenElseEmitter ifGeneratorClosing(this);
|
InternalIfEmitter ifGeneratorClosing(this);
|
||||||
if (!emit1(JSOP_ISGENCLOSING)) // ITER RESULT FTYPE FVALUE CLOSING
|
if (!emit1(JSOP_ISGENCLOSING)) // ITER RESULT FTYPE FVALUE CLOSING
|
||||||
return false;
|
return false;
|
||||||
if (!ifGeneratorClosing.emitIf()) // ITER RESULT FTYPE FVALUE
|
if (!ifGeneratorClosing.emitThen()) // ITER RESULT FTYPE FVALUE
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Step ii.
|
// Step ii.
|
||||||
|
|
@ -9058,7 +8824,7 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter)
|
||||||
// Step iii.
|
// Step iii.
|
||||||
//
|
//
|
||||||
// Do nothing if "return" is undefined.
|
// Do nothing if "return" is undefined.
|
||||||
IfThenElseEmitter ifReturnMethodIsDefined(this);
|
InternalIfEmitter ifReturnMethodIsDefined(this);
|
||||||
if (!emit1(JSOP_DUP)) // ITER RESULT FTYPE FVALUE ITER RET RET
|
if (!emit1(JSOP_DUP)) // ITER RESULT FTYPE FVALUE ITER RET RET
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_UNDEFINED)) // ITER RESULT FTYPE FVALUE ITER RET RET UNDEFINED
|
if (!emit1(JSOP_UNDEFINED)) // ITER RESULT FTYPE FVALUE ITER RET RET UNDEFINED
|
||||||
|
|
@ -9070,7 +8836,7 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter)
|
||||||
//
|
//
|
||||||
// Call "return" with the argument passed to Generator.prototype.return,
|
// Call "return" with the argument passed to Generator.prototype.return,
|
||||||
// which is currently in rval.value.
|
// which is currently in rval.value.
|
||||||
if (!ifReturnMethodIsDefined.emitIfElse()) // ITER OLDRESULT FTYPE FVALUE ITER RET
|
if (!ifReturnMethodIsDefined.emitThenElse()) // ITER OLDRESULT FTYPE FVALUE ITER RET
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_SWAP)) // ITER OLDRESULT FTYPE FVALUE RET ITER
|
if (!emit1(JSOP_SWAP)) // ITER OLDRESULT FTYPE FVALUE RET ITER
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -9095,12 +8861,12 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter)
|
||||||
//
|
//
|
||||||
// Check if the returned object from iterator.return() is done. If not,
|
// Check if the returned object from iterator.return() is done. If not,
|
||||||
// continuing yielding.
|
// continuing yielding.
|
||||||
IfThenElseEmitter ifReturnDone(this);
|
InternalIfEmitter ifReturnDone(this);
|
||||||
if (!emit1(JSOP_DUP)) // ITER OLDRESULT FTYPE FVALUE RESULT RESULT
|
if (!emit1(JSOP_DUP)) // ITER OLDRESULT FTYPE FVALUE RESULT RESULT
|
||||||
return false;
|
return false;
|
||||||
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER OLDRESULT FTYPE FVALUE RESULT DONE
|
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER OLDRESULT FTYPE FVALUE RESULT DONE
|
||||||
return false;
|
return false;
|
||||||
if (!ifReturnDone.emitIfElse()) // ITER OLDRESULT FTYPE FVALUE RESULT
|
if (!ifReturnDone.emitThenElse()) // ITER OLDRESULT FTYPE FVALUE RESULT
|
||||||
return false;
|
return false;
|
||||||
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER OLDRESULT FTYPE FVALUE VALUE
|
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER OLDRESULT FTYPE FVALUE VALUE
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -10288,7 +10054,7 @@ BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional,
|
||||||
if (!emitTree(&conditional.condition()))
|
if (!emitTree(&conditional.condition()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
IfThenElseEmitter ifThenElse(this);
|
IfEmitter ifThenElse(this);
|
||||||
if (!ifThenElse.emitCond())
|
if (!ifThenElse.emitCond())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -12159,7 +11925,7 @@ OptionalEmitter::emitJumpShortCircuit() {
|
||||||
state_ == State::ShortCircuitForCall);
|
state_ == State::ShortCircuitForCall);
|
||||||
MOZ_ASSERT(initialDepth_ + 1 == bce_->stackDepth);
|
MOZ_ASSERT(initialDepth_ + 1 == bce_->stackDepth);
|
||||||
|
|
||||||
IfThenElseEmitter ifEmitter(bce_);
|
InternalIfEmitter ifEmitter(bce_);
|
||||||
if (!bce_->emitPushNotUndefinedOrNull()) {
|
if (!bce_->emitPushNotUndefinedOrNull()) {
|
||||||
// [stack] OBJ NOT-UNDEFINED-OR-NULL
|
// [stack] OBJ NOT-UNDEFINED-OR-NULL
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -12170,7 +11936,7 @@ OptionalEmitter::emitJumpShortCircuit() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ifEmitter.emitIf() /* emitThen() */) {
|
if (!ifEmitter.emitThen()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -12204,7 +11970,7 @@ OptionalEmitter::emitJumpShortCircuitForCall() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IfThenElseEmitter ifEmitter(bce_);
|
InternalIfEmitter ifEmitter(bce_);
|
||||||
if (!bce_->emitPushNotUndefinedOrNull()) {
|
if (!bce_->emitPushNotUndefinedOrNull()) {
|
||||||
// [stack] THIS CALLEE NOT-UNDEFINED-OR-NULL
|
// [stack] THIS CALLEE NOT-UNDEFINED-OR-NULL
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -12215,7 +11981,7 @@ OptionalEmitter::emitJumpShortCircuitForCall() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ifEmitter.emitIf() /* emitThen() */) {
|
if (!ifEmitter.emitThen()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include "jsscript.h"
|
#include "jsscript.h"
|
||||||
|
|
||||||
#include "ds/InlineTable.h"
|
#include "ds/InlineTable.h"
|
||||||
|
#include "frontend/JumpList.h"
|
||||||
#include "frontend/Parser.h"
|
#include "frontend/Parser.h"
|
||||||
#include "frontend/SharedContext.h"
|
#include "frontend/SharedContext.h"
|
||||||
#include "frontend/SourceNotes.h"
|
#include "frontend/SourceNotes.h"
|
||||||
|
|
@ -118,69 +119,15 @@ static size_t MaxSrcNotesLength = INT32_MAX;
|
||||||
typedef Vector<jsbytecode, 256> BytecodeVector;
|
typedef Vector<jsbytecode, 256> BytecodeVector;
|
||||||
typedef Vector<jssrcnote, 64> SrcNotesVector;
|
typedef Vector<jssrcnote, 64> SrcNotesVector;
|
||||||
|
|
||||||
// Linked list of jump instructions that need to be patched. The linked list is
|
|
||||||
// stored in the bytes of the incomplete bytecode that will be patched, so no
|
|
||||||
// extra memory is needed, and patching the instructions destroys the list.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
//
|
|
||||||
// JumpList brList;
|
|
||||||
// if (!emitJump(JSOP_IFEQ, &brList))
|
|
||||||
// return false;
|
|
||||||
// ...
|
|
||||||
// JumpTarget label;
|
|
||||||
// if (!emitJumpTarget(&label))
|
|
||||||
// return false;
|
|
||||||
// ...
|
|
||||||
// if (!emitJump(JSOP_GOTO, &brList))
|
|
||||||
// return false;
|
|
||||||
// ...
|
|
||||||
// patchJumpsToTarget(brList, label);
|
|
||||||
//
|
|
||||||
// +-> -1
|
|
||||||
// |
|
|
||||||
// |
|
|
||||||
// ifeq .. <+ + +-+ ifeq ..
|
|
||||||
// .. | | ..
|
|
||||||
// label: | +-> label:
|
|
||||||
// jumptarget | | jumptarget
|
|
||||||
// .. | | ..
|
|
||||||
// goto .. <+ + +-+ goto .. <+
|
|
||||||
// | |
|
|
||||||
// | |
|
|
||||||
// + +
|
|
||||||
// brList brList
|
|
||||||
//
|
|
||||||
// | ^
|
|
||||||
// +------- patchJumpsToTarget -------+
|
|
||||||
//
|
|
||||||
|
|
||||||
// Offset of a jump target instruction, used for patching jump instructions.
|
|
||||||
struct JumpTarget {
|
|
||||||
ptrdiff_t offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct JumpList {
|
|
||||||
// -1 is used to mark the end of jump lists.
|
|
||||||
JumpList() : offset(-1) {}
|
|
||||||
ptrdiff_t offset;
|
|
||||||
|
|
||||||
// Add a jump instruction to the list.
|
|
||||||
void push(jsbytecode* code, ptrdiff_t jumpOffset);
|
|
||||||
|
|
||||||
// Patch all jump instructions in this list to jump to `target`. This
|
|
||||||
// clobbers the list.
|
|
||||||
void patchAll(jsbytecode* code, JumpTarget target);
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ValueUsage {
|
enum class ValueUsage {
|
||||||
WantValue,
|
WantValue,
|
||||||
IgnoreValue
|
IgnoreValue
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TDZCheckCache;
|
||||||
|
|
||||||
struct MOZ_STACK_CLASS BytecodeEmitter
|
struct MOZ_STACK_CLASS BytecodeEmitter
|
||||||
{
|
{
|
||||||
class TDZCheckCache;
|
|
||||||
class NestableControl;
|
class NestableControl;
|
||||||
class EmitterScope;
|
class EmitterScope;
|
||||||
|
|
||||||
|
|
|
||||||
231
js/src/frontend/IfEmitter.cpp
Normal file
231
js/src/frontend/IfEmitter.cpp
Normal file
|
|
@ -0,0 +1,231 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "frontend/IfEmitter.h"
|
||||||
|
|
||||||
|
#include "frontend/BytecodeEmitter.h"
|
||||||
|
#include "frontend/SourceNotes.h"
|
||||||
|
#include "vm/Opcodes.h"
|
||||||
|
|
||||||
|
using namespace js;
|
||||||
|
using namespace js::frontend;
|
||||||
|
|
||||||
|
IfEmitter::IfEmitter(BytecodeEmitter* bce, Kind kind)
|
||||||
|
: bce_(bce),
|
||||||
|
noteIndex_(-1),
|
||||||
|
thenDepth_(0),
|
||||||
|
kind_(kind)
|
||||||
|
#ifdef DEBUG
|
||||||
|
, pushed_(0),
|
||||||
|
calculatedPushed_(false),
|
||||||
|
state_(State::Start)
|
||||||
|
#endif
|
||||||
|
{}
|
||||||
|
|
||||||
|
IfEmitter::IfEmitter(BytecodeEmitter* bce)
|
||||||
|
: IfEmitter(bce, Kind::MayContainLexicalAccessInBranch)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitIfInternal(SrcNoteType type)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome());
|
||||||
|
MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing());
|
||||||
|
|
||||||
|
// The end of TDZCheckCache for cond for else-if.
|
||||||
|
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
||||||
|
tdzCache_.reset();
|
||||||
|
|
||||||
|
// Emit an annotated branch-if-false around the then part.
|
||||||
|
if (!bce_->newSrcNote(type, ¬eIndex_))
|
||||||
|
return false;
|
||||||
|
if (!bce_->emitJump(JSOP_IFEQ, &jumpAroundThen_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// To restore stack depth in else part, save depth of the then part.
|
||||||
|
#ifdef DEBUG
|
||||||
|
// If DEBUG, this is also necessary to calculate |pushed_|.
|
||||||
|
thenDepth_ = bce_->stackDepth;
|
||||||
|
#else
|
||||||
|
if (type == SRC_COND || type == SRC_IF_ELSE)
|
||||||
|
thenDepth_ = bce_->stackDepth;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Enclose then-branch with TDZCheckCache.
|
||||||
|
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
||||||
|
tdzCache_.emplace(bce_);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
IfEmitter::calculateOrCheckPushed()
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (!calculatedPushed_) {
|
||||||
|
pushed_ = bce_->stackDepth - thenDepth_;
|
||||||
|
calculatedPushed_ = true;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(pushed_ == bce_->stackDepth - thenDepth_);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitThen()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
|
||||||
|
if (!emitIfInternal(SRC_IF))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::Then;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitCond()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::Start);
|
||||||
|
if (!emitIfInternal(SRC_COND))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::Cond;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitThenElse()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
|
||||||
|
if (!emitIfInternal(SRC_IF_ELSE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::ThenElse;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitElseInternal()
|
||||||
|
{
|
||||||
|
calculateOrCheckPushed();
|
||||||
|
|
||||||
|
// The end of TDZCheckCache for then-clause.
|
||||||
|
if (kind_ == Kind::MayContainLexicalAccessInBranch) {
|
||||||
|
MOZ_ASSERT(tdzCache_.isSome());
|
||||||
|
tdzCache_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit a jump from the end of our then part around the else part. The
|
||||||
|
// patchJumpsToTarget call at the bottom of this function will fix up
|
||||||
|
// the offset with jumpsAroundElse value.
|
||||||
|
if (!bce_->emitJump(JSOP_GOTO, &jumpsAroundElse_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Ensure the branch-if-false comes here, then emit the else.
|
||||||
|
if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Annotate SRC_IF_ELSE or SRC_COND with the offset from branch to
|
||||||
|
// jump, for IonMonkey's benefit. We can't just "back up" from the pc
|
||||||
|
// of the else clause, because we don't know whether an extended
|
||||||
|
// jump was required to leap from the end of the then clause over
|
||||||
|
// the else clause.
|
||||||
|
if (!bce_->setSrcNoteOffset(noteIndex_, 0,
|
||||||
|
jumpsAroundElse_.offset - jumpAroundThen_.offset))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear jumpAroundThen_ offset, to tell emitEnd there was an else part.
|
||||||
|
jumpAroundThen_ = JumpList();
|
||||||
|
|
||||||
|
// Restore stack depth of the then part.
|
||||||
|
bce_->stackDepth = thenDepth_;
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::Else;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitElse()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::ThenElse || state_ == State::Cond);
|
||||||
|
|
||||||
|
if (!emitElseInternal())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Enclose else-branch with TDZCheckCache.
|
||||||
|
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
||||||
|
tdzCache_.emplace(bce_);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::Else;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitElseIf()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::ThenElse);
|
||||||
|
|
||||||
|
if (!emitElseInternal())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Enclose cond for else-if with TDZCheckCache.
|
||||||
|
if (kind_ == Kind::MayContainLexicalAccessInBranch)
|
||||||
|
tdzCache_.emplace(bce_);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::ElseIf;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IfEmitter::emitEnd()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(state_ == State::Then || state_ == State::Else);
|
||||||
|
// If there was an else part for the last branch, jumpAroundThen_ is
|
||||||
|
// already fixed up when emitting the else part.
|
||||||
|
MOZ_ASSERT_IF(state_ == State::Then, jumpAroundThen_.offset != -1);
|
||||||
|
MOZ_ASSERT_IF(state_ == State::Else, jumpAroundThen_.offset == -1);
|
||||||
|
|
||||||
|
// The end of TDZCheckCache for then or else-clause.
|
||||||
|
if (kind_ == Kind::MayContainLexicalAccessInBranch) {
|
||||||
|
MOZ_ASSERT(tdzCache_.isSome());
|
||||||
|
tdzCache_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateOrCheckPushed();
|
||||||
|
|
||||||
|
if (jumpAroundThen_.offset != -1) {
|
||||||
|
// No else part for the last branch, fixup the branch-if-false to
|
||||||
|
// come here.
|
||||||
|
if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch all the jumps around else parts.
|
||||||
|
if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::End;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalIfEmitter::InternalIfEmitter(BytecodeEmitter* bce)
|
||||||
|
: IfEmitter(bce, Kind::NoLexicalAccessInBranch)
|
||||||
|
{}
|
||||||
220
js/src/frontend/IfEmitter.h
Normal file
220
js/src/frontend/IfEmitter.h
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef frontend_IfEmitter_h
|
||||||
|
#define frontend_IfEmitter_h
|
||||||
|
|
||||||
|
#include "mozilla/Attributes.h"
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "frontend/JumpList.h"
|
||||||
|
#include "frontend/SourceNotes.h"
|
||||||
|
#include "frontend/TDZCheckCache.h"
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
namespace frontend {
|
||||||
|
|
||||||
|
struct BytecodeEmitter;
|
||||||
|
|
||||||
|
// Class for emitting bytecode for blocks like if-then-else.
|
||||||
|
//
|
||||||
|
// This class can be used to emit single if-then-else block, or cascading
|
||||||
|
// else-if blocks.
|
||||||
|
//
|
||||||
|
// Usage: (check for the return value is omitted for simplicity)
|
||||||
|
//
|
||||||
|
// `if (cond) then_block`
|
||||||
|
// IfEmitter ifThen(this);
|
||||||
|
// emit(cond);
|
||||||
|
// ifThen.emitThen();
|
||||||
|
// emit(then_block);
|
||||||
|
// ifThen.emitEnd();
|
||||||
|
//
|
||||||
|
// `if (cond) then_block else else_block`
|
||||||
|
// IfEmitter ifThenElse(this);
|
||||||
|
// emit(cond);
|
||||||
|
// ifThenElse.emitThenElse();
|
||||||
|
// emit(then_block);
|
||||||
|
// ifThenElse.emitElse();
|
||||||
|
// emit(else_block);
|
||||||
|
// ifThenElse.emitEnd();
|
||||||
|
//
|
||||||
|
// `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
|
||||||
|
// IfEmitter ifThenElse(this);
|
||||||
|
// emit(c1);
|
||||||
|
// ifThenElse.emitThenElse();
|
||||||
|
// emit(b1);
|
||||||
|
// ifThenElse.emitElseIf();
|
||||||
|
// emit(c2);
|
||||||
|
// ifThenElse.emitThenElse();
|
||||||
|
// emit(b2);
|
||||||
|
// ifThenElse.emitElseIf();
|
||||||
|
// emit(c3);
|
||||||
|
// ifThenElse.emitThenElse();
|
||||||
|
// emit(b3);
|
||||||
|
// ifThenElse.emitElse();
|
||||||
|
// emit(b4);
|
||||||
|
// ifThenElse.emitEnd();
|
||||||
|
//
|
||||||
|
// `cond ? then_expr : else_expr`
|
||||||
|
// IfEmitter condElse(this);
|
||||||
|
// emit(cond);
|
||||||
|
// condElse.emitCond();
|
||||||
|
// emit(then_block);
|
||||||
|
// condElse.emitElse();
|
||||||
|
// emit(else_block);
|
||||||
|
// condElse.emitEnd();
|
||||||
|
//
|
||||||
|
class MOZ_STACK_CLASS IfEmitter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Whether the then-clause, the else-clause, or else-if condition may
|
||||||
|
// contain declaration or access to lexical variables, which means they
|
||||||
|
// should have their own TDZCheckCache. Basically TDZCheckCache should be
|
||||||
|
// created for each basic block, which then-clause, else-clause, and
|
||||||
|
// else-if condition are, but for internally used branches which are
|
||||||
|
// known not to touch lexical variables we can skip creating TDZCheckCache
|
||||||
|
// for them.
|
||||||
|
//
|
||||||
|
// See the comment for TDZCheckCache class for more details.
|
||||||
|
enum class Kind {
|
||||||
|
// For syntactic branches (if, if-else, and conditional expression),
|
||||||
|
// which basically may contain declaration or accesses to lexical
|
||||||
|
// variables inside then-clause, else-clause, and else-if condition.
|
||||||
|
MayContainLexicalAccessInBranch,
|
||||||
|
|
||||||
|
// For internally used branches which don't touch lexical variables
|
||||||
|
// inside then-clause, else-clause, nor else-if condition.
|
||||||
|
NoLexicalAccessInBranch
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
BytecodeEmitter* bce_;
|
||||||
|
|
||||||
|
// Jump around the then clause, to the beginning of the else clause.
|
||||||
|
JumpList jumpAroundThen_;
|
||||||
|
|
||||||
|
// Jump around the else clause, to the end of the entire branch.
|
||||||
|
JumpList jumpsAroundElse_;
|
||||||
|
|
||||||
|
// Annotation index for IonBuilder
|
||||||
|
unsigned noteIndex_;
|
||||||
|
|
||||||
|
// The stack depth before emitting the then block.
|
||||||
|
// Used for restoring stack depth before emitting the else block.
|
||||||
|
// Also used for assertion to make sure then and else blocks pushed the
|
||||||
|
// same number of values.
|
||||||
|
int32_t thenDepth_;
|
||||||
|
|
||||||
|
Kind kind_;
|
||||||
|
mozilla::Maybe<TDZCheckCache> tdzCache_;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// The number of values pushed in the then and else blocks.
|
||||||
|
int32_t pushed_;
|
||||||
|
bool calculatedPushed_;
|
||||||
|
|
||||||
|
// The state of this emitter.
|
||||||
|
//
|
||||||
|
// +-------+ emitCond +------+ emitElse +------+ emitEnd +-----+
|
||||||
|
// | Start |-+--------->| Cond |--------->| Else |------>+------->| End |
|
||||||
|
// +-------+ | +------+ +------+ ^ +-----+
|
||||||
|
// | |
|
||||||
|
// v emitThen +------+ |
|
||||||
|
// +->+--------->| Then |------------------------>+
|
||||||
|
// ^ | +------+ ^
|
||||||
|
// | | |
|
||||||
|
// | | +---+
|
||||||
|
// | | |
|
||||||
|
// | | emitThenElse +----------+ emitElse +------+ |
|
||||||
|
// | +------------->| ThenElse |-+--------->| Else |-+
|
||||||
|
// | +----------+ | +------+
|
||||||
|
// | |
|
||||||
|
// | | emitElseIf +--------+
|
||||||
|
// | +----------->| ElseIf |-+
|
||||||
|
// | +--------+ |
|
||||||
|
// | |
|
||||||
|
// +------------------------------------------------------+
|
||||||
|
enum class State {
|
||||||
|
// The initial state.
|
||||||
|
Start,
|
||||||
|
|
||||||
|
// After calling emitThen.
|
||||||
|
Then,
|
||||||
|
|
||||||
|
// After calling emitCond.
|
||||||
|
Cond,
|
||||||
|
|
||||||
|
// After calling emitThenElse.
|
||||||
|
ThenElse,
|
||||||
|
|
||||||
|
// After calling emitElse.
|
||||||
|
Else,
|
||||||
|
|
||||||
|
// After calling emitElseIf.
|
||||||
|
ElseIf,
|
||||||
|
|
||||||
|
// After calling emitEnd.
|
||||||
|
End
|
||||||
|
};
|
||||||
|
State state_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// For InternalIfEmitter.
|
||||||
|
IfEmitter(BytecodeEmitter* bce, Kind kind);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit IfEmitter(BytecodeEmitter* bce);
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitThen();
|
||||||
|
MOZ_MUST_USE bool emitCond();
|
||||||
|
MOZ_MUST_USE bool emitThenElse();
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitElse();
|
||||||
|
MOZ_MUST_USE bool emitElseIf();
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitEnd();
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Returns the number of values pushed onto the value stack inside
|
||||||
|
// `then_block` and `else_block`.
|
||||||
|
// Can be used in assertion after emitting if-then-else.
|
||||||
|
int32_t pushed() const {
|
||||||
|
return pushed_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the number of values popped onto the value stack inside
|
||||||
|
// `then_block` and `else_block`.
|
||||||
|
// Can be used in assertion after emitting if-then-else.
|
||||||
|
int32_t popped() const {
|
||||||
|
return -pushed_;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
MOZ_MUST_USE bool emitIfInternal(SrcNoteType type);
|
||||||
|
void calculateOrCheckPushed();
|
||||||
|
MOZ_MUST_USE bool emitElseInternal();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Class for emitting bytecode for blocks like if-then-else which doesn't touch
|
||||||
|
// lexical variables.
|
||||||
|
//
|
||||||
|
// See the comments above NoLexicalAccessInBranch for more details when to use
|
||||||
|
// this instead of IfEmitter.
|
||||||
|
class MOZ_STACK_CLASS InternalIfEmitter : public IfEmitter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit InternalIfEmitter(BytecodeEmitter* bce);
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace frontend */
|
||||||
|
} /* namespace js */
|
||||||
|
|
||||||
|
#endif /* frontend_IfEmitter_h */
|
||||||
33
js/src/frontend/JumpList.cpp
Normal file
33
js/src/frontend/JumpList.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "frontend/JumpList.h"
|
||||||
|
|
||||||
|
#include "jsopcode.h"
|
||||||
|
|
||||||
|
using namespace js;
|
||||||
|
using namespace js::frontend;
|
||||||
|
|
||||||
|
void
|
||||||
|
JumpList::push(jsbytecode* code, ptrdiff_t jumpOffset)
|
||||||
|
{
|
||||||
|
SET_JUMP_OFFSET(&code[jumpOffset], offset - jumpOffset);
|
||||||
|
offset = jumpOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JumpList::patchAll(jsbytecode* code, JumpTarget target)
|
||||||
|
{
|
||||||
|
ptrdiff_t delta;
|
||||||
|
for (ptrdiff_t jumpOffset = offset; jumpOffset != -1; jumpOffset += delta) {
|
||||||
|
jsbytecode* pc = &code[jumpOffset];
|
||||||
|
MOZ_ASSERT(IsJumpOpcode(JSOp(*pc)) || JSOp(*pc) == JSOP_LABEL);
|
||||||
|
delta = GET_JUMP_OFFSET(pc);
|
||||||
|
MOZ_ASSERT(delta < 0);
|
||||||
|
ptrdiff_t span = target.offset - jumpOffset;
|
||||||
|
SET_JUMP_OFFSET(pc, span);
|
||||||
|
}
|
||||||
|
}
|
||||||
76
js/src/frontend/JumpList.h
Normal file
76
js/src/frontend/JumpList.h
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef frontend_JumpList_h
|
||||||
|
#define frontend_JumpList_h
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "js/TypeDecls.h"
|
||||||
|
#include "jsbytecode.h"
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
namespace frontend {
|
||||||
|
|
||||||
|
// Linked list of jump instructions that need to be patched. The linked list is
|
||||||
|
// stored in the bytes of the incomplete bytecode that will be patched, so no
|
||||||
|
// extra memory is needed, and patching the instructions destroys the list.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// JumpList brList;
|
||||||
|
// if (!emitJump(JSOP_IFEQ, &brList))
|
||||||
|
// return false;
|
||||||
|
// ...
|
||||||
|
// JumpTarget label;
|
||||||
|
// if (!emitJumpTarget(&label))
|
||||||
|
// return false;
|
||||||
|
// ...
|
||||||
|
// if (!emitJump(JSOP_GOTO, &brList))
|
||||||
|
// return false;
|
||||||
|
// ...
|
||||||
|
// patchJumpsToTarget(brList, label);
|
||||||
|
//
|
||||||
|
// +-> -1
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// ifeq .. <+ + +-+ ifeq ..
|
||||||
|
// .. | | ..
|
||||||
|
// label: | +-> label:
|
||||||
|
// jumptarget | | jumptarget
|
||||||
|
// .. | | ..
|
||||||
|
// goto .. <+ + +-+ goto .. <+
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// + +
|
||||||
|
// brList brList
|
||||||
|
//
|
||||||
|
// | ^
|
||||||
|
// +------- patchJumpsToTarget -------+
|
||||||
|
//
|
||||||
|
|
||||||
|
// Offset of a jump target instruction, used for patching jump instructions.
|
||||||
|
struct JumpTarget {
|
||||||
|
ptrdiff_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JumpList {
|
||||||
|
JumpList() {}
|
||||||
|
// -1 is used to mark the end of jump lists.
|
||||||
|
ptrdiff_t offset = -1;
|
||||||
|
|
||||||
|
// Add a jump instruction to the list.
|
||||||
|
void push(jsbytecode* code, ptrdiff_t jumpOffset);
|
||||||
|
|
||||||
|
// Patch all jump instructions in this list to jump to `target`. This
|
||||||
|
// clobbers the list.
|
||||||
|
void patchAll(jsbytecode* code, JumpTarget target);
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace frontend */
|
||||||
|
} /* namespace js */
|
||||||
|
|
||||||
|
#endif /* frontend_JumpList_h */
|
||||||
76
js/src/frontend/TDZCheckCache.cpp
Normal file
76
js/src/frontend/TDZCheckCache.cpp
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "frontend/TDZCheckCache.h"
|
||||||
|
|
||||||
|
#include "frontend/BytecodeEmitter.h"
|
||||||
|
|
||||||
|
using namespace js;
|
||||||
|
using namespace js::frontend;
|
||||||
|
|
||||||
|
using mozilla::Maybe;
|
||||||
|
using mozilla::Nothing;
|
||||||
|
using mozilla::Some;
|
||||||
|
|
||||||
|
TDZCheckCache::TDZCheckCache(BytecodeEmitter* bce)
|
||||||
|
: Nestable<TDZCheckCache>(&bce->innermostTDZCheckCache),
|
||||||
|
cache_(bce->cx->frontendCollectionPool())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool
|
||||||
|
TDZCheckCache::ensureCache(BytecodeEmitter* bce)
|
||||||
|
{
|
||||||
|
return cache_ || cache_.acquire(bce->cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
Maybe<MaybeCheckTDZ>
|
||||||
|
TDZCheckCache::needsTDZCheck(BytecodeEmitter* bce, JSAtom* name)
|
||||||
|
{
|
||||||
|
if (!ensureCache(bce))
|
||||||
|
return Nothing();
|
||||||
|
|
||||||
|
CheckTDZMap::AddPtr p = cache_->lookupForAdd(name);
|
||||||
|
if (p)
|
||||||
|
return Some(p->value().wrapped);
|
||||||
|
|
||||||
|
MaybeCheckTDZ rv = CheckTDZ;
|
||||||
|
for (TDZCheckCache* it = enclosing(); it; it = it->enclosing()) {
|
||||||
|
if (it->cache_) {
|
||||||
|
if (CheckTDZMap::Ptr p2 = it->cache_->lookup(name)) {
|
||||||
|
rv = p2->value();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cache_->add(p, name, rv)) {
|
||||||
|
ReportOutOfMemory(bce->cx);
|
||||||
|
return Nothing();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
TDZCheckCache::noteTDZCheck(BytecodeEmitter* bce, JSAtom* name,
|
||||||
|
MaybeCheckTDZ check)
|
||||||
|
{
|
||||||
|
if (!ensureCache(bce))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
CheckTDZMap::AddPtr p = cache_->lookupForAdd(name);
|
||||||
|
if (p) {
|
||||||
|
MOZ_ASSERT(!check, "TDZ only needs to be checked once per binding per basic block.");
|
||||||
|
p->value() = check;
|
||||||
|
} else {
|
||||||
|
if (!cache_->add(p, name, check)) {
|
||||||
|
ReportOutOfMemory(bce->cx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
55
js/src/frontend/TDZCheckCache.h
Normal file
55
js/src/frontend/TDZCheckCache.h
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef frontend_TDZCheckCache_h
|
||||||
|
#define frontend_TDZCheckCache_h
|
||||||
|
|
||||||
|
#include "mozilla/Attributes.h"
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
|
|
||||||
|
#include "frontend/SharedContext.h" // for Nestable
|
||||||
|
#include "frontend/NameCollections.h"
|
||||||
|
#include "js/TypeDecls.h"
|
||||||
|
#include "vm/Stack.h"
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
namespace frontend {
|
||||||
|
|
||||||
|
struct BytecodeEmitter;
|
||||||
|
|
||||||
|
// A cache that tracks Temporal Dead Zone (TDZ) checks, so that any use of a
|
||||||
|
// lexical variable that's dominated by an earlier use, or by evaluation of its
|
||||||
|
// declaration (which will initialize it, perhaps to |undefined|), doesn't have
|
||||||
|
// to redundantly check that the lexical variable has been initialized
|
||||||
|
//
|
||||||
|
// Each basic block should have a TDZCheckCache in scope. Some NestableControl
|
||||||
|
// subclasses contain a TDZCheckCache.
|
||||||
|
//
|
||||||
|
// When a scope containing lexical variables is entered, all such variables are
|
||||||
|
// marked as CheckTDZ. When a lexical variable is accessed, its entry is
|
||||||
|
// checked. If it's CheckTDZ, a JSOP_CHECKLEXICAL is emitted and then the
|
||||||
|
// entry is marked DontCheckTDZ. If it's DontCheckTDZ, no check is emitted
|
||||||
|
// because a prior check would have already failed. Finally, because
|
||||||
|
// evaluating a lexical variable declaration initializes it (after any
|
||||||
|
// initializer is evaluated), evaluating a lexical declaration marks its entry
|
||||||
|
// as DontCheckTDZ.
|
||||||
|
class TDZCheckCache : public Nestable<TDZCheckCache>
|
||||||
|
{
|
||||||
|
PooledMapPtr<CheckTDZMap> cache_;
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool ensureCache(BytecodeEmitter* bce);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TDZCheckCache(BytecodeEmitter* bce);
|
||||||
|
|
||||||
|
mozilla::Maybe<MaybeCheckTDZ> needsTDZCheck(BytecodeEmitter* bce, JSAtom* name);
|
||||||
|
MOZ_MUST_USE bool noteTDZCheck(BytecodeEmitter* bce, JSAtom* name, MaybeCheckTDZ check);
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace frontend */
|
||||||
|
} /* namespace js */
|
||||||
|
|
||||||
|
#endif /* frontend_TDZCheckCache_h */
|
||||||
|
|
@ -141,8 +141,11 @@ UNIFIED_SOURCES += [
|
||||||
'frontend/BytecodeCompiler.cpp',
|
'frontend/BytecodeCompiler.cpp',
|
||||||
'frontend/BytecodeEmitter.cpp',
|
'frontend/BytecodeEmitter.cpp',
|
||||||
'frontend/FoldConstants.cpp',
|
'frontend/FoldConstants.cpp',
|
||||||
|
'frontend/IfEmitter.cpp',
|
||||||
|
'frontend/JumpList.cpp',
|
||||||
'frontend/NameFunctions.cpp',
|
'frontend/NameFunctions.cpp',
|
||||||
'frontend/ParseNode.cpp',
|
'frontend/ParseNode.cpp',
|
||||||
|
'frontend/TDZCheckCache.cpp',
|
||||||
'frontend/TokenStream.cpp',
|
'frontend/TokenStream.cpp',
|
||||||
'gc/Allocator.cpp',
|
'gc/Allocator.cpp',
|
||||||
'gc/Barrier.cpp',
|
'gc/Barrier.cpp',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue