summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/IfEmitter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/IfEmitter.cpp')
-rw-r--r--js/src/frontend/IfEmitter.cpp286
1 files changed, 286 insertions, 0 deletions
diff --git a/js/src/frontend/IfEmitter.cpp b/js/src/frontend/IfEmitter.cpp
new file mode 100644
index 0000000000..ced510e0b7
--- /dev/null
+++ b/js/src/frontend/IfEmitter.cpp
@@ -0,0 +1,286 @@
+/* -*- 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)
+ : IfEmitter(bce, LexicalKind::NoLexicalAccessInBranch) {
+#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;
+}