mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-09 09:18:42 +09:00
1286948
1286948 - Adds debug mode for WASM baseline compiler. 1286948 - Adds scope and environment for wasm calls (Adds artificial JS scope and environment for wasm frames. That allows debugger to properly handle call stack). 1286948 - Adds prolog and epilog debug traps and handlers (Using toggled call/traps to invoke handler to process enter and leave frame events). 1286948 - onEnterFrame/onLeaveFrame wasm events and callstack (Handles onEnterFrame and onLeaveFrame trap handling. The FrameIter is used in the DebuggerFrame instead of ScriptFrameIter. The debug wasm frame is created and can be found on callstack during those event). Does not compile without 1331452 - Keep scope in a slot in WasmFunctionCallObjects along with 1335773 - Inspects wasm function locals to build
This commit is contained in:
parent
590d0e03f7
commit
9754fb007b
49 changed files with 1447 additions and 158 deletions
|
|
@ -111,6 +111,8 @@ its prototype:
|
|||
|
||||
* `"module"`: a frame running code at the top level of a module.
|
||||
|
||||
* `"wasmcall"`: a frame running a WebAssembly function call.
|
||||
|
||||
* `"debugger"`: a frame for a call to user code invoked by the debugger
|
||||
(see the `eval` method below).
|
||||
|
||||
|
|
@ -124,8 +126,11 @@ its prototype:
|
|||
|
||||
* `"ion"`: a frame running in the optimizing JIT.
|
||||
|
||||
* `"wasm"`: a frame running in WebAssembly baseline JIT.
|
||||
|
||||
`this`
|
||||
: The value of `this` for this frame (a debuggee value).
|
||||
: The value of `this` for this frame (a debuggee value). For a `wasmcall`
|
||||
frame, this property throws a `TypeError`.
|
||||
|
||||
`older`
|
||||
: The next-older visible frame, in which control will resume when this
|
||||
|
|
@ -149,6 +154,7 @@ its prototype:
|
|||
`offset`
|
||||
: The offset of the bytecode instruction currently being executed in
|
||||
`script`, or `undefined` if the frame's `script` property is `null`.
|
||||
For a `wasmcall` frame, this property throws a `TypeError`.
|
||||
|
||||
`environment`
|
||||
: The lexical environment within which evaluation is taking place (a
|
||||
|
|
@ -268,9 +274,9 @@ methods of other kinds of objects.
|
|||
<code id="eval">eval(<i>code</i>, [<i>options</i>])</code>
|
||||
: Evaluate <i>code</i> in the execution context of this frame, and return
|
||||
a [completion value][cv] describing how it completed. <i>Code</i> is a
|
||||
string. If this frame's `environment` property is `null`, throw a
|
||||
`TypeError`. All extant handler methods, breakpoints, and
|
||||
so on remain active during the call. This function follows the
|
||||
string. If this frame's `environment` property is `null` or `type` property
|
||||
is `wasmcall`, throw a `TypeError`. All extant handler methods, breakpoints,
|
||||
and so on remain active during the call. This function follows the
|
||||
[invocation function conventions][inv fr].
|
||||
|
||||
<i>Code</i> is interpreted as strict mode code when it contains a Use
|
||||
|
|
@ -326,3 +332,5 @@ methods of other kinds of objects.
|
|||
|
||||
The <i>options</i> argument is as for
|
||||
[`Debugger.Frame.prototype.eval`][fr eval], described above.
|
||||
Also like `eval`, if this frame's `environment` property is `null` or
|
||||
`type` property is `wasmcall`, throw a `TypeError`.
|
||||
|
|
|
|||
|
|
@ -278,6 +278,10 @@ EmitterScope::searchInEnclosingScope(JSAtom* name, Scope* scope, uint8_t hops)
|
|||
case ScopeKind::With:
|
||||
case ScopeKind::NonSyntactic:
|
||||
return NameLocation::Dynamic();
|
||||
|
||||
|
||||
case ScopeKind::WasmFunction:
|
||||
MOZ_CRASH("No direct eval inside wasm functions");
|
||||
}
|
||||
|
||||
if (hasEnv) {
|
||||
|
|
@ -1034,6 +1038,8 @@ EmitterScope::leave(BytecodeEmitter* bce, bool nonLocal)
|
|||
case ScopeKind::NonSyntactic:
|
||||
case ScopeKind::Module:
|
||||
break;
|
||||
case ScopeKind::WasmFunction:
|
||||
MOZ_CRASH("No wasm function scopes in JS");
|
||||
}
|
||||
|
||||
// Finish up the scope if we are leaving it in LIFO fashion.
|
||||
|
|
|
|||
|
|
@ -1272,6 +1272,14 @@ ModuleScope::Data::trace(JSTracer* trc)
|
|||
TraceNullableEdge(trc, &module, "scope module");
|
||||
TraceBindingNames(trc, trailingNames.start(), length);
|
||||
}
|
||||
|
||||
void
|
||||
WasmFunctionScope::Data::trace(JSTracer* trc)
|
||||
{
|
||||
TraceNullableEdge(trc, &instance, "wasm function");
|
||||
TraceBindingNames(trc, trailingNames.start(), length);
|
||||
}
|
||||
|
||||
void
|
||||
Scope::traceChildren(JSTracer* trc)
|
||||
{
|
||||
|
|
@ -1305,6 +1313,10 @@ Scope::traceChildren(JSTracer* trc)
|
|||
break;
|
||||
case ScopeKind::With:
|
||||
break;
|
||||
case ScopeKind::WasmFunction:
|
||||
reinterpret_cast<WasmFunctionScope::Data*>(data_)->trace(trc);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
inline void
|
||||
|
|
@ -1370,7 +1382,15 @@ js::GCMarker::eagerlyMarkChildren(Scope* scope)
|
|||
|
||||
case ScopeKind::With:
|
||||
break;
|
||||
case ScopeKind::WasmFunction: {
|
||||
WasmFunctionScope::Data* data = reinterpret_cast<WasmFunctionScope::Data*>(scope->data_);
|
||||
traverseEdge(scope, static_cast<JSObject*>(data->instance));
|
||||
names = &data->trailingNames;
|
||||
length = data->length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (scope->kind_ == ScopeKind::Function) {
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (JSAtom* name = names->operator[](i).name())
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ class Shape;
|
|||
class SharedArrayBufferObject;
|
||||
class StructTypeDescr;
|
||||
class UnownedBaseShape;
|
||||
class WasmFunctionScope;
|
||||
class WasmMemoryObject;
|
||||
namespace jit {
|
||||
class JitCode;
|
||||
|
|
@ -92,6 +93,7 @@ class JitCode;
|
|||
D(js::SharedArrayBufferObject*) \
|
||||
D(js::StructTypeDescr*) \
|
||||
D(js::UnownedBaseShape*) \
|
||||
D(js::WasmFunctionScope*) \
|
||||
D(js::WasmInstanceObject*) \
|
||||
D(js::WasmMemoryObject*) \
|
||||
D(js::WasmTableObject*) \
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class InterpreterTypeCache(object):
|
|||
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):
|
||||
|
|
@ -47,7 +48,8 @@ class AbstractFramePtr(object):
|
|||
Tag_InterpreterFrame = 0x1
|
||||
Tag_BaselineFrame = 0x2
|
||||
Tag_RematerializedFrame = 0x3
|
||||
TagMask = 0x3
|
||||
Tag_WasmDebugFrame = 0x4
|
||||
TagMask = 0x7
|
||||
|
||||
def __init__(self, value, cache):
|
||||
self.value = value
|
||||
|
|
@ -72,6 +74,9 @@ class AbstractFramePtr(object):
|
|||
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
|
||||
|
|
|
|||
|
|
@ -49,6 +49,13 @@ GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, jit::RematerializedFrame* p
|
|||
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) {
|
||||
|
|
@ -82,6 +89,9 @@ FRAGMENT(Interpreter, AbstractFramePtr) {
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -523,6 +523,13 @@ class MacroAssembler : public MacroAssemblerSpecific
|
|||
static void patchNopToNearJump(uint8_t* jump, uint8_t* target) PER_SHARED_ARCH;
|
||||
static void patchNearJumpToNop(uint8_t* jump) PER_SHARED_ARCH;
|
||||
|
||||
// Emit a nop that can be patched to and from a nop and a call with int32
|
||||
// relative displacement.
|
||||
CodeOffset nopPatchableToCall(const wasm::CallSiteDesc& desc) PER_SHARED_ARCH;
|
||||
static void patchNopToCall(uint8_t* callsite, uint8_t* target) PER_SHARED_ARCH;
|
||||
static void patchCallToNop(uint8_t* callsite) PER_SHARED_ARCH;
|
||||
|
||||
|
||||
public:
|
||||
// ===============================================================
|
||||
// ABI function calls.
|
||||
|
|
|
|||
|
|
@ -5130,6 +5130,34 @@ MacroAssembler::patchNearJumpToNop(uint8_t* jump)
|
|||
new (jump) InstNOP();
|
||||
}
|
||||
|
||||
CodeOffset
|
||||
MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc)
|
||||
{
|
||||
CodeOffset offset(currentOffset());
|
||||
ma_nop();
|
||||
append(desc, CodeOffset(currentOffset()), framePushed());
|
||||
return offset;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target)
|
||||
{
|
||||
uint8_t* inst = call - 4;
|
||||
MOZ_ASSERT(reinterpret_cast<Instruction*>(inst)->is<InstBLImm>() ||
|
||||
reinterpret_cast<Instruction*>(inst)->is<InstNOP>());
|
||||
|
||||
new (inst) InstBLImm(BOffImm(target - inst), Assembler::Always);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchCallToNop(uint8_t* call)
|
||||
{
|
||||
uint8_t* inst = call - 4;
|
||||
MOZ_ASSERT(reinterpret_cast<Instruction*>(inst)->is<InstBLImm>() ||
|
||||
reinterpret_cast<Instruction*>(inst)->is<InstNOP>());
|
||||
new (inst) InstNOP();
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::pushReturnAddress()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -580,6 +580,25 @@ MacroAssembler::patchNearJumpToNop(uint8_t* jump)
|
|||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
CodeOffset
|
||||
MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
return CodeOffset();
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchCallToNop(uint8_t* call)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::pushReturnAddress()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1901,6 +1901,25 @@ MacroAssembler::call(JitCode* c)
|
|||
callJitNoProfiler(ScratchRegister);
|
||||
}
|
||||
|
||||
CodeOffset
|
||||
MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
return CodeOffset();
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchCallToNop(uint8_t* call)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::pushReturnAddress()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -661,28 +661,6 @@ class CodeLocationLabel
|
|||
|
||||
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).
|
||||
|
|
|
|||
|
|
@ -1090,6 +1090,13 @@ class AssemblerX86Shared : public AssemblerShared
|
|||
X86Encoding::BaseAssembler::patchJumpToTwoByteNop(jump);
|
||||
}
|
||||
|
||||
static void patchFiveByteNopToCall(uint8_t* callsite, uint8_t* target) {
|
||||
X86Encoding::BaseAssembler::patchFiveByteNopToCall(callsite, target);
|
||||
}
|
||||
static void patchCallToFiveByteNop(uint8_t* callsite) {
|
||||
X86Encoding::BaseAssembler::patchCallToFiveByteNop(callsite);
|
||||
}
|
||||
|
||||
void breakpoint() {
|
||||
masm.int3();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,6 +110,40 @@ public:
|
|||
jump[1] = OP_NOP;
|
||||
}
|
||||
|
||||
static void patchFiveByteNopToCall(uint8_t* callsite, uint8_t* target)
|
||||
{
|
||||
// Note: the offset is relative to the address of the instruction after
|
||||
// the call which is five bytes.
|
||||
uint8_t* inst = callsite - sizeof(int32_t) - 1;
|
||||
// The nop can be already patched as call, overriding the call.
|
||||
// See also nop_five.
|
||||
MOZ_ASSERT(inst[0] == OP_NOP_0F || inst[0] == OP_CALL_rel32);
|
||||
MOZ_ASSERT_IF(inst[0] == OP_NOP_0F, inst[1] == OP_NOP_1F ||
|
||||
inst[2] == OP_NOP_44 ||
|
||||
inst[3] == OP_NOP_00 ||
|
||||
inst[4] == OP_NOP_00);
|
||||
inst[0] = OP_CALL_rel32;
|
||||
SetRel32(callsite, target);
|
||||
}
|
||||
|
||||
static void patchCallToFiveByteNop(uint8_t* callsite)
|
||||
{
|
||||
// See also patchFiveByteNopToCall and nop_five.
|
||||
uint8_t* inst = callsite - sizeof(int32_t) - 1;
|
||||
// The call can be already patched as nop.
|
||||
if (inst[0] == OP_NOP_0F) {
|
||||
MOZ_ASSERT(inst[1] == OP_NOP_1F || inst[2] == OP_NOP_44 ||
|
||||
inst[3] == OP_NOP_00 || inst[4] == OP_NOP_00);
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(inst[0] == OP_CALL_rel32);
|
||||
inst[0] = OP_NOP_0F;
|
||||
inst[1] = OP_NOP_1F;
|
||||
inst[2] = OP_NOP_44;
|
||||
inst[3] = OP_NOP_00;
|
||||
inst[4] = OP_NOP_00;
|
||||
}
|
||||
|
||||
/*
|
||||
* The nop multibytes sequences are directly taken from the Intel's
|
||||
* architecture software developer manual.
|
||||
|
|
|
|||
|
|
@ -697,6 +697,28 @@ MacroAssembler::patchNearJumpToNop(uint8_t* jump)
|
|||
Assembler::patchJumpToTwoByteNop(jump);
|
||||
}
|
||||
|
||||
CodeOffset
|
||||
MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc)
|
||||
{
|
||||
CodeOffset offset(currentOffset());
|
||||
masm.nop_five();
|
||||
append(desc, CodeOffset(currentOffset()), framePushed());
|
||||
MOZ_ASSERT_IF(!oom(), size() - offset.offset() == ToggledCallSize(nullptr));
|
||||
return offset;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchNopToCall(uint8_t* callsite, uint8_t* target)
|
||||
{
|
||||
Assembler::patchFiveByteNopToCall(callsite, target);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::patchCallToNop(uint8_t* callsite)
|
||||
{
|
||||
Assembler::patchCallToFiveByteNop(callsite);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Jit Frames.
|
||||
|
||||
|
|
|
|||
|
|
@ -823,6 +823,9 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
|||
case ScopeKind::Module:
|
||||
MOZ_CRASH("NYI");
|
||||
break;
|
||||
case ScopeKind::WasmFunction:
|
||||
MOZ_CRASH("wasm functions cannot be nested in JSScripts");
|
||||
break;
|
||||
default:
|
||||
// Fail in debug, but only soft-fail in release
|
||||
MOZ_ASSERT(false, "Bad XDR scope kind");
|
||||
|
|
|
|||
|
|
@ -356,6 +356,7 @@ main_deunified_sources = [
|
|||
'wasm/WasmCode.cpp',
|
||||
'wasm/WasmCompartment.cpp',
|
||||
'wasm/WasmCompile.cpp',
|
||||
'wasm/WasmDebugFrame.cpp',
|
||||
'wasm/WasmFrameIterator.cpp',
|
||||
'wasm/WasmGenerator.cpp',
|
||||
'wasm/WasmInstance.cpp',
|
||||
|
|
|
|||
|
|
@ -463,6 +463,7 @@
|
|||
macro(variable, variable, "variable") \
|
||||
macro(void0, void0, "(void 0)") \
|
||||
macro(wasm, wasm, "wasm") \
|
||||
macro(wasmcall, wasmcall, "wasmcall") \
|
||||
macro(watch, watch, "watch") \
|
||||
macro(WeakMapConstructorInit, WeakMapConstructorInit, "WeakMapConstructorInit") \
|
||||
macro(WeakSetConstructorInit, WeakSetConstructorInit, "WeakSetConstructorInit") \
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
js::Debugger::onLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc, bool ok)
|
||||
{
|
||||
MOZ_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame());
|
||||
MOZ_ASSERT_IF(frame.script()->isDebuggee(), frame.isDebuggee());
|
||||
MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(), frame.isDebuggee());
|
||||
/* Traps must be cleared from eval frames, see slowPathOnLeaveFrame. */
|
||||
mozilla::DebugOnly<bool> evalTraps = frame.isEvalFrame() &&
|
||||
frame.script()->hasAnyBreakpointsOrStepMode();
|
||||
|
|
@ -43,7 +43,7 @@ js::Debugger::checkNoExecute(JSContext* cx, HandleScript script)
|
|||
/* static */ JSTrapStatus
|
||||
js::Debugger::onEnterFrame(JSContext* cx, AbstractFramePtr frame)
|
||||
{
|
||||
MOZ_ASSERT_IF(frame.script()->isDebuggee(), frame.isDebuggee());
|
||||
MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(), frame.isDebuggee());
|
||||
if (!frame.isDebuggee())
|
||||
return JSTRAP_CONTINUE;
|
||||
return slowPathOnEnterFrame(cx, frame);
|
||||
|
|
@ -73,7 +73,7 @@ js::Debugger::onNewWasmInstance(JSContext* cx, Handle<WasmInstanceObject*> wasmI
|
|||
}
|
||||
|
||||
inline bool
|
||||
js::Debugger::getScriptFrame(JSContext* cx, const ScriptFrameIter& iter,
|
||||
js::Debugger::getScriptFrame(JSContext* cx, const FrameIter& iter,
|
||||
MutableHandle<DebuggerFrame*> result)
|
||||
{
|
||||
return getScriptFrameWithIter(cx, iter.abstractFramePtr(), &iter, result);
|
||||
|
|
|
|||
|
|
@ -737,7 +737,7 @@ Debugger::memory() const
|
|||
|
||||
bool
|
||||
Debugger::getScriptFrameWithIter(JSContext* cx, AbstractFramePtr referent,
|
||||
const ScriptFrameIter* maybeIter, MutableHandleValue vp)
|
||||
const FrameIter* maybeIter, MutableHandleValue vp)
|
||||
{
|
||||
RootedDebuggerFrame result(cx);
|
||||
if (!Debugger::getScriptFrameWithIter(cx, referent, maybeIter, &result))
|
||||
|
|
@ -749,13 +749,13 @@ Debugger::getScriptFrameWithIter(JSContext* cx, AbstractFramePtr referent,
|
|||
|
||||
bool
|
||||
Debugger::getScriptFrameWithIter(JSContext* cx, AbstractFramePtr referent,
|
||||
const ScriptFrameIter* maybeIter,
|
||||
const FrameIter* maybeIter,
|
||||
MutableHandleDebuggerFrame result)
|
||||
{
|
||||
MOZ_ASSERT_IF(maybeIter, maybeIter->abstractFramePtr() == referent);
|
||||
MOZ_ASSERT(!referent.script()->selfHosted());
|
||||
MOZ_ASSERT_IF(referent.hasScript(), !referent.script()->selfHosted());
|
||||
|
||||
if (!referent.script()->ensureHasAnalyzedArgsUsage(cx))
|
||||
if (referent.hasScript() && !referent.script()->ensureHasAnalyzedArgsUsage(cx))
|
||||
return false;
|
||||
|
||||
FrameMap::AddPtr p = frames.lookupForAdd(referent);
|
||||
|
|
@ -1015,7 +1015,7 @@ Debugger::slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame)
|
|||
return JSTRAP_CONTINUE;
|
||||
|
||||
// The Debugger API mustn't muck with frames from self-hosted scripts.
|
||||
if (frame.script()->selfHosted())
|
||||
if (frame.hasScript() && frame.script()->selfHosted())
|
||||
return JSTRAP_CONTINUE;
|
||||
|
||||
RootedValue rval(cx);
|
||||
|
|
@ -1737,7 +1737,7 @@ Debugger::fireDebuggerStatement(JSContext* cx, MutableHandleValue vp)
|
|||
Maybe<AutoCompartment> ac;
|
||||
ac.emplace(cx, object);
|
||||
|
||||
ScriptFrameIter iter(cx);
|
||||
FrameIter iter(cx);
|
||||
RootedValue scriptFrame(cx);
|
||||
if (!getScriptFrame(cx, iter, &scriptFrame))
|
||||
return reportUncaughtException(ac);
|
||||
|
|
@ -1767,7 +1767,7 @@ Debugger::fireExceptionUnwind(JSContext* cx, MutableHandleValue vp)
|
|||
RootedValue scriptFrame(cx);
|
||||
RootedValue wrappedExc(cx, exc);
|
||||
|
||||
ScriptFrameIter iter(cx);
|
||||
FrameIter iter(cx);
|
||||
if (!getScriptFrame(cx, iter, &scriptFrame) || !wrapDebuggeeValue(cx, &wrappedExc))
|
||||
return reportUncaughtException(ac);
|
||||
|
||||
|
|
@ -1792,7 +1792,7 @@ Debugger::fireEnterFrame(JSContext* cx, MutableHandleValue vp)
|
|||
|
||||
RootedValue scriptFrame(cx);
|
||||
|
||||
ScriptFrameIter iter(cx);
|
||||
FrameIter iter(cx);
|
||||
if (!getScriptFrame(cx, iter, &scriptFrame))
|
||||
return reportUncaughtException(ac);
|
||||
|
||||
|
|
@ -1937,7 +1937,7 @@ Debugger::slowPathOnNewWasmInstance(JSContext* cx, Handle<WasmInstanceObject*> w
|
|||
/* static */ JSTrapStatus
|
||||
Debugger::onTrap(JSContext* cx, MutableHandleValue vp)
|
||||
{
|
||||
ScriptFrameIter iter(cx);
|
||||
FrameIter iter(cx);
|
||||
RootedScript script(cx, iter.script());
|
||||
MOZ_ASSERT(script->isDebuggee());
|
||||
Rooted<GlobalObject*> scriptGlobal(cx, &script->global());
|
||||
|
|
@ -2001,7 +2001,7 @@ Debugger::onTrap(JSContext* cx, MutableHandleValue vp)
|
|||
/* static */ JSTrapStatus
|
||||
Debugger::onSingleStep(JSContext* cx, MutableHandleValue vp)
|
||||
{
|
||||
ScriptFrameIter iter(cx);
|
||||
FrameIter iter(cx);
|
||||
|
||||
/*
|
||||
* We may be stepping over a JSOP_EXCEPTION, that pushes the context's
|
||||
|
|
@ -2039,6 +2039,8 @@ Debugger::onSingleStep(JSContext* cx, MutableHandleValue vp)
|
|||
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())
|
||||
{
|
||||
|
|
@ -2336,9 +2338,10 @@ class MOZ_RAII ExecutionObservableCompartments : public Debugger::ExecutionObser
|
|||
bool shouldRecompileOrInvalidate(JSScript* script) const {
|
||||
return script->hasBaselineScript() && compartments_.has(script->compartment());
|
||||
}
|
||||
bool shouldMarkAsDebuggee(ScriptFrameIter& iter) const {
|
||||
// AbstractFramePtr can't refer to non-remateralized Ion frames, so if
|
||||
// iter refers to one such, we know we don't match.
|
||||
bool shouldMarkAsDebuggee(FrameIter& iter) const {
|
||||
// AbstractFramePtr can't refer to non-remateralized Ion frames or
|
||||
// non-debuggee wasm frames, so if iter refers to one such, we know we
|
||||
// don't match.
|
||||
return iter.hasUsableAbstractFramePtr() && compartments_.has(iter.compartment());
|
||||
}
|
||||
|
||||
|
|
@ -2397,9 +2400,10 @@ class MOZ_RAII ExecutionObservableFrame : public Debugger::ExecutionObservableSe
|
|||
script == frame_.asRematerializedFrame()->outerScript();
|
||||
}
|
||||
|
||||
bool shouldMarkAsDebuggee(ScriptFrameIter& iter) const {
|
||||
// AbstractFramePtr can't refer to non-remateralized Ion frames, so if
|
||||
// iter refers to one such, we know we don't match.
|
||||
bool shouldMarkAsDebuggee(FrameIter& iter) const {
|
||||
// AbstractFramePtr can't refer to non-remateralized Ion frames or
|
||||
// non-debuggee wasm frames, so if iter refers to one such, we know we
|
||||
// don't match.
|
||||
//
|
||||
// We never use this 'has' overload for frame invalidation, only for
|
||||
// frame debuggee marking; so this overload doesn't need a parallel to
|
||||
|
|
@ -2427,7 +2431,7 @@ class MOZ_RAII ExecutionObservableScript : public Debugger::ExecutionObservableS
|
|||
bool shouldRecompileOrInvalidate(JSScript* script) const {
|
||||
return script->hasBaselineScript() && script == script_;
|
||||
}
|
||||
bool shouldMarkAsDebuggee(ScriptFrameIter& iter) const {
|
||||
bool shouldMarkAsDebuggee(FrameIter& iter) const {
|
||||
// AbstractFramePtr can't refer to non-remateralized Ion frames, and
|
||||
// while a non-rematerialized Ion frame may indeed be running script_,
|
||||
// we cannot mark them as debuggees until they bail out.
|
||||
|
|
@ -2437,6 +2441,9 @@ class MOZ_RAII ExecutionObservableScript : public Debugger::ExecutionObservableS
|
|||
// debuggee. This is correct in that the only other way a frame may be
|
||||
// marked as debuggee is via Debugger.Frame reflection, which would
|
||||
// have rematerialized any Ion frames.
|
||||
//
|
||||
// Also AbstractFramePtr can't refer to non-debuggee wasm frames, so if
|
||||
// iter refers to one such, we know we don't match.
|
||||
return iter.hasUsableAbstractFramePtr() && iter.abstractFramePtr().script() == script_;
|
||||
}
|
||||
|
||||
|
|
@ -2458,7 +2465,7 @@ Debugger::updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObs
|
|||
}
|
||||
|
||||
AbstractFramePtr oldestEnabledFrame;
|
||||
for (ScriptFrameIter iter(cx);
|
||||
for (FrameIter iter(cx);
|
||||
!iter.done();
|
||||
++iter)
|
||||
{
|
||||
|
|
@ -2468,6 +2475,8 @@ Debugger::updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObs
|
|||
oldestEnabledFrame = iter.abstractFramePtr();
|
||||
oldestEnabledFrame.setIsDebuggee();
|
||||
}
|
||||
if (iter.abstractFramePtr().isWasmDebugFrame())
|
||||
iter.abstractFramePtr().asWasmDebugFrame()->observeFrame(cx);
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
// Debugger.Frame lifetimes are managed by the debug epilogue,
|
||||
|
|
@ -2576,6 +2585,17 @@ UpdateExecutionObservabilityOfScriptsInZone(JSContext* cx, Zone* zone,
|
|||
FinishDiscardBaselineScript(fop, scripts[i]);
|
||||
}
|
||||
|
||||
// Iterate through all wasm instances to find ones that need to be updated.
|
||||
for (JSCompartment* c : zone->compartments()) {
|
||||
for (wasm::Instance* instance : c->wasm.instances()) {
|
||||
if (!instance->debugEnabled())
|
||||
continue;
|
||||
|
||||
bool enableTrap = observing == Debugger::IsObserving::Observing;
|
||||
instance->ensureEnterFrameTrapsState(cx, enableTrap);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2599,7 +2619,7 @@ template <typename FrameFn>
|
|||
/* static */ void
|
||||
Debugger::forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn)
|
||||
{
|
||||
GlobalObject* global = &frame.script()->global();
|
||||
GlobalObject* global = frame.global();
|
||||
if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
|
||||
for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
|
||||
Debugger* dbg = *p;
|
||||
|
|
@ -2658,7 +2678,8 @@ Debugger::ensureExecutionObservabilityOfOsrFrame(JSContext* cx, InterpreterFrame
|
|||
/* static */ bool
|
||||
Debugger::ensureExecutionObservabilityOfFrame(JSContext* cx, AbstractFramePtr frame)
|
||||
{
|
||||
MOZ_ASSERT_IF(frame.script()->isDebuggee(), frame.isDebuggee());
|
||||
MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(), frame.isDebuggee());
|
||||
MOZ_ASSERT_IF(frame.isWasmDebugFrame(), frame.wasmInstance()->debugEnabled());
|
||||
if (frame.isDebuggee())
|
||||
return true;
|
||||
ExecutionObservableFrame obs(frame);
|
||||
|
|
@ -2764,7 +2785,7 @@ Debugger::updateObservesCoverageOnDebuggees(JSContext* cx, IsObserving observing
|
|||
// If any frame on the stack belongs to the debuggee, then we cannot update
|
||||
// the ScriptCounts, because this would imply to invalidate a Debugger.Frame
|
||||
// to recompile it with/without ScriptCount support.
|
||||
for (ScriptFrameIter iter(cx);
|
||||
for (FrameIter iter(cx);
|
||||
!iter.done();
|
||||
++iter)
|
||||
{
|
||||
|
|
@ -3733,15 +3754,15 @@ Debugger::getNewestFrame(JSContext* cx, unsigned argc, Value* vp)
|
|||
{
|
||||
THIS_DEBUGGER(cx, argc, vp, "getNewestFrame", args, dbg);
|
||||
|
||||
/* Since there may be multiple contexts, use AllScriptFramesIter. */
|
||||
for (AllScriptFramesIter i(cx); !i.done(); ++i) {
|
||||
/* Since there may be multiple contexts, use AllFramesIter. */
|
||||
for (AllFramesIter i(cx); !i.done(); ++i) {
|
||||
if (dbg->observesFrame(i)) {
|
||||
// Ensure that Ion frames are rematerialized. Only rematerialized
|
||||
// Ion frames may be used as AbstractFramePtrs.
|
||||
if (i.isIon() && !i.ensureHasRematerializedFrame(cx))
|
||||
return false;
|
||||
AbstractFramePtr frame = i.abstractFramePtr();
|
||||
ScriptFrameIter iter(i.activation()->cx());
|
||||
FrameIter iter(i.activation()->cx());
|
||||
while (!iter.hasUsableAbstractFramePtr() || iter.abstractFramePtr() != frame)
|
||||
++iter;
|
||||
return dbg->getScriptFrame(cx, iter, args.rval());
|
||||
|
|
@ -4004,7 +4025,7 @@ Debugger::removeDebuggeeGlobal(FreeOp* fop, GlobalObject* global,
|
|||
for (FrameMap::Enum e(frames); !e.empty(); e.popFront()) {
|
||||
AbstractFramePtr frame = e.front().key();
|
||||
NativeObject* frameobj = e.front().value();
|
||||
if (&frame.script()->global() == global) {
|
||||
if (frame.global() == global) {
|
||||
DebuggerFrame_freeScriptFrameIterData(fop, frameobj);
|
||||
DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj);
|
||||
e.removeFront();
|
||||
|
|
@ -6251,6 +6272,8 @@ DebuggerScript_getLineOffsets(JSContext* cx, unsigned argc, Value* vp)
|
|||
bool
|
||||
Debugger::observesFrame(AbstractFramePtr frame) const
|
||||
{
|
||||
if (frame.isWasmDebugFrame())
|
||||
return observesWasm(frame.wasmInstance());
|
||||
return observesScript(frame.script());
|
||||
}
|
||||
|
||||
|
|
@ -6264,7 +6287,7 @@ Debugger::observesFrame(const FrameIter& iter) const
|
|||
return false;
|
||||
}
|
||||
if (iter.isWasm())
|
||||
return false;
|
||||
return observesWasm(iter.wasmInstance());
|
||||
return observesScript(iter.script());
|
||||
}
|
||||
|
||||
|
|
@ -6278,6 +6301,14 @@ Debugger::observesScript(JSScript* script) const
|
|||
return observesGlobal(&script->global()) && !script->selfHosted();
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::observesWasm(wasm::Instance* instance) const
|
||||
{
|
||||
if (!enabled || !instance->debugEnabled())
|
||||
return false;
|
||||
return observesGlobal(&instance->object()->global());
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to,
|
||||
ScriptFrameIter& iter)
|
||||
|
|
@ -7227,7 +7258,7 @@ DebuggerFrame::initClass(JSContext* cx, HandleObject dbgCtor, HandleObject obj)
|
|||
|
||||
/* static */ DebuggerFrame*
|
||||
DebuggerFrame::create(JSContext* cx, HandleObject proto, AbstractFramePtr referent,
|
||||
const ScriptFrameIter* maybeIter, HandleNativeObject debugger)
|
||||
const FrameIter* maybeIter, HandleNativeObject debugger)
|
||||
{
|
||||
JSObject* obj = NewObjectWithGivenProto(cx, &DebuggerFrame::class_, proto);
|
||||
if (!obj)
|
||||
|
|
@ -7235,7 +7266,7 @@ DebuggerFrame::create(JSContext* cx, HandleObject proto, AbstractFramePtr refere
|
|||
|
||||
DebuggerFrame& frame = obj->as<DebuggerFrame>();
|
||||
|
||||
// Eagerly copy ScriptFrameIter data if we've already walked the stack.
|
||||
// Eagerly copy FrameIter data if we've already walked the stack.
|
||||
if (maybeIter) {
|
||||
AbstractFramePtr data = maybeIter->copyDataAsAbstractFramePtr();
|
||||
if (!data)
|
||||
|
|
@ -7273,10 +7304,10 @@ DebuggerFrame::getIsConstructing(JSContext* cx, HandleDebuggerFrame frame, bool&
|
|||
{
|
||||
MOZ_ASSERT(frame->isLive());
|
||||
|
||||
Maybe<ScriptFrameIter> maybeIter;
|
||||
if (!DebuggerFrame::getScriptFrameIter(cx, frame, maybeIter))
|
||||
Maybe<FrameIter> maybeIter;
|
||||
if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
|
||||
return false;
|
||||
ScriptFrameIter& iter = *maybeIter;
|
||||
FrameIter& iter = *maybeIter;
|
||||
|
||||
result = iter.isFunctionFrame() && iter.isConstructing();
|
||||
return true;
|
||||
|
|
@ -7285,6 +7316,11 @@ DebuggerFrame::getIsConstructing(JSContext* cx, HandleDebuggerFrame frame, bool&
|
|||
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
|
||||
|
|
@ -7327,10 +7363,10 @@ DebuggerFrame::getEnvironment(JSContext* cx, HandleDebuggerFrame frame,
|
|||
|
||||
Debugger* dbg = frame->owner();
|
||||
|
||||
Maybe<ScriptFrameIter> maybeIter;
|
||||
if (!DebuggerFrame::getScriptFrameIter(cx, frame, maybeIter))
|
||||
Maybe<FrameIter> maybeIter;
|
||||
if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
|
||||
return false;
|
||||
ScriptFrameIter& iter = *maybeIter;
|
||||
FrameIter& iter = *maybeIter;
|
||||
|
||||
Rooted<Env*> env(cx);
|
||||
{
|
||||
|
|
@ -7347,8 +7383,9 @@ DebuggerFrame::getEnvironment(JSContext* cx, HandleDebuggerFrame frame,
|
|||
/* static */ bool
|
||||
DebuggerFrame::getIsGenerator(HandleDebuggerFrame frame)
|
||||
{
|
||||
return DebuggerFrame::getReferent(frame).script()->isStarGenerator() ||
|
||||
DebuggerFrame::getReferent(frame).script()->isLegacyGenerator();
|
||||
AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
|
||||
return referent.hasScript() && referent.script()->isStarGenerator() ||
|
||||
referent.hasScript() && referent.script()->isLegacyGenerator();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
|
@ -7356,10 +7393,13 @@ DebuggerFrame::getOffset(JSContext* cx, HandleDebuggerFrame frame, size_t& resul
|
|||
{
|
||||
MOZ_ASSERT(frame->isLive());
|
||||
|
||||
Maybe<ScriptFrameIter> maybeIter;
|
||||
if (!DebuggerFrame::getScriptFrameIter(cx, frame, maybeIter))
|
||||
if (!requireScriptReferent(cx, frame))
|
||||
return false;
|
||||
ScriptFrameIter& iter = *maybeIter;
|
||||
|
||||
Maybe<FrameIter> maybeIter;
|
||||
if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
|
||||
return false;
|
||||
FrameIter& iter = *maybeIter;
|
||||
|
||||
JSScript* script = iter.script();
|
||||
UpdateFrameIterPc(iter);
|
||||
|
|
@ -7376,10 +7416,10 @@ DebuggerFrame::getOlder(JSContext* cx, HandleDebuggerFrame frame,
|
|||
|
||||
Debugger* dbg = frame->owner();
|
||||
|
||||
Maybe<ScriptFrameIter> maybeIter;
|
||||
if (!DebuggerFrame::getScriptFrameIter(cx, frame, maybeIter))
|
||||
Maybe<FrameIter> maybeIter;
|
||||
if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
|
||||
return false;
|
||||
ScriptFrameIter& iter = *maybeIter;
|
||||
FrameIter& iter = *maybeIter;
|
||||
|
||||
for (++iter; !iter.done(); ++iter) {
|
||||
if (dbg->observesFrame(iter)) {
|
||||
|
|
@ -7397,13 +7437,15 @@ DebuggerFrame::getOlder(JSContext* cx, HandleDebuggerFrame frame,
|
|||
DebuggerFrame::getThis(JSContext* cx, HandleDebuggerFrame frame, MutableHandleValue result)
|
||||
{
|
||||
MOZ_ASSERT(frame->isLive());
|
||||
if (!requireScriptReferent(cx, frame))
|
||||
return false;
|
||||
|
||||
Debugger* dbg = frame->owner();
|
||||
|
||||
Maybe<ScriptFrameIter> maybeIter;
|
||||
if (!DebuggerFrame::getScriptFrameIter(cx, frame, maybeIter))
|
||||
Maybe<FrameIter> maybeIter;
|
||||
if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
|
||||
return false;
|
||||
ScriptFrameIter& iter = *maybeIter;
|
||||
FrameIter& iter = *maybeIter;
|
||||
|
||||
{
|
||||
AbstractFramePtr frame = iter.abstractFramePtr();
|
||||
|
|
@ -7435,6 +7477,8 @@ DebuggerFrame::getType(HandleDebuggerFrame frame)
|
|||
return DebuggerFrameType::Call;
|
||||
else if (referent.isModuleFrame())
|
||||
return DebuggerFrameType::Module;
|
||||
else if (referent.isWasmDebugFrame())
|
||||
return DebuggerFrameType::WasmCall;
|
||||
MOZ_CRASH("Unknown frame type");
|
||||
}
|
||||
|
||||
|
|
@ -7447,6 +7491,8 @@ DebuggerFrame::getImplementation(HandleDebuggerFrame frame)
|
|||
return DebuggerFrameImplementation::Baseline;
|
||||
else if (referent.isRematerializedFrame())
|
||||
return DebuggerFrameImplementation::Ion;
|
||||
else if (referent.isWasmDebugFrame())
|
||||
return DebuggerFrameImplementation::Wasm;
|
||||
return DebuggerFrameImplementation::Interpreter;
|
||||
}
|
||||
|
||||
|
|
@ -7514,7 +7560,7 @@ static bool
|
|||
DebuggerGenericEval(JSContext* cx, const mozilla::Range<const char16_t> chars,
|
||||
HandleObject bindings, const EvalOptions& options,
|
||||
JSTrapStatus& status, MutableHandleValue value,
|
||||
Debugger* dbg, HandleObject envArg, ScriptFrameIter* iter)
|
||||
Debugger* dbg, HandleObject envArg, FrameIter* iter)
|
||||
{
|
||||
/* Either we're specifying the frame, or a global. */
|
||||
MOZ_ASSERT_IF(iter, !envArg);
|
||||
|
|
@ -7605,13 +7651,15 @@ DebuggerFrame::eval(JSContext* cx, HandleDebuggerFrame frame, mozilla::Range<con
|
|||
MutableHandleValue value)
|
||||
{
|
||||
MOZ_ASSERT(frame->isLive());
|
||||
if (!requireScriptReferent(cx, frame))
|
||||
return false;
|
||||
|
||||
Debugger* dbg = frame->owner();
|
||||
|
||||
Maybe<ScriptFrameIter> maybeIter;
|
||||
if (!DebuggerFrame::getScriptFrameIter(cx, frame, maybeIter))
|
||||
Maybe<FrameIter> maybeIter;
|
||||
if (!DebuggerFrame::getFrameIter(cx, frame, maybeIter))
|
||||
return false;
|
||||
ScriptFrameIter& iter = *maybeIter;
|
||||
FrameIter& iter = *maybeIter;
|
||||
|
||||
UpdateFrameIterPc(iter);
|
||||
|
||||
|
|
@ -7641,22 +7689,22 @@ DebuggerFrame::getReferent(HandleDebuggerFrame frame)
|
|||
{
|
||||
AbstractFramePtr referent = AbstractFramePtr::FromRaw(frame->getPrivate());
|
||||
if (referent.isScriptFrameIterData()) {
|
||||
ScriptFrameIter iter(*(ScriptFrameIter::Data*)(referent.raw()));
|
||||
FrameIter iter(*(FrameIter::Data*)(referent.raw()));
|
||||
referent = iter.abstractFramePtr();
|
||||
}
|
||||
return referent;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DebuggerFrame::getScriptFrameIter(JSContext* cx, HandleDebuggerFrame frame,
|
||||
Maybe<ScriptFrameIter>& result)
|
||||
DebuggerFrame::getFrameIter(JSContext* cx, HandleDebuggerFrame frame,
|
||||
Maybe<FrameIter>& result)
|
||||
{
|
||||
AbstractFramePtr referent = AbstractFramePtr::FromRaw(frame->getPrivate());
|
||||
if (referent.isScriptFrameIterData()) {
|
||||
result.emplace(*reinterpret_cast<ScriptFrameIter::Data*>(referent.raw()));
|
||||
result.emplace(*reinterpret_cast<FrameIter::Data*>(referent.raw()));
|
||||
} else {
|
||||
result.emplace(cx, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK);
|
||||
ScriptFrameIter& iter = *result;
|
||||
result.emplace(cx, FrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK);
|
||||
FrameIter& iter = *result;
|
||||
while (!iter.hasUsableAbstractFramePtr() || iter.abstractFramePtr() != referent)
|
||||
++iter;
|
||||
AbstractFramePtr data = iter.copyDataAsAbstractFramePtr();
|
||||
|
|
@ -7667,12 +7715,26 @@ DebuggerFrame::getScriptFrameIter(JSContext* cx, HandleDebuggerFrame frame,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DebuggerFrame::requireScriptReferent(JSContext* cx, HandleDebuggerFrame frame)
|
||||
{
|
||||
AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
|
||||
if (!referent.hasScript()) {
|
||||
RootedValue frameobj(cx, ObjectValue(*frame));
|
||||
ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT,
|
||||
JSDVG_SEARCH_STACK, frameobj, nullptr,
|
||||
"a script frame", nullptr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
DebuggerFrame_freeScriptFrameIterData(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
AbstractFramePtr frame = AbstractFramePtr::FromRaw(obj->as<NativeObject>().getPrivate());
|
||||
if (frame.isScriptFrameIterData())
|
||||
fop->delete_((ScriptFrameIter::Data*) frame.raw());
|
||||
fop->delete_((FrameIter::Data*) frame.raw());
|
||||
obj->as<NativeObject>().setPrivate(nullptr);
|
||||
}
|
||||
|
||||
|
|
@ -7761,20 +7823,20 @@ DebuggerFrame_checkThis(JSContext* cx, const CallArgs& args, const char* fnname,
|
|||
THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj); \
|
||||
AbstractFramePtr frame = AbstractFramePtr::FromRaw(thisobj->getPrivate()); \
|
||||
if (frame.isScriptFrameIterData()) { \
|
||||
ScriptFrameIter iter(*(ScriptFrameIter::Data*)(frame.raw())); \
|
||||
FrameIter iter(*(FrameIter::Data*)(frame.raw())); \
|
||||
frame = iter.abstractFramePtr(); \
|
||||
}
|
||||
|
||||
#define THIS_FRAME_ITER(cx, argc, vp, fnname, args, thisobj, maybeIter, iter) \
|
||||
THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj); \
|
||||
Maybe<ScriptFrameIter> maybeIter; \
|
||||
Maybe<FrameIter> maybeIter; \
|
||||
{ \
|
||||
AbstractFramePtr f = AbstractFramePtr::FromRaw(thisobj->getPrivate()); \
|
||||
if (f.isScriptFrameIterData()) { \
|
||||
maybeIter.emplace(*(ScriptFrameIter::Data*)(f.raw())); \
|
||||
maybeIter.emplace(*(FrameIter::Data*)(f.raw())); \
|
||||
} else { \
|
||||
maybeIter.emplace(cx, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK); \
|
||||
ScriptFrameIter& iter = *maybeIter; \
|
||||
maybeIter.emplace(cx, FrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK); \
|
||||
FrameIter& iter = *maybeIter; \
|
||||
while (!iter.hasUsableAbstractFramePtr() || iter.abstractFramePtr() != f) \
|
||||
++iter; \
|
||||
AbstractFramePtr data = iter.copyDataAsAbstractFramePtr(); \
|
||||
|
|
@ -7783,7 +7845,7 @@ DebuggerFrame_checkThis(JSContext* cx, const CallArgs& args, const char* fnname,
|
|||
thisobj->setPrivate(data.raw()); \
|
||||
} \
|
||||
} \
|
||||
ScriptFrameIter& iter = *maybeIter
|
||||
FrameIter& iter = *maybeIter
|
||||
|
||||
#define THIS_FRAME_OWNER(cx, argc, vp, fnname, args, thisobj, frame, dbg) \
|
||||
THIS_FRAME(cx, argc, vp, fnname, args, thisobj, frame); \
|
||||
|
|
@ -7814,6 +7876,9 @@ DebuggerFrame::typeGetter(JSContext* cx, unsigned argc, Value* vp)
|
|||
case DebuggerFrameType::Module:
|
||||
str = cx->names().module;
|
||||
break;
|
||||
case DebuggerFrameType::WasmCall:
|
||||
str = cx->names().wasmcall;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("bad DebuggerFrameType value");
|
||||
}
|
||||
|
|
@ -7840,6 +7905,9 @@ DebuggerFrame::implementationGetter(JSContext* cx, unsigned argc, Value* vp)
|
|||
case DebuggerFrameImplementation::Interpreter:
|
||||
s = "interpreter";
|
||||
break;
|
||||
case DebuggerFrameImplementation::Wasm:
|
||||
s = "wasm";
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("bad DebuggerFrameImplementation value");
|
||||
}
|
||||
|
|
@ -8058,6 +8126,11 @@ DebuggerFrame_getScript(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!scriptObject)
|
||||
return false;
|
||||
}
|
||||
} else if (frame.isWasmDebugFrame()) {
|
||||
RootedWasmInstanceObject instance(cx, frame.wasmInstance()->object());
|
||||
scriptObject = debug->wrapWasmScript(cx, instance);
|
||||
if (!scriptObject)
|
||||
return false;
|
||||
} else {
|
||||
/*
|
||||
* We got eval, JS_Evaluate*, or JS_ExecuteScript non-function script
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
virtual const HashSet<Zone*>* zones() const { return nullptr; }
|
||||
|
||||
virtual bool shouldRecompileOrInvalidate(JSScript* script) const = 0;
|
||||
virtual bool shouldMarkAsDebuggee(ScriptFrameIter& iter) const = 0;
|
||||
virtual bool shouldMarkAsDebuggee(FrameIter& iter) const = 0;
|
||||
};
|
||||
|
||||
// This enum is converted to and compare with bool values; NotObserving
|
||||
|
|
@ -768,11 +768,11 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
* Gets a Debugger.Frame object. If maybeIter is non-null, we eagerly copy
|
||||
* its data if we need to make a new Debugger.Frame.
|
||||
*/
|
||||
MOZ_MUST_USE bool getScriptFrameWithIter(JSContext* cx, AbstractFramePtr frame,
|
||||
const ScriptFrameIter* maybeIter,
|
||||
[[nodiscard]] bool getScriptFrameWithIter(JSContext* cx, AbstractFramePtr frame,
|
||||
const FrameIter* maybeIter,
|
||||
MutableHandleValue vp);
|
||||
MOZ_MUST_USE bool getScriptFrameWithIter(JSContext* cx, AbstractFramePtr frame,
|
||||
const ScriptFrameIter* maybeIter,
|
||||
[[nodiscard]] bool getScriptFrameWithIter(JSContext* cx, AbstractFramePtr frame,
|
||||
const FrameIter* maybeIter,
|
||||
MutableHandleDebuggerFrame result);
|
||||
|
||||
inline Breakpoint* firstBreakpoint() const;
|
||||
|
|
@ -911,6 +911,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
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
|
||||
|
|
@ -995,11 +996,11 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
* frame, in which case the cost of walking the stack has already been
|
||||
* paid.
|
||||
*/
|
||||
MOZ_MUST_USE bool getScriptFrame(JSContext* cx, const ScriptFrameIter& iter,
|
||||
[[nodiscard]] bool getScriptFrame(JSContext* cx, const FrameIter& iter,
|
||||
MutableHandleValue vp) {
|
||||
return getScriptFrameWithIter(cx, iter.abstractFramePtr(), &iter, vp);
|
||||
}
|
||||
MOZ_MUST_USE bool getScriptFrame(JSContext* cx, const ScriptFrameIter& iter,
|
||||
[[nodiscard]] bool getScriptFrame(JSContext* cx, const FrameIter& iter,
|
||||
MutableHandleDebuggerFrame result);
|
||||
|
||||
|
||||
|
|
@ -1144,13 +1145,15 @@ enum class DebuggerFrameType {
|
|||
Eval,
|
||||
Global,
|
||||
Call,
|
||||
Module
|
||||
Module,
|
||||
WasmCall
|
||||
};
|
||||
|
||||
enum class DebuggerFrameImplementation {
|
||||
Interpreter,
|
||||
Baseline,
|
||||
Ion
|
||||
Ion,
|
||||
Wasm
|
||||
};
|
||||
|
||||
class DebuggerFrame : public NativeObject
|
||||
|
|
@ -1166,7 +1169,7 @@ class DebuggerFrame : public NativeObject
|
|||
|
||||
static NativeObject* initClass(JSContext* cx, HandleObject dbgCtor, HandleObject objProto);
|
||||
static DebuggerFrame* create(JSContext* cx, HandleObject proto, AbstractFramePtr referent,
|
||||
const ScriptFrameIter* maybeIter, HandleNativeObject debugger);
|
||||
const FrameIter* maybeIter, HandleNativeObject debugger);
|
||||
|
||||
static MOZ_MUST_USE bool getCallee(JSContext* cx, HandleDebuggerFrame frame,
|
||||
MutableHandleDebuggerObject result);
|
||||
|
|
@ -1197,8 +1200,9 @@ class DebuggerFrame : public NativeObject
|
|||
static const JSFunctionSpec methods_[];
|
||||
|
||||
static AbstractFramePtr getReferent(HandleDebuggerFrame frame);
|
||||
static MOZ_MUST_USE bool getScriptFrameIter(JSContext* cx, HandleDebuggerFrame frame,
|
||||
mozilla::Maybe<ScriptFrameIter>& result);
|
||||
[[nodiscard]] static bool getFrameIter(JSContext* cx, HandleDebuggerFrame frame,
|
||||
mozilla::Maybe<FrameIter>& result);
|
||||
[[nodiscard]] static bool requireScriptReferent(JSContext* cx, HandleDebuggerFrame frame);
|
||||
|
||||
static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "vm/ProxyObject.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/Xdr.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
|
@ -626,6 +627,37 @@ ModuleEnvironmentObject::enumerate(JSContext* cx, HandleObject obj, AutoIdVector
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
const Class WasmFunctionCallObject::class_ = {
|
||||
"WasmCall",
|
||||
JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(WasmFunctionCallObject::RESERVED_SLOTS)
|
||||
};
|
||||
|
||||
/* static */ WasmFunctionCallObject*
|
||||
WasmFunctionCallObject::createHollowForDebug(JSContext* cx, WasmFunctionScope* scope)
|
||||
{
|
||||
RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
RootedShape shape(cx, scope->getEmptyEnvironmentShape(cx));
|
||||
if (!shape)
|
||||
return nullptr;
|
||||
|
||||
gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
|
||||
MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
|
||||
kind = gc::GetBackgroundAllocKind(kind);
|
||||
|
||||
JSObject* obj;
|
||||
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, NativeObject::create(cx, kind, gc::DefaultHeap, shape, group));
|
||||
|
||||
Rooted<WasmFunctionCallObject*> callobj(cx, &obj->as<WasmFunctionCallObject>());
|
||||
callobj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
|
||||
|
||||
return callobj;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
WithEnvironmentObject*
|
||||
WithEnvironmentObject::create(JSContext* cx, HandleObject object, HandleObject enclosing,
|
||||
Handle<WithScope*> scope)
|
||||
|
|
@ -1198,6 +1230,17 @@ EnvironmentIter::EnvironmentIter(JSContext* cx, AbstractFramePtr frame, jsbyteco
|
|||
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()
|
||||
{
|
||||
|
|
@ -1218,7 +1261,9 @@ 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>() &&
|
||||
|
|
@ -1235,9 +1280,11 @@ EnvironmentIter::settle()
|
|||
|
||||
// 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()) {
|
||||
|
|
@ -2222,6 +2269,7 @@ DebugEnvironmentProxy::isForDeclarative() const
|
|||
return e.is<CallObject>() ||
|
||||
e.is<VarEnvironmentObject>() ||
|
||||
e.is<ModuleEnvironmentObject>() ||
|
||||
e.is<WasmFunctionCallObject>() ||
|
||||
e.is<LexicalEnvironmentObject>();
|
||||
}
|
||||
|
||||
|
|
@ -2736,7 +2784,12 @@ DebugEnvironments::updateLiveEnvironments(JSContext* cx)
|
|||
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);
|
||||
|
|
@ -2860,6 +2913,7 @@ GetDebugEnvironmentForMissing(JSContext* cx, const EnvironmentIter& ei)
|
|||
MOZ_ASSERT(!ei.hasSyntacticEnvironment() &&
|
||||
(ei.scope().is<FunctionScope>() ||
|
||||
ei.scope().is<LexicalScope>() ||
|
||||
ei.scope().is<WasmFunctionScope>() ||
|
||||
ei.scope().is<VarScope>()));
|
||||
|
||||
if (DebugEnvironmentProxy* debugEnv = DebugEnvironments::hasDebugEnvironment(cx, ei))
|
||||
|
|
@ -2903,6 +2957,13 @@ GetDebugEnvironmentForMissing(JSContext* cx, const EnvironmentIter& ei)
|
|||
return nullptr;
|
||||
|
||||
debugEnv = DebugEnvironmentProxy::create(cx, *env, enclosingDebug);
|
||||
} else if (ei.scope().is<WasmFunctionScope>()) {
|
||||
Rooted<WasmFunctionScope*> wasmFunctionScope(cx, &ei.scope().as<WasmFunctionScope>());
|
||||
Rooted<WasmFunctionCallObject*> callobj(cx, WasmFunctionCallObject::createHollowForDebug(cx, wasmFunctionScope));
|
||||
if (!callobj)
|
||||
return nullptr;
|
||||
|
||||
debugEnv = DebugEnvironmentProxy::create(cx, *callobj, enclosingDebug);
|
||||
} else {
|
||||
Rooted<VarScope*> varScope(cx, &ei.scope().as<VarScope>());
|
||||
Rooted<VarEnvironmentObject*> env(cx,
|
||||
|
|
@ -2947,6 +3008,7 @@ GetDebugEnvironment(JSContext* cx, const EnvironmentIter& ei)
|
|||
|
||||
if (ei.scope().is<FunctionScope>() ||
|
||||
ei.scope().is<LexicalScope>() ||
|
||||
ei.scope().is<WasmFunctionScope>() ||
|
||||
ei.scope().is<VarScope>())
|
||||
{
|
||||
return GetDebugEnvironmentForMissing(cx, ei);
|
||||
|
|
@ -2977,7 +3039,12 @@ js::GetDebugEnvironmentForFrame(JSContext* cx, AbstractFramePtr frame, jsbytecod
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
@ -3099,7 +3166,12 @@ 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;
|
||||
|
|
@ -3172,8 +3244,6 @@ js::GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr fra
|
|||
|
||||
MOZ_CRASH("'this' binding must be found");
|
||||
}
|
||||
|
||||
RootedObject scopeChain(cx, frame.environmentChain());
|
||||
return GetNonSyntacticGlobalThis(cx, scopeChain, res);
|
||||
}
|
||||
|
||||
|
|
@ -3442,6 +3512,24 @@ js::PushVarEnvironmentObject(JSContext* cx, HandleScope scope, AbstractFramePtr
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -425,6 +425,17 @@ typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
|
|||
typedef Handle<ModuleEnvironmentObject*> HandleModuleEnvironmentObject;
|
||||
typedef MutableHandle<ModuleEnvironmentObject*> MutableHandleModuleEnvironmentObject;
|
||||
|
||||
class WasmFunctionCallObject : public EnvironmentObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
static const uint32_t RESERVED_SLOTS = 1;
|
||||
|
||||
static WasmFunctionCallObject* createHollowForDebug(JSContext* cx,
|
||||
WasmFunctionScope* scope);
|
||||
};
|
||||
|
||||
class LexicalEnvironmentObject : public EnvironmentObject
|
||||
{
|
||||
// Global and non-syntactic lexical environments need to store a 'this'
|
||||
|
|
@ -660,6 +671,12 @@ class MOZ_RAII EnvironmentIter
|
|||
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();
|
||||
}
|
||||
|
|
@ -984,6 +1001,7 @@ JSObject::is<js::EnvironmentObject>() const
|
|||
return is<js::CallObject>() ||
|
||||
is<js::VarEnvironmentObject>() ||
|
||||
is<js::ModuleEnvironmentObject>() ||
|
||||
is<js::WasmFunctionCallObject>() ||
|
||||
is<js::LexicalEnvironmentObject>() ||
|
||||
is<js::WithEnvironmentObject>() ||
|
||||
is<js::NonSyntacticVariablesObject>() ||
|
||||
|
|
@ -1114,6 +1132,10 @@ InitFunctionEnvironmentObjects(JSContext* cx, AbstractFramePtr frame);
|
|||
MOZ_MUST_USE bool
|
||||
PushVarEnvironmentObject(JSContext* cx, HandleScope scope, AbstractFramePtr frame);
|
||||
|
||||
[[nodiscard]] bool
|
||||
GetFrameEnvironmentAndScope(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
|
||||
MutableHandleObject env, MutableHandleScope scope);
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
AnalyzeEntrainedVariables(JSContext* cx, HandleScript script);
|
||||
|
|
|
|||
|
|
@ -896,6 +896,9 @@ PopEnvironment(JSContext* cx, EnvironmentIter& ei)
|
|||
case ScopeKind::NonSyntactic:
|
||||
case ScopeKind::Module:
|
||||
break;
|
||||
case ScopeKind::WasmFunction:
|
||||
MOZ_CRASH("wasm is not interpreted");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -156,8 +156,8 @@ struct SavedFrame::Lookup {
|
|||
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;
|
||||
|
|
@ -1323,7 +1323,7 @@ SavedStacks::insertFrames(JSContext* cx, FrameIter& iter, MutableHandleSavedFram
|
|||
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(),
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ js::ScopeKindString(ScopeKind kind)
|
|||
return "non-syntactic";
|
||||
case ScopeKind::Module:
|
||||
return "module";
|
||||
case ScopeKind::WasmFunction:
|
||||
return "wasm function";
|
||||
}
|
||||
MOZ_CRASH("Bad ScopeKind");
|
||||
}
|
||||
|
|
@ -398,6 +400,10 @@ Scope::clone(JSContext* cx, HandleScope scope, HandleScope enclosing)
|
|||
MOZ_CRASH("Use GlobalScope::clone.");
|
||||
break;
|
||||
|
||||
case ScopeKind::WasmFunction:
|
||||
MOZ_CRASH("wasm functions are not nested in JSScript");
|
||||
break;
|
||||
|
||||
case ScopeKind::Module:
|
||||
MOZ_CRASH("NYI");
|
||||
break;
|
||||
|
|
@ -485,6 +491,9 @@ LexicalScope::nextFrameSlot(Scope* scope)
|
|||
return 0;
|
||||
case ScopeKind::Module:
|
||||
return si.scope()->as<ModuleScope>().nextFrameSlot();
|
||||
case ScopeKind::WasmFunction:
|
||||
// TODO return si.scope()->as<WasmFunctionScope>().nextFrameSlot();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
MOZ_CRASH("Not an enclosing intra-frame Scope");
|
||||
|
|
@ -1191,6 +1200,48 @@ ModuleScope::script() const
|
|||
return module()->script();
|
||||
}
|
||||
|
||||
// TODO Check what Debugger behavior should be when it evaluates a
|
||||
// var declaration.
|
||||
static const uint32_t WasmFunctionEnvShapeFlags =
|
||||
BaseShape::NOT_EXTENSIBLE | BaseShape::DELEGATE;
|
||||
|
||||
/* static */ WasmFunctionScope*
|
||||
WasmFunctionScope::create(JSContext* cx, WasmInstanceObject* instance, uint32_t funcIndex)
|
||||
{
|
||||
// WasmFunctionScope::Data has GCManagedDeletePolicy because it contains a
|
||||
// GCPtr. Destruction of |data| below may trigger calls into the GC.
|
||||
Rooted<WasmFunctionScope*> wasmFunctionScope(cx);
|
||||
|
||||
{
|
||||
// TODO pull the local variable names from the wasm function definition.
|
||||
|
||||
Rooted<UniquePtr<Data>> data(cx, NewEmptyScopeData<WasmFunctionScope>(cx));
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
Rooted<Scope*> enclosingScope(cx, &cx->global()->emptyGlobalScope());
|
||||
|
||||
data->instance.init(instance);
|
||||
data->funcIndex = funcIndex;
|
||||
|
||||
Scope* scope = Scope::create(cx, ScopeKind::WasmFunction, enclosingScope, /* envShape = */ nullptr);
|
||||
if (!scope)
|
||||
return nullptr;
|
||||
|
||||
wasmFunctionScope = &scope->as<WasmFunctionScope>();
|
||||
wasmFunctionScope->initData(Move(data.get()));
|
||||
}
|
||||
|
||||
return wasmFunctionScope;
|
||||
}
|
||||
|
||||
/* static */ Shape*
|
||||
WasmFunctionScope::getEmptyEnvironmentShape(JSContext* cx)
|
||||
{
|
||||
const Class* cls = &WasmFunctionCallObject::class_;
|
||||
return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), WasmFunctionEnvShapeFlags);
|
||||
}
|
||||
|
||||
ScopeIter::ScopeIter(JSScript* script)
|
||||
: scope_(script->bodyScope())
|
||||
{ }
|
||||
|
|
@ -1242,6 +1293,9 @@ BindingIter::BindingIter(Scope* scope)
|
|||
case ScopeKind::Module:
|
||||
init(scope->as<ModuleScope>().data());
|
||||
break;
|
||||
case ScopeKind::WasmFunction:
|
||||
init(scope->as<WasmFunctionScope>().data());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1372,6 +1426,22 @@ BindingIter::init(ModuleScope::Data& data)
|
|||
data.trailingNames.start(), data.length);
|
||||
}
|
||||
|
||||
void
|
||||
BindingIter::init(WasmFunctionScope::Data& data)
|
||||
{
|
||||
// imports - [0, 0)
|
||||
// positional formals - [0, 0)
|
||||
// other formals - [0, 0)
|
||||
// top-level funcs - [0, 0)
|
||||
// vars - [0, 0)
|
||||
// lets - [0, 0)
|
||||
// consts - [0, 0)
|
||||
init(0, 0, 0, 0, 0, 0,
|
||||
CanHaveFrameSlots | CanHaveEnvironmentSlots,
|
||||
UINT32_MAX, UINT32_MAX,
|
||||
data.trailingNames.start(), data.length);
|
||||
}
|
||||
|
||||
PositionalFormalParameterIter::PositionalFormalParameterIter(JSScript* script)
|
||||
: BindingIter(script)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -71,7 +71,10 @@ enum class ScopeKind : uint8_t
|
|||
NonSyntactic,
|
||||
|
||||
// ModuleScope
|
||||
Module
|
||||
Module,
|
||||
|
||||
// WasmFunctionScope
|
||||
WasmFunction
|
||||
};
|
||||
|
||||
static inline bool
|
||||
|
|
@ -963,6 +966,60 @@ class ModuleScope : public Scope
|
|||
static Shape* getEmptyEnvironmentShape(ExclusiveContext* cx);
|
||||
};
|
||||
|
||||
// Scope corresponding to the wasm function. A WasmFunctionScope is used by
|
||||
// Debugger only, and not for wasm execution.
|
||||
//
|
||||
class WasmFunctionScope : public Scope
|
||||
{
|
||||
friend class BindingIter;
|
||||
friend class Scope;
|
||||
static const ScopeKind classScopeKind_ = ScopeKind::WasmFunction;
|
||||
|
||||
public:
|
||||
struct Data
|
||||
{
|
||||
uint32_t length= 0;
|
||||
uint32_t nextFrameSlot= 0;
|
||||
uint32_t funcIndex= 0;
|
||||
|
||||
// The wasm instance of the scope.
|
||||
GCPtr<WasmInstanceObject*> instance = {};
|
||||
|
||||
TrailingNamesArray trailingNames;
|
||||
|
||||
explicit Data(size_t nameCount) : trailingNames(nameCount) {}
|
||||
Data() = delete;
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
};
|
||||
|
||||
static WasmFunctionScope* create(JSContext* cx, WasmInstanceObject* instance, uint32_t funcIndex);
|
||||
|
||||
static size_t sizeOfData(uint32_t length) {
|
||||
return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
|
||||
}
|
||||
|
||||
private:
|
||||
Data& data() {
|
||||
return *reinterpret_cast<Data*>(data_);
|
||||
}
|
||||
|
||||
const Data& data() const {
|
||||
return *reinterpret_cast<Data*>(data_);
|
||||
}
|
||||
|
||||
public:
|
||||
WasmInstanceObject* instance() const {
|
||||
return data().instance;
|
||||
}
|
||||
|
||||
uint32_t funcIndex() const {
|
||||
return data().funcIndex;
|
||||
}
|
||||
|
||||
static Shape* getEmptyEnvironmentShape(JSContext* cx);
|
||||
};
|
||||
|
||||
//
|
||||
// An iterator for a Scope's bindings. This is the source of truth for frame
|
||||
// and environment object layout.
|
||||
|
|
@ -1072,6 +1129,7 @@ class BindingIter
|
|||
void init(GlobalScope::Data& data);
|
||||
void init(EvalScope::Data& data, bool strict);
|
||||
void init(ModuleScope::Data& data);
|
||||
void init(WasmFunctionScope::Data& data);
|
||||
|
||||
bool hasFormalParameterExprs() const {
|
||||
return flags_ & HasFormalParameterExprs;
|
||||
|
|
@ -1144,6 +1202,10 @@ class BindingIter
|
|||
init(data);
|
||||
}
|
||||
|
||||
explicit BindingIter(WasmFunctionScope::Data& data) {
|
||||
init(data);
|
||||
}
|
||||
|
||||
BindingIter(EvalScope::Data& data, bool strict) {
|
||||
init(data, strict);
|
||||
}
|
||||
|
|
@ -1448,6 +1510,7 @@ DEFINE_SCOPE_DATA_GCPOLICY(js::VarScope::Data);
|
|||
DEFINE_SCOPE_DATA_GCPOLICY(js::GlobalScope::Data);
|
||||
DEFINE_SCOPE_DATA_GCPOLICY(js::EvalScope::Data);
|
||||
DEFINE_SCOPE_DATA_GCPOLICY(js::ModuleScope::Data);
|
||||
DEFINE_SCOPE_DATA_GCPOLICY(js::WasmFunctionScope::Data);
|
||||
|
||||
#undef DEFINE_SCOPE_DATA_GCPOLICY
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
#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"
|
||||
|
|
@ -429,6 +431,8 @@ AbstractFramePtr::returnValue() const
|
|||
{
|
||||
if (isInterpreterFrame())
|
||||
return asInterpreterFrame()->returnValue();
|
||||
if (isWasmDebugFrame())
|
||||
return UndefinedHandleValue;
|
||||
return asBaselineFrame()->returnValue();
|
||||
}
|
||||
|
||||
|
|
@ -443,6 +447,12 @@ AbstractFramePtr::setReturnValue(const Value& rval) const
|
|||
asBaselineFrame()->setReturnValue(rval);
|
||||
return;
|
||||
}
|
||||
if (isWasmDebugFrame()) {
|
||||
// TODO handle wasm function return value
|
||||
// The function is called from Debugger::slowPathOnLeaveFrame --
|
||||
// ignoring value for wasm.
|
||||
return;
|
||||
}
|
||||
asRematerializedFrame()->setReturnValue(rval);
|
||||
}
|
||||
|
||||
|
|
@ -453,6 +463,8 @@ AbstractFramePtr::environmentChain() const
|
|||
return asInterpreterFrame()->environmentChain();
|
||||
if (isBaselineFrame())
|
||||
return asBaselineFrame()->environmentChain();
|
||||
if (isWasmDebugFrame())
|
||||
return asWasmDebugFrame()->environmentChain();
|
||||
return asRematerializedFrame()->environmentChain();
|
||||
}
|
||||
|
||||
|
|
@ -589,6 +601,8 @@ AbstractFramePtr::isGlobalFrame() const
|
|||
return asInterpreterFrame()->isGlobalFrame();
|
||||
if (isBaselineFrame())
|
||||
return asBaselineFrame()->isGlobalFrame();
|
||||
if (isWasmDebugFrame())
|
||||
return false;
|
||||
return asRematerializedFrame()->isGlobalFrame();
|
||||
}
|
||||
|
||||
|
|
@ -599,6 +613,8 @@ AbstractFramePtr::isModuleFrame() const
|
|||
return asInterpreterFrame()->isModuleFrame();
|
||||
if (isBaselineFrame())
|
||||
return asBaselineFrame()->isModuleFrame();
|
||||
if (isWasmDebugFrame())
|
||||
return false;
|
||||
return asRematerializedFrame()->isModuleFrame();
|
||||
}
|
||||
|
||||
|
|
@ -609,6 +625,8 @@ AbstractFramePtr::isEvalFrame() const
|
|||
return asInterpreterFrame()->isEvalFrame();
|
||||
if (isBaselineFrame())
|
||||
return asBaselineFrame()->isEvalFrame();
|
||||
if (isWasmDebugFrame())
|
||||
return false;
|
||||
MOZ_ASSERT(isRematerializedFrame());
|
||||
return false;
|
||||
}
|
||||
|
|
@ -631,6 +649,8 @@ AbstractFramePtr::hasCachedSavedFrame() const
|
|||
return asInterpreterFrame()->hasCachedSavedFrame();
|
||||
if (isBaselineFrame())
|
||||
return asBaselineFrame()->hasCachedSavedFrame();
|
||||
if (isWasmDebugFrame())
|
||||
return asWasmDebugFrame()->hasCachedSavedFrame();
|
||||
return asRematerializedFrame()->hasCachedSavedFrame();
|
||||
}
|
||||
|
||||
|
|
@ -641,6 +661,8 @@ AbstractFramePtr::setHasCachedSavedFrame()
|
|||
asInterpreterFrame()->setHasCachedSavedFrame();
|
||||
else if (isBaselineFrame())
|
||||
asBaselineFrame()->setHasCachedSavedFrame();
|
||||
else if (isWasmDebugFrame())
|
||||
asWasmDebugFrame()->setHasCachedSavedFrame();
|
||||
else
|
||||
asRematerializedFrame()->setHasCachedSavedFrame();
|
||||
}
|
||||
|
|
@ -652,6 +674,8 @@ AbstractFramePtr::isDebuggee() const
|
|||
return asInterpreterFrame()->isDebuggee();
|
||||
if (isBaselineFrame())
|
||||
return asBaselineFrame()->isDebuggee();
|
||||
if (isWasmDebugFrame())
|
||||
return asWasmDebugFrame()->isDebuggee();
|
||||
return asRematerializedFrame()->isDebuggee();
|
||||
}
|
||||
|
||||
|
|
@ -662,6 +686,8 @@ AbstractFramePtr::setIsDebuggee()
|
|||
asInterpreterFrame()->setIsDebuggee();
|
||||
else if (isBaselineFrame())
|
||||
asBaselineFrame()->setIsDebuggee();
|
||||
else if (isWasmDebugFrame())
|
||||
asWasmDebugFrame()->setIsDebuggee();
|
||||
else
|
||||
asRematerializedFrame()->setIsDebuggee();
|
||||
}
|
||||
|
|
@ -673,6 +699,8 @@ AbstractFramePtr::unsetIsDebuggee()
|
|||
asInterpreterFrame()->unsetIsDebuggee();
|
||||
else if (isBaselineFrame())
|
||||
asBaselineFrame()->unsetIsDebuggee();
|
||||
else if (isWasmDebugFrame())
|
||||
asWasmDebugFrame()->unsetIsDebuggee();
|
||||
else
|
||||
asRematerializedFrame()->unsetIsDebuggee();
|
||||
}
|
||||
|
|
@ -690,10 +718,17 @@ AbstractFramePtr::isConstructing() const
|
|||
}
|
||||
|
||||
inline bool
|
||||
AbstractFramePtr::hasArgs() const {
|
||||
AbstractFramePtr::hasArgs() const
|
||||
{
|
||||
return isFunctionFrame();
|
||||
}
|
||||
|
||||
inline bool
|
||||
AbstractFramePtr::hasScript() const
|
||||
{
|
||||
return !isWasmDebugFrame();
|
||||
}
|
||||
|
||||
inline JSScript*
|
||||
AbstractFramePtr::script() const
|
||||
{
|
||||
|
|
@ -704,6 +739,21 @@ AbstractFramePtr::script() const
|
|||
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
|
||||
{
|
||||
|
|
@ -731,6 +781,8 @@ AbstractFramePtr::isFunctionFrame() const
|
|||
return asInterpreterFrame()->isFunctionFrame();
|
||||
if (isBaselineFrame())
|
||||
return asBaselineFrame()->isFunctionFrame();
|
||||
if (isWasmDebugFrame())
|
||||
return false;
|
||||
return asRematerializedFrame()->isFunctionFrame();
|
||||
}
|
||||
|
||||
|
|
@ -803,6 +855,8 @@ AbstractFramePtr::prevUpToDate() const
|
|||
return asInterpreterFrame()->prevUpToDate();
|
||||
if (isBaselineFrame())
|
||||
return asBaselineFrame()->prevUpToDate();
|
||||
if (isWasmDebugFrame())
|
||||
return asWasmDebugFrame()->prevUpToDate();
|
||||
return asRematerializedFrame()->prevUpToDate();
|
||||
}
|
||||
|
||||
|
|
@ -817,6 +871,10 @@ AbstractFramePtr::setPrevUpToDate() const
|
|||
asBaselineFrame()->setPrevUpToDate();
|
||||
return;
|
||||
}
|
||||
if (isWasmDebugFrame()) {
|
||||
asWasmDebugFrame()->setPrevUpToDate();
|
||||
return;
|
||||
}
|
||||
asRematerializedFrame()->setPrevUpToDate();
|
||||
}
|
||||
|
||||
|
|
@ -831,6 +889,10 @@ AbstractFramePtr::unsetPrevUpToDate() const
|
|||
asBaselineFrame()->unsetPrevUpToDate();
|
||||
return;
|
||||
}
|
||||
if (isWasmDebugFrame()) {
|
||||
asWasmDebugFrame()->unsetPrevUpToDate();
|
||||
return;
|
||||
}
|
||||
asRematerializedFrame()->unsetPrevUpToDate();
|
||||
}
|
||||
|
||||
|
|
@ -857,6 +919,8 @@ AbstractFramePtr::newTarget() const
|
|||
inline bool
|
||||
AbstractFramePtr::debuggerNeedsCheckPrimitiveReturn() const
|
||||
{
|
||||
if (isWasmDebugFrame())
|
||||
return false;
|
||||
return script()->isDerivedClassConstructor();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#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"
|
||||
|
|
@ -155,6 +156,10 @@ AssertScopeMatchesEnvironment(Scope* scope, JSObject* originalEnv)
|
|||
si.scope()->as<ModuleScope>().module());
|
||||
env = &env->as<ModuleEnvironmentObject>().enclosingEnvironment();
|
||||
break;
|
||||
|
||||
case ScopeKind::WasmFunction:
|
||||
env = &env->as<WasmFunctionCallObject>().enclosingEnvironment();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -551,7 +556,7 @@ FrameIter::settleOnActivation()
|
|||
continue;
|
||||
}
|
||||
|
||||
data_.pc_ = (jsbytecode*)data_.wasmFrames_.pc();
|
||||
data_.pc_ = nullptr;
|
||||
data_.state_ = WASM;
|
||||
return;
|
||||
}
|
||||
|
|
@ -684,7 +689,7 @@ FrameIter::popWasmFrame()
|
|||
MOZ_ASSERT(data_.state_ == WASM);
|
||||
|
||||
++data_.wasmFrames_;
|
||||
data_.pc_ = (jsbytecode*)data_.wasmFrames_.pc();
|
||||
data_.pc_ = nullptr;
|
||||
if (data_.wasmFrames_.done())
|
||||
popActivation();
|
||||
}
|
||||
|
|
@ -732,7 +737,6 @@ FrameIter::copyData() const
|
|||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(data_.state_ != WASM);
|
||||
if (data && data_.jitFrames_.isIonScripted())
|
||||
data->ionInlineFrameNo_ = ionInlineFrames_.frameNo();
|
||||
return data;
|
||||
|
|
@ -758,7 +762,7 @@ FrameIter::rawFramePtr() const
|
|||
case INTERP:
|
||||
return interpFrame();
|
||||
case WASM:
|
||||
return data_.wasmFrames_.fp();
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_CRASH("Unexpected state");
|
||||
}
|
||||
|
|
@ -810,7 +814,7 @@ FrameIter::isFunctionFrame() const
|
|||
return data_.jitFrames_.baselineFrame()->isFunctionFrame();
|
||||
return script()->functionNonDelazifying();
|
||||
case WASM:
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
MOZ_CRASH("Unexpected state");
|
||||
}
|
||||
|
|
@ -818,15 +822,15 @@ FrameIter::isFunctionFrame() const
|
|||
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();
|
||||
}
|
||||
|
||||
|
|
@ -945,7 +949,6 @@ FrameIter::hasUsableAbstractFramePtr() const
|
|||
{
|
||||
switch (data_.state_) {
|
||||
case DONE:
|
||||
case WASM:
|
||||
return false;
|
||||
case JIT:
|
||||
if (data_.jitFrames_.isBaselineJS())
|
||||
|
|
@ -957,6 +960,8 @@ FrameIter::hasUsableAbstractFramePtr() const
|
|||
break;
|
||||
case INTERP:
|
||||
return true;
|
||||
case WASM:
|
||||
return data_.wasmFrames_.debugEnabled();
|
||||
}
|
||||
MOZ_CRASH("Unexpected state");
|
||||
}
|
||||
|
|
@ -967,7 +972,6 @@ FrameIter::abstractFramePtr() const
|
|||
MOZ_ASSERT(hasUsableAbstractFramePtr());
|
||||
switch (data_.state_) {
|
||||
case DONE:
|
||||
case WASM:
|
||||
break;
|
||||
case JIT: {
|
||||
if (data_.jitFrames_.isBaselineJS())
|
||||
|
|
@ -981,6 +985,9 @@ FrameIter::abstractFramePtr() const
|
|||
case INTERP:
|
||||
MOZ_ASSERT(interpFrame());
|
||||
return AbstractFramePtr(interpFrame());
|
||||
case WASM:
|
||||
MOZ_ASSERT(data_.wasmFrames_.debugEnabled());
|
||||
return data_.wasmFrames_.debugFrame();
|
||||
}
|
||||
MOZ_CRASH("Unexpected state");
|
||||
}
|
||||
|
|
@ -990,6 +997,7 @@ FrameIter::updatePcQuadratic()
|
|||
{
|
||||
switch (data_.state_) {
|
||||
case DONE:
|
||||
case WASM:
|
||||
break;
|
||||
case INTERP: {
|
||||
InterpreterFrame* frame = interpFrame();
|
||||
|
|
@ -1027,10 +1035,6 @@ FrameIter::updatePcQuadratic()
|
|||
return;
|
||||
}
|
||||
break;
|
||||
case WASM:
|
||||
// Update the pc.
|
||||
data_.pc_ = (jsbytecode*)data_.wasmFrames_.pc();
|
||||
break;
|
||||
}
|
||||
MOZ_CRASH("Unexpected state");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ namespace jit {
|
|||
class CommonFrameLayout;
|
||||
}
|
||||
namespace wasm {
|
||||
class DebugFrame;
|
||||
class Instance;
|
||||
}
|
||||
|
||||
|
|
@ -134,7 +135,8 @@ class AbstractFramePtr
|
|||
Tag_InterpreterFrame = 0x1,
|
||||
Tag_BaselineFrame = 0x2,
|
||||
Tag_RematerializedFrame = 0x3,
|
||||
TagMask = 0x3
|
||||
Tag_WasmDebugFrame = 0x4,
|
||||
TagMask = 0x7
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
@ -160,6 +162,12 @@ class AbstractFramePtr
|
|||
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);
|
||||
|
|
@ -196,6 +204,15 @@ class AbstractFramePtr
|
|||
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_); }
|
||||
|
||||
|
|
@ -223,7 +240,10 @@ class AbstractFramePtr
|
|||
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;
|
||||
|
|
@ -268,6 +288,7 @@ class AbstractFramePtr
|
|||
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
|
||||
|
|
@ -1808,6 +1829,11 @@ class FrameIter
|
|||
|
||||
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()
|
||||
// -----------------------------------------------------------
|
||||
|
|
@ -1869,7 +1895,7 @@ class FrameIter
|
|||
|
||||
// -----------------------------------------------------------
|
||||
// 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().
|
||||
// -----------------------------------------------------------
|
||||
|
||||
|
|
@ -2054,6 +2080,14 @@ FrameIter::script() const
|
|||
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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@
|
|||
#endif
|
||||
|
||||
#include "wasm/WasmBinaryIterator.h"
|
||||
#include "wasm/WasmDebugFrame.h"
|
||||
#include "wasm/WasmGenerator.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
#include "wasm/WasmValidate.h"
|
||||
|
|
@ -507,6 +508,7 @@ class BaseCompiler
|
|||
int32_t varHigh_; // High byte offset + 1 of local area for true locals
|
||||
int32_t maxFramePushed_; // Max value of masm.framePushed() observed
|
||||
bool deadCode_; // Flag indicating we should decode & discard the opcode
|
||||
bool debugEnabled_;
|
||||
ValTypeVector SigI64I64_;
|
||||
ValTypeVector SigDD_;
|
||||
ValTypeVector SigD_;
|
||||
|
|
@ -570,7 +572,10 @@ class BaseCompiler
|
|||
Decoder& decoder,
|
||||
const FuncBytes& func,
|
||||
const ValTypeVector& locals,
|
||||
FuncCompileResults& compileResults);
|
||||
bool debugEnabled,
|
||||
TempAllocator* alloc,
|
||||
MacroAssembler* masm);
|
||||
|
||||
|
||||
MOZ_MUST_USE bool init();
|
||||
|
||||
|
|
@ -2059,6 +2064,11 @@ class BaseCompiler
|
|||
//
|
||||
// Labels
|
||||
|
||||
void insertBreakablePoint(CallSiteDesc::Kind kind) {
|
||||
const uint32_t offset = iter_.currentOffset();
|
||||
masm.nopPatchableToCall(CallSiteDesc(offset, kind));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Function prologue and epilogue.
|
||||
|
|
@ -2115,6 +2125,14 @@ class BaseCompiler
|
|||
// 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.
|
||||
//
|
||||
|
|
@ -2136,8 +2154,59 @@ class BaseCompiler
|
|||
for (int32_t i = varLow_ ; i < varHigh_ ; i += 4)
|
||||
storeToFrameI32(scratch, i + 4);
|
||||
}
|
||||
if (debugEnabled_)
|
||||
insertBreakablePoint(CallSiteDesc::EnterFrame);
|
||||
}
|
||||
|
||||
void saveResult() {
|
||||
MOZ_ASSERT(debugEnabled_);
|
||||
size_t debugFrameOffset = masm.framePushed() - DebugFrame::offsetOfFrame();
|
||||
Address resultsAddress(StackPointer, debugFrameOffset + DebugFrame::offsetOfResults());
|
||||
switch (func_.sig().ret()) {
|
||||
case ExprType::Void:
|
||||
break;
|
||||
case ExprType::I32:
|
||||
masm.store32(RegI32(ReturnReg), resultsAddress);
|
||||
break;
|
||||
|
||||
case ExprType::I64:
|
||||
masm.store64(RegI64(ReturnReg64), resultsAddress);
|
||||
break;
|
||||
case ExprType::F64:
|
||||
masm.storeDouble(RegF64(ReturnDoubleReg), resultsAddress);
|
||||
break;
|
||||
case ExprType::F32:
|
||||
masm.storeFloat32(RegF32(ReturnFloat32Reg), resultsAddress);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Function return type");
|
||||
}
|
||||
}
|
||||
|
||||
void restoreResult() {
|
||||
MOZ_ASSERT(debugEnabled_);
|
||||
size_t debugFrameOffset = masm.framePushed() - DebugFrame::offsetOfFrame();
|
||||
Address resultsAddress(StackPointer, debugFrameOffset + DebugFrame::offsetOfResults());
|
||||
switch (func_.sig().ret()) {
|
||||
case ExprType::Void:
|
||||
break;
|
||||
case ExprType::I32:
|
||||
masm.load32(resultsAddress, RegI32(ReturnReg));
|
||||
break;
|
||||
case ExprType::I64:
|
||||
masm.load64(resultsAddress, RegI64(ReturnReg64));
|
||||
break;
|
||||
case ExprType::F64:
|
||||
masm.loadDouble(resultsAddress, RegF64(ReturnDoubleReg));
|
||||
break;
|
||||
case ExprType::F32:
|
||||
masm.loadFloat32(resultsAddress, RegF32(ReturnFloat32Reg));
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Function return type");
|
||||
}
|
||||
}
|
||||
|
||||
bool endFunction() {
|
||||
// Out-of-line prologue. Assumes that the in-line prologue has
|
||||
// been executed and that a frame of size = localSize_ + sizeof(Frame)
|
||||
|
|
@ -2166,6 +2235,14 @@ class BaseCompiler
|
|||
|
||||
masm.bind(&returnLabel_);
|
||||
|
||||
if (debugEnabled_) {
|
||||
// Store and reload the return value from DebugFrame::return so that
|
||||
// it can be clobbered, and/or modified by the debug trap.
|
||||
saveResult();
|
||||
insertBreakablePoint(CallSiteDesc::LeaveFrame);
|
||||
restoreResult();
|
||||
}
|
||||
|
||||
// Restore the TLS register in case it was overwritten by the function.
|
||||
loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
|
||||
|
||||
|
|
@ -8186,6 +8263,7 @@ BaseCompiler::BaseCompiler(const ModuleEnvironment& env,
|
|||
Decoder& decoder,
|
||||
const FuncBytes& func,
|
||||
const ValTypeVector& locals,
|
||||
bool debugEnabled,
|
||||
TempAllocator* alloc,
|
||||
MacroAssembler* masm)
|
||||
: env_(env),
|
||||
|
|
@ -8199,6 +8277,7 @@ BaseCompiler::BaseCompiler(const ModuleEnvironment& env,
|
|||
varHigh_(0),
|
||||
maxFramePushed_(0),
|
||||
deadCode_(false),
|
||||
debugEnabled_(debugEnabled),
|
||||
prologueTrapOffset_(trapOffset()),
|
||||
compileResults_(compileResults),
|
||||
masm(compileResults_.masm()),
|
||||
|
|
@ -8276,6 +8355,18 @@ BaseCompiler::init()
|
|||
|
||||
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()) {
|
||||
|
|
@ -8308,10 +8399,6 @@ BaseCompiler::init()
|
|||
}
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
|
|
@ -8425,7 +8512,7 @@ js::wasm::BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit, Uniq
|
|||
|
||||
// One-pass baseline compilation.
|
||||
|
||||
BaseCompiler f(task->env(), d, func, locals, &task->alloc(), &task->masm());
|
||||
BaseCompiler f(task->env(), d, func, locals, task->debugEnabled(), &task->alloc(), &task->masm());
|
||||
if (!f.init())
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -364,7 +364,8 @@ CodeRange::CodeRange(Kind kind, Offsets offsets)
|
|||
kind_(kind)
|
||||
{
|
||||
MOZ_ASSERT(begin_ <= end_);
|
||||
MOZ_ASSERT(kind_ == Entry || kind_ == Inline || kind_ == FarJumpIsland);
|
||||
MOZ_ASSERT(kind_ == Entry || kind_ == Inline ||
|
||||
kind_ == FarJumpIsland || kind_ == DebugTrap);
|
||||
}
|
||||
|
||||
CodeRange::CodeRange(Kind kind, ProfilingOffsets offsets)
|
||||
|
|
@ -476,6 +477,7 @@ Metadata::serializedSize() const
|
|||
uint8_t*
|
||||
Metadata::serialize(uint8_t* cursor) const
|
||||
{
|
||||
MOZ_ASSERT(!debugEnabled && debugTrapFarJumpOffsets.empty());
|
||||
cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
|
||||
cursor = SerializeVector(cursor, funcImports);
|
||||
cursor = SerializeVector(cursor, funcExports);
|
||||
|
|
@ -512,6 +514,8 @@ Metadata::deserialize(const uint8_t* cursor)
|
|||
(cursor = DeserializePodVector(cursor, &funcNames)) &&
|
||||
(cursor = DeserializePodVector(cursor, &customSections)) &&
|
||||
(cursor = filename.deserialize(cursor));
|
||||
debugEnabled = false;
|
||||
debugTrapFarJumpOffsets.clear();
|
||||
return cursor;
|
||||
}
|
||||
|
||||
|
|
@ -608,8 +612,11 @@ Code::Code(UniqueCodeSegment segment,
|
|||
: segment_(Move(segment)),
|
||||
metadata_(&metadata),
|
||||
maybeBytecode_(maybeBytecode),
|
||||
enterAndLeaveFrameTrapsCounter_(0),
|
||||
profilingEnabled_(false)
|
||||
{}
|
||||
{
|
||||
MOZ_ASSERT_IF(metadata_->debugEnabled, maybeBytecode);
|
||||
}
|
||||
|
||||
struct CallSiteRetAddrOffset
|
||||
{
|
||||
|
|
@ -821,6 +828,52 @@ Code::ensureProfilingState(JSContext* cx, bool newProfilingEnabled)
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Code::toggleDebugTrap(uint32_t offset, bool enabled)
|
||||
{
|
||||
MOZ_ASSERT(offset);
|
||||
uint8_t* trap = segment_->base() + offset;
|
||||
const Uint32Vector& farJumpOffsets = metadata_->debugTrapFarJumpOffsets;
|
||||
if (enabled) {
|
||||
MOZ_ASSERT(farJumpOffsets.length() > 0);
|
||||
size_t i = 0;
|
||||
while (i < farJumpOffsets.length() && offset < farJumpOffsets[i])
|
||||
i++;
|
||||
if (i >= farJumpOffsets.length() ||
|
||||
(i > 0 && offset - farJumpOffsets[i - 1] < farJumpOffsets[i] - offset))
|
||||
i--;
|
||||
uint8_t* farJump = segment_->base() + farJumpOffsets[i];
|
||||
MacroAssembler::patchNopToCall(trap, farJump);
|
||||
} else {
|
||||
MacroAssembler::patchCallToNop(trap);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Code::adjustEnterAndLeaveFrameTrapsState(JSContext* cx, bool enabled)
|
||||
{
|
||||
MOZ_ASSERT(metadata_->debugEnabled);
|
||||
MOZ_ASSERT_IF(!enabled, enterAndLeaveFrameTrapsCounter_ > 0);
|
||||
|
||||
bool wasEnabled = enterAndLeaveFrameTrapsCounter_ > 0;
|
||||
if (enabled)
|
||||
++enterAndLeaveFrameTrapsCounter_;
|
||||
else
|
||||
--enterAndLeaveFrameTrapsCounter_;
|
||||
bool stillEnabled = enterAndLeaveFrameTrapsCounter_ > 0;
|
||||
if (wasEnabled == stillEnabled)
|
||||
return;
|
||||
|
||||
AutoWritableJitCode awjc(cx->runtime(), segment_->base(), segment_->codeLength());
|
||||
AutoFlushICache afc("Code::adjustEnterAndLeaveFrameTrapsState");
|
||||
AutoFlushICache::setRange(uintptr_t(segment_->base()), segment_->codeLength());
|
||||
for (const CallSite& callSite : metadata_->callSites) {
|
||||
if (callSite.kind() != CallSite::EnterFrame && callSite.kind() != CallSite::LeaveFrame)
|
||||
continue;
|
||||
toggleDebugTrap(callSite.returnAddressOffset(), stillEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Code::addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
||||
Metadata::SeenSet* seenMetadata,
|
||||
|
|
|
|||
|
|
@ -19,16 +19,20 @@
|
|||
#define wasm_code_h
|
||||
|
||||
#include "wasm/WasmGeneratedSourceMap.h"
|
||||
|
||||
#include "js/HashTable.h"
|
||||
#include "wasm/WasmTypes.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
struct AsmJSMetadata;
|
||||
class WasmActivation;
|
||||
|
||||
namespace wasm {
|
||||
|
||||
struct LinkData;
|
||||
struct Metadata;
|
||||
class FrameIterator;
|
||||
|
||||
// A wasm CodeSegment owns the allocated executable code for a wasm module.
|
||||
// This allocation also currently includes the global data segment, which allows
|
||||
|
|
@ -240,6 +244,8 @@ class CodeRange
|
|||
ImportJitExit, // fast-path calling from wasm into JIT code
|
||||
ImportInterpExit, // slow-path calling from wasm into C++ interp
|
||||
TrapExit, // calls C++ to report and jumps to throw stub
|
||||
DebugTrap, // calls C++ to handle debug event such as
|
||||
// enter/leave frame or breakpoint
|
||||
FarJumpIsland, // inserted to connect otherwise out-of-range insns
|
||||
Inline // stub that is jumped-to, not called, and thus
|
||||
// replaces/loses preceding innermost frame
|
||||
|
|
@ -468,6 +474,10 @@ struct Metadata : ShareableBase<Metadata>, MetadataCacheablePod
|
|||
CustomSectionVector customSections;
|
||||
CacheableChars filename;
|
||||
|
||||
// Debug-enabled code is not serialized.
|
||||
bool debugEnabled;
|
||||
Uint32Vector debugTrapFarJumpOffsets;
|
||||
|
||||
bool usesMemory() const { return UsesMemory(memoryUsage); }
|
||||
bool hasSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
|
||||
|
||||
|
|
@ -515,8 +525,11 @@ class Code
|
|||
const SharedBytes maybeBytecode_;
|
||||
UniqueGeneratedSourceMap maybeSourceMap_;
|
||||
CacheableCharsVector funcLabels_;
|
||||
uint32_t enterAndLeaveFrameTrapsCounter_;
|
||||
bool profilingEnabled_;
|
||||
|
||||
void toggleDebugTrap(uint32_t offset, bool enabled);
|
||||
|
||||
public:
|
||||
Code(UniqueCodeSegment segment,
|
||||
const Metadata& metadata,
|
||||
|
|
@ -555,6 +568,12 @@ class Code
|
|||
bool profilingEnabled() const { return profilingEnabled_; }
|
||||
const char* profilingLabel(uint32_t funcIndex) const { return funcLabels_[funcIndex].get(); }
|
||||
|
||||
// The Code can track enter/leave frame events. Any such event triggers
|
||||
// debug trap. The enter frame events enabled across all functions, but
|
||||
// the leave frame events only for particular function.
|
||||
|
||||
void adjustEnterAndLeaveFrameTrapsState(JSContext* cx, bool enabled);
|
||||
|
||||
// about:memory reporting:
|
||||
|
||||
void addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
||||
|
|
|
|||
|
|
@ -95,6 +95,13 @@ bool
|
|||
CompileArgs::initFromContext(ExclusiveContext* cx, ScriptedCaller&& scriptedCaller)
|
||||
{
|
||||
alwaysBaseline = cx->options().wasmAlwaysBaseline();
|
||||
|
||||
// Debug information such as source view or debug traps will require
|
||||
// additional memory and permanently stay in baseline code, so we try to
|
||||
// only enable it when a developer actually cares: when the debugger tab
|
||||
// is open.
|
||||
debugEnabled = cx->compartment()->debuggerObservesAsmJS();
|
||||
|
||||
this->scriptedCaller = Move(scriptedCaller);
|
||||
return assumptions.initBuildIdFromContext(cx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,11 +39,13 @@ struct CompileArgs
|
|||
Assumptions assumptions;
|
||||
ScriptedCaller scriptedCaller;
|
||||
bool alwaysBaseline;
|
||||
bool debugEnabled;
|
||||
|
||||
CompileArgs(Assumptions&& assumptions, ScriptedCaller&& scriptedCaller)
|
||||
: assumptions(Move(assumptions)),
|
||||
scriptedCaller(Move(scriptedCaller)),
|
||||
alwaysBaseline(false)
|
||||
alwaysBaseline(false),
|
||||
debugEnabled(false)
|
||||
{}
|
||||
|
||||
// If CompileArgs is constructed without arguments, initFromContext() must
|
||||
|
|
|
|||
65
js/src/wasm/WasmDebugFrame.cpp
Normal file
65
js/src/wasm/WasmDebugFrame.cpp
Normal file
|
|
@ -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;
|
||||
|
||||
instance()->code().adjustEnterAndLeaveFrameTrapsState(cx, /* enabled = */ true);
|
||||
observing_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
DebugFrame::leaveFrame(JSContext* cx)
|
||||
{
|
||||
if (!observing_)
|
||||
return;
|
||||
|
||||
instance()->code().adjustEnterAndLeaveFrameTrapsState(cx, /* enabled = */ false);
|
||||
observing_ = false;
|
||||
}
|
||||
110
js/src/wasm/WasmDebugFrame.h
Normal file
110
js/src/wasm/WasmDebugFrame.h
Normal file
|
|
@ -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
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "wasm/WasmFrameIterator.h"
|
||||
|
||||
#include "wasm/WasmDebugFrame.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
|
|
@ -43,6 +44,13 @@ 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),
|
||||
|
|
@ -142,6 +150,7 @@ FrameIterator::settle()
|
|||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
case CodeRange::TrapExit:
|
||||
case CodeRange::DebugTrap:
|
||||
case CodeRange::Inline:
|
||||
case CodeRange::FarJumpIsland:
|
||||
MOZ_CRASH("Should not encounter an exit during iteration");
|
||||
|
|
@ -207,6 +216,38 @@ FrameIterator::lineOrBytecode() const
|
|||
: (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);
|
||||
}
|
||||
|
||||
const CallSite*
|
||||
FrameIterator::debugTrapCallsite() const
|
||||
{
|
||||
MOZ_ASSERT(!done() && debugEnabled());
|
||||
MOZ_ASSERT(callsite_->kind() == CallSite::EnterFrame || callsite_->kind() == CallSite::LeaveFrame);
|
||||
return callsite_;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
// Prologue/epilogue code generation
|
||||
|
||||
|
|
@ -566,6 +607,7 @@ ProfilingFrameIterator::initFromFP()
|
|||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
case CodeRange::TrapExit:
|
||||
case CodeRange::DebugTrap:
|
||||
case CodeRange::Inline:
|
||||
case CodeRange::FarJumpIsland:
|
||||
MOZ_CRASH("Unexpected CodeRange kind");
|
||||
|
|
@ -698,6 +740,7 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
|||
callerFP_ = nullptr;
|
||||
break;
|
||||
}
|
||||
case CodeRange::DebugTrap:
|
||||
case CodeRange::Inline: {
|
||||
// The throw stub clears WasmActivation::fp on it's way out.
|
||||
if (!fp) {
|
||||
|
|
@ -754,6 +797,7 @@ ProfilingFrameIterator::operator++()
|
|||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
case CodeRange::TrapExit:
|
||||
case CodeRange::DebugTrap:
|
||||
case CodeRange::Inline:
|
||||
case CodeRange::FarJumpIsland:
|
||||
stackAddress_ = callerFP_;
|
||||
|
|
@ -780,6 +824,7 @@ ProfilingFrameIterator::label() const
|
|||
const char* importInterpDescription = "slow FFI trampoline (in asm.js)";
|
||||
const char* nativeDescription = "native call (in asm.js)";
|
||||
const char* trapDescription = "trap handling (in asm.js)";
|
||||
const char* debugTrapDescription = "debug trap handling (in asm.js)";
|
||||
|
||||
switch (exitReason_) {
|
||||
case ExitReason::None:
|
||||
|
|
@ -792,6 +837,8 @@ ProfilingFrameIterator::label() const
|
|||
return nativeDescription;
|
||||
case ExitReason::Trap:
|
||||
return trapDescription;
|
||||
case ExitReason::DebugTrap:
|
||||
return debugTrapDescription;
|
||||
}
|
||||
|
||||
switch (codeRange_->kind()) {
|
||||
|
|
@ -800,6 +847,7 @@ ProfilingFrameIterator::label() const
|
|||
case CodeRange::ImportJitExit: return importJitDescription;
|
||||
case CodeRange::ImportInterpExit: return importInterpDescription;
|
||||
case CodeRange::TrapExit: return trapDescription;
|
||||
case CodeRange::DebugTrap: return debugTrapDescription;
|
||||
case CodeRange::Inline: return "inline stub (in asm.js)";
|
||||
case CodeRange::FarJumpIsland: return "interstitial (in asm.js)";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ namespace wasm {
|
|||
class CallSite;
|
||||
class Code;
|
||||
class CodeRange;
|
||||
class DebugFrame;
|
||||
class Instance;
|
||||
class SigIdDesc;
|
||||
struct CallThunk;
|
||||
struct FuncOffsets;
|
||||
|
|
@ -68,8 +70,11 @@ class FrameIterator
|
|||
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;
|
||||
const CallSite* debugTrapCallsite() const;
|
||||
};
|
||||
|
||||
// An ExitReason describes the possible reasons for leaving compiled wasm code
|
||||
|
|
@ -80,7 +85,8 @@ enum class ExitReason : uint32_t
|
|||
ImportJit, // fast-path call directly into JIT code
|
||||
ImportInterp, // slow-path call into C++ Invoke()
|
||||
Native, // call to native C++ code (e.g., Math.sin, ToInt32(), interrupt)
|
||||
Trap // call to trap handler for the trap in WasmActivation::trap
|
||||
Trap, // call to trap handler for the trap in WasmActivation::trap
|
||||
DebugTrap // call to debug trap handler
|
||||
};
|
||||
|
||||
// Iterates over the frames of a single WasmActivation, given an
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ static const uint32_t BAD_CODE_RANGE = UINT32_MAX;
|
|||
|
||||
ModuleGenerator::ModuleGenerator(UniqueChars* error)
|
||||
: alwaysBaseline_(false),
|
||||
debugEnabled_(false),
|
||||
error_(error),
|
||||
numSigs_(0),
|
||||
numTables_(0),
|
||||
|
|
@ -198,6 +199,7 @@ ModuleGenerator::init(UniqueModuleEnvironment env, const CompileArgs& args,
|
|||
linkData_.globalDataLength = AlignBytes(InitialGlobalDataBytes, sizeof(void*));
|
||||
|
||||
alwaysBaseline_ = args.alwaysBaseline;
|
||||
debugEnabled_ = args.debugEnabled;
|
||||
if (!funcToCodeRange_.appendN(BAD_CODE_RANGE, env_->funcSigs.length()))
|
||||
return false;
|
||||
|
||||
|
|
@ -372,6 +374,28 @@ ModuleGenerator::patchCallSites(TrapExitOffsetArray* maybeTrapExits)
|
|||
|
||||
masm_.patchCall(callerOffset, *existingTrapFarJumps[cs.trap()]);
|
||||
break;
|
||||
}
|
||||
case CallSiteDesc::EnterFrame:
|
||||
case CallSiteDesc::LeaveFrame: {
|
||||
Uint32Vector& jumps = metadata_->debugTrapFarJumpOffsets;
|
||||
if (jumps.empty() ||
|
||||
uint32_t(abs(int32_t(jumps.back()) - int32_t(callerOffset))) >= JumpRange())
|
||||
{
|
||||
Offsets offsets;
|
||||
offsets.begin = masm_.currentOffset();
|
||||
uint32_t jumpOffset = masm_.farJumpWithPatch().offset();
|
||||
offsets.end = masm_.currentOffset();
|
||||
if (masm_.oom())
|
||||
return false;
|
||||
|
||||
if (!metadata_->codeRanges.emplaceBack(CodeRange::FarJumpIsland, offsets))
|
||||
return false;
|
||||
if (!debugTrapFarJumps_.emplaceBack(jumpOffset))
|
||||
return false;
|
||||
if (!jumps.emplaceBack(offsets.begin))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -380,7 +404,7 @@ ModuleGenerator::patchCallSites(TrapExitOffsetArray* maybeTrapExits)
|
|||
}
|
||||
|
||||
bool
|
||||
ModuleGenerator::patchFarJumps(const TrapExitOffsetArray& trapExits)
|
||||
ModuleGenerator::patchFarJumps(const TrapExitOffsetArray& trapExits, const Offsets& debugTrapStub)
|
||||
{
|
||||
MacroAssembler::AutoPrepareForPatching patching(masm_);
|
||||
|
||||
|
|
@ -394,6 +418,9 @@ ModuleGenerator::patchFarJumps(const TrapExitOffsetArray& trapExits)
|
|||
for (const TrapFarJump& farJump : masm_.trapFarJumps())
|
||||
masm_.patchFarJump(farJump.jump, trapExits[farJump.trap].begin);
|
||||
|
||||
for (uint32_t debugTrapFarJump : debugTrapFarJumps_)
|
||||
masm_.patchFarJump(CodeOffset(debugTrapFarJump), debugTrapStub.begin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -504,6 +531,7 @@ ModuleGenerator::finishCodegen()
|
|||
Offsets unalignedAccessExit;
|
||||
Offsets interruptExit;
|
||||
Offsets throwStub;
|
||||
Offsets debugTrapStub;
|
||||
|
||||
{
|
||||
TempAllocator alloc(&lifo_);
|
||||
|
|
@ -531,6 +559,7 @@ ModuleGenerator::finishCodegen()
|
|||
unalignedAccessExit = GenerateUnalignedExit(masm, &throwLabel);
|
||||
interruptExit = GenerateInterruptExit(masm, &throwLabel);
|
||||
throwStub = GenerateThrowStub(masm, &throwLabel);
|
||||
debugTrapStub = GenerateDebugTrapStub(masm, &throwLabel);
|
||||
|
||||
if (masm.oom() || !masm_.asmMergeWith(masm))
|
||||
return false;
|
||||
|
|
@ -580,6 +609,10 @@ ModuleGenerator::finishCodegen()
|
|||
if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, throwStub))
|
||||
return false;
|
||||
|
||||
debugTrapStub.offsetBy(offsetInWhole);
|
||||
if (!metadata_->codeRanges.emplaceBack(CodeRange::DebugTrap, debugTrapStub))
|
||||
return false;
|
||||
|
||||
// Fill in LinkData with the offsets of these stubs.
|
||||
|
||||
linkData_.outOfBoundsOffset = outOfBoundsExit.begin;
|
||||
|
|
@ -592,7 +625,7 @@ ModuleGenerator::finishCodegen()
|
|||
if (!patchCallSites(&trapExits))
|
||||
return false;
|
||||
|
||||
if (!patchFarJumps(trapExits))
|
||||
if (!patchFarJumps(trapExits, debugTrapStub))
|
||||
return false;
|
||||
|
||||
// Code-generation is complete!
|
||||
|
|
@ -890,6 +923,8 @@ ModuleGenerator::launchBatchCompile()
|
|||
{
|
||||
MOZ_ASSERT(currentTask_);
|
||||
|
||||
currentTask_->setDebugEnabled(debugEnabled_);
|
||||
|
||||
size_t numBatchedFuncs = currentTask_->units().length();
|
||||
MOZ_ASSERT(numBatchedFuncs);
|
||||
|
||||
|
|
@ -924,9 +959,15 @@ ModuleGenerator::finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg)
|
|||
if (!func)
|
||||
return false;
|
||||
|
||||
auto mode = alwaysBaseline_ && BaselineCanCompile(fg)
|
||||
? IonCompileTask::CompileMode::Baseline
|
||||
: IonCompileTask::CompileMode::Ion;
|
||||
CompileMode mode;
|
||||
if ((alwaysBaseline_ || debugEnabled_) && BaselineCanCompile(fg)) {
|
||||
mode = CompileMode::Baseline;
|
||||
} else {
|
||||
mode = CompileMode::Ion;
|
||||
// Ion does not support debugging -- reset debugEnabled_ flags to avoid
|
||||
// turning debugging for wasm::Code.
|
||||
debugEnabled_ = false;
|
||||
}
|
||||
|
||||
fg->task_->init(Move(func), mode);
|
||||
|
||||
|
|
@ -1119,12 +1160,15 @@ ModuleGenerator::finish(const ShareableBytes& bytecode)
|
|||
metadata_->codeRanges.podResizeToFit();
|
||||
metadata_->callSites.podResizeToFit();
|
||||
metadata_->callThunks.podResizeToFit();
|
||||
metadata_->debugTrapFarJumpOffsets.podResizeToFit();
|
||||
|
||||
// For asm.js, the tables vector is over-allocated (to avoid resize during
|
||||
// parallel copilation). Shrink it back down to fit.
|
||||
if (isAsmJS() && !metadata_->tables.resize(numTables_))
|
||||
return nullptr;
|
||||
|
||||
metadata_->debugEnabled = debugEnabled_;
|
||||
|
||||
// Assert CodeRanges are sorted.
|
||||
#ifdef DEBUG
|
||||
uint32_t lastEnd = 0;
|
||||
|
|
@ -1134,6 +1178,15 @@ ModuleGenerator::finish(const ShareableBytes& bytecode)
|
|||
}
|
||||
#endif
|
||||
|
||||
// Assert debugTrapFarJumpOffsets are sorted.
|
||||
#ifdef DEBUG
|
||||
uint32_t lastOffset = 0;
|
||||
for (uint32_t debugTrapFarJumpOffset : metadata_->debugTrapFarJumpOffsets) {
|
||||
MOZ_ASSERT(debugTrapFarJumpOffset >= lastOffset);
|
||||
lastOffset = debugTrapFarJumpOffset;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!finishLinkData(code))
|
||||
return nullptr;
|
||||
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ class CompileTask
|
|||
Maybe<jit::TempAllocator> alloc_;
|
||||
Maybe<jit::MacroAssembler> masm_;
|
||||
FuncCompileUnitVector units_;
|
||||
bool debugEnabled_;
|
||||
|
||||
CompileTask(const CompileTask&) = delete;
|
||||
CompileTask& operator=(const CompileTask&) = delete;
|
||||
|
|
@ -151,6 +152,7 @@ class CompileTask
|
|||
void init() {
|
||||
alloc_.emplace(&lifo_);
|
||||
masm_.emplace(jit::MacroAssembler::WasmToken(), *alloc_);
|
||||
debugEnabled_ = false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -175,6 +177,12 @@ class CompileTask
|
|||
FuncCompileUnitVector& units() {
|
||||
return units_;
|
||||
}
|
||||
bool debugEnabled() const {
|
||||
return debugEnabled_;
|
||||
}
|
||||
void setDebugEnabled(bool enabled) {
|
||||
debugEnabled_ = enabled;
|
||||
}
|
||||
bool reset(UniqueFuncBytesVector* freeFuncBytes) {
|
||||
for (FuncCompileUnit& unit : units_) {
|
||||
if (!freeFuncBytes->emplaceBack(Move(unit.recycle())))
|
||||
|
|
@ -205,6 +213,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
|||
|
||||
// Constant parameters
|
||||
bool alwaysBaseline_;
|
||||
bool debugEnabled_;
|
||||
UniqueChars* error_;
|
||||
|
||||
// Data that is moved into the result of finish()
|
||||
|
|
@ -224,6 +233,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
|||
Uint32Set exportedFuncs_;
|
||||
uint32_t lastPatchedCallsite_;
|
||||
uint32_t startOfUnpatchedCallsites_;
|
||||
Uint32Vector debugTrapFarJumps_;
|
||||
|
||||
// Parallel compilation
|
||||
bool parallel_;
|
||||
|
|
@ -243,7 +253,7 @@ public:
|
|||
uint32_t numFuncImports() const;
|
||||
private:
|
||||
[[nodiscard]] bool patchCallSites(TrapExitOffsetArray* maybeTrapExits = nullptr);
|
||||
[[nodiscard]] bool patchFarJumps(const TrapExitOffsetArray& trapExits);
|
||||
[[nodiscard]] bool patchFarJumps(const TrapExitOffsetArray& trapExits, const Offsets& debugTrapStub);
|
||||
[[nodiscard]] bool finishTask(CompileTask* task);
|
||||
[[nodiscard]] bool finishOutstandingTask();
|
||||
[[nodiscard]] bool finishFuncExports();
|
||||
|
|
|
|||
|
|
@ -318,7 +318,8 @@ Instance::Instance(JSContext* cx,
|
|||
object_(object),
|
||||
code_(Move(code)),
|
||||
memory_(memory),
|
||||
tables_(Move(tables))
|
||||
tables_(Move(tables)),
|
||||
enterFrameTrapsEnabled_(false)
|
||||
{
|
||||
MOZ_ASSERT(funcImports.length() == metadata().funcImports.length());
|
||||
MOZ_ASSERT(tables_.length() == metadata().tables.length());
|
||||
|
|
@ -734,6 +735,16 @@ Instance::ensureProfilingState(JSContext* cx, bool newProfilingEnabled)
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Instance::ensureEnterFrameTrapsState(JSContext* cx, bool enabled)
|
||||
{
|
||||
if (enterFrameTrapsEnabled_ == enabled)
|
||||
return;
|
||||
|
||||
code_->adjustEnterAndLeaveFrameTrapsState(cx, enabled);
|
||||
enterFrameTrapsEnabled_ = enabled;
|
||||
}
|
||||
|
||||
void
|
||||
Instance::addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
||||
Metadata::SeenSet* seenMetadata,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ class Instance
|
|||
GCPtrWasmMemoryObject memory_;
|
||||
SharedTableVector tables_;
|
||||
TlsData tlsData_;
|
||||
bool enterFrameTrapsEnabled_;
|
||||
|
||||
// Internal helpers:
|
||||
const void** addressOfSigId(const SigIdDesc& sigId) const;
|
||||
|
|
@ -123,6 +124,11 @@ class Instance
|
|||
|
||||
MOZ_MUST_USE bool ensureProfilingState(JSContext* cx, bool enabled);
|
||||
|
||||
// Debug support:
|
||||
bool debugEnabled() const { return code_->metadata().debugEnabled; }
|
||||
bool enterFrameTrapsEnabled() const { return enterFrameTrapsEnabled_; }
|
||||
void ensureEnterFrameTrapsState(JSContext* cx, bool enabled);
|
||||
|
||||
// about:memory reporting:
|
||||
|
||||
void addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
||||
|
|
|
|||
|
|
@ -904,6 +904,7 @@ WasmInstanceObject::isNewborn() const
|
|||
WasmInstanceObject::finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
fop->delete_(&obj->as<WasmInstanceObject>().exports());
|
||||
fop->delete_(&obj->as<WasmInstanceObject>().scopes());
|
||||
if (!obj->as<WasmInstanceObject>().isNewborn())
|
||||
fop->delete_(&obj->as<WasmInstanceObject>().instance());
|
||||
}
|
||||
|
|
@ -930,12 +931,20 @@ WasmInstanceObject::create(JSContext* cx,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
UniquePtr<WeakScopeMap> scopes = js::MakeUnique<WeakScopeMap>(cx->zone(), ScopeMap());
|
||||
if (!scopes || !scopes->init()) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
AutoSetNewObjectMetadata metadata(cx);
|
||||
RootedWasmInstanceObject obj(cx, NewObjectWithGivenProto<WasmInstanceObject>(cx, proto));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
obj->setReservedSlot(EXPORTS_SLOT, PrivateValue(exports.release()));
|
||||
obj->setReservedSlot(SCOPES_SLOT, PrivateValue(scopes.release()));
|
||||
MOZ_ASSERT(obj->isNewborn());
|
||||
|
||||
MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
|
||||
|
|
@ -1021,6 +1030,12 @@ WasmInstanceObject::exports() const
|
|||
return *(WeakExportMap*)getReservedSlot(EXPORTS_SLOT).toPrivate();
|
||||
}
|
||||
|
||||
WasmInstanceObject::WeakScopeMap&
|
||||
WasmInstanceObject::scopes() const
|
||||
{
|
||||
return *(WeakScopeMap*)getReservedSlot(SCOPES_SLOT).toPrivate();
|
||||
}
|
||||
|
||||
static bool
|
||||
WasmCall(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
|
@ -1085,6 +1100,25 @@ WasmInstanceObject::getExportedFunctionCodeRange(HandleFunction fun)
|
|||
return metadata.codeRanges[metadata.lookupFuncExport(funcIndex).codeRangeIndex()];
|
||||
}
|
||||
|
||||
/* static */ WasmFunctionScope*
|
||||
WasmInstanceObject::getFunctionScope(JSContext* cx, HandleWasmInstanceObject instanceObj,
|
||||
uint32_t funcIndex)
|
||||
{
|
||||
if (ScopeMap::Ptr p = instanceObj->scopes().lookup(funcIndex))
|
||||
return p->value();
|
||||
|
||||
Rooted<WasmFunctionScope*> funcScope(cx, WasmFunctionScope::create(cx, instanceObj, funcIndex));
|
||||
if (!funcScope)
|
||||
return nullptr;
|
||||
|
||||
if (!instanceObj->scopes().putNew(funcIndex, funcScope)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return funcScope;
|
||||
}
|
||||
|
||||
bool
|
||||
wasm::IsExportedFunction(JSFunction* fun)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
namespace js {
|
||||
|
||||
class TypedArrayObject;
|
||||
class WasmFunctionScope;
|
||||
|
||||
namespace wasm {
|
||||
|
||||
|
|
@ -147,13 +148,14 @@ class WasmInstanceObject : public NativeObject
|
|||
{
|
||||
static const unsigned INSTANCE_SLOT = 0;
|
||||
static const unsigned EXPORTS_SLOT = 1;
|
||||
static const unsigned SCOPES_SLOT = 2;
|
||||
static const ClassOps classOps_;
|
||||
bool isNewborn() const;
|
||||
static void finalize(FreeOp* fop, JSObject* obj);
|
||||
static void trace(JSTracer* trc, JSObject* obj);
|
||||
|
||||
// ExportMap maps from function definition index to exported function
|
||||
// object. This map is weak to avoid holding objects alive; the point is
|
||||
// ExportMap maps from function index to exported function object.
|
||||
// This allows the instance to lazily create exported function
|
||||
// just to ensure a unique object identity for any given function object.
|
||||
using ExportMap = GCHashMap<uint32_t,
|
||||
ReadBarrieredFunction,
|
||||
|
|
@ -162,8 +164,18 @@ class WasmInstanceObject : public NativeObject
|
|||
using WeakExportMap = JS::WeakCache<ExportMap>;
|
||||
WeakExportMap& exports() const;
|
||||
|
||||
// WeakScopeMap maps from function index to js::Scope. This maps is weak
|
||||
// to avoid holding scope objects alive. The scopes are normally created
|
||||
// during debugging.
|
||||
using ScopeMap = GCHashMap<uint32_t,
|
||||
ReadBarriered<WasmFunctionScope*>,
|
||||
DefaultHasher<uint32_t>,
|
||||
SystemAllocPolicy>;
|
||||
using WeakScopeMap = JS::WeakCache<ScopeMap>;
|
||||
WeakScopeMap& scopes() const;
|
||||
|
||||
public:
|
||||
static const unsigned RESERVED_SLOTS = 2;
|
||||
static const unsigned RESERVED_SLOTS = 3;
|
||||
static const Class class_;
|
||||
static const JSPropertySpec properties[];
|
||||
static const JSFunctionSpec methods[];
|
||||
|
|
@ -185,6 +197,10 @@ class WasmInstanceObject : public NativeObject
|
|||
MutableHandleFunction fun);
|
||||
|
||||
const wasm::CodeRange& getExportedFunctionCodeRange(HandleFunction fun);
|
||||
|
||||
static WasmFunctionScope* getFunctionScope(JSContext* cx,
|
||||
HandleWasmInstanceObject instanceObj,
|
||||
uint32_t funcIndex);
|
||||
};
|
||||
|
||||
// The class of WebAssembly.Memory. A WasmMemoryObject references an ArrayBuffer
|
||||
|
|
|
|||
|
|
@ -144,7 +144,13 @@ Module::serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) con
|
|||
if (maybeBytecodeSize)
|
||||
*maybeBytecodeSize = bytecode_->bytes.length();
|
||||
|
||||
if (maybeCompiledSize) {
|
||||
// The compiled debug code must not be saved, set compiled size to 0,
|
||||
// so Module::assumptionsMatch will return false during assumptions
|
||||
// deserialization.
|
||||
if (maybeCompiledSize && metadata_->debugEnabled)
|
||||
*maybeCompiledSize = 0;
|
||||
|
||||
if (maybeCompiledSize && !metadata_->debugEnabled) {
|
||||
*maybeCompiledSize = assumptions_.serializedSize() +
|
||||
SerializedPodVectorSize(code_) +
|
||||
linkData_.serializedSize() +
|
||||
|
|
@ -175,7 +181,9 @@ Module::serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize,
|
|||
MOZ_RELEASE_ASSERT(bytecodeEnd == maybeBytecodeBegin + maybeBytecodeSize);
|
||||
}
|
||||
|
||||
if (maybeCompiledBegin) {
|
||||
MOZ_ASSERT_IF(maybeCompiledBegin && metadata_->debugEnabled, maybeCompiledSize == 0);
|
||||
|
||||
if (maybeCompiledBegin && !metadata_->debugEnabled) {
|
||||
// Assumption must be serialized at the beginning of the compiled bytes so
|
||||
// that compiledAssumptionsMatch can detect a build-id mismatch before any
|
||||
// other decoding occurs.
|
||||
|
|
|
|||
|
|
@ -897,6 +897,10 @@ static const LiveRegisterSet AllRegsExceptSP(
|
|||
GeneralRegisterSet(Registers::AllMask & ~(uint32_t(1) << Registers::StackPointer)),
|
||||
FloatRegisterSet(FloatRegisters::AllMask));
|
||||
|
||||
static const LiveRegisterSet AllAllocatableRegs = LiveRegisterSet(
|
||||
GeneralRegisterSet(Registers::AllocatableMask),
|
||||
FloatRegisterSet(FloatRegisters::AllMask));
|
||||
|
||||
// The async interrupt-callback exit is called from arbitrarily-interrupted wasm
|
||||
// code. That means we must first save *all* registers and restore *all*
|
||||
// registers (except the stack pointer) when we resume. The address to resume to
|
||||
|
|
@ -1109,6 +1113,11 @@ wasm::GenerateThrowStub(MacroAssembler& masm, Label* throwLabel)
|
|||
Offsets offsets;
|
||||
offsets.begin = masm.currentOffset();
|
||||
|
||||
masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
|
||||
if (ShadowStackSpace)
|
||||
masm.subFromStackPtr(Imm32(ShadowStackSpace));
|
||||
masm.call(SymbolicAddress::HandleDebugThrow);
|
||||
|
||||
// We are about to pop all frames in this WasmActivation. Set fp to null to
|
||||
// maintain the invariant that fp is either null or pointing to a valid
|
||||
// frame.
|
||||
|
|
@ -1128,3 +1137,50 @@ wasm::GenerateThrowStub(MacroAssembler& masm, Label* throwLabel)
|
|||
offsets.end = masm.currentOffset();
|
||||
return offsets;
|
||||
}
|
||||
|
||||
// Generate a stub that handle toggable enter/leave frame traps or breakpoints.
|
||||
// The trap records frame pointer (via GenerateExitPrologue) and saves most of
|
||||
// registers to not affect the code generated by WasmBaselineCompile.
|
||||
Offsets
|
||||
wasm::GenerateDebugTrapStub(MacroAssembler& masm, Label* throwLabel)
|
||||
{
|
||||
masm.haltingAlign(CodeAlignment);
|
||||
|
||||
masm.setFramePushed(0);
|
||||
|
||||
ProfilingOffsets offsets;
|
||||
GenerateExitPrologue(masm, 0, ExitReason::DebugTrap, &offsets);
|
||||
|
||||
// Save all registers used between baseline compiler operations.
|
||||
masm.PushRegsInMask(AllAllocatableRegs);
|
||||
|
||||
uint32_t framePushed = masm.framePushed();
|
||||
|
||||
// This method might be called with unaligned stack -- aligning and
|
||||
// saving old stack pointer at the top.
|
||||
Register scratch = ABINonArgReturnReg0;
|
||||
masm.moveStackPtrTo(scratch);
|
||||
masm.subFromStackPtr(Imm32(sizeof(intptr_t)));
|
||||
masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
|
||||
masm.storePtr(scratch, Address(masm.getStackPointer(), 0));
|
||||
|
||||
if (ShadowStackSpace)
|
||||
masm.subFromStackPtr(Imm32(ShadowStackSpace));
|
||||
masm.assertStackAlignment(ABIStackAlignment);
|
||||
masm.call(SymbolicAddress::HandleDebugTrap);
|
||||
|
||||
masm.branchIfFalseBool(ReturnReg, throwLabel);
|
||||
|
||||
if (ShadowStackSpace)
|
||||
masm.addToStackPtr(Imm32(ShadowStackSpace));
|
||||
masm.Pop(scratch);
|
||||
masm.moveToStackPtr(scratch);
|
||||
|
||||
masm.setFramePushed(framePushed);
|
||||
masm.PopRegsInMask(AllAllocatableRegs);
|
||||
|
||||
GenerateExitEpilogue(masm, 0, ExitReason::DebugTrap, &offsets);
|
||||
|
||||
offsets.end = masm.currentOffset();
|
||||
return offsets;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,9 @@ GenerateInterruptExit(jit::MacroAssembler& masm, jit::Label* throwLabel);
|
|||
extern Offsets
|
||||
GenerateThrowStub(jit::MacroAssembler& masm, jit::Label* throwLabel);
|
||||
|
||||
extern Offsets
|
||||
GenerateDebugTrapStub(jit::MacroAssembler& masm, jit::Label* throwLabel);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "wasm/WasmSerialize.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
|
||||
#include "vm/Debugger-inl.h"
|
||||
#include "vm/Stack-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
|
@ -89,6 +90,75 @@ WasmHandleExecutionInterrupt()
|
|||
return success;
|
||||
}
|
||||
|
||||
static bool
|
||||
WasmHandleDebugTrap()
|
||||
{
|
||||
WasmActivation* activation = JSContext::innermostWasmActivation();
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
FrameIterator iter(*activation);
|
||||
MOZ_ASSERT(iter.debugEnabled());
|
||||
const CallSite* site = iter.debugTrapCallsite();
|
||||
MOZ_ASSERT(site);
|
||||
if (site->kind() == CallSite::EnterFrame) {
|
||||
if (!iter.instance()->enterFrameTrapsEnabled())
|
||||
return true;
|
||||
DebugFrame* frame = iter.debugFrame();
|
||||
frame->setIsDebuggee();
|
||||
frame->observeFrame(cx);
|
||||
// TODO call onEnterFrame
|
||||
JSTrapStatus status = Debugger::onEnterFrame(cx, frame);
|
||||
if (status == JSTRAP_RETURN) {
|
||||
// Ignoring forced return (JSTRAP_RETURN) -- changing code execution
|
||||
// order is not yet implemented in the wasm baseline.
|
||||
// TODO properly handle JSTRAP_RETURN and resume wasm execution.
|
||||
JS_ReportErrorASCII(cx, "Unexpected resumption value from onEnterFrame");
|
||||
return false;
|
||||
}
|
||||
return status == JSTRAP_CONTINUE;
|
||||
}
|
||||
if (site->kind() == CallSite::LeaveFrame) {
|
||||
DebugFrame* frame = iter.debugFrame();
|
||||
bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, true);
|
||||
frame->leaveFrame(cx);
|
||||
return ok;
|
||||
}
|
||||
// TODO baseline debug traps
|
||||
MOZ_CRASH();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
WasmHandleDebugThrow()
|
||||
{
|
||||
WasmActivation* activation = JSContext::innermostWasmActivation();
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
for (FrameIterator iter(*activation); !iter.done(); ++iter) {
|
||||
if (!iter.debugEnabled())
|
||||
continue;
|
||||
|
||||
DebugFrame* frame = iter.debugFrame();
|
||||
|
||||
JSTrapStatus status = Debugger::onExceptionUnwind(cx, frame);
|
||||
if (status == JSTRAP_RETURN) {
|
||||
// Unexpected trap return -- raising error since throw recovery
|
||||
// is not yet implemented in the wasm baseline.
|
||||
// TODO properly handle JSTRAP_RETURN and resume wasm execution.
|
||||
JS_ReportErrorASCII(cx, "Unexpected resumption value from onExceptionUnwind");
|
||||
}
|
||||
|
||||
bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, false);
|
||||
if (ok) {
|
||||
// Unexpected success from the handler onLeaveFrame -- raising error
|
||||
// since throw recovery is not yet implemented in the wasm baseline.
|
||||
// TODO properly handle success and resume wasm execution.
|
||||
JS_ReportErrorASCII(cx, "Unexpected success from onLeaveFrame");
|
||||
}
|
||||
frame->leaveFrame(cx);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
WasmReportTrap(int32_t trapIndex)
|
||||
{
|
||||
|
|
@ -265,6 +335,10 @@ wasm::AddressOf(SymbolicAddress imm, ExclusiveContext* cx)
|
|||
return FuncCast(WasmReportOverRecursed, Args_General0);
|
||||
case SymbolicAddress::HandleExecutionInterrupt:
|
||||
return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
|
||||
case SymbolicAddress::HandleDebugTrap:
|
||||
return FuncCast(WasmHandleDebugTrap, Args_General0);
|
||||
case SymbolicAddress::HandleDebugThrow:
|
||||
return FuncCast(WasmHandleDebugThrow, Args_General0);
|
||||
case SymbolicAddress::ReportTrap:
|
||||
return FuncCast(WasmReportTrap, Args_General1);
|
||||
case SymbolicAddress::ReportOutOfBounds:
|
||||
|
|
|
|||
|
|
@ -733,14 +733,16 @@ struct TrapOffset
|
|||
|
||||
class CallSiteDesc
|
||||
{
|
||||
uint32_t lineOrBytecode_ : 30;
|
||||
uint32_t kind_ : 2;
|
||||
uint32_t lineOrBytecode_ : 29;
|
||||
uint32_t kind_ : 3;
|
||||
public:
|
||||
enum Kind {
|
||||
Func, // pc-relative call to a specific function
|
||||
Dynamic, // dynamic callee called via register
|
||||
Symbolic, // call to a single symbolic callee
|
||||
TrapExit // call to a trap exit
|
||||
TrapExit, // call to a trap exit
|
||||
EnterFrame, // call to a enter frame handler
|
||||
LeaveFrame // call to a leave frame handler
|
||||
};
|
||||
CallSiteDesc() {}
|
||||
explicit CallSiteDesc(Kind kind)
|
||||
|
|
@ -857,6 +859,8 @@ enum class SymbolicAddress
|
|||
InterruptUint32,
|
||||
ReportOverRecursed,
|
||||
HandleExecutionInterrupt,
|
||||
HandleDebugTrap,
|
||||
HandleDebugThrow,
|
||||
ReportTrap,
|
||||
ReportOutOfBounds,
|
||||
ReportUnalignedAccess,
|
||||
|
|
@ -1333,6 +1337,28 @@ struct MemoryPatch
|
|||
|
||||
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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue