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

Quelle  Recover.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 "jit/Recover.h"

#include "mozilla/Casting.h"

#include "jsmath.h"

#include "builtin/Object.h"
#include "builtin/String.h"
#include "jit/AtomicOperations.h"
#include "jit/Bailouts.h"
#include "jit/CompileInfo.h"
#include "jit/Ion.h"
#include "jit/JitSpewer.h"
#include "jit/JSJitFrameIter.h"
#include "jit/MIR-wasm.h"
#include "jit/MIR.h"
#include "jit/MIRGraph.h"
#include "jit/VMFunctions.h"
#include "js/ScalarType.h"
#include "util/DifferentialTesting.h"
#include "vm/BigIntType.h"
#include "vm/EqualityOperations.h"
#include "vm/Interpreter.h"
#include "vm/Iteration.h"
#include "vm/JSContext.h"
#include "vm/JSObject.h"
#include "vm/PlainObject.h"  // js::PlainObject
#include "vm/StringType.h"
#include "vm/Watchtower.h"

#include "vm/Interpreter-inl.h"

using namespace js;
using namespace js::jit;

bool MNode::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_CRASH("This instruction is not serializable");
}

void RInstruction::readRecoverData(CompactBufferReader& reader,
                                   RInstructionStorage* raw) {
  uint32_t op = reader.readUnsigned();
  switch (Opcode(op)) {
#define MATCH_OPCODES_(op)                                                  \
  case Recover_##op:                                                        \
    static_assert(sizeof(R##op) <= sizeof(RInstructionStorage),             \
                  "storage space must be big enough to store R" #op);       \
    static_assert(alignof(R##op) <= alignof(RInstructionStorage),           \
                  "storage space must be aligned adequate to store R" #op); \
    new (raw->addr()) R##op(reader);                                        \
    break;

    RECOVER_OPCODE_LIST(MATCH_OPCODES_)
#undef MATCH_OPCODES_

    case Recover_Invalid:
    default:
      MOZ_CRASH("Bad decoding of the previous instruction?");
  }
}

bool MResumePoint::writeRecoverData(CompactBufferWriter& writer) const {
  writer.writeUnsigned(uint32_t(RInstruction::Recover_ResumePoint));

  MBasicBlock* bb = block();
  bool hasFun = bb->info().hasFunMaybeLazy();
  uint32_t nargs = bb->info().nargs();
  JSScript* script = bb->info().script();
  uint32_t exprStack = stackDepth() - bb->info().ninvoke();

#ifdef DEBUG
  // Ensure that all snapshot which are encoded can safely be used for
  // bailouts.
  uint32_t numIntermediate = NumIntermediateValues(mode());
  if (JSContext* cx = GetJitContext()->cx) {
    if (!AssertBailoutStackDepth(cx, script, pc(), mode(),
                                 exprStack - numIntermediate)) {
      return false;
    }
  }
#endif

  uint32_t formalArgs = CountArgSlots(script, hasFun, nargs);

  // Test if we honor the maximum of arguments at all times.  This is a sanity
  // check and not an algorithm limit. So check might be a bit too loose.  +4
  // to account for scope chain, return value, this value and maybe
  // arguments_object.
  MOZ_ASSERT(formalArgs < SNAPSHOT_MAX_NARGS + 4);

#ifdef JS_JITSPEW
  uint32_t implicit = StartArgSlot(script);
#endif
  uint32_t nallocs = formalArgs + script->nfixed() + exprStack;

  JitSpew(JitSpew_IonSnapshots,
          "Starting frame; implicit %u, formals %u, fixed %zu, exprs %u",
          implicit, formalArgs - implicit, script->nfixed(), exprStack);

  uint32_t pcOff = script->pcToOffset(pc());
  JitSpew(JitSpew_IonSnapshots, "Writing pc offset %u, mode %s, nslots %u",
          pcOff, ResumeModeToString(mode()), nallocs);

  uint32_t pcOffAndMode =
      (pcOff << RResumePoint::PCOffsetShift) | uint32_t(mode());
  MOZ_RELEASE_ASSERT((pcOffAndMode >> RResumePoint::PCOffsetShift) == pcOff,
                     "pcOff doesn't fit in pcOffAndMode");
  writer.writeUnsigned(pcOffAndMode);

  writer.writeUnsigned(nallocs);
  return true;
}

RResumePoint::RResumePoint(CompactBufferReader& reader) {
  pcOffsetAndMode_ = reader.readUnsigned();
  numOperands_ = reader.readUnsigned();
  JitSpew(JitSpew_IonSnapshots,
          "Read RResumePoint (pc offset %u, mode %s, nslots %u)", pcOffset(),
          ResumeModeToString(mode()), numOperands_);
}

bool RResumePoint::recover(JSContext* cx, SnapshotIterator& iter) const {
  MOZ_CRASH("This instruction is not recoverable.");
}

bool MBitNot::writeRecoverData(CompactBufferWriter& writer) const {
  // 64-bit int bitnots exist only when compiling wasm; they exist neither for
  // JS nor asm.js.  So we don't expect them here.
  MOZ_ASSERT(type() != MIRType::Int64);
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BitNot));
  return true;
}

RBitNot::RBitNot(CompactBufferReader& reader) {}

bool RBitNot::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue operand(cx, iter.read());
  RootedValue result(cx);

  if (!js::BitNot(cx, &operand, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MBitAnd::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BitAnd));
  return true;
}

RBitAnd::RBitAnd(CompactBufferReader& reader) {}

bool RBitAnd::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());
  RootedValue result(cx);
  MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());

  if (!js::BitAnd(cx, &lhs, &rhs, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MBitOr::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BitOr));
  return true;
}

RBitOr::RBitOr(CompactBufferReader& reader) {}

bool RBitOr::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());
  RootedValue result(cx);
  MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());

  if (!js::BitOr(cx, &lhs, &rhs, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MBitXor::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BitXor));
  return true;
}

RBitXor::RBitXor(CompactBufferReader& reader) {}

bool RBitXor::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());
  RootedValue result(cx);

  if (!js::BitXor(cx, &lhs, &rhs, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MLsh::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Lsh));
  return true;
}

RLsh::RLsh(CompactBufferReader& reader) {}

bool RLsh::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());
  RootedValue result(cx);
  MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());

  if (!js::BitLsh(cx, &lhs, &rhs, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MRsh::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Rsh));
  return true;
}

RRsh::RRsh(CompactBufferReader& reader) {}

bool RRsh::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());
  RootedValue result(cx);
  MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());

  if (!js::BitRsh(cx, &lhs, &rhs, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MUrsh::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Ursh));
  return true;
}

RUrsh::RUrsh(CompactBufferReader& reader) {}

bool RUrsh::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());
  MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());

  RootedValue result(cx);
  if (!js::UrshValues(cx, &lhs, &rhs, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MSignExtendInt32::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_SignExtendInt32));
  MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_);
  writer.writeByte(uint8_t(mode_));
  return true;
}

RSignExtendInt32::RSignExtendInt32(CompactBufferReader& reader) {
  mode_ = reader.readByte();
}

bool RSignExtendInt32::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue operand(cx, iter.read());

  int32_t i;
  if (!ToInt32(cx, operand, &i)) {
    return false;
  }

  int32_t result;
  switch (MSignExtendInt32::Mode(mode_)) {
    case MSignExtendInt32::Byte:
      result = static_cast<int8_t>(i);
      break;
    case MSignExtendInt32::Half:
      result = static_cast<int16_t>(i);
      break;
  }

  iter.storeInstructionResult(JS::Int32Value(result));
  return true;
}

bool MAdd::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Add));
  writer.writeByte(type() == MIRType::Float32);
  return true;
}

RAdd::RAdd(CompactBufferReader& reader) {
  isFloatOperation_ = reader.readByte();
}

bool RAdd::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());
  RootedValue result(cx);

  MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
  if (!js::AddValues(cx, &lhs, &rhs, &result)) {
    return false;
  }

  // MIRType::Float32 is a specialization embedding the fact that the result is
  // rounded to a Float32.
  if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MSub::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Sub));
  writer.writeByte(type() == MIRType::Float32);
  return true;
}

RSub::RSub(CompactBufferReader& reader) {
  isFloatOperation_ = reader.readByte();
}

bool RSub::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());
  RootedValue result(cx);

  MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
  if (!js::SubValues(cx, &lhs, &rhs, &result)) {
    return false;
  }

  // MIRType::Float32 is a specialization embedding the fact that the result is
  // rounded to a Float32.
  if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MMul::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Mul));
  writer.writeByte(type() == MIRType::Float32);
  MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_);
  writer.writeByte(uint8_t(mode_));
  return true;
}

RMul::RMul(CompactBufferReader& reader) {
  isFloatOperation_ = reader.readByte();
  mode_ = reader.readByte();
}

bool RMul::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());
  RootedValue result(cx);

  if (MMul::Mode(mode_) == MMul::Normal) {
    if (!js::MulValues(cx, &lhs, &rhs, &result)) {
      return false;
    }

    // MIRType::Float32 is a specialization embedding the fact that the
    // result is rounded to a Float32.
    if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) {
      return false;
    }
  } else {
    MOZ_ASSERT(MMul::Mode(mode_) == MMul::Integer);
    if (!js::math_imul_handle(cx, lhs, rhs, &result)) {
      return false;
    }
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MDiv::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Div));
  writer.writeByte(type() == MIRType::Float32);
  return true;
}

RDiv::RDiv(CompactBufferReader& reader) {
  isFloatOperation_ = reader.readByte();
}

bool RDiv::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());
  RootedValue result(cx);

  if (!js::DivValues(cx, &lhs, &rhs, &result)) {
    return false;
  }

  // MIRType::Float32 is a specialization embedding the fact that the result is
  // rounded to a Float32.
  if (isFloatOperation_ && !RoundFloat32(cx, result, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MMod::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Mod));
  return true;
}

RMod::RMod(CompactBufferReader& reader) {}

bool RMod::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());
  RootedValue result(cx);

  MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
  if (!js::ModValues(cx, &lhs, &rhs, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MNot::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Not));
  return true;
}

RNot::RNot(CompactBufferReader& reader) {}

bool RNot::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<Value> value(cx);
  if (!iter.readMaybeUnpackedBigInt(cx, &value)) {
    return false;
  }

  bool result = !ToBoolean(value);
  iter.storeInstructionResult(BooleanValue(result));
  return true;
}

bool MBigIntAdd::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntAdd));
  return true;
}

RBigIntAdd::RBigIntAdd(CompactBufferReader& reader) {}

bool RBigIntAdd::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt());
  Rooted<BigInt*> rhs(cx, iter.readBigInt());

  BigInt* result = BigInt::add(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntSub::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntSub));
  return true;
}

RBigIntSub::RBigIntSub(CompactBufferReader& reader) {}

bool RBigIntSub::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt());
  Rooted<BigInt*> rhs(cx, iter.readBigInt());

  BigInt* result = BigInt::sub(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntMul::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntMul));
  return true;
}

RBigIntMul::RBigIntMul(CompactBufferReader& reader) {}

bool RBigIntMul::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt());
  Rooted<BigInt*> rhs(cx, iter.readBigInt());

  BigInt* result = BigInt::mul(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntDiv::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntDiv));
  return true;
}

RBigIntDiv::RBigIntDiv(CompactBufferReader& reader) {}

bool RBigIntDiv::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt());
  Rooted<BigInt*> rhs(cx, iter.readBigInt());
  MOZ_ASSERT(!rhs->isZero(),
             "division by zero throws and therefore can't be recovered");

  BigInt* result = BigInt::div(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntMod::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntMod));
  return true;
}

RBigIntMod::RBigIntMod(CompactBufferReader& reader) {}

bool RBigIntMod::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt());
  Rooted<BigInt*> rhs(cx, iter.readBigInt());
  MOZ_ASSERT(!rhs->isZero(),
             "division by zero throws and therefore can't be recovered");

  BigInt* result = BigInt::mod(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntPow::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPow));
  return true;
}

RBigIntPow::RBigIntPow(CompactBufferReader& reader) {}

bool RBigIntPow::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt());
  Rooted<BigInt*> rhs(cx, iter.readBigInt());
  MOZ_ASSERT(!rhs->isNegative(),
             "negative exponent throws and therefore can't be recovered");

  BigInt* result = BigInt::pow(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntBitAnd::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntBitAnd));
  return true;
}

RBigIntBitAnd::RBigIntBitAnd(CompactBufferReader& reader) {}

bool RBigIntBitAnd::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt());
  Rooted<BigInt*> rhs(cx, iter.readBigInt());

  BigInt* result = BigInt::bitAnd(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntBitOr::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntBitOr));
  return true;
}

RBigIntBitOr::RBigIntBitOr(CompactBufferReader& reader) {}

bool RBigIntBitOr::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt());
  Rooted<BigInt*> rhs(cx, iter.readBigInt());

  BigInt* result = BigInt::bitOr(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntBitXor::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntBitXor));
  return true;
}

RBigIntBitXor::RBigIntBitXor(CompactBufferReader& reader) {}

bool RBigIntBitXor::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt());
  Rooted<BigInt*> rhs(cx, iter.readBigInt());

  BigInt* result = BigInt::bitXor(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntLsh::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntLsh));
  return true;
}

RBigIntLsh::RBigIntLsh(CompactBufferReader& reader) {}

bool RBigIntLsh::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt());
  Rooted<BigInt*> rhs(cx, iter.readBigInt());

  BigInt* result = BigInt::lsh(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntRsh::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntRsh));
  return true;
}

RBigIntRsh::RBigIntRsh(CompactBufferReader& reader) {}

bool RBigIntRsh::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt());
  Rooted<BigInt*> rhs(cx, iter.readBigInt());

  BigInt* result = BigInt::rsh(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntIncrement::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntIncrement));
  return true;
}

RBigIntIncrement::RBigIntIncrement(CompactBufferReader& reader) {}

bool RBigIntIncrement::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> operand(cx, iter.readBigInt());

  BigInt* result = BigInt::inc(cx, operand);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntDecrement::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntDecrement));
  return true;
}

RBigIntDecrement::RBigIntDecrement(CompactBufferReader& reader) {}

bool RBigIntDecrement::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> operand(cx, iter.readBigInt());

  BigInt* result = BigInt::dec(cx, operand);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntNegate::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntNegate));
  return true;
}

RBigIntNegate::RBigIntNegate(CompactBufferReader& reader) {}

bool RBigIntNegate::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> operand(cx, iter.readBigInt());

  BigInt* result = BigInt::neg(cx, operand);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntBitNot::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntBitNot));
  return true;
}

RBigIntBitNot::RBigIntBitNot(CompactBufferReader& reader) {}

bool RBigIntBitNot::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> operand(cx, iter.readBigInt());

  BigInt* result = BigInt::bitNot(cx, operand);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(BigIntValue(result));
  return true;
}

bool MBigIntToIntPtr::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntToIntPtr));
  return true;
}

RBigIntToIntPtr::RBigIntToIntPtr(CompactBufferReader& reader) {}

bool RBigIntToIntPtr::recover(JSContext* cx, SnapshotIterator& iter) const {
  Value input = iter.read();
  MOZ_ASSERT(input.isBigInt());

  iter.storeInstructionResult(input);
  return true;
}

bool MIntPtrToBigInt::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_IntPtrToBigInt));
  return true;
}

RIntPtrToBigInt::RIntPtrToBigInt(CompactBufferReader& reader) {}

bool RIntPtrToBigInt::recover(JSContext* cx, SnapshotIterator& iter) const {
  BigInt* input = iter.readBigInt(cx);
  if (!input) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(input));
  return true;
}

bool MBigIntPtrAdd::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPtrAdd));
  return true;
}

RBigIntPtrAdd::RBigIntPtrAdd(CompactBufferReader& reader) {}

bool RBigIntPtrAdd::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt(cx));
  if (!lhs) {
    return false;
  }

  Rooted<BigInt*> rhs(cx, iter.readBigInt(cx));
  if (!rhs) {
    return false;
  }

  BigInt* result = BigInt::add(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(result));
  return true;
}

bool MBigIntPtrSub::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPtrSub));
  return true;
}

RBigIntPtrSub::RBigIntPtrSub(CompactBufferReader& reader) {}

bool RBigIntPtrSub::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt(cx));
  if (!lhs) {
    return false;
  }

  Rooted<BigInt*> rhs(cx, iter.readBigInt(cx));
  if (!rhs) {
    return false;
  }

  BigInt* result = BigInt::sub(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(result));
  return true;
}

bool MBigIntPtrMul::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPtrMul));
  return true;
}

RBigIntPtrMul::RBigIntPtrMul(CompactBufferReader& reader) {}

bool RBigIntPtrMul::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt(cx));
  if (!lhs) {
    return false;
  }

  Rooted<BigInt*> rhs(cx, iter.readBigInt(cx));
  if (!rhs) {
    return false;
  }

  BigInt* result = BigInt::mul(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(result));
  return true;
}

bool MBigIntPtrDiv::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPtrDiv));
  return true;
}

RBigIntPtrDiv::RBigIntPtrDiv(CompactBufferReader& reader) {}

bool RBigIntPtrDiv::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt(cx));
  if (!lhs) {
    return false;
  }

  Rooted<BigInt*> rhs(cx, iter.readBigInt(cx));
  if (!rhs) {
    return false;
  }

  BigInt* result = BigInt::div(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(result));
  return true;
}

bool MBigIntPtrMod::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPtrMod));
  return true;
}

RBigIntPtrMod::RBigIntPtrMod(CompactBufferReader& reader) {}

bool RBigIntPtrMod::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt(cx));
  if (!lhs) {
    return false;
  }

  Rooted<BigInt*> rhs(cx, iter.readBigInt(cx));
  if (!rhs) {
    return false;
  }

  BigInt* result = BigInt::mod(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(result));
  return true;
}

bool MBigIntPtrPow::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPtrPow));
  return true;
}

RBigIntPtrPow::RBigIntPtrPow(CompactBufferReader& reader) {}

bool RBigIntPtrPow::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt(cx));
  if (!lhs) {
    return false;
  }

  Rooted<BigInt*> rhs(cx, iter.readBigInt(cx));
  if (!rhs) {
    return false;
  }

  BigInt* result = BigInt::pow(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(result));
  return true;
}

bool MBigIntPtrBitAnd::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPtrBitAnd));
  return true;
}

RBigIntPtrBitAnd::RBigIntPtrBitAnd(CompactBufferReader& reader) {}

bool RBigIntPtrBitAnd::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt(cx));
  if (!lhs) {
    return false;
  }

  Rooted<BigInt*> rhs(cx, iter.readBigInt(cx));
  if (!rhs) {
    return false;
  }

  BigInt* result = BigInt::bitAnd(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(result));
  return true;
}

bool MBigIntPtrBitOr::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPtrBitOr));
  return true;
}

RBigIntPtrBitOr::RBigIntPtrBitOr(CompactBufferReader& reader) {}

bool RBigIntPtrBitOr::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt(cx));
  if (!lhs) {
    return false;
  }

  Rooted<BigInt*> rhs(cx, iter.readBigInt(cx));
  if (!rhs) {
    return false;
  }

  BigInt* result = BigInt::bitOr(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(result));
  return true;
}

bool MBigIntPtrBitXor::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPtrBitXor));
  return true;
}

RBigIntPtrBitXor::RBigIntPtrBitXor(CompactBufferReader& reader) {}

bool RBigIntPtrBitXor::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt(cx));
  if (!lhs) {
    return false;
  }

  Rooted<BigInt*> rhs(cx, iter.readBigInt(cx));
  if (!rhs) {
    return false;
  }

  BigInt* result = BigInt::bitXor(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(result));
  return true;
}

bool MBigIntPtrLsh::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPtrLsh));
  return true;
}

RBigIntPtrLsh::RBigIntPtrLsh(CompactBufferReader& reader) {}

bool RBigIntPtrLsh::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt(cx));
  if (!lhs) {
    return false;
  }

  Rooted<BigInt*> rhs(cx, iter.readBigInt(cx));
  if (!rhs) {
    return false;
  }

  BigInt* result = BigInt::lsh(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(result));
  return true;
}

bool MBigIntPtrRsh::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPtrRsh));
  return true;
}

RBigIntPtrRsh::RBigIntPtrRsh(CompactBufferReader& reader) {}

bool RBigIntPtrRsh::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> lhs(cx, iter.readBigInt(cx));
  if (!lhs) {
    return false;
  }

  Rooted<BigInt*> rhs(cx, iter.readBigInt(cx));
  if (!rhs) {
    return false;
  }

  BigInt* result = BigInt::rsh(cx, lhs, rhs);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(result));
  return true;
}

bool MBigIntPtrBitNot::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_BigIntPtrBitNot));
  return true;
}

RBigIntPtrBitNot::RBigIntPtrBitNot(CompactBufferReader& reader) {}

bool RBigIntPtrBitNot::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<BigInt*> operand(cx, iter.readBigInt(cx));
  if (!operand) {
    return false;
  }

  BigInt* result = BigInt::bitNot(cx, operand);
  if (!result) {
    return false;
  }

  iter.storeInstructionResult(JS::BigIntValue(result));
  return true;
}

bool MCompare::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Compare));

  static_assert(sizeof(JSOp) == sizeof(uint8_t));
  writer.writeByte(uint8_t(jsop_));
  return true;
}

RCompare::RCompare(CompactBufferReader& reader) {
  jsop_ = JSOp(reader.readByte());

  MOZ_ASSERT(IsEqualityOp(jsop_) || IsRelationalOp(jsop_));
}

bool RCompare::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());

  bool result;
  switch (jsop_) {
    case JSOp::Eq:
    case JSOp::Ne:
      if (!js::LooselyEqual(cx, lhs, rhs, &result)) {
        return false;
      }
      if (jsop_ == JSOp::Ne) {
        result = !result;
      }
      break;
    case JSOp::StrictEq:
    case JSOp::StrictNe:
      if (!StrictlyEqual(cx, lhs, rhs, &result)) {
        return false;
      }
      if (jsop_ == JSOp::StrictNe) {
        result = !result;
      }
      break;
    case JSOp::Lt:
      if (!js::LessThan(cx, &lhs, &rhs, &result)) {
        return false;
      }
      break;
    case JSOp::Le:
      if (!js::LessThanOrEqual(cx, &lhs, &rhs, &result)) {
        return false;
      }
      break;
    case JSOp::Gt:
      if (!js::GreaterThan(cx, &lhs, &rhs, &result)) {
        return false;
      }
      break;
    case JSOp::Ge:
      if (!js::GreaterThanOrEqual(cx, &lhs, &rhs, &result)) {
        return false;
      }
      break;
    default:
      MOZ_CRASH("Unexpected op.");
  }

  iter.storeInstructionResult(BooleanValue(result));
  return true;
}

bool MConcat::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Concat));
  return true;
}

RConcat::RConcat(CompactBufferReader& reader) {}

bool RConcat::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue lhs(cx, iter.read());
  RootedValue rhs(cx, iter.read());
  RootedValue result(cx);

  MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
  if (!js::AddValues(cx, &lhs, &rhs, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

RStringLength::RStringLength(CompactBufferReader& reader) {}

bool RStringLength::recover(JSContext* cx, SnapshotIterator& iter) const {
  JSString* string = iter.readString();

  static_assert(JSString::MAX_LENGTH <= INT32_MAX,
                "Can cast string length to int32_t");

  iter.storeInstructionResult(Int32Value(int32_t(string->length())));
  return true;
}

bool MStringLength::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_StringLength));
  return true;
}

bool MArgumentsLength::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_ArgumentsLength));
  return true;
}

RArgumentsLength::RArgumentsLength(CompactBufferReader& reader) {}

bool RArgumentsLength::recover(JSContext* cx, SnapshotIterator& iter) const {
  uintptr_t numActualArgs = iter.frame()->numActualArgs();

  static_assert(ARGS_LENGTH_MAX <= INT32_MAX,
                "Can cast arguments count to int32_t");
  MOZ_ASSERT(numActualArgs <= ARGS_LENGTH_MAX);

  iter.storeInstructionResult(JS::Int32Value(int32_t(numActualArgs)));
  return true;
}

bool MFloor::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Floor));
  return true;
}

RFloor::RFloor(CompactBufferReader& reader) {}

bool RFloor::recover(JSContext* cx, SnapshotIterator& iter) const {
  double num = iter.readNumber();
  double result = js::math_floor_impl(num);

  iter.storeInstructionResult(NumberValue(result));
  return true;
}

bool MCeil::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Ceil));
  return true;
}

RCeil::RCeil(CompactBufferReader& reader) {}

bool RCeil::recover(JSContext* cx, SnapshotIterator& iter) const {
  double num = iter.readNumber();
  double result = js::math_ceil_impl(num);

  iter.storeInstructionResult(NumberValue(result));
  return true;
}

bool MRound::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Round));
  return true;
}

RRound::RRound(CompactBufferReader& reader) {}

bool RRound::recover(JSContext* cx, SnapshotIterator& iter) const {
  double num = iter.readNumber();
  double result = js::math_round_impl(num);

  iter.storeInstructionResult(NumberValue(result));
  return true;
}

bool MTrunc::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Trunc));
  return true;
}

RTrunc::RTrunc(CompactBufferReader& reader) {}

bool RTrunc::recover(JSContext* cx, SnapshotIterator& iter) const {
  double num = iter.readNumber();
  double result = js::math_trunc_impl(num);

  iter.storeInstructionResult(NumberValue(result));
  return true;
}

bool MCharCodeAt::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_CharCodeAt));
  return true;
}

RCharCodeAt::RCharCodeAt(CompactBufferReader& reader) {}

bool RCharCodeAt::recover(JSContext* cx, SnapshotIterator& iter) const {
  JSString* string = iter.readString();

  // Int32 because |index| is computed from MBoundsCheck.
  int32_t index = iter.readInt32();
  MOZ_RELEASE_ASSERT(0 <= index && size_t(index) < string->length());

  char16_t c;
  if (!string->getChar(cx, index, &c)) {
    return false;
  }

  iter.storeInstructionResult(Int32Value(c));
  return true;
}

bool MFromCharCode::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_FromCharCode));
  return true;
}

RFromCharCode::RFromCharCode(CompactBufferReader& reader) {}

bool RFromCharCode::recover(JSContext* cx, SnapshotIterator& iter) const {
  // Number because |charCode| is computed from (recoverable) user input.
  int32_t charCode = JS::ToInt32(iter.readNumber());

  JSString* str = StringFromCharCode(cx, charCode);
  if (!str) {
    return false;
  }

  iter.storeInstructionResult(StringValue(str));
  return true;
}

bool MFromCharCodeEmptyIfNegative::writeRecoverData(
    CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(
      uint32_t(RInstruction::Recover_FromCharCodeEmptyIfNegative));
  return true;
}

RFromCharCodeEmptyIfNegative::RFromCharCodeEmptyIfNegative(
    CompactBufferReader& reader) {}

bool RFromCharCodeEmptyIfNegative::recover(JSContext* cx,
                                           SnapshotIterator& iter) const {
  // Int32 because |charCode| is computed from MCharCodeAtOrNegative.
  int32_t charCode = iter.readInt32();

  JSString* str;
  if (charCode < 0) {
    str = cx->emptyString();
  } else {
    str = StringFromCharCode(cx, charCode);
    if (!str) {
      return false;
    }
  }

  iter.storeInstructionResult(StringValue(str));
  return true;
}

bool MPow::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Pow));
  return true;
}

RPow::RPow(CompactBufferReader& reader) {}

bool RPow::recover(JSContext* cx, SnapshotIterator& iter) const {
  double base = iter.readNumber();
  double power = iter.readNumber();
  double result = ecmaPow(base, power);

  iter.storeInstructionResult(NumberValue(result));
  return true;
}

bool MPowHalf::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_PowHalf));
  return true;
}

RPowHalf::RPowHalf(CompactBufferReader& reader) {}

bool RPowHalf::recover(JSContext* cx, SnapshotIterator& iter) const {
  double base = iter.readNumber();
  double power = 0.5;
  double result = ecmaPow(base, power);

  iter.storeInstructionResult(NumberValue(result));
  return true;
}

bool MMinMax::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_MinMax));
  writer.writeByte(isMax_);
  return true;
}

RMinMax::RMinMax(CompactBufferReader& reader) { isMax_ = reader.readByte(); }

bool RMinMax::recover(JSContext* cx, SnapshotIterator& iter) const {
  double x = iter.readNumber();
  double y = iter.readNumber();

  double result;
  if (isMax_) {
    result = js::math_max_impl(x, y);
  } else {
    result = js::math_min_impl(x, y);
  }

  iter.storeInstructionResult(NumberValue(result));
  return true;
}

bool MAbs::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Abs));
  return true;
}

RAbs::RAbs(CompactBufferReader& reader) {}

bool RAbs::recover(JSContext* cx, SnapshotIterator& iter) const {
  double num = iter.readNumber();
  double result = js::math_abs_impl(num);

  iter.storeInstructionResult(NumberValue(result));
  return true;
}

bool MSqrt::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Sqrt));
  writer.writeByte(type() == MIRType::Float32);
  return true;
}

RSqrt::RSqrt(CompactBufferReader& reader) {
  isFloatOperation_ = reader.readByte();
}

bool RSqrt::recover(JSContext* cx, SnapshotIterator& iter) const {
  double num = iter.readNumber();
  double result = js::math_sqrt_impl(num);

  // MIRType::Float32 is a specialization embedding the fact that the result is
  // rounded to a Float32.
  if (isFloatOperation_) {
    result = js::RoundFloat32(result);
  }

  iter.storeInstructionResult(DoubleValue(result));
  return true;
}

bool MAtan2::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Atan2));
  return true;
}

RAtan2::RAtan2(CompactBufferReader& reader) {}

bool RAtan2::recover(JSContext* cx, SnapshotIterator& iter) const {
  double y = iter.readNumber();
  double x = iter.readNumber();
  double result = js::ecmaAtan2(y, x);

  iter.storeInstructionResult(DoubleValue(result));
  return true;
}

bool MHypot::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Hypot));
  writer.writeUnsigned(uint32_t(numOperands()));
  return true;
}

RHypot::RHypot(CompactBufferReader& reader)
    : numOperands_(reader.readUnsigned()) {}

bool RHypot::recover(JSContext* cx, SnapshotIterator& iter) const {
  JS::RootedValueVector vec(cx);

  if (!vec.reserve(numOperands_)) {
    return false;
  }

  for (uint32_t i = 0; i < numOperands_; ++i) {
    vec.infallibleAppend(NumberValue(iter.readNumber()));
  }

  RootedValue result(cx);

  if (!js::math_hypot_handle(cx, vec, &result)) {
    return false;
  }

  iter.storeInstructionResult(result);
  return true;
}

bool MNearbyInt::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  switch (roundingMode_) {
    case RoundingMode::Up:
      writer.writeUnsigned(uint32_t(RInstruction::Recover_Ceil));
      return true;
    case RoundingMode::Down:
      writer.writeUnsigned(uint32_t(RInstruction::Recover_Floor));
      return true;
    case RoundingMode::TowardsZero:
      writer.writeUnsigned(uint32_t(RInstruction::Recover_Trunc));
      return true;
    default:
      MOZ_CRASH("Unsupported rounding mode.");
  }
}

RNearbyInt::RNearbyInt(CompactBufferReader& reader) {
  roundingMode_ = reader.readByte();
}

bool RNearbyInt::recover(JSContext* cx, SnapshotIterator& iter) const {
  MOZ_CRASH("Unsupported rounding mode.");
}

bool MSign::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Sign));
  return true;
}

RSign::RSign(CompactBufferReader& reader) {}

bool RSign::recover(JSContext* cx, SnapshotIterator& iter) const {
  double num = iter.readNumber();
  double result = js::math_sign_impl(num);

  iter.storeInstructionResult(NumberValue(result));
  return true;
}

bool MMathFunction::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  switch (function_) {
    case UnaryMathFunction::Ceil:
      writer.writeUnsigned(uint32_t(RInstruction::Recover_Ceil));
      return true;
    case UnaryMathFunction::Floor:
      writer.writeUnsigned(uint32_t(RInstruction::Recover_Floor));
      return true;
    case UnaryMathFunction::Round:
      writer.writeUnsigned(uint32_t(RInstruction::Recover_Round));
      return true;
    case UnaryMathFunction::Trunc:
      writer.writeUnsigned(uint32_t(RInstruction::Recover_Trunc));
      return true;
    case UnaryMathFunction::SinNative:
    case UnaryMathFunction::SinFdlibm:
    case UnaryMathFunction::CosNative:
    case UnaryMathFunction::CosFdlibm:
    case UnaryMathFunction::TanNative:
    case UnaryMathFunction::TanFdlibm:
    case UnaryMathFunction::Log:
    case UnaryMathFunction::Exp:
    case UnaryMathFunction::ACos:
    case UnaryMathFunction::ASin:
    case UnaryMathFunction::ATan:
    case UnaryMathFunction::Log10:
    case UnaryMathFunction::Log2:
    case UnaryMathFunction::Log1P:
    case UnaryMathFunction::ExpM1:
    case UnaryMathFunction::CosH:
    case UnaryMathFunction::SinH:
    case UnaryMathFunction::TanH:
    case UnaryMathFunction::ACosH:
    case UnaryMathFunction::ASinH:
    case UnaryMathFunction::ATanH:
    case UnaryMathFunction::Cbrt:
      static_assert(sizeof(UnaryMathFunction) == sizeof(uint8_t));
      writer.writeUnsigned(uint32_t(RInstruction::Recover_MathFunction));
      writer.writeByte(uint8_t(function_));
      return true;
  }
  MOZ_CRASH("Unknown math function.");
}

RMathFunction::RMathFunction(CompactBufferReader& reader) {
  function_ = UnaryMathFunction(reader.readByte());
}

bool RMathFunction::recover(JSContext* cx, SnapshotIterator& iter) const {
  double num = iter.readNumber();

  double result;
  switch (function_) {
    case UnaryMathFunction::SinNative:
      result = js::math_sin_native_impl(num);
      break;
    case UnaryMathFunction::SinFdlibm:
      result = js::math_sin_fdlibm_impl(num);
      break;
    case UnaryMathFunction::CosNative:
      result = js::math_cos_native_impl(num);
      break;
    case UnaryMathFunction::CosFdlibm:
      result = js::math_cos_fdlibm_impl(num);
      break;
    case UnaryMathFunction::TanNative:
      result = js::math_tan_native_impl(num);
      break;
    case UnaryMathFunction::TanFdlibm:
      result = js::math_tan_fdlibm_impl(num);
      break;
    case UnaryMathFunction::Log:
      result = js::math_log_impl(num);
      break;
    case UnaryMathFunction::Exp:
      result = js::math_exp_impl(num);
      break;
    case UnaryMathFunction::ACos:
      result = js::math_acos_impl(num);
      break;
    case UnaryMathFunction::ASin:
      result = js::math_asin_impl(num);
      break;
    case UnaryMathFunction::ATan:
      result = js::math_atan_impl(num);
      break;
    case UnaryMathFunction::Log10:
      result = js::math_log10_impl(num);
      break;
    case UnaryMathFunction::Log2:
      result = js::math_log2_impl(num);
      break;
    case UnaryMathFunction::Log1P:
      result = js::math_log1p_impl(num);
      break;
    case UnaryMathFunction::ExpM1:
      result = js::math_expm1_impl(num);
      break;
    case UnaryMathFunction::CosH:
      result = js::math_cosh_impl(num);
      break;
    case UnaryMathFunction::SinH:
      result = js::math_sinh_impl(num);
      break;
    case UnaryMathFunction::TanH:
      result = js::math_tanh_impl(num);
      break;
    case UnaryMathFunction::ACosH:
      result = js::math_acosh_impl(num);
      break;
    case UnaryMathFunction::ASinH:
      result = js::math_asinh_impl(num);
      break;
    case UnaryMathFunction::ATanH:
      result = js::math_atanh_impl(num);
      break;
    case UnaryMathFunction::Cbrt:
      result = js::math_cbrt_impl(num);
      break;

    case UnaryMathFunction::Trunc:
    case UnaryMathFunction::Floor:
    case UnaryMathFunction::Ceil:
    case UnaryMathFunction::Round:
      // These have their own recover instructions.
      MOZ_CRASH("Unexpected rounding math function.");
  }

  iter.storeInstructionResult(DoubleValue(result));
  return true;
}

bool MRandom::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(this->canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Random));
  return true;
}

bool MRandom::canRecoverOnBailout() const {
  return !js::SupportDifferentialTesting();
}

RRandom::RRandom(CompactBufferReader& reader) {}

bool RRandom::recover(JSContext* cx, SnapshotIterator& iter) const {
  iter.storeInstructionResult(DoubleValue(math_random_impl(cx)));
  return true;
}

bool MStringSplit::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_StringSplit));
  return true;
}

RStringSplit::RStringSplit(CompactBufferReader& reader) {}

bool RStringSplit::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedString str(cx, iter.readString());
  RootedString sep(cx, iter.readString());

  JSObject* res = StringSplitString(cx, str, sep, INT32_MAX);
  if (!res) {
    return false;
  }

  iter.storeInstructionResult(ObjectValue(*res));
  return true;
}

bool MNaNToZero::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_NaNToZero));
  return true;
}

RNaNToZero::RNaNToZero(CompactBufferReader& reader) {}

bool RNaNToZero::recover(JSContext* cx, SnapshotIterator& iter) const {
  double v = iter.readNumber();
  if (std::isnan(v) || mozilla::IsNegativeZero(v)) {
    v = 0.0;
  }

  iter.storeInstructionResult(DoubleValue(v));
  return true;
}

bool MTypeOf::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_TypeOf));
  return true;
}

RTypeOf::RTypeOf(CompactBufferReader& reader) {}

bool RTypeOf::recover(JSContext* cx, SnapshotIterator& iter) const {
  JS::Value v = iter.read();

  iter.storeInstructionResult(Int32Value(TypeOfValue(v)));
  return true;
}

bool MTypeOfName::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_TypeOfName));
  return true;
}

RTypeOfName::RTypeOfName(CompactBufferReader& reader) {}

bool RTypeOfName::recover(JSContext* cx, SnapshotIterator& iter) const {
  // Int32 because |type| is computed from MTypeOf.
  int32_t type = iter.readInt32();
  MOZ_ASSERT(JSTYPE_UNDEFINED <= type && type < JSTYPE_LIMIT);

  JSString* name = TypeName(JSType(type), *cx->runtime()->commonNames);
  iter.storeInstructionResult(StringValue(name));
  return true;
}

bool MToDouble::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_ToDouble));
  return true;
}

RToDouble::RToDouble(CompactBufferReader& reader) {}

bool RToDouble::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue v(cx, iter.read());

  MOZ_ASSERT(!v.isObject());
  MOZ_ASSERT(!v.isSymbol());
  MOZ_ASSERT(!v.isBigInt());

  double dbl;
  if (!ToNumber(cx, v, &dbl)) {
    return false;
  }

  iter.storeInstructionResult(DoubleValue(dbl));
  return true;
}

bool MToFloat32::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_ToFloat32));
  return true;
}

RToFloat32::RToFloat32(CompactBufferReader& reader) {}

bool RToFloat32::recover(JSContext* cx, SnapshotIterator& iter) const {
  double num = iter.readNumber();
  double result = js::RoundFloat32(num);

  iter.storeInstructionResult(DoubleValue(result));
  return true;
}

bool MToFloat16::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_ToFloat16));
  return true;
}

RToFloat16::RToFloat16(CompactBufferReader& reader) {}

bool RToFloat16::recover(JSContext* cx, SnapshotIterator& iter) const {
  double num = iter.readNumber();
  double result = js::RoundFloat16(num);

  iter.storeInstructionResult(DoubleValue(result));
  return true;
}

bool MTruncateToInt32::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_TruncateToInt32));
  return true;
}

RTruncateToInt32::RTruncateToInt32(CompactBufferReader& reader) {}

bool RTruncateToInt32::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedValue value(cx, iter.read());

  int32_t trunc;
  if (!JS::ToInt32(cx, value, &trunc)) {
    return false;
  }

  iter.storeInstructionResult(Int32Value(trunc));
  return true;
}

bool MNewObject::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());

  writer.writeUnsigned(uint32_t(RInstruction::Recover_NewObject));

  // Recover instructions are only supported if we have a template object.
  MOZ_ASSERT(mode_ == MNewObject::ObjectCreate);
  return true;
}

RNewObject::RNewObject(CompactBufferReader& reader) {}

bool RNewObject::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedObject templateObject(cx, iter.readObject());

  // See CodeGenerator::visitNewObjectVMCall.
  // Note that recover instructions are only used if mode == ObjectCreate.
  JSObject* resultObject =
      ObjectCreateWithTemplate(cx, templateObject.as<PlainObject>());
  if (!resultObject) {
    return false;
  }

  iter.storeInstructionResult(ObjectValue(*resultObject));
  return true;
}

bool MNewPlainObject::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_NewPlainObject));

  MOZ_ASSERT(gc::AllocKind(uint8_t(allocKind_)) == allocKind_);
  writer.writeByte(uint8_t(allocKind_));
  MOZ_ASSERT(gc::Heap(uint8_t(initialHeap_)) == initialHeap_);
  writer.writeByte(uint8_t(initialHeap_));
  return true;
}

RNewPlainObject::RNewPlainObject(CompactBufferReader& reader) {
  allocKind_ = gc::AllocKind(reader.readByte());
  MOZ_ASSERT(gc::IsValidAllocKind(allocKind_));
  initialHeap_ = gc::Heap(reader.readByte());
  MOZ_ASSERT(initialHeap_ == gc::Heap::Default ||
             initialHeap_ == gc::Heap::Tenured);
}

bool RNewPlainObject::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<SharedShape*> shape(cx, &iter.readGCCellPtr().as<Shape>().asShared());

  // See CodeGenerator::visitNewPlainObject.
  JSObject* resultObject =
      NewPlainObjectOptimizedFallback(cx, shape, allocKind_, initialHeap_);
  if (!resultObject) {
    return false;
  }

  iter.storeInstructionResult(ObjectValue(*resultObject));
  return true;
}

bool MNewArrayObject::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArrayObject));

  writer.writeUnsigned(length_);
  MOZ_ASSERT(gc::Heap(uint8_t(initialHeap_)) == initialHeap_);
  writer.writeByte(uint8_t(initialHeap_));
  return true;
}

RNewArrayObject::RNewArrayObject(CompactBufferReader& reader) {
  length_ = reader.readUnsigned();
  initialHeap_ = gc::Heap(reader.readByte());
  MOZ_ASSERT(initialHeap_ == gc::Heap::Default ||
             initialHeap_ == gc::Heap::Tenured);
}

bool RNewArrayObject::recover(JSContext* cx, SnapshotIterator& iter) const {
  iter.read();  // Skip unused shape field.

  NewObjectKind kind =
      initialHeap_ == gc::Heap::Tenured ? TenuredObject : GenericObject;
  JSObject* array = NewArrayOperation(cx, length_, kind);
  if (!array) {
    return false;
  }

  iter.storeInstructionResult(ObjectValue(*array));
  return true;
}

bool MNewTypedArray::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_NewTypedArray));
  return true;
}

RNewTypedArray::RNewTypedArray(CompactBufferReader& reader) {}

bool RNewTypedArray::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedObject templateObject(cx, iter.readObject());

  size_t length = templateObject.as<FixedLengthTypedArrayObject>()->length();
  MOZ_ASSERT(length <= INT32_MAX,
             "Template objects are only created for int32 lengths");

  JSObject* resultObject =
      NewTypedArrayWithTemplateAndLength(cx, templateObject, length);
  if (!resultObject) {
    return false;
  }

  iter.storeInstructionResult(ObjectValue(*resultObject));
  return true;
}

bool MNewArray::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArray));
  writer.writeUnsigned(length());
  return true;
}

RNewArray::RNewArray(CompactBufferReader& reader) {
  count_ = reader.readUnsigned();
}

bool RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedObject templateObject(cx, iter.readObject());
  Rooted<Shape*> shape(cx, templateObject->shape());

  ArrayObject* resultObject = NewArrayWithShape(cx, count_, shape);
  if (!resultObject) {
    return false;
  }

  iter.storeInstructionResult(ObjectValue(*resultObject));
  return true;
}

bool MNewIterator::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_NewIterator));
  writer.writeByte(type_);
  return true;
}

RNewIterator::RNewIterator(CompactBufferReader& reader) {
  type_ = reader.readByte();
}

bool RNewIterator::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedObject templateObject(cx, iter.readObject());

  JSObject* resultObject = nullptr;
  switch (MNewIterator::Type(type_)) {
    case MNewIterator::ArrayIterator:
      resultObject = NewArrayIterator(cx);
      break;
    case MNewIterator::StringIterator:
      resultObject = NewStringIterator(cx);
      break;
    case MNewIterator::RegExpStringIterator:
      resultObject = NewRegExpStringIterator(cx);
      break;
  }

  if (!resultObject) {
    return false;
  }

  iter.storeInstructionResult(ObjectValue(*resultObject));
  return true;
}

bool MLambda::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Lambda));
  return true;
}

RLambda::RLambda(CompactBufferReader& reader) {}

bool RLambda::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedObject scopeChain(cx, iter.readObject());
  RootedFunction fun(cx, &iter.readObject()->as<JSFunction>());

  JSObject* resultObject = js::Lambda(cx, fun, scopeChain);
  if (!resultObject) {
    return false;
  }

  iter.storeInstructionResult(ObjectValue(*resultObject));
  return true;
}

bool MFunctionWithProto::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_FunctionWithProto));
  return true;
}

RFunctionWithProto::RFunctionWithProto(CompactBufferReader& reader) {}

bool RFunctionWithProto::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedObject scopeChain(cx, iter.readObject());
  RootedObject prototype(cx, iter.readObject());
  RootedFunction fun(cx, &iter.readObject()->as<JSFunction>());

  JSObject* resultObject =
      js::FunWithProtoOperation(cx, fun, scopeChain, prototype);
  if (!resultObject) {
    return false;
  }

  iter.storeInstructionResult(ObjectValue(*resultObject));
  return true;
}

bool MNewCallObject::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_NewCallObject));
  return true;
}

RNewCallObject::RNewCallObject(CompactBufferReader& reader) {}

bool RNewCallObject::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<CallObject*> templateObj(cx, &iter.readObject()->as<CallObject>());

  Rooted<SharedShape*> shape(cx, templateObj->sharedShape());

  JSObject* resultObject = CallObject::createWithShape(cx, shape);
  if (!resultObject) {
    return false;
  }

  iter.storeInstructionResult(ObjectValue(*resultObject));
  return true;
}

bool MObjectKeys::canRecoverOnBailout() const {
  // Only claim that this operation can be recovered on bailout if some other
  // optimization already marked it as such.
  return isRecoveredOnBailout();
}

bool MObjectKeys::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_ObjectKeys));
  return true;
}

RObjectKeys::RObjectKeys(CompactBufferReader& reader) {}

bool RObjectKeys::recover(JSContext* cx, SnapshotIterator& iter) const {
  Rooted<JSObject*> obj(cx, iter.readObject());

  JSObject* resultKeys = ObjectKeys(cx, obj);
  if (!resultKeys) {
    return false;
  }

  iter.storeInstructionResult(ObjectValue(*resultKeys));
  return true;
}

bool MObjectState::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_ObjectState));
  writer.writeUnsigned(numSlots());
  return true;
}

RObjectState::RObjectState(CompactBufferReader& reader) {
  numSlots_ = reader.readUnsigned();
}

bool RObjectState::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedObject object(cx, iter.readObject());
  Handle<NativeObject*> nativeObject = object.as<NativeObject>();
  MOZ_ASSERT(!Watchtower::watchesPropertyModification(nativeObject));
  MOZ_ASSERT(nativeObject->slotSpan() == numSlots());

  for (size_t i = 0; i < numSlots(); i++) {
    Value val = iter.read();
    nativeObject->setSlot(i, val);
  }

  iter.storeInstructionResult(ObjectValue(*object));
  return true;
}

bool MArrayState::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_ArrayState));
  writer.writeUnsigned(numElements());
  return true;
}

RArrayState::RArrayState(CompactBufferReader& reader) {
  numElements_ = reader.readUnsigned();
}

bool RArrayState::recover(JSContext* cx, SnapshotIterator& iter) const {
  ArrayObject* object = &iter.readObject()->as<ArrayObject>();

  // Int32 because |initLength| is computed from MConstant.
  uint32_t initLength = iter.readInt32();

  MOZ_ASSERT(object->getDenseInitializedLength() == 0,
             "initDenseElement call below relies on this");
  object->setDenseInitializedLength(initLength);

  for (size_t index = 0; index < numElements(); index++) {
    Value val = iter.read();

    if (index >= initLength) {
      MOZ_ASSERT(val.isUndefined());
      continue;
    }

    object->initDenseElement(index, val);
  }

  iter.storeInstructionResult(ObjectValue(*object));
  return true;
}

bool MAssertRecoveredOnBailout::writeRecoverData(
    CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  MOZ_RELEASE_ASSERT(input()->isRecoveredOnBailout() == mustBeRecovered_,
                     "assertRecoveredOnBailout failed during compilation");
  writer.writeUnsigned(
      uint32_t(RInstruction::Recover_AssertRecoveredOnBailout));
  return true;
}

RAssertRecoveredOnBailout::RAssertRecoveredOnBailout(
    CompactBufferReader& reader) {}

bool RAssertRecoveredOnBailout::recover(JSContext* cx,
                                        SnapshotIterator& iter) const {
  iter.read();  // skip the unused operand.
  iter.storeInstructionResult(UndefinedValue());
  return true;
}

bool MStringReplace::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_StringReplace));
  writer.writeByte(isFlatReplacement_);
  return true;
}

RStringReplace::RStringReplace(CompactBufferReader& reader) {
  isFlatReplacement_ = reader.readByte();
}

bool RStringReplace::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedString string(cx, iter.readString());
  RootedString pattern(cx, iter.readString());
  RootedString replace(cx, iter.readString());

  JSString* result =
      isFlatReplacement_
          ? js::StringFlatReplaceString(cx, string, pattern, replace)
          : js::str_replace_string_raw(cx, string, pattern, replace);

  if (!result) {
    return false;
  }

  iter.storeInstructionResult(StringValue(result));
  return true;
}

bool MSubstr::writeRecoverData(CompactBufferWriter& writer) const {
  MOZ_ASSERT(canRecoverOnBailout());
  writer.writeUnsigned(uint32_t(RInstruction::Recover_Substr));
  return true;
}

RSubstr::RSubstr(CompactBufferReader& reader) {}

bool RSubstr::recover(JSContext* cx, SnapshotIterator& iter) const {
  RootedString str(cx, iter.readString());

  // Int32 because |begin| is computed from MStringTrimStartIndex, MConstant,
  // or CallSubstringKernelResult.
  int32_t begin = iter.readInt32();

  // |length| is computed from MSub(truncated), MStringTrimEndIndex, or
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=99 H=100 G=99

¤ Dauer der Verarbeitung: 0.10 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.