#ifndef vm_BytecodeUtil_inl_h
#define vm_BytecodeUtil_inl_h
#include "vm/BytecodeUtil.h"
#include "frontend/SourceNotes.h"
#include "vm/JSScript.h"
namespace js {
static inline unsigned GetDefCount(jsbytecode* pc) {
switch (JSOp(*pc)) {
case JSOP_OR:
case JSOP_AND:
return 1;
case JSOP_PICK:
case JSOP_UNPICK:
return pc[1] + 1;
default:
return StackDefs(pc);
}
}
static inline unsigned GetUseCount(jsbytecode* pc) {
if (JSOp(*pc) == JSOP_PICK || JSOp(*pc) == JSOP_UNPICK) {
return pc[1] + 1;
}
return StackUses(pc);
}
static inline JSOp ReverseCompareOp(JSOp op) {
switch (op) {
case JSOP_GT:
return JSOP_LT;
case JSOP_GE:
return JSOP_LE;
case JSOP_LT:
return JSOP_GT;
case JSOP_LE:
return JSOP_GE;
case JSOP_EQ:
case JSOP_NE:
case JSOP_STRICTEQ:
case JSOP_STRICTNE:
return op;
default:
MOZ_CRASH("unrecognized op");
}
}
static inline JSOp NegateCompareOp(JSOp op) {
switch (op) {
case JSOP_GT:
return JSOP_LE;
case JSOP_GE:
return JSOP_LT;
case JSOP_LT:
return JSOP_GE;
case JSOP_LE:
return JSOP_GT;
case JSOP_EQ:
return JSOP_NE;
case JSOP_NE:
return JSOP_EQ;
case JSOP_STRICTNE:
return JSOP_STRICTEQ;
case JSOP_STRICTEQ:
return JSOP_STRICTNE;
default:
MOZ_CRASH("unrecognized op");
}
}
class BytecodeRange {
public:
BytecodeRange(JSContext* cx, JSScript* script)
: script(cx, script), pc(script->code()), end(pc + script->length()) {}
bool empty() const { return pc == end; }
jsbytecode* frontPC() const { return pc; }
JSOp frontOpcode() const { return JSOp(*pc); }
size_t frontOffset() const { return script->pcToOffset(pc); }
void popFront() { pc += GetBytecodeLength(pc); }
private:
RootedScript script;
jsbytecode* pc;
jsbytecode* end;
};
class BytecodeRangeWithPosition : private BytecodeRange {
public:
using BytecodeRange::empty;
using BytecodeRange::frontOffset;
using BytecodeRange::frontOpcode;
using BytecodeRange::frontPC;
BytecodeRangeWithPosition(JSContext* cx, JSScript* script)
: BytecodeRange(cx, script),
lineno(script->lineno()),
column(0),
sn(script->notes()),
snpc(script->code()),
isEntryPoint(false),
isBreakpoint(false),
seenStepSeparator(false),
wasArtifactEntryPoint(false) {
if (!SN_IS_TERMINATOR(sn)) {
snpc += SN_DELTA(sn);
}
updatePosition();
while (frontPC() != script->main()) {
popFront();
}
if (frontOpcode() != JSOP_JUMPTARGET) {
isEntryPoint = true;
} else {
wasArtifactEntryPoint = true;
}
}
void popFront() {
BytecodeRange::popFront();
if (empty()) {
isEntryPoint = false;
} else {
updatePosition();
}
if (wasArtifactEntryPoint) {
wasArtifactEntryPoint = false;
isEntryPoint = true;
}
if (isEntryPoint && frontOpcode() == JSOP_JUMPTARGET) {
wasArtifactEntryPoint = isEntryPoint;
isEntryPoint = false;
}
}
size_t frontLineNumber() const { return lineno; }
size_t frontColumnNumber() const { return column; }
bool frontIsEntryPoint() const { return isEntryPoint; }
bool frontIsBreakablePoint() const { return isBreakpoint; }
bool frontIsBreakableStepPoint() const {
return isBreakpoint && seenStepSeparator;
}
private:
void updatePosition() {
if (isBreakpoint) {
isBreakpoint = false;
seenStepSeparator = false;
}
jsbytecode* lastLinePC = nullptr;
while (!SN_IS_TERMINATOR(sn) && snpc <= frontPC()) {
SrcNoteType type = SN_TYPE(sn);
if (type == SRC_COLSPAN) {
ptrdiff_t colspan =
SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, SrcNote::ColSpan::Span));
MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0);
column += colspan;
lastLinePC = snpc;
} else if (type == SRC_SETLINE) {
lineno = size_t(GetSrcNoteOffset(sn, SrcNote::SetLine::Line));
column = 0;
lastLinePC = snpc;
} else if (type == SRC_NEWLINE) {
lineno++;
column = 0;
lastLinePC = snpc;
} else if (type == SRC_BREAKPOINT) {
isBreakpoint = true;
lastLinePC = snpc;
} else if (type == SRC_STEP_SEP) {
seenStepSeparator = true;
lastLinePC = snpc;
}
sn = SN_NEXT(sn);
snpc += SN_DELTA(sn);
}
isEntryPoint = lastLinePC == frontPC();
}
size_t lineno;
size_t column;
jssrcnote* sn;
jsbytecode* snpc;
bool isEntryPoint;
bool isBreakpoint;
bool seenStepSeparator;
bool wasArtifactEntryPoint;
};
}
#endif