--- a/js/src/gdb/mozilla/Interpreter.py
+++ b/js/src/gdb/mozilla/Interpreter.py
@@ -11,16 +11,17 @@ from mozilla.prettyprinters import prett
class InterpreterTypeCache(object):
def __init__(self):
self.tValue = gdb.lookup_type('JS::Value')
self.tJSOp = gdb.lookup_type('JSOp')
self.tScriptFrameIterData = gdb.lookup_type('js::ScriptFrameIter::Data')
self.tInterpreterFrame = gdb.lookup_type('js::InterpreterFrame')
self.tBaselineFrame = gdb.lookup_type('js::jit::BaselineFrame')
self.tRematerializedFrame = gdb.lookup_type('js::jit::RematerializedFrame')
+ self.tDebugFrame = gdb.lookup_type('js::wasm::DebugFrame')
@pretty_printer('js::InterpreterRegs')
class InterpreterRegs(object):
def __init__(self, value, cache):
self.value = value
self.cache = cache
if not cache.mod_Interpreter:
cache.mod_Interpreter = InterpreterTypeCache()
@@ -42,17 +43,18 @@ class InterpreterRegs(object):
return '{{ {}, {}, {} }}'.format(fp_, sp, pc)
@pretty_printer('js::AbstractFramePtr')
class AbstractFramePtr(object):
Tag_ScriptFrameIterData = 0x0
Tag_InterpreterFrame = 0x1
Tag_BaselineFrame = 0x2
Tag_RematerializedFrame = 0x3
- TagMask = 0x3
+ Tag_WasmDebugFrame = 0x4
+ TagMask = 0x7
def __init__(self, value, cache):
self.value = value
self.cache = cache
if not cache.mod_Interpreter:
cache.mod_Interpreter = InterpreterTypeCache()
self.itc = cache.mod_Interpreter
@@ -67,14 +69,17 @@ class AbstractFramePtr(object):
label = 'js::InterpreterFrame'
ptr = ptr.cast(self.itc.tInterpreterFrame.pointer())
if tag == AbstractFramePtr.Tag_BaselineFrame:
label = 'js::jit::BaselineFrame'
ptr = ptr.cast(self.itc.tBaselineFrame.pointer())
if tag == AbstractFramePtr.Tag_RematerializedFrame:
label = 'js::jit::RematerializedFrame'
ptr = ptr.cast(self.itc.tRematerializedFrame.pointer())
+ if tag == AbstractFramePtr.Tag_WasmDebugFrame:
+ label = 'js::wasm::DebugFrame'
+ ptr = ptr.cast(self.itc.tDebugFrame.pointer())
return 'AbstractFramePtr (({} *) {})'.format(label, ptr)
# Provide the ptr_ field as a child, so it prints after the pretty string
# provided above.
def children(self):
yield ('ptr_', self.value['ptr_'])
--- a/js/src/gdb/tests/test-Interpreter.cpp
+++ b/js/src/gdb/tests/test-Interpreter.cpp
@@ -45,16 +45,23 @@ GDBTestInitAbstractFramePtr(AbstractFram
void
GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, jit::RematerializedFrame* ptr)
{
MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_RematerializedFrame;
}
+void
+GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, wasm::DebugFrame* ptr)
+{
+ MOZ_ASSERT((uintptr_t(ptr) & AbstractFramePtr::TagMask) == 0);
+ frame.ptr_ = uintptr_t(ptr) | AbstractFramePtr::Tag_WasmDebugFrame;
+}
+
} // namespace js
FRAGMENT(Interpreter, Regs) {
struct FakeFrame {
js::InterpreterFrame frame;
JS::Value slot0;
JS::Value slot1;
JS::Value slot2;
@@ -78,15 +85,18 @@ FRAGMENT(Interpreter, AbstractFramePtr)
GDBTestInitAbstractFramePtr(ifptr, (js::InterpreterFrame*) uintptr_t(0x8badf00));
js::AbstractFramePtr bfptr;
GDBTestInitAbstractFramePtr(bfptr, (js::jit::BaselineFrame*) uintptr_t(0xbadcafe0));
js::AbstractFramePtr rfptr;
GDBTestInitAbstractFramePtr(rfptr, (js::jit::RematerializedFrame*) uintptr_t(0xdabbad00));
+ js::AbstractFramePtr sfptr;
+ GDBTestInitAbstractFramePtr(sfptr, (js::wasm::DebugFrame*) uintptr_t(0xcb98ad00));
+
breakpoint();
(void) sfidptr;
(void) ifptr;
(void) bfptr;
(void) rfptr;
}
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -660,38 +660,16 @@ class CodeLocationLabel
return raw_;
}
};
} // namespace jit
namespace wasm {
-// As an invariant across architectures, within wasm code:
-// $sp % WasmStackAlignment = (sizeof(wasm::Frame) + masm.framePushed) % WasmStackAlignment
-// Thus, wasm::Frame represents the bytes pushed after the call (which occurred
-// with a WasmStackAlignment-aligned StackPointer) that are not included in
-// masm.framePushed.
-
-struct Frame
-{
- // The caller's saved frame pointer. In non-profiling mode, internal
- // wasm-to-wasm calls don't update fp and thus don't save the caller's
- // frame pointer; the space is reserved, however, so that profiling mode can
- // reuse the same function body without recompiling.
- uint8_t* callerFP;
-
- // The return address pushed by the call (in the case of ARM/MIPS the return
- // address is pushed by the first instruction of the prologue).
- void* returnAddress;
-};
-
-static_assert(sizeof(Frame) == 2 * sizeof(void*), "?!");
-static const uint32_t FrameBytesAfterReturnAddress = sizeof(void*);
-
// Represents an instruction to be patched and the intended pointee. These
// links are accumulated in the MacroAssembler, but patching is done outside
// the MacroAssembler (in Module::staticallyLink).
struct SymbolicAccess
{
SymbolicAccess(jit::CodeOffset patchAt, SymbolicAddress target)
: patchAt(patchAt), target(target) {}
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -368,16 +368,17 @@ UNIFIED_SOURCES += [
'wasm/WasmBaselineCompile.cpp',
'wasm/WasmBinaryIterator.cpp',
'wasm/WasmBinaryToAST.cpp',
'wasm/WasmBinaryToExperimentalText.cpp',
'wasm/WasmBinaryToText.cpp',
'wasm/WasmCode.cpp',
'wasm/WasmCompartment.cpp',
'wasm/WasmCompile.cpp',
+ 'wasm/WasmDebugFrame.cpp',
'wasm/WasmFrameIterator.cpp',
'wasm/WasmGenerator.cpp',
'wasm/WasmInstance.cpp',
'wasm/WasmIonCompile.cpp',
'wasm/WasmJS.cpp',
'wasm/WasmModule.cpp',
'wasm/WasmSignalHandlers.cpp',
'wasm/WasmStubs.cpp',
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2044,16 +2044,18 @@ Debugger::onSingleStep(JSContext* cx, Mu
JSScript* trappingScript = iter.script();
GlobalObject* global = cx->global();
if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
Debugger* dbg = *p;
for (FrameMap::Range r = dbg->frames.all(); !r.empty(); r.popFront()) {
AbstractFramePtr frame = r.front().key();
NativeObject* frameobj = r.front().value();
+ if (frame.isWasmDebugFrame())
+ continue;
if (frame.script() == trappingScript &&
!frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
{
stepperCount++;
}
}
}
}
@@ -6256,43 +6258,54 @@ DebuggerScript_getLineOffsets(JSContext*
args.rval().setObject(*matcher.result());
return true;
}
bool
Debugger::observesFrame(AbstractFramePtr frame) const
{
+ if (frame.isWasmDebugFrame())
+ return observesWasm(frame.wasmInstance());
+
return observesScript(frame.script());
}
bool
Debugger::observesFrame(const FrameIter& iter) const
{
// Skip frames not yet fully initialized during their prologue.
if (iter.isInterp() && iter.isFunctionFrame()) {
const Value& thisVal = iter.interpFrame()->thisArgument();
if (thisVal.isMagic() && thisVal.whyMagic() == JS_IS_CONSTRUCTING)
return false;
}
if (iter.isWasm())
- return false;
+ return observesWasm(iter.wasmInstance());
return observesScript(iter.script());
}
bool
Debugger::observesScript(JSScript* script) const
{
if (!enabled)
return false;
// Don't ever observe self-hosted scripts: the Debugger API can break
// self-hosted invariants.
return observesGlobal(&script->global()) && !script->selfHosted();
}
+bool
+Debugger::observesWasm(wasm::Instance* instance) const
+{
+ if (!enabled || !instance->code().metadata().debugEnabled)
+ return false;
+ return false; // TODO check global
+}
+
/* static */ bool
Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to,
ScriptFrameIter& iter)
{
auto removeFromDebuggerFramesOnExit = MakeScopeExit([&] {
// Remove any remaining old entries on exit, as the 'from' frame will
// be gone. This is only done in the failure case. On failure, the
// removeToDebuggerFramesOnExit lambda below will rollback any frames
@@ -7370,16 +7383,21 @@ DebuggerFrame::getIsConstructing(JSConte
result = iter.isFunctionFrame() && iter.isConstructing();
return true;
}
static void
UpdateFrameIterPc(FrameIter& iter)
{
+ if (iter.abstractFramePtr().isWasmDebugFrame()) {
+ // Wasm debug frames don't need their pc updated -- it's null.
+ return;
+ }
+
if (iter.abstractFramePtr().isRematerializedFrame()) {
#ifdef DEBUG
// Rematerialized frames don't need their pc updated. The reason we
// need to update pc is because we might get the same Debugger.Frame
// object for multiple re-entries into debugger code from debuggee
// code. This reentrancy is not possible with rematerialized frames,
// because when returning to debuggee code, we would have bailed out
// to baseline.
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -912,16 +912,17 @@ class Debugger : private mozilla::Linked
inline bool observesEnterFrame() const;
inline bool observesNewScript() const;
inline bool observesNewGlobalObject() const;
inline bool observesGlobal(GlobalObject* global) const;
bool observesFrame(AbstractFramePtr frame) const;
bool observesFrame(const FrameIter& iter) const;
bool observesScript(JSScript* script) const;
+ bool observesWasm(wasm::Instance* instance) const;
/*
* If env is nullptr, call vp->setNull() and return true. Otherwise, find
* or create a Debugger.Environment object for the given Env. On success,
* store the Environment object in *vp and return true.
*/
MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env, MutableHandleValue vp);
MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env,
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -17,16 +17,17 @@
#include "frontend/ParseNode.h"
#include "gc/Policy.h"
#include "vm/ArgumentsObject.h"
#include "vm/AsyncFunction.h"
#include "vm/GlobalObject.h"
#include "vm/ProxyObject.h"
#include "vm/Shape.h"
#include "vm/Xdr.h"
+#include "wasm/WasmInstance.h"
#include "jsatominlines.h"
#include "jsobjinlines.h"
#include "jsscriptinlines.h"
#include "vm/Stack-inl.h"
using namespace js;
@@ -1228,16 +1229,27 @@ EnvironmentIter::EnvironmentIter(JSConte
env_(cx, frame.environmentChain()),
frame_(frame)
{
assertSameCompartment(cx, frame);
settle();
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
+EnvironmentIter::EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope, AbstractFramePtr frame
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
+ : si_(cx, ScopeIter(scope)),
+ env_(cx, env),
+ frame_(frame)
+{
+ assertSameCompartment(cx, frame);
+ settle();
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+}
+
void
EnvironmentIter::incrementScopeIter()
{
if (si_.scope()->is<GlobalScope>()) {
// GlobalScopes may be syntactic or non-syntactic. Non-syntactic
// GlobalScopes correspond to zero or more non-syntactic
// EnvironmentsObjects followed by the global lexical scope, then the
// GlobalObject or another non-EnvironmentObject object.
@@ -1248,35 +1260,40 @@ EnvironmentIter::incrementScopeIter()
}
}
void
EnvironmentIter::settle()
{
// Check for trying to iterate a function or eval frame before the prologue has
// created the CallObject, in which case we have to skip.
- if (frame_ && frame_.script()->initialEnvironmentShape() && !frame_.hasInitialEnvironment()) {
+ if (frame_ && frame_.hasScript() &&
+ frame_.script()->initialEnvironmentShape() && !frame_.hasInitialEnvironment())
+ {
// Skip until we're at the enclosing scope of the script.
while (si_.scope() != frame_.script()->enclosingScope()) {
if (env_->is<LexicalEnvironmentObject>() &&
!env_->as<LexicalEnvironmentObject>().isExtensible() &&
&env_->as<LexicalEnvironmentObject>().scope() == si_.scope())
{
MOZ_ASSERT(si_.kind() == ScopeKind::NamedLambda ||
si_.kind() == ScopeKind::StrictNamedLambda);
env_ = &env_->as<EnvironmentObject>().enclosingEnvironment();
}
incrementScopeIter();
}
}
// Check if we have left the extent of the initial frame after we've
// settled on a static scope.
- if (frame_ && (!si_ || si_.scope() == frame_.script()->enclosingScope()))
+ if (frame_ && (frame_.isWasmDebugFrame() ||
+ (!si_ || si_.scope() == frame_.script()->enclosingScope())))
+ {
frame_ = NullFramePtr();
+ }
#ifdef DEBUG
if (si_) {
if (hasSyntacticEnvironment()) {
Scope* scope = si_.scope();
if (scope->is<LexicalScope>()) {
MOZ_ASSERT(scope == &env_->as<LexicalEnvironmentObject>().scope());
} else if (scope->is<FunctionScope>()) {
@@ -2757,17 +2774,22 @@ DebugEnvironments::updateLiveEnvironment
continue;
if (frame.isFunctionFrame() && frame.callee()->isGenerator())
continue;
if (!frame.isDebuggee())
continue;
- for (EnvironmentIter ei(cx, frame, i.pc()); ei.withinInitialFrame(); ei++) {
+ RootedObject env(cx);
+ RootedScope scope(cx);
+ if (!GetFrameEnvironmentAndScope(cx, frame, i.pc(), &env, &scope))
+ return false;
+
+ for (EnvironmentIter ei(cx, env, scope, frame); ei.withinInitialFrame(); ei++) {
if (ei.hasSyntacticEnvironment() && !ei.scope().is<GlobalScope>()) {
MOZ_ASSERT(ei.environment().compartment() == cx->compartment());
DebugEnvironments* envs = ensureCompartmentData(cx);
if (!envs)
return false;
if (!envs->liveEnvs.put(&ei.environment(), LiveEnvironmentVal(ei)))
return false;
}
@@ -3006,17 +3028,22 @@ js::GetDebugEnvironmentForFunction(JSCon
JSObject*
js::GetDebugEnvironmentForFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc)
{
assertSameCompartment(cx, frame);
if (CanUseDebugEnvironmentMaps(cx) && !DebugEnvironments::updateLiveEnvironments(cx))
return nullptr;
- EnvironmentIter ei(cx, frame, pc);
+ RootedObject env(cx);
+ RootedScope scope(cx);
+ if (!GetFrameEnvironmentAndScope(cx, frame, pc, &env, &scope))
+ return nullptr;
+
+ EnvironmentIter ei(cx, env, scope, frame);
return GetDebugEnvironment(cx, ei);
}
JSObject*
js::GetDebugEnvironmentForGlobalLexicalEnvironment(JSContext* cx)
{
EnvironmentIter ei(cx, &cx->global()->lexicalEnvironment(), &cx->global()->emptyGlobalScope());
return GetDebugEnvironment(cx, ei);
@@ -3101,17 +3128,22 @@ js::GetModuleEnvironmentForScript(JSScri
}
return nullptr;
}
bool
js::GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
MutableHandleValue res)
{
- for (EnvironmentIter ei(cx, frame, pc); ei; ei++) {
+ RootedObject scopeChain(cx);
+ RootedScope scope(cx);
+ if (!GetFrameEnvironmentAndScope(cx, frame, pc, &scopeChain, &scope))
+ return false;
+
+ for (EnvironmentIter ei(cx, scopeChain, scope, frame); ei; ei++) {
if (ei.scope().kind() == ScopeKind::Module) {
res.setUndefined();
return true;
}
if (!ei.scope().is<FunctionScope>() ||
ei.scope().as<FunctionScope>().canonicalFunction()->hasLexicalThis())
{
@@ -3175,17 +3207,16 @@ js::GetThisValueForDebuggerMaybeOptimize
res.setMagic(JS_OPTIMIZED_OUT);
return true;
}
MOZ_CRASH("'this' binding must be found");
}
- RootedObject scopeChain(cx, frame.environmentChain());
return GetNonSyntacticGlobalThis(cx, scopeChain, res);
}
bool
js::CheckLexicalNameConflict(JSContext* cx, Handle<LexicalEnvironmentObject*> lexicalEnv,
HandleObject varObj, HandlePropertyName name)
{
const char* redeclKind = nullptr;
@@ -3444,16 +3475,35 @@ js::PushVarEnvironmentObject(JSContext*
{
VarEnvironmentObject* env = VarEnvironmentObject::create(cx, scope, frame);
if (!env)
return false;
frame.pushOnEnvironmentChain(*env);
return true;
}
+bool
+js::GetFrameEnvironmentAndScope(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
+ MutableHandleObject env, MutableHandleScope scope)
+{
+ env.set(frame.environmentChain());
+
+ if (frame.isWasmDebugFrame()) {
+ RootedWasmInstanceObject instance(cx, frame.wasmInstance()->object());
+ uint32_t funcIndex = frame.asWasmDebugFrame()->funcIndex();
+ scope.set(WasmInstanceObject::getFunctionScope(cx, instance, funcIndex));
+ if (!scope)
+ return false;
+ } else {
+ scope.set(frame.script()->innermostScope(pc));
+ }
+ return true;
+}
+
+
#ifdef DEBUG
typedef HashSet<PropertyName*> PropertyNameSet;
static bool
RemoveReferencedNames(JSContext* cx, HandleScript script, PropertyNameSet& remainingNames)
{
// Remove from remainingNames --- the closure variables in some outer
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -667,16 +667,22 @@ class MOZ_RAII EnvironmentIter
EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
// Constructing from a frame. Places the EnvironmentIter on the innermost
// environment at pc.
EnvironmentIter(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc
MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+ // Constructing from an environment, scope and frame. The frame is given
+ // to initialize to proper enclosing environment/scope.
+ EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope, AbstractFramePtr frame
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+
+
bool done() const {
return si_.done();
}
explicit operator bool() const {
return !done();
}
@@ -1115,16 +1121,20 @@ CheckEvalDeclarationConflicts(JSContext*
HandleObject varObj);
MOZ_MUST_USE bool
InitFunctionEnvironmentObjects(JSContext* cx, AbstractFramePtr frame);
MOZ_MUST_USE bool
PushVarEnvironmentObject(JSContext* cx, HandleScope scope, AbstractFramePtr frame);
+MOZ_MUST_USE bool
+GetFrameEnvironmentAndScope(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
+ MutableHandleObject env, MutableHandleScope scope);
+
#ifdef DEBUG
bool
AnalyzeEntrainedVariables(JSContext* cx, HandleScript script);
#endif
} // namespace js
namespace JS {
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -152,18 +152,18 @@ struct SavedFrame::Lookup {
asyncCause(asyncCause),
parent(parent),
principals(principals),
framePtr(framePtr),
pc(pc),
activation(activation)
{
MOZ_ASSERT(source);
- MOZ_ASSERT_IF(framePtr.isSome(), pc);
MOZ_ASSERT_IF(framePtr.isSome(), activation);
+ MOZ_ASSERT_IF(framePtr.isSome() && !activation->isWasm(), pc);
#ifdef JS_MORE_DETERMINISTIC
column = 0;
#endif
}
explicit Lookup(SavedFrame& savedFrame)
: source(savedFrame.getSource()),
@@ -1319,17 +1319,17 @@ SavedStacks::insertFrames(JSContext* cx,
}
// The bit set means that the next older parent (frame, pc) pair *must*
// be in the cache.
if (capture.is<JS::AllFrames>())
parentIsInCache = iter.hasCachedSavedFrame();
auto principals = iter.compartment()->principals();
- auto displayAtom = iter.isFunctionFrame() ? iter.functionDisplayAtom() : nullptr;
+ auto displayAtom = (iter.isWasm() || iter.isFunctionFrame()) ? iter.functionDisplayAtom() : nullptr;
if (!stackChain->emplaceBack(location.source(),
location.line(),
location.column(),
displayAtom,
nullptr,
nullptr,
principals,
LiveSavedFrameCache::getFramePtr(iter),
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -14,16 +14,18 @@
#include "jscntxt.h"
#include "jsscript.h"
#include "jit/BaselineFrame.h"
#include "jit/RematerializedFrame.h"
#include "js/Debug.h"
#include "vm/EnvironmentObject.h"
#include "vm/GeneratorObject.h"
+#include "wasm/WasmDebugFrame.h"
+#include "wasm/WasmInstance.h"
#include "jsobjinlines.h"
#include "jsscriptinlines.h"
#include "jit/BaselineFrame-inl.h"
namespace js {
@@ -439,16 +441,18 @@ AbstractFramePtr::setReturnValue(const V
inline JSObject*
AbstractFramePtr::environmentChain() const
{
if (isInterpreterFrame())
return asInterpreterFrame()->environmentChain();
if (isBaselineFrame())
return asBaselineFrame()->environmentChain();
+ if (isWasmDebugFrame())
+ return asWasmDebugFrame()->environmentChain();
return asRematerializedFrame()->environmentChain();
}
template <typename SpecificEnvironment>
inline void
AbstractFramePtr::pushOnEnvironmentChain(SpecificEnvironment& env)
{
if (isInterpreterFrame()) {
@@ -575,36 +579,42 @@ AbstractFramePtr::createSingleton() cons
inline bool
AbstractFramePtr::isGlobalFrame() const
{
if (isInterpreterFrame())
return asInterpreterFrame()->isGlobalFrame();
if (isBaselineFrame())
return asBaselineFrame()->isGlobalFrame();
+ if (isWasmDebugFrame())
+ return false;
return asRematerializedFrame()->isGlobalFrame();
}
inline bool
AbstractFramePtr::isModuleFrame() const
{
if (isInterpreterFrame())
return asInterpreterFrame()->isModuleFrame();
if (isBaselineFrame())
return asBaselineFrame()->isModuleFrame();
+ if (isWasmDebugFrame())
+ return false;
return asRematerializedFrame()->isModuleFrame();
}
inline bool
AbstractFramePtr::isEvalFrame() const
{
if (isInterpreterFrame())
return asInterpreterFrame()->isEvalFrame();
if (isBaselineFrame())
return asBaselineFrame()->isEvalFrame();
+ if (isWasmDebugFrame())
+ return false;
MOZ_ASSERT(isRematerializedFrame());
return false;
}
inline bool
AbstractFramePtr::isDebuggerEvalFrame() const
{
if (isInterpreterFrame())
@@ -617,77 +627,108 @@ AbstractFramePtr::isDebuggerEvalFrame()
inline bool
AbstractFramePtr::hasCachedSavedFrame() const
{
if (isInterpreterFrame())
return asInterpreterFrame()->hasCachedSavedFrame();
if (isBaselineFrame())
return asBaselineFrame()->hasCachedSavedFrame();
+ if (isWasmDebugFrame())
+ return asWasmDebugFrame()->hasCachedSavedFrame();
return asRematerializedFrame()->hasCachedSavedFrame();
}
inline void
AbstractFramePtr::setHasCachedSavedFrame()
{
if (isInterpreterFrame())
asInterpreterFrame()->setHasCachedSavedFrame();
else if (isBaselineFrame())
asBaselineFrame()->setHasCachedSavedFrame();
+ else if (isWasmDebugFrame())
+ asWasmDebugFrame()->setHasCachedSavedFrame();
else
asRematerializedFrame()->setHasCachedSavedFrame();
}
inline bool
AbstractFramePtr::isDebuggee() const
{
if (isInterpreterFrame())
return asInterpreterFrame()->isDebuggee();
if (isBaselineFrame())
return asBaselineFrame()->isDebuggee();
+ if (isWasmDebugFrame())
+ return asWasmDebugFrame()->isDebuggee();
return asRematerializedFrame()->isDebuggee();
}
inline void
AbstractFramePtr::setIsDebuggee()
{
if (isInterpreterFrame())
asInterpreterFrame()->setIsDebuggee();
else if (isBaselineFrame())
asBaselineFrame()->setIsDebuggee();
+ else if (isWasmDebugFrame())
+ asWasmDebugFrame()->setIsDebuggee();
else
asRematerializedFrame()->setIsDebuggee();
}
inline void
AbstractFramePtr::unsetIsDebuggee()
{
if (isInterpreterFrame())
asInterpreterFrame()->unsetIsDebuggee();
else if (isBaselineFrame())
asBaselineFrame()->unsetIsDebuggee();
+ else if (isWasmDebugFrame())
+ asWasmDebugFrame()->unsetIsDebuggee();
else
asRematerializedFrame()->unsetIsDebuggee();
}
inline bool
-AbstractFramePtr::hasArgs() const {
+AbstractFramePtr::hasArgs() const
+{
return isFunctionFrame();
}
+inline bool
+AbstractFramePtr::hasScript() const
+{
+ return !isWasmDebugFrame();
+}
+
inline JSScript*
AbstractFramePtr::script() const
{
if (isInterpreterFrame())
return asInterpreterFrame()->script();
if (isBaselineFrame())
return asBaselineFrame()->script();
return asRematerializedFrame()->script();
}
+inline wasm::Instance*
+AbstractFramePtr::wasmInstance() const
+{
+ return asWasmDebugFrame()->instance();
+}
+
+inline GlobalObject*
+AbstractFramePtr::global() const
+{
+ if (isWasmDebugFrame())
+ return &wasmInstance()->object()->global();
+ return &script()->global();
+}
+
inline JSFunction*
AbstractFramePtr::callee() const
{
if (isInterpreterFrame())
return &asInterpreterFrame()->callee();
if (isBaselineFrame())
return asBaselineFrame()->callee();
return asRematerializedFrame()->callee();
@@ -705,16 +746,18 @@ AbstractFramePtr::calleev() const
inline bool
AbstractFramePtr::isFunctionFrame() const
{
if (isInterpreterFrame())
return asInterpreterFrame()->isFunctionFrame();
if (isBaselineFrame())
return asBaselineFrame()->isFunctionFrame();
+ if (isWasmDebugFrame())
+ return false;
return asRematerializedFrame()->isFunctionFrame();
}
inline bool
AbstractFramePtr::isNonStrictDirectEvalFrame() const
{
if (isInterpreterFrame())
return asInterpreterFrame()->isNonStrictDirectEvalFrame();
@@ -777,44 +820,54 @@ AbstractFramePtr::initArgsObj(ArgumentsO
inline bool
AbstractFramePtr::prevUpToDate() const
{
if (isInterpreterFrame())
return asInterpreterFrame()->prevUpToDate();
if (isBaselineFrame())
return asBaselineFrame()->prevUpToDate();
+ if (isWasmDebugFrame())
+ return asWasmDebugFrame()->prevUpToDate();
return asRematerializedFrame()->prevUpToDate();
}
inline void
AbstractFramePtr::setPrevUpToDate() const
{
if (isInterpreterFrame()) {
asInterpreterFrame()->setPrevUpToDate();
return;
}
if (isBaselineFrame()) {
asBaselineFrame()->setPrevUpToDate();
return;
}
+ if (isWasmDebugFrame()) {
+ asWasmDebugFrame()->setPrevUpToDate();
+ return;
+ }
asRematerializedFrame()->setPrevUpToDate();
}
inline void
AbstractFramePtr::unsetPrevUpToDate() const
{
if (isInterpreterFrame()) {
asInterpreterFrame()->unsetPrevUpToDate();
return;
}
if (isBaselineFrame()) {
asBaselineFrame()->unsetPrevUpToDate();
return;
}
+ if (isWasmDebugFrame()) {
+ asWasmDebugFrame()->unsetPrevUpToDate();
+ return;
+ }
asRematerializedFrame()->unsetPrevUpToDate();
}
inline Value&
AbstractFramePtr::thisArgument() const
{
if (isInterpreterFrame())
return asInterpreterFrame()->thisArgument();
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -12,16 +12,17 @@
#include "gc/Marking.h"
#include "jit/BaselineFrame.h"
#include "jit/JitcodeMap.h"
#include "jit/JitCompartment.h"
#include "js/GCAPI.h"
#include "vm/Debugger.h"
#include "vm/Opcodes.h"
+#include "wasm/WasmDebugFrame.h"
#include "jit/JitFrameIterator-inl.h"
#include "vm/EnvironmentObject-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/Probes-inl.h"
using namespace js;
@@ -546,17 +547,17 @@ FrameIter::settleOnActivation()
if (activation->isWasm()) {
data_.wasmFrames_ = wasm::FrameIterator(*data_.activations_->asWasm());
if (data_.wasmFrames_.done()) {
++data_.activations_;
continue;
}
- data_.pc_ = (jsbytecode*)data_.wasmFrames_.pc();
+ data_.pc_ = nullptr;
data_.state_ = WASM;
return;
}
MOZ_ASSERT(activation->isInterpreter());
InterpreterActivation* interpAct = activation->asInterpreter();
data_.interpFrames_ = InterpreterFrameIterator(interpAct);
@@ -679,17 +680,17 @@ FrameIter::popJitFrame()
}
void
FrameIter::popWasmFrame()
{
MOZ_ASSERT(data_.state_ == WASM);
++data_.wasmFrames_;
- data_.pc_ = (jsbytecode*)data_.wasmFrames_.pc();
+ data_.pc_ = nullptr;
if (data_.wasmFrames_.done())
popActivation();
}
FrameIter&
FrameIter::operator++()
{
switch (data_.state_) {
@@ -727,17 +728,16 @@ FrameIter::operator++()
FrameIter::Data*
FrameIter::copyData() const
{
Data* data = data_.cx_->new_<Data>(data_);
if (!data)
return nullptr;
- MOZ_ASSERT(data_.state_ != WASM);
if (data && data_.jitFrames_.isIonScripted())
data->ionInlineFrameNo_ = ionInlineFrames_.frameNo();
return data;
}
AbstractFramePtr
FrameIter::copyDataAsAbstractFramePtr() const
{
@@ -753,17 +753,17 @@ FrameIter::rawFramePtr() const
switch (data_.state_) {
case DONE:
return nullptr;
case JIT:
return data_.jitFrames_.fp();
case INTERP:
return interpFrame();
case WASM:
- return data_.wasmFrames_.fp();
+ return nullptr;
}
MOZ_CRASH("Unexpected state");
}
JSCompartment*
FrameIter::compartment() const
{
switch (data_.state_) {
@@ -805,33 +805,33 @@ FrameIter::isFunctionFrame() const
break;
case INTERP:
return interpFrame()->isFunctionFrame();
case JIT:
if (data_.jitFrames_.isBaselineJS())
return data_.jitFrames_.baselineFrame()->isFunctionFrame();
return script()->functionNonDelazifying();
case WASM:
- return true;
+ return false;
}
MOZ_CRASH("Unexpected state");
}
JSAtom*
FrameIter::functionDisplayAtom() const
{
- MOZ_ASSERT(isFunctionFrame());
-
switch (data_.state_) {
case DONE:
break;
case INTERP:
case JIT:
+ MOZ_ASSERT(isFunctionFrame());
return calleeTemplate()->displayAtom();
case WASM:
+ MOZ_ASSERT(isWasm());
return data_.wasmFrames_.functionDisplayAtom();
}
MOZ_CRASH("Unexpected state");
}
ScriptSource*
FrameIter::scriptSource() const
@@ -940,60 +940,64 @@ FrameIter::ensureHasRematerializedFrame(
return !!activation()->asJit()->getRematerializedFrame(cx, data_.jitFrames_);
}
bool
FrameIter::hasUsableAbstractFramePtr() const
{
switch (data_.state_) {
case DONE:
- case WASM:
return false;
case JIT:
if (data_.jitFrames_.isBaselineJS())
return true;
MOZ_ASSERT(data_.jitFrames_.isIonScripted());
return !!activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
ionInlineFrames_.frameNo());
break;
case INTERP:
return true;
+ case WASM:
+ return data_.wasmFrames_.debugEnabled();
}
MOZ_CRASH("Unexpected state");
}
AbstractFramePtr
FrameIter::abstractFramePtr() const
{
MOZ_ASSERT(hasUsableAbstractFramePtr());
switch (data_.state_) {
case DONE:
- case WASM:
break;
case JIT: {
if (data_.jitFrames_.isBaselineJS())
return data_.jitFrames_.baselineFrame();
MOZ_ASSERT(data_.jitFrames_.isIonScripted());
return activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
ionInlineFrames_.frameNo());
break;
}
case INTERP:
MOZ_ASSERT(interpFrame());
return AbstractFramePtr(interpFrame());
+ case WASM:
+ MOZ_ASSERT(data_.wasmFrames_.debugEnabled());
+ return data_.wasmFrames_.debugFrame();
}
MOZ_CRASH("Unexpected state");
}
void
FrameIter::updatePcQuadratic()
{
switch (data_.state_) {
+ case WASM:
case DONE:
break;
case INTERP: {
InterpreterFrame* frame = interpFrame();
InterpreterActivation* activation = data_.activations_->asInterpreter();
// Look for the current frame.
data_.interpFrames_ = InterpreterFrameIterator(activation);
@@ -1022,20 +1026,16 @@ FrameIter::updatePcQuadratic()
++data_.jitFrames_;
// Update the pc.
MOZ_ASSERT(data_.jitFrames_.baselineFrame() == frame);
data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
return;
}
break;
- case WASM:
- // Update the pc.
- data_.pc_ = (jsbytecode*)data_.wasmFrames_.pc();
- break;
}
MOZ_CRASH("Unexpected state");
}
JSFunction*
FrameIter::calleeTemplate() const
{
switch (data_.state_) {
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -48,16 +48,17 @@ class EnvironmentIter;
class EnvironmentCoordinate;
class SavedFrame;
namespace jit {
class CommonFrameLayout;
}
namespace wasm {
+class DebugFrame;
class Instance;
}
// VM stack layout
//
// A JSRuntime's stack consists of a linked list of activations. Every activation
// contains a number of scripted frames that are either running in the interpreter
// (InterpreterActivation) or JIT code (JitActivation). The frames inside a single
@@ -121,17 +122,18 @@ class AbstractFramePtr
uintptr_t ptr_;
enum {
Tag_ScriptFrameIterData = 0x0,
Tag_InterpreterFrame = 0x1,
Tag_BaselineFrame = 0x2,
Tag_RematerializedFrame = 0x3,
- TagMask = 0x3
+ Tag_WasmDebugFrame = 0x4,
+ TagMask = 0x7
};
public:
AbstractFramePtr()
: ptr_(0)
{}
MOZ_IMPLICIT AbstractFramePtr(InterpreterFrame* fp)
@@ -147,16 +149,22 @@ class AbstractFramePtr
}
MOZ_IMPLICIT AbstractFramePtr(jit::RematerializedFrame* fp)
: ptr_(fp ? uintptr_t(fp) | Tag_RematerializedFrame : 0)
{
MOZ_ASSERT_IF(fp, asRematerializedFrame() == fp);
}
+ MOZ_IMPLICIT AbstractFramePtr(wasm::DebugFrame* fp)
+ : ptr_(fp ? uintptr_t(fp) | Tag_WasmDebugFrame : 0)
+ {
+ MOZ_ASSERT_IF(fp, asWasmDebugFrame() == fp);
+ }
+
static AbstractFramePtr FromRaw(void* raw) {
AbstractFramePtr frame;
frame.ptr_ = uintptr_t(raw);
return frame;
}
bool isScriptFrameIterData() const {
return !!ptr_ && (ptr_ & TagMask) == Tag_ScriptFrameIterData;
@@ -183,16 +191,25 @@ class AbstractFramePtr
return (ptr_ & TagMask) == Tag_RematerializedFrame;
}
jit::RematerializedFrame* asRematerializedFrame() const {
MOZ_ASSERT(isRematerializedFrame());
jit::RematerializedFrame* res = (jit::RematerializedFrame*)(ptr_ & ~TagMask);
MOZ_ASSERT(res);
return res;
}
+ bool isWasmDebugFrame() const {
+ return (ptr_ & TagMask) == Tag_WasmDebugFrame;
+ }
+ wasm::DebugFrame* asWasmDebugFrame() const {
+ MOZ_ASSERT(isWasmDebugFrame());
+ wasm::DebugFrame* res = (wasm::DebugFrame*)(ptr_ & ~TagMask);
+ MOZ_ASSERT(res);
+ return res;
+ }
void* raw() const { return reinterpret_cast<void*>(ptr_); }
bool operator ==(const AbstractFramePtr& other) const { return ptr_ == other.ptr_; }
bool operator !=(const AbstractFramePtr& other) const { return ptr_ != other.ptr_; }
explicit operator bool() const { return !!ptr_; }
@@ -210,17 +227,20 @@ class AbstractFramePtr
inline bool hasInitialEnvironment() const;
inline bool isGlobalFrame() const;
inline bool isModuleFrame() const;
inline bool isEvalFrame() const;
inline bool isDebuggerEvalFrame() const;
inline bool hasCachedSavedFrame() const;
inline void setHasCachedSavedFrame();
+ inline bool hasScript() const;
inline JSScript* script() const;
+ inline wasm::Instance* wasmInstance() const;
+ inline GlobalObject* global() const;
inline JSFunction* callee() const;
inline Value calleev() const;
inline Value& thisArgument() const;
inline Value newTarget() const;
inline bool debuggerNeedsCheckPrimitiveReturn() const;
@@ -254,16 +274,17 @@ class AbstractFramePtr
inline HandleValue returnValue() const;
inline void setReturnValue(const Value& rval) const;
friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&, void*);
friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&, InterpreterFrame*);
friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&, jit::BaselineFrame*);
friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&, jit::RematerializedFrame*);
+ friend void GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, wasm::DebugFrame* ptr);
};
class NullFramePtr : public AbstractFramePtr
{
public:
NullFramePtr()
: AbstractFramePtr()
{ }
@@ -1783,16 +1804,21 @@ class FrameIter
const char16_t* displayURL() const;
unsigned computeLine(uint32_t* column = nullptr) const;
JSAtom* functionDisplayAtom() const;
bool mutedErrors() const;
bool hasScript() const { return !isWasm(); }
// -----------------------------------------------------------
+ // The following functions can only be called when isWasm()
+ // -----------------------------------------------------------
+ inline wasm::Instance* wasmInstance() const;
+
+ // -----------------------------------------------------------
// The following functions can only be called when hasScript()
// -----------------------------------------------------------
inline JSScript* script() const;
bool isConstructing() const;
jsbytecode* pc() const { MOZ_ASSERT(!done()); return data_.pc_; }
void updatePcQuadratic();
@@ -1843,17 +1869,17 @@ class FrameIter
bool ensureHasRematerializedFrame(JSContext* cx);
// True when isInterp() or isBaseline(). True when isIon() if it
// has a rematerialized frame. False otherwise false otherwise.
bool hasUsableAbstractFramePtr() const;
// -----------------------------------------------------------
// The following functions can only be called when isInterp(),
- // isBaseline(), or isIon(). Further, abstractFramePtr() can
+ // isBaseline(), isWasm() or isIon(). Further, abstractFramePtr() can
// only be called when hasUsableAbstractFramePtr().
// -----------------------------------------------------------
AbstractFramePtr abstractFramePtr() const;
AbstractFramePtr copyDataAsAbstractFramePtr() const;
Data* copyData() const;
// This can only be called when isInterp():
@@ -2025,16 +2051,24 @@ FrameIter::script() const
if (data_.state_ == INTERP)
return interpFrame()->script();
MOZ_ASSERT(data_.state_ == JIT);
if (data_.jitFrames_.isIonJS())
return ionInlineFrames_.script();
return data_.jitFrames_.script();
}
+inline wasm::Instance*
+FrameIter::wasmInstance() const
+{
+ MOZ_ASSERT(!done());
+ MOZ_ASSERT(data_.state_ == WASM);
+ return data_.wasmFrames_.instance();
+}
+
inline bool
FrameIter::isIon() const
{
return isJit() && data_.jitFrames_.isIonJS();
}
inline bool
FrameIter::isBaseline() const
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -99,16 +99,17 @@
# include "jit/arm/Assembler-arm.h"
#endif
#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
# include "jit/x86-shared/Architecture-x86-shared.h"
# include "jit/x86-shared/Assembler-x86-shared.h"
#endif
#include "wasm/WasmBinaryIterator.h"
+#include "wasm/WasmDebugFrame.h"
#include "wasm/WasmGenerator.h"
#include "wasm/WasmSignalHandlers.h"
#include "wasm/WasmValidate.h"
#include "jit/MacroAssembler-inl.h"
using mozilla::DebugOnly;
using mozilla::FloatingPoint;
@@ -2132,16 +2133,24 @@ class BaseCompiler
default:
MOZ_CRASH("Function argument type");
}
}
// The TLS pointer is always passed as a hidden argument in WasmTlsReg.
// Save it into its assigned local slot.
storeToFramePtr(WasmTlsReg, localInfo_[tlsSlot_].offs());
+ if (debugEnabled_) {
+ // Initialize funcIndex and flag fields of DebugFrame.
+ size_t debugFrame = masm.framePushed() - DebugFrame::offsetOfFrame();
+ masm.store32(Imm32(func_.index()),
+ Address(masm.getStackPointer(), debugFrame + DebugFrame::offsetOfFuncIndex()));
+ masm.storePtr(ImmWord(0),
+ Address(masm.getStackPointer(), debugFrame + DebugFrame::offsetOfFlagsWord()));
+ }
// Initialize the stack locals to zero.
//
// The following are all Bug 1316820:
//
// TODO / OPTIMIZE: on x64, at least, scratch will be a 64-bit
// register and we can move 64 bits at a time.
//
@@ -7770,16 +7779,27 @@ BaseCompiler::init()
// entries for special locals. Currently the only special local is the TLS
// pointer.
tlsSlot_ = locals_.length();
if (!localInfo_.resize(locals_.length() + 1))
return false;
localSize_ = 0;
+ // Reserve a stack slot for the TLS pointer outside the varLow..varHigh
+ // range so it isn't zero-filled like the normal locals.
+ localInfo_[tlsSlot_].init(MIRType::Pointer, pushLocal(sizeof(void*)));
+ if (debugEnabled_) {
+ // If debug information is generated, constructing DebugFrame record:
+ // reserving some data before TLS pointer. The TLS pointer allocated
+ // above and regular wasm::Frame data starts after locals.
+ localSize_ += DebugFrame::offsetOfTlsData();
+ MOZ_ASSERT(DebugFrame::offsetOfFrame() == localSize_);
+ }
+
for (ABIArgIter<const ValTypeVector> i(args); !i.done(); i++) {
Local& l = localInfo_[i.index()];
switch (i.mirType()) {
case MIRType::Int32:
if (i->argInRegister())
l.init(MIRType::Int32, pushLocal(4));
else
l.init(MIRType::Int32, -(i->offsetFromArgBase() + sizeof(Frame)));
@@ -7802,20 +7822,16 @@ BaseCompiler::init()
else
l.init(MIRType::Float32, -(i->offsetFromArgBase() + sizeof(Frame)));
break;
default:
MOZ_CRASH("Argument type");
}
}
- // Reserve a stack slot for the TLS pointer outside the varLow..varHigh
- // range so it isn't zero-filled like the normal locals.
- localInfo_[tlsSlot_].init(MIRType::Pointer, pushLocal(sizeof(void*)));
-
varLow_ = localSize_;
for (size_t i = args.length(); i < locals_.length(); i++) {
Local& l = localInfo_[i];
switch (locals_[i]) {
case ValType::I32:
l.init(MIRType::Int32, pushLocal(4));
break;
new file mode 100644
--- /dev/null
+++ b/js/src/wasm/WasmDebugFrame.cpp
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wasm/WasmDebugFrame.h"
+
+#include "vm/EnvironmentObject.h"
+#include "wasm/WasmInstance.h"
+
+#include "jsobjinlines.h"
+
+using namespace js;
+using namespace js::wasm;
+
+Instance*
+DebugFrame::instance() const
+{
+ return tlsData_->instance;
+}
+
+GlobalObject*
+DebugFrame::global() const
+{
+ return &instance()->object()->global();
+}
+
+JSObject*
+DebugFrame::environmentChain() const
+{
+ return &global()->lexicalEnvironment();
+}
+
+void
+DebugFrame::observeFrame(JSContext* cx)
+{
+ if (observing_)
+ return;
+
+ // TODO make sure wasm::Code onLeaveFrame traps are on
+ observing_ = true;
+}
+
+void
+DebugFrame::leaveFrame(JSContext* cx)
+{
+ if (!observing_)
+ return;
+
+ // TODO make sure wasm::Code onLeaveFrame traps are off
+ observing_ = false;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/wasm/WasmDebugFrame.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef wasmdebugframe_js_h
+#define wasmdebugframe_js_h
+
+#include "gc/Barrier.h"
+#include "js/RootingAPI.h"
+#include "js/TracingAPI.h"
+#include "wasm/WasmTypes.h"
+
+namespace js {
+
+class WasmFunctionCallObject;
+
+namespace wasm {
+
+class DebugFrame
+{
+ union
+ {
+ int32_t resultI32_;
+ int64_t resultI64_;
+ float resultF32_;
+ double resultF64_;
+ };
+
+ // The fields below are initialized by the baseline compiler.
+ uint32_t funcIndex_;
+ uint32_t reserved0_;
+
+ union
+ {
+ struct
+ {
+ bool observing_ : 1;
+ bool isDebuggee_ : 1;
+ bool prevUpToDate_ : 1;
+ bool hasCachedSavedFrame_ : 1;
+ };
+ void* reserved1_;
+ };
+
+ TlsData* tlsData_;
+ Frame frame_;
+
+ explicit DebugFrame() {}
+
+ public:
+ inline uint32_t funcIndex() const { return funcIndex_; }
+ inline TlsData* tlsData() const { return tlsData_; }
+ inline Frame& frame() { return frame_; }
+
+ Instance* instance() const;
+ GlobalObject* global() const;
+
+ JSObject* environmentChain() const;
+
+ void observeFrame(JSContext* cx);
+ void leaveFrame(JSContext* cx);
+
+ void trace(JSTracer* trc);
+
+ // These are opaque boolean flags used by the debugger and
+ // saved-frame-chains code.
+ inline bool isDebuggee() const { return isDebuggee_; }
+ inline void setIsDebuggee() { isDebuggee_ = true; }
+ inline void unsetIsDebuggee() { isDebuggee_ = false; }
+
+ inline bool prevUpToDate() const { return prevUpToDate_; }
+ inline void setPrevUpToDate() { prevUpToDate_ = true; }
+ inline void unsetPrevUpToDate() { prevUpToDate_ = false; }
+
+ inline bool hasCachedSavedFrame() const { return hasCachedSavedFrame_; }
+ inline void setHasCachedSavedFrame() { hasCachedSavedFrame_ = true; }
+
+ inline void* resultsPtr() { return &resultI32_; }
+
+ static constexpr size_t offsetOfResults() { return offsetof(DebugFrame, resultI32_); }
+ static constexpr size_t offsetOfFlagsWord() { return offsetof(DebugFrame, reserved1_); }
+ static constexpr size_t offsetOfFuncIndex() { return offsetof(DebugFrame, funcIndex_); }
+ static constexpr size_t offsetOfTlsData() { return offsetof(DebugFrame, tlsData_); }
+ static constexpr size_t offsetOfFrame() { return offsetof(DebugFrame, frame_); }
+};
+
+static_assert(DebugFrame::offsetOfResults() == 0, "results shall be at offset 0");
+static_assert(DebugFrame::offsetOfTlsData() + sizeof(TlsData*) == DebugFrame::offsetOfFrame(),
+ "TLS pointer must be a field just before the wasm frame");
+static_assert(sizeof(DebugFrame) % 8 == 0 && DebugFrame::offsetOfFrame() % 8 == 0,
+ "DebugFrame and its portion is 8-bytes aligned for AbstractFramePtr");
+
+} // namespace wasm
+} // namespace js
+
+#endif // wasmdebugframe_js_h
--- a/js/src/wasm/WasmFrameIterator.cpp
+++ b/js/src/wasm/WasmFrameIterator.cpp
@@ -13,16 +13,17 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "wasm/WasmFrameIterator.h"
+#include "wasm/WasmDebugFrame.h"
#include "wasm/WasmInstance.h"
#include "jit/MacroAssembler-inl.h"
using namespace js;
using namespace js::jit;
using namespace js::wasm;
@@ -39,16 +40,23 @@ ReturnAddressFromFP(void* fp)
}
static uint8_t*
CallerFPFromFP(void* fp)
{
return reinterpret_cast<Frame*>(fp)->callerFP;
}
+static TlsData*
+TlsDataFromFP(void *fp)
+{
+ void* debugFrame = (uint8_t*)fp - DebugFrame::offsetOfFrame();
+ return reinterpret_cast<DebugFrame*>(debugFrame)->tlsData();
+}
+
FrameIterator::FrameIterator()
: activation_(nullptr),
code_(nullptr),
callsite_(nullptr),
codeRange_(nullptr),
fp_(nullptr),
pc_(nullptr),
missingFrameMessage_(false)
@@ -203,16 +211,40 @@ FrameIterator::functionDisplayAtom() con
unsigned
FrameIterator::lineOrBytecode() const
{
MOZ_ASSERT(!done());
return callsite_ ? callsite_->lineOrBytecode()
: (codeRange_ ? codeRange_->funcLineOrBytecode() : 0);
}
+Instance*
+FrameIterator::instance() const
+{
+ MOZ_ASSERT(!done() && debugEnabled());
+ return TlsDataFromFP(fp_ + callsite_->stackDepth())->instance;
+}
+
+bool
+FrameIterator::debugEnabled() const
+{
+ MOZ_ASSERT(!done() && code_);
+ MOZ_ASSERT_IF(!missingFrameMessage_, codeRange_->kind() == CodeRange::Function);
+ return code_->metadata().debugEnabled;
+}
+
+DebugFrame*
+FrameIterator::debugFrame() const
+{
+ MOZ_ASSERT(!done() && debugEnabled());
+ // The fp() points to wasm::Frame.
+ void* buf = static_cast<uint8_t*>(fp_ + callsite_->stackDepth()) - DebugFrame::offsetOfFrame();
+ return static_cast<DebugFrame*>(buf);
+}
+
/*****************************************************************************/
// Prologue/epilogue code generation
// These constants reflect statically-determined offsets in the profiling
// prologue/epilogue. The offsets are dynamically asserted during code
// generation.
#if defined(JS_CODEGEN_X64)
# if defined(DEBUG)
--- a/js/src/wasm/WasmFrameIterator.h
+++ b/js/src/wasm/WasmFrameIterator.h
@@ -28,16 +28,18 @@ namespace js {
class WasmActivation;
namespace jit { class MacroAssembler; }
namespace wasm {
class CallSite;
class Code;
class CodeRange;
+class DebugFrame;
+class Instance;
class SigIdDesc;
struct CallThunk;
struct FuncOffsets;
struct ProfilingOffsets;
struct TrapOffset;
// Iterates over the frames of a single WasmActivation, called synchronously
// from C++ in the thread of the asm.js.
@@ -64,18 +66,20 @@ class FrameIterator
explicit FrameIterator(const WasmActivation& activation);
void operator++();
bool done() const;
const char* filename() const;
const char16_t* displayURL() const;
bool mutedErrors() const;
JSAtom* functionDisplayAtom() const;
unsigned lineOrBytecode() const;
- inline void* fp() const { return fp_; }
- inline uint8_t* pc() const { return pc_; }
+ const CodeRange* codeRange() const { return codeRange_; }
+ Instance* instance() const;
+ bool debugEnabled() const;
+ DebugFrame* debugFrame() const;
};
// An ExitReason describes the possible reasons for leaving compiled wasm code
// or the state of not having left compiled wasm code (ExitReason::None).
enum class ExitReason : uint32_t
{
None, // default state, the pc is in wasm code
ImportJit, // fast-path call directly into JIT code
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1485,12 +1485,34 @@ struct MemoryPatch
void offsetBy(uint32_t delta) {
offset += delta;
}
};
WASM_DECLARE_POD_VECTOR(MemoryPatch, MemoryPatchVector)
+// As an invariant across architectures, within wasm code:
+// $sp % WasmStackAlignment = (sizeof(wasm::Frame) + masm.framePushed) % WasmStackAlignment
+// Thus, wasm::Frame represents the bytes pushed after the call (which occurred
+// with a WasmStackAlignment-aligned StackPointer) that are not included in
+// masm.framePushed.
+
+struct Frame
+{
+ // The caller's saved frame pointer. In non-profiling mode, internal
+ // wasm-to-wasm calls don't update fp and thus don't save the caller's
+ // frame pointer; the space is reserved, however, so that profiling mode can
+ // reuse the same function body without recompiling.
+ uint8_t* callerFP;
+
+ // The return address pushed by the call (in the case of ARM/MIPS the return
+ // address is pushed by the first instruction of the prologue).
+ void* returnAddress;
+};
+
+static_assert(sizeof(Frame) == 2 * sizeof(void*), "?!");
+static const uint32_t FrameBytesAfterReturnAddress = sizeof(void*);
+
} // namespace wasm
} // namespace js
#endif // wasm_types_h