diff options
Diffstat (limited to 'js/src/frontend/IfEmitter.cpp')
-rw-r--r-- | js/src/frontend/IfEmitter.cpp | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/js/src/frontend/IfEmitter.cpp b/js/src/frontend/IfEmitter.cpp new file mode 100644 index 0000000000..7bcd948a78 --- /dev/null +++ b/js/src/frontend/IfEmitter.cpp @@ -0,0 +1,287 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * 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 "vm/Opcodes.h" + +using namespace js; +using namespace js::frontend; + +using mozilla::Maybe; + +BranchEmitterBase::BranchEmitterBase(BytecodeEmitter* bce, + LexicalKind lexicalKind) + : bce_(bce), lexicalKind_(lexicalKind) {} + +IfEmitter::IfEmitter(BytecodeEmitter* bce, LexicalKind lexicalKind) + : BranchEmitterBase(bce, lexicalKind) {} + +IfEmitter::IfEmitter(BytecodeEmitter* bce) + : IfEmitter(bce, LexicalKind::MayContainLexicalAccessInBranch) {} + +bool BranchEmitterBase::emitThenInternal(ConditionKind conditionKind) { + // The end of TDZCheckCache for cond for else-if. + if (lexicalKind_ == LexicalKind::MayContainLexicalAccessInBranch) { + tdzCache_.reset(); + } + + // Emit a jump around the then part. + JSOp op = conditionKind == ConditionKind::Positive ? JSOp::JumpIfFalse + : JSOp::JumpIfTrue; + if (!bce_->emitJump(op, &jumpAroundThen_)) { + return false; + } + + // To restore stack depth in else part (if present), save depth of the then + // part. + thenDepth_ = bce_->bytecodeSection().stackDepth(); + + // Enclose then-branch with TDZCheckCache. + if (lexicalKind_ == LexicalKind::MayContainLexicalAccessInBranch) { + tdzCache_.emplace(bce_); + } + + return true; +} + +void BranchEmitterBase::calculateOrCheckPushed() { +#ifdef DEBUG + if (!calculatedPushed_) { + pushed_ = bce_->bytecodeSection().stackDepth() - thenDepth_; + calculatedPushed_ = true; + } else { + MOZ_ASSERT(pushed_ == bce_->bytecodeSection().stackDepth() - thenDepth_); + } +#endif +} + +bool BranchEmitterBase::emitElseInternal() { + calculateOrCheckPushed(); + + // The end of TDZCheckCache for then-clause. + if (lexicalKind_ == LexicalKind::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; + } + + // Clear jumpAroundThen_ offset, to tell emitEnd there was an else part. + jumpAroundThen_ = JumpList(); + + // Restore stack depth of the then part. + bce_->bytecodeSection().setStackDepth(thenDepth_); + + // Enclose else-branch with TDZCheckCache. + if (lexicalKind_ == LexicalKind::MayContainLexicalAccessInBranch) { + tdzCache_.emplace(bce_); + } + + return true; +} + +bool BranchEmitterBase::emitEndInternal() { + // The end of TDZCheckCache for then or else-clause. + if (lexicalKind_ == LexicalKind::MayContainLexicalAccessInBranch) { + MOZ_ASSERT(tdzCache_.isSome()); + tdzCache_.reset(); + } + + calculateOrCheckPushed(); + + if (jumpAroundThen_.offset.valid()) { + // 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; + } + + return true; +} + +bool IfEmitter::emitIf(const Maybe<uint32_t>& ifPos) { + MOZ_ASSERT(state_ == State::Start); + + if (ifPos) { + // Make sure this code is attributed to the "if" so that it gets a + // useful column number, instead of the default 0 value. + if (!bce_->updateSourceCoordNotes(*ifPos)) { + return false; + } + } + +#ifdef DEBUG + state_ = State::If; +#endif + return true; +} + +bool IfEmitter::emitThen( + ConditionKind conditionKind /* = ConditionKind::Positive */) { + MOZ_ASSERT(state_ == State::If || state_ == State::ElseIf); + + if (lexicalKind_ == LexicalKind::MayContainLexicalAccessInBranch) { + MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome()); + MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing()); + } + + if (!emitThenInternal(conditionKind)) { + return false; + } + +#ifdef DEBUG + state_ = State::Then; +#endif + return true; +} + +bool IfEmitter::emitThenElse( + ConditionKind conditionKind /* = ConditionKind::Positive */) { + MOZ_ASSERT(state_ == State::If || state_ == State::ElseIf); + + if (lexicalKind_ == LexicalKind::MayContainLexicalAccessInBranch) { + MOZ_ASSERT_IF(state_ == State::ElseIf, tdzCache_.isSome()); + MOZ_ASSERT_IF(state_ != State::ElseIf, tdzCache_.isNothing()); + } + + if (!emitThenInternal(conditionKind)) { + return false; + } + +#ifdef DEBUG + state_ = State::ThenElse; +#endif + return true; +} + +bool IfEmitter::emitElseIf(const Maybe<uint32_t>& ifPos) { + MOZ_ASSERT(state_ == State::ThenElse); + + if (!emitElseInternal()) { + return false; + } + + if (ifPos) { + // Make sure this code is attributed to the "if" so that it gets a + // useful column number, instead of the default 0 value. + if (!bce_->updateSourceCoordNotes(*ifPos)) { + return false; + } + } + +#ifdef DEBUG + state_ = State::ElseIf; +#endif + return true; +} + +bool IfEmitter::emitElse() { + MOZ_ASSERT(state_ == State::ThenElse); + + if (!emitElseInternal()) { + return false; + } + +#ifdef DEBUG + state_ = State::Else; +#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.valid()); + MOZ_ASSERT_IF(state_ == State::Else, !jumpAroundThen_.offset.valid()); + + if (!emitEndInternal()) { + return false; + } + +#ifdef DEBUG + state_ = State::End; +#endif + return true; +} + +InternalIfEmitter::InternalIfEmitter(BytecodeEmitter* bce, + LexicalKind lexicalKind) + : IfEmitter(bce, lexicalKind) { +#ifdef DEBUG + // Skip emitIf (see the comment above InternalIfEmitter declaration). + state_ = State::If; +#endif +} + +CondEmitter::CondEmitter(BytecodeEmitter* bce) + : BranchEmitterBase(bce, LexicalKind::MayContainLexicalAccessInBranch) {} + +bool CondEmitter::emitCond() { + MOZ_ASSERT(state_ == State::Start); +#ifdef DEBUG + state_ = State::Cond; +#endif + return true; +} + +bool CondEmitter::emitThenElse( + ConditionKind conditionKind /* = ConditionKind::Positive */) { + MOZ_ASSERT(state_ == State::Cond); + if (!emitThenInternal(conditionKind)) { + return false; + } + +#ifdef DEBUG + state_ = State::ThenElse; +#endif + return true; +} + +bool CondEmitter::emitElse() { + MOZ_ASSERT(state_ == State::ThenElse); + + if (!emitElseInternal()) { + return false; + } + +#ifdef DEBUG + state_ = State::Else; +#endif + return true; +} + +bool CondEmitter::emitEnd() { + MOZ_ASSERT(state_ == State::Else); + MOZ_ASSERT(!jumpAroundThen_.offset.valid()); + + if (!emitEndInternal()) { + return false; + } + +#ifdef DEBUG + state_ = State::End; +#endif + return true; +} |