| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * 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 "jsapi.h" |
| #include "jscntxt.h" |
| #include "jsmath.h" |
| #include "jsobj.h" |
| #include "jsstr.h" |
| |
| #include "builtin/RegExp.h" |
| #include "builtin/SIMD.h" |
| #include "builtin/TypedObject.h" |
| |
| #include "gc/Heap.h" |
| |
| #include "jit/JitFrameIterator.h" |
| #include "jit/JitSpewer.h" |
| #include "jit/MIR.h" |
| #include "jit/MIRGraph.h" |
| #include "jit/VMFunctions.h" |
| #include "vm/Interpreter.h" |
| #include "vm/String.h" |
| |
| #include "vm/Interpreter-inl.h" |
| #include "vm/NativeObject-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 is too small to decode R" #op " instructions."); \ |
| 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(); |
| JSFunction* fun = bb->info().funMaybeLazy(); |
| 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. |
| if (GetJitContext()->cx) { |
| uint32_t stackDepth; |
| bool reachablePC; |
| jsbytecode* bailPC = pc(); |
| |
| if (mode() == MResumePoint::ResumeAfter) |
| bailPC = GetNextPc(pc()); |
| |
| if (!ReconstructStackDepth(GetJitContext()->cx, script, |
| bailPC, &stackDepth, &reachablePC)) |
| { |
| return false; |
| } |
| |
| if (reachablePC) { |
| if (JSOp(*bailPC) == JSOP_FUNCALL) { |
| // For fun.call(this, ...); the reconstructStackDepth will |
| // include the this. When inlining that is not included. So the |
| // exprStackSlots will be one less. |
| MOZ_ASSERT(stackDepth - exprStack <= 1); |
| } else if (JSOp(*bailPC) != JSOP_FUNAPPLY && |
| !IsGetPropPC(bailPC) && !IsSetPropPC(bailPC)) |
| { |
| // For fun.apply({}, arguments) the reconstructStackDepth will |
| // have stackdepth 4, but it could be that we inlined the |
| // funapply. In that case exprStackSlots, will have the real |
| // arguments in the slots and not be 4. |
| |
| // With accessors, we have different stack depths depending on |
| // whether or not we inlined the accessor, as the inlined stack |
| // contains a callee function that should never have been there |
| // and we might just be capturing an uneventful property site, |
| // in which case there won't have been any violence. |
| MOZ_ASSERT(exprStack == stackDepth); |
| } |
| } |
| } |
| #endif |
| |
| // 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(CountArgSlots(script, fun) < SNAPSHOT_MAX_NARGS + 4); |
| |
| #ifdef JS_JITSPEW |
| uint32_t implicit = StartArgSlot(script); |
| #endif |
| uint32_t formalArgs = CountArgSlots(script, fun); |
| uint32_t nallocs = formalArgs + script->nfixed() + exprStack; |
| |
| JitSpew(JitSpew_IonSnapshots, "Starting frame; implicit %u, formals %u, fixed %u, exprs %u", |
| implicit, formalArgs - implicit, script->nfixed(), exprStack); |
| |
| uint32_t pcoff = script->pcToOffset(pc()); |
| JitSpew(JitSpew_IonSnapshots, "Writing pc offset %u, nslots %u", pcoff, nallocs); |
| writer.writeUnsigned(pcoff); |
| writer.writeUnsigned(nallocs); |
| return true; |
| } |
| |
| RResumePoint::RResumePoint(CompactBufferReader& reader) |
| { |
| pcOffset_ = reader.readUnsigned(); |
| numOperands_ = reader.readUnsigned(); |
| JitSpew(JitSpew_IonSnapshots, "Read RResumePoint (pc offset %u, nslots %u)", |
| pcOffset_, numOperands_); |
| } |
| |
| bool |
| RResumePoint::recover(JSContext* cx, SnapshotIterator& iter) const |
| { |
| MOZ_CRASH("This instruction is not recoverable."); |
| } |
| |
| bool |
| MBitNot::writeRecoverData(CompactBufferWriter& writer) const |
| { |
| 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()); |
| |
| int32_t result; |
| if (!js::BitNot(cx, operand, &result)) |
| return false; |
| |
| RootedValue rootedResult(cx, js::Int32Value(result)); |
| iter.storeInstructionResult(rootedResult); |
| 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()); |
| int32_t result; |
| MOZ_ASSERT(!lhs.isObject() && !rhs.isObject()); |
| |
| if (!js::BitAnd(cx, lhs, rhs, &result)) |
| return false; |
| |
| RootedValue rootedResult(cx, js::Int32Value(result)); |
| iter.storeInstructionResult(rootedResult); |
| 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()); |
| int32_t result; |
| MOZ_ASSERT(!lhs.isObject() && !rhs.isObject()); |
| |
| if (!js::BitOr(cx, lhs, rhs, &result)) |
| return false; |
| |
| RootedValue asValue(cx, js::Int32Value(result)); |
| iter.storeInstructionResult(asValue); |
| 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()); |
| |
| int32_t result; |
| if (!js::BitXor(cx, lhs, rhs, &result)) |
| return false; |
| |
| RootedValue rootedResult(cx, js::Int32Value(result)); |
| iter.storeInstructionResult(rootedResult); |
| 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()); |
| int32_t result; |
| MOZ_ASSERT(!lhs.isObject() && !rhs.isObject()); |
| |
| if (!js::BitLsh(cx, lhs, rhs, &result)) |
| return false; |
| |
| RootedValue asValue(cx, js::Int32Value(result)); |
| iter.storeInstructionResult(asValue); |
| 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()); |
| MOZ_ASSERT(!lhs.isObject() && !rhs.isObject()); |
| |
| int32_t result; |
| if (!js::BitRsh(cx, lhs, rhs, &result)) |
| return false; |
| |
| RootedValue rootedResult(cx, js::Int32Value(result)); |
| iter.storeInstructionResult(rootedResult); |
| 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::UrshOperation(cx, lhs, rhs, &result)) |
| return false; |
| |
| iter.storeInstructionResult(result); |
| return true; |
| } |
| |
| bool |
| MAdd::writeRecoverData(CompactBufferWriter& writer) const |
| { |
| MOZ_ASSERT(canRecoverOnBailout()); |
| writer.writeUnsigned(uint32_t(RInstruction::Recover_Add)); |
| writer.writeByte(specialization_ == 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(specialization_ == 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(specialization_ == 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(specialization_ == 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 |
| { |
| RootedValue v(cx, iter.read()); |
| RootedValue result(cx); |
| |
| result.setBoolean(!ToBoolean(v)); |
| |
| iter.storeInstructionResult(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 |
| { |
| RootedValue operand(cx, iter.read()); |
| RootedValue result(cx); |
| |
| MOZ_ASSERT(!operand.isObject()); |
| if (!js::GetLengthProperty(operand, &result)) |
| return false; |
| |
| iter.storeInstructionResult(result); |
| 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 |
| { |
| RootedValue result(cx); |
| |
| result.setInt32(iter.readOuterNumActualArgs()); |
| |
| iter.storeInstructionResult(result); |
| 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 |
| { |
| RootedValue v(cx, iter.read()); |
| RootedValue result(cx); |
| |
| if (!js::math_floor_handle(cx, v, &result)) |
| return false; |
| |
| iter.storeInstructionResult(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 |
| { |
| RootedValue v(cx, iter.read()); |
| RootedValue result(cx); |
| |
| if (!js::math_ceil_handle(cx, v, &result)) |
| return false; |
| |
| iter.storeInstructionResult(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 |
| { |
| RootedValue arg(cx, iter.read()); |
| RootedValue result(cx); |
| |
| MOZ_ASSERT(!arg.isObject()); |
| if(!js::math_round_handle(cx, arg, &result)) |
| return false; |
| |
| iter.storeInstructionResult(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 |
| { |
| RootedString lhs(cx, iter.read().toString()); |
| RootedValue rhs(cx, iter.read()); |
| RootedValue result(cx); |
| |
| if (!js::str_charCodeAt_impl(cx, lhs, rhs, &result)) |
| return false; |
| |
| iter.storeInstructionResult(result); |
| 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 |
| { |
| RootedValue operand(cx, iter.read()); |
| RootedValue result(cx); |
| |
| MOZ_ASSERT(!operand.isObject()); |
| if (!js::str_fromCharCode_one_arg(cx, operand, &result)) |
| return false; |
| |
| iter.storeInstructionResult(result); |
| 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 |
| { |
| RootedValue base(cx, iter.read()); |
| RootedValue power(cx, iter.read()); |
| RootedValue result(cx); |
| |
| MOZ_ASSERT(base.isNumber() && power.isNumber()); |
| if (!js::math_pow_handle(cx, base, power, &result)) |
| return false; |
| |
| iter.storeInstructionResult(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 |
| { |
| RootedValue base(cx, iter.read()); |
| RootedValue power(cx); |
| RootedValue result(cx); |
| power.setNumber(0.5); |
| |
| MOZ_ASSERT(base.isNumber()); |
| if (!js::math_pow_handle(cx, base, power, &result)) |
| return false; |
| |
| iter.storeInstructionResult(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 |
| { |
| RootedValue a(cx, iter.read()); |
| RootedValue b(cx, iter.read()); |
| RootedValue result(cx); |
| |
| if (!js::minmax_impl(cx, isMax_, a, b, &result)) |
| return false; |
| |
| iter.storeInstructionResult(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 |
| { |
| RootedValue v(cx, iter.read()); |
| RootedValue result(cx); |
| |
| if (!js::math_abs_handle(cx, v, &result)) |
| return false; |
| |
| iter.storeInstructionResult(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 |
| { |
| RootedValue num(cx, iter.read()); |
| RootedValue result(cx); |
| |
| MOZ_ASSERT(num.isNumber()); |
| if (!math_sqrt_handle(cx, num, &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 |
| 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 |
| { |
| RootedValue y(cx, iter.read()); |
| RootedValue x(cx, iter.read()); |
| RootedValue result(cx); |
| |
| if(!math_atan2_handle(cx, y, x, &result)) |
| return false; |
| |
| iter.storeInstructionResult(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::AutoValueVector vec(cx); |
| |
| if (!vec.reserve(numOperands_)) |
| return false; |
| |
| for (uint32_t i = 0 ; i < numOperands_ ; ++i) |
| vec.infallibleAppend(iter.read()); |
| |
| RootedValue result(cx); |
| |
| if(!js::math_hypot_handle(cx, vec, &result)) |
| return false; |
| |
| iter.storeInstructionResult(result); |
| return true; |
| } |
| |
| bool |
| MMathFunction::writeRecoverData(CompactBufferWriter& writer) const |
| { |
| MOZ_ASSERT(canRecoverOnBailout()); |
| switch (function_) { |
| case Round: |
| writer.writeUnsigned(uint32_t(RInstruction::Recover_Round)); |
| return true; |
| case Sin: |
| case Log: |
| writer.writeUnsigned(uint32_t(RInstruction::Recover_MathFunction)); |
| writer.writeByte(function_); |
| return true; |
| default: |
| MOZ_CRASH("Unknown math function."); |
| } |
| } |
| |
| RMathFunction::RMathFunction(CompactBufferReader& reader) |
| { |
| function_ = reader.readByte(); |
| } |
| |
| bool |
| RMathFunction::recover(JSContext* cx, SnapshotIterator& iter) const |
| { |
| switch (function_) { |
| case MMathFunction::Sin: { |
| RootedValue arg(cx, iter.read()); |
| RootedValue result(cx); |
| |
| if (!js::math_sin_handle(cx, arg, &result)) |
| return false; |
| |
| iter.storeInstructionResult(result); |
| return true; |
| } |
| case MMathFunction::Log: { |
| RootedValue arg(cx, iter.read()); |
| RootedValue result(cx); |
| |
| if (!js::math_log_handle(cx, arg, &result)) |
| return false; |
| |
| iter.storeInstructionResult(result); |
| return true; |
| } |
| default: |
| MOZ_CRASH("Unknown math function."); |
| } |
| } |
| |
| 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.read().toString()); |
| RootedString sep(cx, iter.read().toString()); |
| RootedObjectGroup group(cx, iter.read().toObject().group()); |
| RootedValue result(cx); |
| |
| JSObject* res = str_split_string(cx, group, str, sep); |
| if (!res) |
| return false; |
| |
| result.setObject(*res); |
| iter.storeInstructionResult(result); |
| return true; |
| } |
| |
| bool MRegExpExec::writeRecoverData(CompactBufferWriter& writer) const |
| { |
| MOZ_ASSERT(canRecoverOnBailout()); |
| writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpExec)); |
| return true; |
| } |
| |
| RRegExpExec::RRegExpExec(CompactBufferReader& reader) |
| {} |
| |
| bool RRegExpExec::recover(JSContext* cx, SnapshotIterator& iter) const{ |
| RootedObject regexp(cx, &iter.read().toObject()); |
| RootedString input(cx, iter.read().toString()); |
| |
| RootedValue result(cx); |
| |
| if (!regexp_exec_raw(cx, regexp, input, nullptr, &result)) |
| return false; |
| |
| iter.storeInstructionResult(result); |
| return true; |
| } |
| |
| bool |
| MRegExpTest::writeRecoverData(CompactBufferWriter& writer) const |
| { |
| MOZ_ASSERT(canRecoverOnBailout()); |
| writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpTest)); |
| return true; |
| } |
| |
| RRegExpTest::RRegExpTest(CompactBufferReader& reader) |
| { } |
| |
| bool |
| RRegExpTest::recover(JSContext* cx, SnapshotIterator& iter) const |
| { |
| RootedString string(cx, iter.read().toString()); |
| RootedObject regexp(cx, &iter.read().toObject()); |
| bool resultBool; |
| |
| if (!js::regexp_test_raw(cx, regexp, string, &resultBool)) |
| return false; |
| |
| RootedValue result(cx); |
| result.setBoolean(resultBool); |
| iter.storeInstructionResult(result); |
| return true; |
| } |
| |
| bool |
| MRegExpReplace::writeRecoverData(CompactBufferWriter& writer) const |
| { |
| MOZ_ASSERT(canRecoverOnBailout()); |
| writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpReplace)); |
| return true; |
| } |
| |
| RRegExpReplace::RRegExpReplace(CompactBufferReader& reader) |
| { } |
| |
| bool |
| RRegExpReplace::recover(JSContext* cx, SnapshotIterator& iter) const |
| { |
| RootedString string(cx, iter.read().toString()); |
| Rooted<RegExpObject*> regexp(cx, &iter.read().toObject().as<RegExpObject>()); |
| RootedString repl(cx, iter.read().toString()); |
| |
| JSString* result = js::str_replace_regexp_raw(cx, string, regexp, repl); |
| if (!result) |
| return false; |
| |
| iter.storeInstructionResult(StringValue(result)); |
| 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 |
| { |
| RootedValue v(cx, iter.read()); |
| |
| RootedValue result(cx, StringValue(TypeOfOperation(v, cx->runtime()))); |
| iter.storeInstructionResult(result); |
| 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()); |
| RootedValue result(cx); |
| |
| MOZ_ASSERT(!v.isObject()); |
| MOZ_ASSERT(!v.isSymbol()); |
| |
| double dbl; |
| if (!ToNumber(cx, v, &dbl)) |
| return false; |
| |
| result.setDouble(dbl); |
| iter.storeInstructionResult(result); |
| 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 |
| { |
| RootedValue v(cx, iter.read()); |
| RootedValue result(cx); |
| |
| MOZ_ASSERT(!v.isObject()); |
| if (!RoundFloat32(cx, v, &result)) |
| return false; |
| |
| iter.storeInstructionResult(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()); |
| RootedValue result(cx); |
| |
| int32_t trunc; |
| if (!JS::ToInt32(cx, value, &trunc)) |
| return false; |
| |
| result.setInt32(trunc); |
| iter.storeInstructionResult(result); |
| return true; |
| } |
| |
| bool |
| MNewObject::writeRecoverData(CompactBufferWriter& writer) const |
| { |
| MOZ_ASSERT(canRecoverOnBailout()); |
| writer.writeUnsigned(uint32_t(RInstruction::Recover_NewObject)); |
| MOZ_ASSERT(Mode(uint8_t(mode_)) == mode_); |
| writer.writeByte(uint8_t(mode_)); |
| return true; |
| } |
| |
| RNewObject::RNewObject(CompactBufferReader& reader) |
| { |
| mode_ = MNewObject::Mode(reader.readByte()); |
| } |
| |
| bool |
| RNewObject::recover(JSContext* cx, SnapshotIterator& iter) const |
| { |
| RootedObject templateObject(cx, &iter.read().toObject()); |
| RootedValue result(cx); |
| JSObject* resultObject = nullptr; |
| |
| // See CodeGenerator::visitNewObjectVMCall |
| if (mode_ == MNewObject::ObjectLiteral) { |
| resultObject = NewObjectOperationWithTemplate(cx, templateObject); |
| } else { |
| MOZ_ASSERT(mode_ == MNewObject::ObjectCreate); |
| resultObject = ObjectCreateWithTemplate(cx, templateObject.as<PlainObject>()); |
| } |
| |
| if (!resultObject) |
| return false; |
| |
| result.setObject(*resultObject); |
| iter.storeInstructionResult(result); |
| 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.read().toObject()); |
| RootedValue result(cx); |
| RootedObjectGroup group(cx, templateObject->group()); |
| |
| JSObject* resultObject = NewFullyAllocatedArrayTryUseGroup(cx, group, count_); |
| if (!resultObject) |
| return false; |
| |
| result.setObject(*resultObject); |
| iter.storeInstructionResult(result); |
| return true; |
| } |
| |
| bool |
| MNewDerivedTypedObject::writeRecoverData(CompactBufferWriter& writer) const |
| { |
| MOZ_ASSERT(canRecoverOnBailout()); |
| writer.writeUnsigned(uint32_t(RInstruction::Recover_NewDerivedTypedObject)); |
| return true; |
| } |
| |
| RNewDerivedTypedObject::RNewDerivedTypedObject(CompactBufferReader& reader) |
| { } |
| |
| bool |
| RNewDerivedTypedObject::recover(JSContext* cx, SnapshotIterator& iter) const |
| { |
| Rooted<TypeDescr*> descr(cx, &iter.read().toObject().as<TypeDescr>()); |
| Rooted<TypedObject*> owner(cx, &iter.read().toObject().as<TypedObject>()); |
| int32_t offset = iter.read().toInt32(); |
| |
| JSObject* obj = OutlineTypedObject::createDerived(cx, descr, owner, offset); |
| if (!obj) |
| return false; |
| |
| RootedValue result(cx, ObjectValue(*obj)); |
| iter.storeInstructionResult(result); |
| return true; |
| } |
| |
| bool |
| MCreateThisWithTemplate::writeRecoverData(CompactBufferWriter& writer) const |
| { |
| MOZ_ASSERT(canRecoverOnBailout()); |
| writer.writeUnsigned(uint32_t(RInstruction::Recover_CreateThisWithTemplate)); |
| return true; |
| } |
| |
| RCreateThisWithTemplate::RCreateThisWithTemplate(CompactBufferReader& reader) |
| { |
| } |
| |
| bool |
| RCreateThisWithTemplate::recover(JSContext* cx, SnapshotIterator& iter) const |
| { |
| RootedObject templateObject(cx, &iter.read().toObject()); |
| |
| // See CodeGenerator::visitCreateThisWithTemplate |
| JSObject* resultObject = NewObjectOperationWithTemplate(cx, templateObject); |
| if (!resultObject) |
| return false; |
| |
| RootedValue result(cx); |
| result.setObject(*resultObject); |
| iter.storeInstructionResult(result); |
| 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.read().toObject()); |
| RootedFunction fun(cx, &iter.read().toObject().as<JSFunction>()); |
| |
| JSObject* resultObject = js::Lambda(cx, fun, scopeChain); |
| if (!resultObject) |
| return false; |
| |
| RootedValue result(cx); |
| result.setObject(*resultObject); |
| iter.storeInstructionResult(result); |
| return true; |
| } |
| |
| bool |
| MSimdBox::writeRecoverData(CompactBufferWriter& writer) const |
| { |
| MOZ_ASSERT(canRecoverOnBailout()); |
| writer.writeUnsigned(uint32_t(RInstruction::Recover_SimdBox)); |
| SimdTypeDescr& simdTypeDescr = templateObject()->typeDescr().as<SimdTypeDescr>(); |
| SimdTypeDescr::Type type = simdTypeDescr.type(); |
| writer.writeByte(uint8_t(type)); |
| return true; |
| } |
| |
| RSimdBox::RSimdBox(CompactBufferReader& reader) |
| { |
| type_ = reader.readByte(); |
| } |
| |
| bool |
| RSimdBox::recover(JSContext* cx, SnapshotIterator& iter) const |
| { |
| JSObject* resultObject = nullptr; |
| RValueAllocation a = iter.readAllocation(); |
| MOZ_ASSERT(iter.allocationReadable(a)); |
| const FloatRegisters::RegisterContent* raw = iter.floatAllocationPointer(a); |
| switch (SimdTypeDescr::Type(type_)) { |
| case SimdTypeDescr::Int32x4: |
| MOZ_ASSERT_IF(a.mode() == RValueAllocation::ANY_FLOAT_REG, |
| a.fpuReg().isSimd128()); |
| resultObject = js::CreateSimd<Int32x4>(cx, (const Int32x4::Elem*) raw); |
| break; |
| case SimdTypeDescr::Float32x4: |
| MOZ_ASSERT_IF(a.mode() == RValueAllocation::ANY_FLOAT_REG, |
| a.fpuReg().isSimd128()); |
| resultObject = js::CreateSimd<Float32x4>(cx, (const Float32x4::Elem*) raw); |
| break; |
| case SimdTypeDescr::Float64x2: |
| MOZ_CRASH("NYI, RSimdBox of Float64x2"); |
| break; |
| case SimdTypeDescr::Int8x16: |
| MOZ_CRASH("NYI, RSimdBox of Int8x16"); |
| break; |
| case SimdTypeDescr::Int16x8: |
| MOZ_CRASH("NYI, RSimdBox of Int16x8"); |
| break; |
| } |
| |
| if (!resultObject) |
| return false; |
| |
| RootedValue result(cx); |
| result.setObject(*resultObject); |
| iter.storeInstructionResult(result); |
| 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.read().toObject()); |
| RootedValue val(cx); |
| |
| if (object->is<UnboxedPlainObject>()) { |
| const UnboxedLayout& layout = object->as<UnboxedPlainObject>().layout(); |
| |
| RootedId id(cx); |
| RootedValue receiver(cx, ObjectValue(*object)); |
| const UnboxedLayout::PropertyVector& properties = layout.properties(); |
| for (size_t i = 0; i < properties.length(); i++) { |
| val = iter.read(); |
| |
| // This is the default placeholder value of MObjectState, when no |
| // properties are defined yet. |
| if (val.isUndefined()) |
| continue; |
| |
| id = NameToId(properties[i].name); |
| ObjectOpResult result; |
| |
| // SetProperty can only fail due to OOM. |
| if (!SetProperty(cx, object, id, val, receiver, result)) |
| return false; |
| if (!result) |
| return result.reportError(cx, object, id); |
| } |
| } else { |
| RootedNativeObject nativeObject(cx, &object->as<NativeObject>()); |
| MOZ_ASSERT(nativeObject->slotSpan() == numSlots()); |
| |
| for (size_t i = 0; i < numSlots(); i++) { |
| val = iter.read(); |
| nativeObject->setSlot(i, val); |
| } |
| } |
| |
| val.setObject(*object); |
| iter.storeInstructionResult(val); |
| 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 |
| { |
| RootedValue result(cx); |
| ArrayObject* object = &iter.read().toObject().as<ArrayObject>(); |
| uint32_t initLength = iter.read().toInt32(); |
| |
| 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); |
| } |
| |
| result.setObject(*object); |
| iter.storeInstructionResult(result); |
| 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 |
| { |
| RootedValue result(cx); |
| result.setUndefined(); |
| iter.storeInstructionResult(result); |
| return true; |
| } |
| |
| bool |
| MStringReplace::writeRecoverData(CompactBufferWriter& writer) const |
| { |
| MOZ_ASSERT(canRecoverOnBailout()); |
| writer.writeUnsigned(uint32_t(RInstruction::Recover_StringReplace)); |
| return true; |
| } |
| |
| RStringReplace::RStringReplace(CompactBufferReader& reader) |
| { } |
| |
| bool RStringReplace::recover(JSContext* cx, SnapshotIterator& iter) const |
| { |
| RootedString string(cx, iter.read().toString()); |
| RootedString pattern(cx, iter.read().toString()); |
| RootedString replace(cx, iter.read().toString()); |
| |
| JSString* result = js::str_replace_string_raw(cx, string, pattern, replace); |
| if (!result) |
| return false; |
| |
| iter.storeInstructionResult(StringValue(result)); |
| return true; |
| } |
| |
| bool |
| MAtomicIsLockFree::writeRecoverData(CompactBufferWriter& writer) const |
| { |
| MOZ_ASSERT(canRecoverOnBailout()); |
| writer.writeUnsigned(uint32_t(RInstruction::Recover_AtomicIsLockFree)); |
| return true; |
| } |
| |
| RAtomicIsLockFree::RAtomicIsLockFree(CompactBufferReader& reader) |
| { } |
| |
| bool |
| RAtomicIsLockFree::recover(JSContext* cx, SnapshotIterator& iter) const |
| { |
| RootedValue operand(cx, iter.read()); |
| MOZ_ASSERT(operand.isInt32()); |
| |
| int32_t result; |
| if (!js::AtomicIsLockFree(cx, operand, &result)) |
| return false; |
| |
| RootedValue rootedResult(cx, js::Int32Value(result)); |
| iter.storeInstructionResult(rootedResult); |
| return true; |
| } |