Source code

Revision control

Other Tools

/* -*- 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/. */
#ifndef vm_BytecodeUtil_inl_h
#define vm_BytecodeUtil_inl_h
#include "vm/BytecodeUtil.h"
#include "frontend/SourceNotes.h" // SrcNote, SrcNoteType, SrcNoteIterator
#include "vm/JSScript.h"
namespace js {
static inline unsigned GetDefCount(jsbytecode* pc) {
/*
* Add an extra pushed value for Or/And opcodes, so that they are included
* in the pushed array of stack values for type inference.
*/
switch (JSOp(*pc)) {
case JSOp::Or:
case JSOp::And:
case JSOp::Coalesce:
return 1;
case JSOp::Pick:
case JSOp::Unpick:
/*
* Pick pops and pushes how deep it looks in the stack + 1
* items. i.e. if the stack were |a b[2] c[1] d[0]|, pick 2
* would pop b, c, and d to rearrange the stack to |a c[0]
* d[1] b[2]|.
*/
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),
initialLine(script->lineno()),
lineno(script->lineno()),
column(script->column()),
sn(script->notes()),
snpc(script->code()),
isEntryPoint(false),
isBreakpoint(false),
seenStepSeparator(false),
wasArtifactEntryPoint(false) {
if (!sn->isTerminator()) {
snpc += sn->delta();
}
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();
}
// The following conditions are handling artifacts introduced by the
// bytecode emitter, such that we do not add breakpoints on empty
// statements of the source code of the user.
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; }
// Entry points are restricted to bytecode offsets that have an
// explicit mention in the line table. This restriction avoids a
// number of failing cases caused by some instructions not having
// sensible (to the user) line numbers, and it is one way to
// implement the idea that the bytecode emitter should tell the
// debugger exactly which offsets represent "interesting" (to the
// user) places to stop.
bool frontIsEntryPoint() const { return isEntryPoint; }
// Breakable points are explicitly marked by the emitter as locations where
// the debugger may want to allow users to pause.
bool frontIsBreakablePoint() const { return isBreakpoint; }
// Breakable step points are the first breakable point after a
// SrcNote::StepSep note has been encountered.
bool frontIsBreakableStepPoint() const {
return isBreakpoint && seenStepSeparator;
}
private:
void updatePosition() {
if (isBreakpoint) {
isBreakpoint = false;
seenStepSeparator = false;
}
// Determine the current line number by reading all source notes up to
// and including the current offset.
jsbytecode* lastLinePC = nullptr;
SrcNoteIterator iter(sn);
for (; !iter.atEnd() && snpc <= frontPC();
++iter, snpc += (*iter)->delta()) {
auto sn = *iter;
SrcNoteType type = sn->type();
if (type == SrcNoteType::ColSpan) {
ptrdiff_t colspan = SrcNote::ColSpan::getSpan(sn);
MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0);
column += colspan;
lastLinePC = snpc;
} else if (type == SrcNoteType::SetLine) {
lineno = SrcNote::SetLine::getLine(sn, initialLine);
column = 0;
lastLinePC = snpc;
} else if (type == SrcNoteType::NewLine) {
lineno++;
column = 0;
lastLinePC = snpc;
} else if (type == SrcNoteType::Breakpoint) {
isBreakpoint = true;
lastLinePC = snpc;
} else if (type == SrcNoteType::StepSep) {
seenStepSeparator = true;
lastLinePC = snpc;
}
}
sn = *iter;
isEntryPoint = lastLinePC == frontPC();
}
size_t initialLine;
size_t lineno;
size_t column;
const SrcNote* sn;
jsbytecode* snpc;
bool isEntryPoint;
bool isBreakpoint;
bool seenStepSeparator;
bool wasArtifactEntryPoint;
};
} // namespace js
#endif /* vm_BytecodeUtil_inl_h */