Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/js/src/frontend/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 10 kB image not shown  

Quelle  SwitchEmitter.cpp   Sprache: C

 
/* -*- 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/SwitchEmitter.h"

#include "mozilla/Assertions.h"  // MOZ_ASSERT
#include "mozilla/Span.h"        // mozilla::Span

#include <algorithm>  // std::min, std::max

#include "jstypes.h"  // JS_BIT

#include "frontend/BytecodeEmitter.h"  // BytecodeEmitter
#include "frontend/SharedContext.h"    // StatementKind
#include "js/friend/ErrorMessages.h"   // JSMSG_*
#include "js/TypeDecls.h"              // jsbytecode
#include "util/BitArray.h"
#include "vm/BytecodeUtil.h"  // SET_JUMP_OFFSET, JUMP_OFFSET_LEN, SET_RESUMEINDEX
#include "vm/Opcodes.h"       // JSOp, JSOpLength_TableSwitch
#include "vm/Runtime.h"       // ReportOutOfMemory

using namespace js;
using namespace js::frontend;

bool SwitchEmitter::TableGenerator::addNumber(int32_t caseValue) {
  if (isInvalid()) {
    return true;
  }

  if (unsigned(caseValue + int(Bit(15))) >= unsigned(Bit(16))) {
    setInvalid();
    return true;
  }

  if (intmap_.isNothing()) {
    intmap_.emplace();
  }

  low_ = std::min(low_, caseValue);
  high_ = std::max(high_, caseValue);

  // Check for duplicates, which are not supported in a table switch.
  // We bias caseValue by 65536 if it's negative, and hope that's a rare case
  // (because it requires a malloc'd bitmap).
  if (caseValue < 0) {
    caseValue += Bit(16);
  }
  if (caseValue >= intmapBitLength_) {
    size_t newLength = NumWordsForBitArrayOfLength(caseValue + 1);
    if (!intmap_->resize(newLength)) {
      ReportOutOfMemory(bce_->fc);
      return false;
    }
    intmapBitLength_ = newLength * BitArrayElementBits;
  }
  if (IsBitArrayElementSet(intmap_->begin(), intmap_->length(), caseValue)) {
    // Duplicate entry is not supported in table switch.
    setInvalid();
    return true;
  }
  SetBitArrayElement(intmap_->begin(), intmap_->length(), caseValue);
  return true;
}

void SwitchEmitter::TableGenerator::finish(uint32_t caseCount) {
  intmap_.reset();

#ifdef DEBUG
  finished_ = true;
#endif

  if (isInvalid()) {
    return;
  }

  if (caseCount == 0) {
    low_ = 0;
    high_ = -1;
    return;
  }

  // Compute table length. Don't use table switch if overlarge or more than
  // half-sparse.
  tableLength_ = uint32_t(high_ - low_ + 1);
  if (tableLength_ >= Bit(16) || tableLength_ > 2 * caseCount) {
    setInvalid();
  }
}

uint32_t SwitchEmitter::TableGenerator::toCaseIndex(int32_t caseValue) const {
  MOZ_ASSERT(finished_);
  MOZ_ASSERT(isValid());
  uint32_t caseIndex = uint32_t(caseValue - low_);
  MOZ_ASSERT(caseIndex < tableLength_);
  return caseIndex;
}

uint32_t SwitchEmitter::TableGenerator::tableLength() const {
  MOZ_ASSERT(finished_);
  MOZ_ASSERT(isValid());
  return tableLength_;
}

SwitchEmitter::SwitchEmitter(BytecodeEmitter* bce) : bce_(bce) {}

bool SwitchEmitter::emitDiscriminant(uint32_t switchPos) {
  MOZ_ASSERT(state_ == State::Start);
  switchPos_ = switchPos;

  // Ensure that the column of the switch statement is set properly.
  if (!bce_->updateSourceCoordNotes(switchPos_)) {
    return false;
  }

  state_ = State::Discriminant;
  return true;
}

bool SwitchEmitter::emitLexical(LexicalScope::ParserData* bindings) {
  MOZ_ASSERT(state_ == State::Discriminant);
  MOZ_ASSERT(bindings);

  tdzCacheLexical_.emplace(bce_);
  emitterScope_.emplace(bce_);
  if (!emitterScope_->enterLexical(bce_, ScopeKind::Lexical, bindings
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
                                   ,
                                   BlockKind::Switch
#endif
                                   )) {
    return false;
  }

  state_ = State::Lexical;
  return true;
}

bool SwitchEmitter::validateCaseCount(uint32_t caseCount) {
  MOZ_ASSERT(state_ == State::Discriminant || state_ == State::Lexical);
  if (caseCount > Bit(16)) {
    bce_->reportError(switchPos_, JSMSG_TOO_MANY_CASES);
    return false;
  }
  caseCount_ = caseCount;

  state_ = State::CaseCount;
  return true;
}

bool SwitchEmitter::emitCond() {
  MOZ_ASSERT(state_ == State::CaseCount);

  kind_ = Kind::Cond;

  // After entering the scope if necessary, push the switch control.
  controlInfo_.emplace(bce_, StatementKind::Switch);
  top_ = bce_->bytecodeSection().offset();

  if (!caseOffsets_.resize(caseCount_)) {
    ReportOutOfMemory(bce_->fc);
    return false;
  }

  MOZ_ASSERT(top_ == bce_->bytecodeSection().offset());

  tdzCacheCaseAndBody_.emplace(bce_);

  state_ = State::Cond;
  return true;
}

bool SwitchEmitter::emitTable(const TableGenerator& tableGen) {
  MOZ_ASSERT(state_ == State::CaseCount);
  kind_ = Kind::Table;

  // After entering the scope if necessary, push the switch control.
  controlInfo_.emplace(bce_, StatementKind::Switch);
  top_ = bce_->bytecodeSection().offset();

  if (!caseOffsets_.resize(tableGen.tableLength())) {
    ReportOutOfMemory(bce_->fc);
    return false;
  }

  MOZ_ASSERT(top_ == bce_->bytecodeSection().offset());
  if (!bce_->emitN(JSOp::TableSwitch,
                   JSOpLength_TableSwitch - sizeof(jsbytecode))) {
    return false;
  }

  // Skip default offset.
  jsbytecode* pc =
      bce_->bytecodeSection().code(top_ + BytecodeOffsetDiff(JUMP_OFFSET_LEN));

  // Fill in switch bounds, which we know fit in 16-bit offsets.
  SET_JUMP_OFFSET(pc, tableGen.low());
  SET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN, tableGen.high());

  state_ = State::Table;
  return true;
}

bool SwitchEmitter::emitCaseOrDefaultJump(uint32_t caseIndex, bool isDefault) {
  MOZ_ASSERT(kind_ == Kind::Cond);

  if (isDefault) {
    if (!bce_->emitJump(JSOp::Default, &condSwitchDefaultOffset_)) {
      return false;
    }
    return true;
  }

  JumpList caseJump;
  if (!bce_->emitJump(JSOp::Case, &caseJump)) {
    return false;
  }
  caseOffsets_[caseIndex] = caseJump.offset;
  lastCaseOffset_ = caseJump.offset;

  return true;
}

bool SwitchEmitter::prepareForCaseValue() {
  MOZ_ASSERT(kind_ == Kind::Cond);
  MOZ_ASSERT(state_ == State::Cond || state_ == State::Case);

  if (!bce_->emit1(JSOp::Dup)) {
    return false;
  }

  state_ = State::CaseValue;
  return true;
}

bool SwitchEmitter::emitCaseJump() {
  MOZ_ASSERT(kind_ == Kind::Cond);
  MOZ_ASSERT(state_ == State::CaseValue);

  if (!bce_->emit1(JSOp::StrictEq)) {
    return false;
  }

  if (!emitCaseOrDefaultJump(caseIndex_, false)) {
    return false;
  }
  caseIndex_++;

  state_ = State::Case;
  return true;
}

bool SwitchEmitter::emitImplicitDefault() {
  MOZ_ASSERT(kind_ == Kind::Cond);
  MOZ_ASSERT(state_ == State::Cond || state_ == State::Case);
  if (!emitCaseOrDefaultJump(0, true)) {
    return false;
  }

  caseIndex_ = 0;

  // No internal state after emitting default jump.
  return true;
}

bool SwitchEmitter::emitCaseBody() {
  MOZ_ASSERT(kind_ == Kind::Cond);
  MOZ_ASSERT(state_ == State::Cond || state_ == State::Case ||
             state_ == State::CaseBody || state_ == State::DefaultBody);

  tdzCacheCaseAndBody_.reset();

  if (state_ == State::Cond || state_ == State::Case) {
    // For cond switch, JSOp::Default is always emitted.
    if (!emitImplicitDefault()) {
      return false;
    }
  }

  JumpList caseJump;
  caseJump.offset = caseOffsets_[caseIndex_];
  if (!bce_->emitJumpTargetAndPatch(caseJump)) {
    return false;
  }

  JumpTarget here;
  if (!bce_->emitJumpTarget(&here)) {
    return false;
  }
  caseIndex_++;

  tdzCacheCaseAndBody_.emplace(bce_);

  state_ = State::CaseBody;
  return true;
}

bool SwitchEmitter::emitCaseBody(int32_t caseValue,
                                 const TableGenerator& tableGen) {
  MOZ_ASSERT(kind_ == Kind::Table);
  MOZ_ASSERT(state_ == State::Table || state_ == State::CaseBody ||
             state_ == State::DefaultBody);

  tdzCacheCaseAndBody_.reset();

  JumpTarget here;
  if (!bce_->emitJumpTarget(&here)) {
    return false;
  }
  caseOffsets_[tableGen.toCaseIndex(caseValue)] = here.offset;

  tdzCacheCaseAndBody_.emplace(bce_);

  state_ = State::CaseBody;
  return true;
}

bool SwitchEmitter::emitDefaultBody() {
  MOZ_ASSERT(state_ == State::Cond || state_ == State::Table ||
             state_ == State::Case || state_ == State::CaseBody);
  MOZ_ASSERT(!hasDefault_);

  tdzCacheCaseAndBody_.reset();

  if (state_ == State::Cond || state_ == State::Case) {
    // For cond switch, JSOp::Default is always emitted.
    if (!emitImplicitDefault()) {
      return false;
    }
  }
  JumpTarget here;
  if (!bce_->emitJumpTarget(&here)) {
    return false;
  }
  defaultJumpTargetOffset_ = here;

  tdzCacheCaseAndBody_.emplace(bce_);

  hasDefault_ = true;
  state_ = State::DefaultBody;
  return true;
}

bool SwitchEmitter::emitEnd() {
  MOZ_ASSERT(state_ == State::Cond || state_ == State::Table ||
             state_ == State::CaseBody || state_ == State::DefaultBody);

  tdzCacheCaseAndBody_.reset();

  if (!hasDefault_) {
    // If no default case, offset for default is to end of switch.
    if (!bce_->emitJumpTarget(&defaultJumpTargetOffset_)) {
      return false;
    }
  }
  MOZ_ASSERT(defaultJumpTargetOffset_.offset.valid());

  // Set the default offset (to end of switch if no default).
  jsbytecode* pc;
  if (kind_ == Kind::Cond) {
    pc = nullptr;
    bce_->patchJumpsToTarget(condSwitchDefaultOffset_,
                             defaultJumpTargetOffset_);
  } else {
    // Fill in the default jump target.
    pc = bce_->bytecodeSection().code(top_);
    SET_JUMP_OFFSET(pc, (defaultJumpTargetOffset_.offset - top_).value());
    pc += JUMP_OFFSET_LEN;
  }

  if (kind_ == Kind::Table) {
    // Skip over the already-initialized switch bounds.
    pc += 2 * JUMP_OFFSET_LEN;

    // Use the 'default' offset for missing cases.
    for (uint32_t i = 0, length = caseOffsets_.length(); i < length; i++) {
      if (caseOffsets_[i].value() == 0) {
        caseOffsets_[i] = defaultJumpTargetOffset_.offset;
      }
    }

    // Allocate resume index range.
    uint32_t firstResumeIndex = 0;
    mozilla::Span<BytecodeOffset> offsets =
        mozilla::Span(caseOffsets_.begin(), caseOffsets_.end());
    if (!bce_->allocateResumeIndexRange(offsets, &firstResumeIndex)) {
      return false;
    }
    SET_RESUMEINDEX(pc, firstResumeIndex);
  }

  // Patch breaks before leaving the scope, as all breaks are under the
  // lexical scope if it exists.
  if (!controlInfo_->patchBreaks(bce_)) {
    return false;
  }

  if (emitterScope_ && !emitterScope_->leave(bce_)) {
    return false;
  }

  tdzCacheLexical_.reset();

  controlInfo_.reset();

  // LIFO construction order is enforced hence we should reset the
  // emitterScope_ after controlInfo_ has been reset.
  emitterScope_.reset();

  state_ = State::End;
  return true;
}

InternalSwitchEmitter::InternalSwitchEmitter(BytecodeEmitter* bce)
    : SwitchEmitter(bce) {
#ifdef DEBUG
  // Skip emitDiscriminant (see the comment above InternalSwitchEmitter)
  state_ = State::Discriminant;
#endif
}

Messung V0.5
C=93 H=92 G=92

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.