/* -*- 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& 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& 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; }