#include "frontend/CForEmitter.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/EmitterScope.h"
#include "frontend/SourceNotes.h"
#include "vm/Opcodes.h"
#include "vm/Scope.h"
using namespace js;
using namespace js::frontend;
using mozilla::Maybe;
CForEmitter::CForEmitter(BytecodeEmitter* bce,
const EmitterScope* headLexicalEmitterScopeForLet)
: bce_(bce),
headLexicalEmitterScopeForLet_(headLexicalEmitterScopeForLet) {}
bool CForEmitter::emitInit(const Maybe<uint32_t>& initPos) {
MOZ_ASSERT(state_ == State::Start);
loopInfo_.emplace(bce_, StatementKind::ForLoop);
if (initPos) {
if (!bce_->updateSourceCoordNotes(*initPos)) {
return false;
}
}
#ifdef DEBUG
state_ = State::Init;
#endif
return true;
}
bool CForEmitter::emitBody(Cond cond, const Maybe<uint32_t>& bodyPos) {
MOZ_ASSERT(state_ == State::Init);
cond_ = cond;
if (headLexicalEmitterScopeForLet_) {
MOZ_ASSERT(headLexicalEmitterScopeForLet_ == bce_->innermostEmitterScope());
MOZ_ASSERT(headLexicalEmitterScopeForLet_->scope(bce_)->kind() ==
ScopeKind::Lexical);
if (headLexicalEmitterScopeForLet_->hasEnvironment()) {
if (!bce_->emit1(JSOP_FRESHENLEXICALENV)) {
return false;
}
}
}
if (!bce_->newSrcNote(SRC_FOR, ¬eIndex_)) {
return false;
}
if (!bce_->emit1(JSOP_NOP)) {
return false;
}
biasedTop_ = bce_->offset();
if (cond_ == Cond::Present) {
if (!loopInfo_->emitEntryJump(bce_)) {
return false;
}
}
if (!loopInfo_->emitLoopHead(bce_, bodyPos)) {
return false;
}
if (cond_ == Cond::Missing) {
if (!loopInfo_->emitLoopEntry(bce_, bodyPos)) {
return false;
}
}
tdzCache_.emplace(bce_);
#ifdef DEBUG
state_ = State::Body;
#endif
return true;
}
bool CForEmitter::emitUpdate(Update update, const Maybe<uint32_t>& updatePos) {
MOZ_ASSERT(state_ == State::Body);
update_ = update;
tdzCache_.reset();
if (!loopInfo_->emitContinueTarget(bce_)) {
return false;
}
if (headLexicalEmitterScopeForLet_) {
MOZ_ASSERT(headLexicalEmitterScopeForLet_ == bce_->innermostEmitterScope());
MOZ_ASSERT(headLexicalEmitterScopeForLet_->scope(bce_)->kind() ==
ScopeKind::Lexical);
if (headLexicalEmitterScopeForLet_->hasEnvironment()) {
if (!bce_->emit1(JSOP_FRESHENLEXICALENV)) {
return false;
}
}
}
if (update_ == Update::Present) {
tdzCache_.emplace(bce_);
if (updatePos) {
if (!bce_->updateSourceCoordNotes(*updatePos)) {
return false;
}
}
}
#ifdef DEBUG
state_ = State::Update;
#endif
return true;
}
bool CForEmitter::emitCond(const Maybe<uint32_t>& forPos,
const Maybe<uint32_t>& condPos,
const Maybe<uint32_t>& endPos) {
MOZ_ASSERT(state_ == State::Update);
if (update_ == Update::Present) {
if (!bce_->emit1(JSOP_POP)) {
return false;
}
if (endPos) {
uint32_t lineNum = bce_->parser->errorReporter().lineAt(*endPos);
if (bce_->currentLine() != lineNum) {
if (!bce_->newSrcNote2(SRC_SETLINE, ptrdiff_t(lineNum))) {
return false;
}
bce_->setCurrentLine(lineNum);
}
}
}
if (update_ == Update::Present) {
tdzCache_.reset();
}
condOffset_ = bce_->offset();
if (cond_ == Cond::Present) {
if (!loopInfo_->emitLoopEntry(bce_, condPos)) {
return false;
}
} else if (update_ == Update::Missing) {
if (forPos) {
if (!bce_->updateSourceCoordNotes(*forPos)) {
return false;
}
}
}
#ifdef DEBUG
state_ = State::Cond;
#endif
return true;
}
bool CForEmitter::emitEnd() {
MOZ_ASSERT(state_ == State::Cond);
if (!bce_->setSrcNoteOffset(noteIndex_, SrcNote::For::CondOffset,
condOffset_ - biasedTop_)) {
return false;
}
if (!bce_->setSrcNoteOffset(noteIndex_, SrcNote::For::UpdateOffset,
loopInfo_->continueTargetOffset() - biasedTop_)) {
return false;
}
if (!loopInfo_->emitLoopEnd(bce_,
cond_ == Cond::Present ? JSOP_IFNE : JSOP_GOTO)) {
return false;
}
if (!bce_->setSrcNoteOffset(noteIndex_, SrcNote::For::BackJumpOffset,
loopInfo_->loopEndOffset() - biasedTop_))
{
return false;
}
if (!bce_->addTryNote(JSTRY_LOOP, bce_->stackDepth, loopInfo_->headOffset(),
loopInfo_->breakTargetOffset())) {
return false;
}
if (!loopInfo_->patchBreaksAndContinues(bce_)) {
return false;
}
loopInfo_.reset();
#ifdef DEBUG
state_ = State::End;
#endif
return true;
}