1574 lines
69 KiB
C++
1574 lines
69 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef vm_Debugger_h
|
|
#define vm_Debugger_h
|
|
|
|
#include "mozilla/GuardObjects.h"
|
|
#include "mozilla/LinkedList.h"
|
|
#include "mozilla/Range.h"
|
|
#include "mozilla/Vector.h"
|
|
|
|
#include "jsclist.h"
|
|
#include "jscntxt.h"
|
|
#include "jscompartment.h"
|
|
#include "jsweakmap.h"
|
|
#include "jswrapper.h"
|
|
|
|
#include "builtin/Promise.h"
|
|
#include "ds/TraceableFifo.h"
|
|
#include "gc/Barrier.h"
|
|
#include "js/Debug.h"
|
|
#include "js/GCVariant.h"
|
|
#include "js/HashTable.h"
|
|
#include "vm/GlobalObject.h"
|
|
#include "vm/SavedStacks.h"
|
|
#include "wasm/WasmJS.h"
|
|
|
|
enum JSTrapStatus {
|
|
JSTRAP_ERROR,
|
|
JSTRAP_CONTINUE,
|
|
JSTRAP_RETURN,
|
|
JSTRAP_THROW,
|
|
JSTRAP_LIMIT
|
|
};
|
|
|
|
namespace js {
|
|
|
|
class Breakpoint;
|
|
class DebuggerMemory;
|
|
class WasmInstanceObject;
|
|
|
|
typedef HashSet<ReadBarrieredGlobalObject,
|
|
MovableCellHasher<ReadBarrieredGlobalObject>,
|
|
RuntimeAllocPolicy> WeakGlobalObjectSet;
|
|
|
|
/*
|
|
* A weakmap from GC thing keys to JSObject values that supports the keys being
|
|
* in different compartments to the values. All values must be in the same
|
|
* compartment.
|
|
*
|
|
* The purpose of this is to allow the garbage collector to easily find edges
|
|
* from debuggee object compartments to debugger compartments when calculating
|
|
* the compartment groups. Note that these edges are the inverse of the edges
|
|
* stored in the cross compartment map.
|
|
*
|
|
* The current implementation results in all debuggee object compartments being
|
|
* swept in the same group as the debugger. This is a conservative approach,
|
|
* and compartments may be unnecessarily grouped, however it results in a
|
|
* simpler and faster implementation.
|
|
*
|
|
* If InvisibleKeysOk is true, then the map can have keys in invisible-to-
|
|
* debugger compartments. If it is false, we assert that such entries are never
|
|
* created.
|
|
*
|
|
* Also note that keys in these weakmaps can be in any compartment, debuggee or
|
|
* not, because they cannot be deleted when a compartment is no longer a
|
|
* debuggee: the values need to maintain object identity across add/remove/add
|
|
* transitions.
|
|
*/
|
|
template <class UnbarrieredKey, bool InvisibleKeysOk=false>
|
|
class DebuggerWeakMap : private WeakMap<HeapPtr<UnbarrieredKey>, HeapPtr<JSObject*>,
|
|
MovableCellHasher<HeapPtr<UnbarrieredKey>>>
|
|
{
|
|
private:
|
|
typedef HeapPtr<UnbarrieredKey> Key;
|
|
typedef HeapPtr<JSObject*> Value;
|
|
|
|
typedef HashMap<JS::Zone*,
|
|
uintptr_t,
|
|
DefaultHasher<JS::Zone*>,
|
|
RuntimeAllocPolicy> CountMap;
|
|
|
|
CountMap zoneCounts;
|
|
JSCompartment* compartment;
|
|
|
|
public:
|
|
typedef WeakMap<Key, Value, MovableCellHasher<Key>> Base;
|
|
|
|
explicit DebuggerWeakMap(JSContext* cx)
|
|
: Base(cx),
|
|
zoneCounts(cx->runtime()),
|
|
compartment(cx->compartment())
|
|
{ }
|
|
|
|
public:
|
|
/* Expose those parts of HashMap public interface that are used by Debugger methods. */
|
|
|
|
typedef typename Base::Entry Entry;
|
|
typedef typename Base::Ptr Ptr;
|
|
typedef typename Base::AddPtr AddPtr;
|
|
typedef typename Base::Range Range;
|
|
typedef typename Base::Enum Enum;
|
|
typedef typename Base::Lookup Lookup;
|
|
|
|
/* Expose WeakMap public interface */
|
|
|
|
using Base::lookupForAdd;
|
|
using Base::all;
|
|
using Base::trace;
|
|
|
|
MOZ_MUST_USE bool init(uint32_t len = 16) {
|
|
return Base::init(len) && zoneCounts.init();
|
|
}
|
|
|
|
template<typename KeyInput, typename ValueInput>
|
|
bool relookupOrAdd(AddPtr& p, const KeyInput& k, const ValueInput& v) {
|
|
MOZ_ASSERT(v->compartment() == this->compartment);
|
|
MOZ_ASSERT(!k->compartment()->creationOptions().mergeable());
|
|
MOZ_ASSERT_IF(!InvisibleKeysOk,
|
|
!k->compartment()->creationOptions().invisibleToDebugger());
|
|
MOZ_ASSERT(!Base::has(k));
|
|
if (!incZoneCount(k->zone()))
|
|
return false;
|
|
bool ok = Base::relookupOrAdd(p, k, v);
|
|
if (!ok)
|
|
decZoneCount(k->zone());
|
|
return ok;
|
|
}
|
|
|
|
void remove(const Lookup& l) {
|
|
MOZ_ASSERT(Base::has(l));
|
|
Base::remove(l);
|
|
decZoneCount(l->zone());
|
|
}
|
|
|
|
public:
|
|
template <void (traceValueEdges)(JSTracer*, JSObject*)>
|
|
void markCrossCompartmentEdges(JSTracer* tracer) {
|
|
for (Enum e(*static_cast<Base*>(this)); !e.empty(); e.popFront()) {
|
|
traceValueEdges(tracer, e.front().value());
|
|
Key key = e.front().key();
|
|
TraceEdge(tracer, &key, "Debugger WeakMap key");
|
|
if (key != e.front().key())
|
|
e.rekeyFront(key);
|
|
key.unsafeSet(nullptr);
|
|
}
|
|
}
|
|
|
|
bool hasKeyInZone(JS::Zone* zone) {
|
|
CountMap::Ptr p = zoneCounts.lookup(zone);
|
|
MOZ_ASSERT_IF(p.found(), p->value() > 0);
|
|
return p.found();
|
|
}
|
|
|
|
private:
|
|
/* Override sweep method to also update our edge cache. */
|
|
void sweep() {
|
|
for (Enum e(*static_cast<Base*>(this)); !e.empty(); e.popFront()) {
|
|
if (gc::IsAboutToBeFinalized(&e.front().mutableKey())) {
|
|
decZoneCount(e.front().key()->zone());
|
|
e.removeFront();
|
|
}
|
|
}
|
|
Base::assertEntriesNotAboutToBeFinalized();
|
|
}
|
|
|
|
MOZ_MUST_USE bool incZoneCount(JS::Zone* zone) {
|
|
CountMap::Ptr p = zoneCounts.lookupWithDefault(zone, 0);
|
|
if (!p)
|
|
return false;
|
|
++p->value();
|
|
return true;
|
|
}
|
|
|
|
void decZoneCount(JS::Zone* zone) {
|
|
CountMap::Ptr p = zoneCounts.lookup(zone);
|
|
MOZ_ASSERT(p);
|
|
MOZ_ASSERT(p->value() > 0);
|
|
--p->value();
|
|
if (p->value() == 0)
|
|
zoneCounts.remove(zone);
|
|
}
|
|
};
|
|
|
|
class LeaveDebuggeeNoExecute;
|
|
|
|
// Suppresses all debuggee NX checks, i.e., allow all execution. Used to allow
|
|
// certain whitelisted operations to execute code.
|
|
//
|
|
// WARNING
|
|
// WARNING Do not use this unless you know what you are doing!
|
|
// WARNING
|
|
class AutoSuppressDebuggeeNoExecuteChecks
|
|
{
|
|
EnterDebuggeeNoExecute** stack_;
|
|
EnterDebuggeeNoExecute* prev_;
|
|
|
|
public:
|
|
explicit AutoSuppressDebuggeeNoExecuteChecks(JSContext* cx) {
|
|
stack_ = &cx->runtime()->noExecuteDebuggerTop;
|
|
prev_ = *stack_;
|
|
*stack_ = nullptr;
|
|
}
|
|
|
|
~AutoSuppressDebuggeeNoExecuteChecks() {
|
|
MOZ_ASSERT(!*stack_);
|
|
*stack_ = prev_;
|
|
}
|
|
};
|
|
|
|
class MOZ_RAII EvalOptions {
|
|
const char* filename_;
|
|
unsigned lineno_;
|
|
|
|
public:
|
|
EvalOptions() : filename_(nullptr), lineno_(1) {}
|
|
~EvalOptions();
|
|
const char* filename() const { return filename_; }
|
|
unsigned lineno() const { return lineno_; }
|
|
MOZ_MUST_USE bool setFilename(JSContext* cx, const char* filename);
|
|
void setLineno(unsigned lineno) { lineno_ = lineno; }
|
|
};
|
|
|
|
/*
|
|
* Env is the type of what ES5 calls "lexical environments" (runtime activations
|
|
* of lexical scopes). This is currently just JSObject, and is implemented by
|
|
* CallObject, LexicalEnvironmentObject, and WithEnvironmentObject, among
|
|
* others--but environments and objects are really two different concepts.
|
|
*/
|
|
typedef JSObject Env;
|
|
|
|
// Either a real JSScript or synthesized.
|
|
//
|
|
// If synthesized, the referent is one of the following:
|
|
//
|
|
// 1. A WasmInstanceObject, denoting a synthesized toplevel wasm module
|
|
// script.
|
|
// 2. A wasm JSFunction, denoting a synthesized wasm function script.
|
|
// NYI!
|
|
typedef mozilla::Variant<JSScript*, WasmInstanceObject*> DebuggerScriptReferent;
|
|
|
|
// Either a ScriptSourceObject, for ordinary JS, or a WasmInstanceObject,
|
|
// denoting the synthesized source of a wasm module.
|
|
typedef mozilla::Variant<ScriptSourceObject*, WasmInstanceObject*> DebuggerSourceReferent;
|
|
|
|
class Debugger : private mozilla::LinkedListElement<Debugger>
|
|
{
|
|
friend class Breakpoint;
|
|
friend class DebuggerMemory;
|
|
friend class SavedStacks;
|
|
friend class mozilla::LinkedListElement<Debugger>;
|
|
friend class mozilla::LinkedList<Debugger>;
|
|
friend bool (::JS_DefineDebuggerObject)(JSContext* cx, JS::HandleObject obj);
|
|
friend bool (::JS::dbg::IsDebugger)(JSObject&);
|
|
friend bool (::JS::dbg::GetDebuggeeGlobals)(JSContext*, JSObject&, AutoObjectVector&);
|
|
friend void JS::dbg::onNewPromise(JSContext* cx, HandleObject promise);
|
|
friend void JS::dbg::onPromiseSettled(JSContext* cx, HandleObject promise);
|
|
friend bool JS::dbg::FireOnGarbageCollectionHook(JSContext* cx,
|
|
JS::dbg::GarbageCollectionEvent::Ptr&& data);
|
|
|
|
public:
|
|
enum Hook {
|
|
OnDebuggerStatement,
|
|
OnExceptionUnwind,
|
|
OnNewScript,
|
|
OnEnterFrame,
|
|
OnNewGlobalObject,
|
|
OnNewPromise,
|
|
OnPromiseSettled,
|
|
OnGarbageCollection,
|
|
HookCount
|
|
};
|
|
enum {
|
|
JSSLOT_DEBUG_PROTO_START,
|
|
JSSLOT_DEBUG_FRAME_PROTO = JSSLOT_DEBUG_PROTO_START,
|
|
JSSLOT_DEBUG_ENV_PROTO,
|
|
JSSLOT_DEBUG_OBJECT_PROTO,
|
|
JSSLOT_DEBUG_SCRIPT_PROTO,
|
|
JSSLOT_DEBUG_SOURCE_PROTO,
|
|
JSSLOT_DEBUG_MEMORY_PROTO,
|
|
JSSLOT_DEBUG_PROTO_STOP,
|
|
JSSLOT_DEBUG_HOOK_START = JSSLOT_DEBUG_PROTO_STOP,
|
|
JSSLOT_DEBUG_HOOK_STOP = JSSLOT_DEBUG_HOOK_START + HookCount,
|
|
JSSLOT_DEBUG_MEMORY_INSTANCE = JSSLOT_DEBUG_HOOK_STOP,
|
|
JSSLOT_DEBUG_COUNT
|
|
};
|
|
|
|
class ExecutionObservableSet
|
|
{
|
|
public:
|
|
typedef HashSet<Zone*>::Range ZoneRange;
|
|
|
|
virtual Zone* singleZone() const { return nullptr; }
|
|
virtual JSScript* singleScriptForZoneInvalidation() const { return nullptr; }
|
|
virtual const HashSet<Zone*>* zones() const { return nullptr; }
|
|
|
|
virtual bool shouldRecompileOrInvalidate(JSScript* script) const = 0;
|
|
virtual bool shouldMarkAsDebuggee(ScriptFrameIter& iter) const = 0;
|
|
};
|
|
|
|
// This enum is converted to and compare with bool values; NotObserving
|
|
// must be 0 and Observing must be 1.
|
|
enum IsObserving {
|
|
NotObserving = 0,
|
|
Observing = 1
|
|
};
|
|
|
|
// Return true if the given compartment is a debuggee of this debugger,
|
|
// false otherwise.
|
|
bool isDebuggeeUnbarriered(const JSCompartment* compartment) const;
|
|
|
|
// Return true if this Debugger observed a debuggee that participated in the
|
|
// GC identified by the given GC number. Return false otherwise.
|
|
// May return false negatives if we have hit OOM.
|
|
bool observedGC(uint64_t majorGCNumber) const {
|
|
return observedGCs.has(majorGCNumber);
|
|
}
|
|
|
|
// Notify this Debugger that one or more of its debuggees is participating
|
|
// in the GC identified by the given GC number.
|
|
bool debuggeeIsBeingCollected(uint64_t majorGCNumber) {
|
|
return observedGCs.put(majorGCNumber);
|
|
}
|
|
|
|
bool isEnabled() const {
|
|
return enabled;
|
|
}
|
|
|
|
static SavedFrame* getObjectAllocationSite(JSObject& obj);
|
|
|
|
struct AllocationsLogEntry
|
|
{
|
|
AllocationsLogEntry(HandleObject frame, double when, const char* className,
|
|
HandleAtom ctorName, size_t size, bool inNursery)
|
|
: frame(frame),
|
|
when(when),
|
|
className(className),
|
|
ctorName(ctorName),
|
|
size(size),
|
|
inNursery(inNursery)
|
|
{
|
|
MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>());
|
|
};
|
|
|
|
HeapPtr<JSObject*> frame;
|
|
double when;
|
|
const char* className;
|
|
HeapPtr<JSAtom*> ctorName;
|
|
size_t size;
|
|
bool inNursery;
|
|
|
|
void trace(JSTracer* trc) {
|
|
TraceNullableEdge(trc, &frame, "Debugger::AllocationsLogEntry::frame");
|
|
TraceNullableEdge(trc, &ctorName, "Debugger::AllocationsLogEntry::ctorName");
|
|
}
|
|
};
|
|
|
|
// Barrier methods so we can have ReadBarriered<Debugger*>.
|
|
static void readBarrier(Debugger* dbg) {
|
|
InternalBarrierMethods<JSObject*>::readBarrier(dbg->object);
|
|
}
|
|
static void writeBarrierPost(Debugger** vp, Debugger* prev, Debugger* next) {}
|
|
|
|
private:
|
|
GCPtrNativeObject object; /* The Debugger object. Strong reference. */
|
|
WeakGlobalObjectSet debuggees; /* Debuggee globals. Cross-compartment weak references. */
|
|
JS::ZoneSet debuggeeZones; /* Set of zones that we have debuggees in. */
|
|
js::GCPtrObject uncaughtExceptionHook; /* Strong reference. */
|
|
bool enabled;
|
|
bool allowUnobservedAsmJS;
|
|
|
|
// Whether to enable code coverage on the Debuggee.
|
|
bool collectCoverageInfo;
|
|
|
|
JSCList breakpoints; /* Circular list of all js::Breakpoints in this debugger */
|
|
|
|
// The set of GC numbers for which one or more of this Debugger's observed
|
|
// debuggees participated in.
|
|
using GCNumberSet = HashSet<uint64_t, DefaultHasher<uint64_t>, RuntimeAllocPolicy>;
|
|
GCNumberSet observedGCs;
|
|
|
|
using AllocationsLog = js::TraceableFifo<AllocationsLogEntry>;
|
|
|
|
AllocationsLog allocationsLog;
|
|
bool trackingAllocationSites;
|
|
double allocationSamplingProbability;
|
|
size_t maxAllocationsLogLength;
|
|
bool allocationsLogOverflowed;
|
|
|
|
static const size_t DEFAULT_MAX_LOG_LENGTH = 5000;
|
|
|
|
MOZ_MUST_USE bool appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
|
|
double when);
|
|
|
|
/*
|
|
* Recompute the set of debuggee zones based on the set of debuggee globals.
|
|
*/
|
|
void recomputeDebuggeeZoneSet();
|
|
|
|
/*
|
|
* Return true if there is an existing object metadata callback for the
|
|
* given global's compartment that will prevent our instrumentation of
|
|
* allocations.
|
|
*/
|
|
static bool cannotTrackAllocations(const GlobalObject& global);
|
|
|
|
/*
|
|
* Return true if the given global is being observed by at least one
|
|
* Debugger that is tracking allocations.
|
|
*/
|
|
static bool isObservedByDebuggerTrackingAllocations(const GlobalObject& global);
|
|
|
|
/*
|
|
* Add allocations tracking for objects allocated within the given
|
|
* debuggee's compartment. The given debuggee global must be observed by at
|
|
* least one Debugger that is enabled and tracking allocations.
|
|
*/
|
|
static MOZ_MUST_USE bool addAllocationsTracking(JSContext* cx, Handle<GlobalObject*> debuggee);
|
|
|
|
/*
|
|
* Remove allocations tracking for objects allocated within the given
|
|
* global's compartment. This is a no-op if there are still Debuggers
|
|
* observing this global and who are tracking allocations.
|
|
*/
|
|
static void removeAllocationsTracking(GlobalObject& global);
|
|
|
|
/*
|
|
* Add or remove allocations tracking for all debuggees.
|
|
*/
|
|
MOZ_MUST_USE bool addAllocationsTrackingForAllDebuggees(JSContext* cx);
|
|
void removeAllocationsTrackingForAllDebuggees();
|
|
|
|
/*
|
|
* If this Debugger is enabled, and has a onNewGlobalObject handler, then
|
|
* this link is inserted into the circular list headed by
|
|
* JSRuntime::onNewGlobalObjectWatchers. Otherwise, this is set to a
|
|
* singleton cycle.
|
|
*/
|
|
JSCList onNewGlobalObjectWatchersLink;
|
|
|
|
/*
|
|
* Map from stack frames that are currently on the stack to Debugger.Frame
|
|
* instances.
|
|
*
|
|
* The keys are always live stack frames. We drop them from this map as
|
|
* soon as they leave the stack (see slowPathOnLeaveFrame) and in
|
|
* removeDebuggee.
|
|
*
|
|
* We don't trace the keys of this map (the frames are on the stack and
|
|
* thus necessarily live), but we do trace the values. It's like a WeakMap
|
|
* that way, but since stack frames are not gc-things, the implementation
|
|
* has to be different.
|
|
*/
|
|
typedef HashMap<AbstractFramePtr,
|
|
HeapPtr<DebuggerFrame*>,
|
|
DefaultHasher<AbstractFramePtr>,
|
|
RuntimeAllocPolicy> FrameMap;
|
|
FrameMap frames;
|
|
|
|
/* An ephemeral map from JSScript* to Debugger.Script instances. */
|
|
typedef DebuggerWeakMap<JSScript*> ScriptWeakMap;
|
|
ScriptWeakMap scripts;
|
|
|
|
/* The map from debuggee source script objects to their Debugger.Source instances. */
|
|
typedef DebuggerWeakMap<JSObject*, true> SourceWeakMap;
|
|
SourceWeakMap sources;
|
|
|
|
/* The map from debuggee objects to their Debugger.Object instances. */
|
|
typedef DebuggerWeakMap<JSObject*> ObjectWeakMap;
|
|
ObjectWeakMap objects;
|
|
|
|
/* The map from debuggee Envs to Debugger.Environment instances. */
|
|
ObjectWeakMap environments;
|
|
|
|
/* The map from WasmInstanceObjects to synthesized Debugger.Script instances. */
|
|
typedef DebuggerWeakMap<WasmInstanceObject*> WasmInstanceWeakMap;
|
|
WasmInstanceWeakMap wasmInstanceScripts;
|
|
|
|
/* The map from WasmInstanceObjects to synthesized Debugger.Source instances. */
|
|
WasmInstanceWeakMap wasmInstanceSources;
|
|
|
|
/*
|
|
* Keep track of tracelogger last drained identifiers to know if there are
|
|
* lost events.
|
|
*/
|
|
#ifdef NIGHTLY_BUILD
|
|
uint32_t traceLoggerLastDrainedSize;
|
|
uint32_t traceLoggerLastDrainedIteration;
|
|
#endif
|
|
uint32_t traceLoggerScriptedCallsLastDrainedSize;
|
|
uint32_t traceLoggerScriptedCallsLastDrainedIteration;
|
|
|
|
class ScriptQuery;
|
|
class ObjectQuery;
|
|
|
|
MOZ_MUST_USE bool addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> obj);
|
|
void removeDebuggeeGlobal(FreeOp* fop, GlobalObject* global,
|
|
WeakGlobalObjectSet::Enum* debugEnum);
|
|
|
|
/*
|
|
* Report and clear the pending exception on ac.context, if any, and return
|
|
* JSTRAP_ERROR.
|
|
*/
|
|
JSTrapStatus reportUncaughtException(mozilla::Maybe<AutoCompartment>& ac);
|
|
|
|
/*
|
|
* Cope with an error or exception in a debugger hook.
|
|
*
|
|
* If callHook is true, then call the uncaughtExceptionHook, if any. If, in
|
|
* addition, vp is given, then parse the value returned by
|
|
* uncaughtExceptionHook as a resumption value.
|
|
*
|
|
* If there is no uncaughtExceptionHook, or if it fails, report and clear
|
|
* the pending exception on ac.context and return JSTRAP_ERROR.
|
|
*
|
|
* This always calls ac.leave(); ac is a parameter because this method must
|
|
* do some things in the debugger compartment and some things in the
|
|
* debuggee compartment.
|
|
*/
|
|
JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment>& ac);
|
|
JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment>& ac,
|
|
MutableHandleValue vp,
|
|
const mozilla::Maybe<HandleValue>& thisVForCheck = mozilla::Nothing(),
|
|
AbstractFramePtr frame = NullFramePtr());
|
|
|
|
JSTrapStatus handleUncaughtExceptionHelper(mozilla::Maybe<AutoCompartment>& ac,
|
|
MutableHandleValue* vp,
|
|
const mozilla::Maybe<HandleValue>& thisVForCheck,
|
|
AbstractFramePtr frame);
|
|
|
|
/*
|
|
* Handle the result of a hook that is expected to return a resumption
|
|
* value <https://wiki.mozilla.org/Debugger#Resumption_Values>. This is called
|
|
* when we return from a debugging hook to debuggee code. The interpreter wants
|
|
* a (JSTrapStatus, Value) pair telling it how to proceed.
|
|
*
|
|
* Precondition: ac is entered. We are in the debugger compartment.
|
|
*
|
|
* Postcondition: This called ac.leave(). See handleUncaughtException.
|
|
*
|
|
* If ok is false, the hook failed. If an exception is pending in
|
|
* ac.context(), return handleUncaughtException(ac, vp, callhook).
|
|
* Otherwise just return JSTRAP_ERROR.
|
|
*
|
|
* If ok is true, there must be no exception pending in ac.context(). rv may be:
|
|
* undefined - Return JSTRAP_CONTINUE to continue execution normally.
|
|
* {return: value} or {throw: value} - Call unwrapDebuggeeValue to
|
|
* unwrap value. Store the result in *vp and return JSTRAP_RETURN
|
|
* or JSTRAP_THROW. The interpreter will force the current frame to
|
|
* return or throw an exception.
|
|
* null - Return JSTRAP_ERROR to terminate the debuggee with an
|
|
* uncatchable error.
|
|
* anything else - Make a new TypeError the pending exception and
|
|
* return handleUncaughtException(ac, vp, callHook).
|
|
*/
|
|
JSTrapStatus processHandlerResult(mozilla::Maybe<AutoCompartment>& ac, bool OK, const Value& rv,
|
|
AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp);
|
|
|
|
JSTrapStatus processParsedHandlerResult(mozilla::Maybe<AutoCompartment>& ac,
|
|
AbstractFramePtr frame, jsbytecode* pc,
|
|
bool success, JSTrapStatus status,
|
|
MutableHandleValue vp);
|
|
|
|
JSTrapStatus processParsedHandlerResultHelper(mozilla::Maybe<AutoCompartment>& ac,
|
|
AbstractFramePtr frame,
|
|
const mozilla::Maybe<HandleValue>& maybeThisv,
|
|
bool success, JSTrapStatus status,
|
|
MutableHandleValue vp);
|
|
|
|
bool processResumptionValue(mozilla::Maybe<AutoCompartment>& ac, AbstractFramePtr frame,
|
|
const mozilla::Maybe<HandleValue>& maybeThis, HandleValue rval,
|
|
JSTrapStatus& statusp, MutableHandleValue vp);
|
|
|
|
GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);
|
|
|
|
static void traceObject(JSTracer* trc, JSObject* obj);
|
|
void trace(JSTracer* trc);
|
|
static void finalize(FreeOp* fop, JSObject* obj);
|
|
void markCrossCompartmentEdges(JSTracer* tracer);
|
|
|
|
static const ClassOps classOps_;
|
|
|
|
public:
|
|
static const Class class_;
|
|
|
|
private:
|
|
static MOZ_MUST_USE bool getHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
|
|
static MOZ_MUST_USE bool setHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
|
|
|
|
static bool getEnabled(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool setEnabled(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool setOnDebuggerStatement(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool setOnExceptionUnwind(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getOnNewScript(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool setOnNewScript(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getOnEnterFrame(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool setOnEnterFrame(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool setOnNewGlobalObject(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getOnNewPromise(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool setOnNewPromise(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool setOnPromiseSettled(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool setUncaughtExceptionHook(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool setAllowUnobservedAsmJS(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool setCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getMemory(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool addDebuggee(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool removeDebuggee(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool removeAllDebuggees(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool hasDebuggee(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getDebuggees(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool getNewestFrame(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool findScripts(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool findObjects(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool findAllGlobals(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool makeGlobalObjectReference(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool setupTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool startTraceLogger(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool endTraceLogger(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool isCompilableUnit(JSContext* cx, unsigned argc, Value* vp);
|
|
#ifdef NIGHTLY_BUILD
|
|
static bool setupTraceLogger(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool drainTraceLogger(JSContext* cx, unsigned argc, Value* vp);
|
|
#endif
|
|
static bool adoptDebuggeeValue(JSContext* cx, unsigned argc, Value* vp);
|
|
static bool construct(JSContext* cx, unsigned argc, Value* vp);
|
|
static const JSPropertySpec properties[];
|
|
static const JSFunctionSpec methods[];
|
|
static const JSFunctionSpec static_methods[];
|
|
|
|
static void removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx, AbstractFramePtr frame);
|
|
static bool updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObservableSet& obs,
|
|
IsObserving observing);
|
|
static bool updateExecutionObservabilityOfScripts(JSContext* cx, const ExecutionObservableSet& obs,
|
|
IsObserving observing);
|
|
static bool updateExecutionObservability(JSContext* cx, ExecutionObservableSet& obs,
|
|
IsObserving observing);
|
|
|
|
template <typename FrameFn /* void (NativeObject*) */>
|
|
static void forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn);
|
|
|
|
/*
|
|
* Return a vector containing all Debugger.Frame instances referring to
|
|
* |frame|. |global| is |frame|'s global object; if nullptr or omitted, we
|
|
* compute it ourselves from |frame|.
|
|
*/
|
|
using DebuggerFrameVector = GCVector<DebuggerFrame*>;
|
|
static MOZ_MUST_USE bool getDebuggerFrames(AbstractFramePtr frame,
|
|
MutableHandle<DebuggerFrameVector> frames);
|
|
|
|
public:
|
|
static MOZ_MUST_USE bool ensureExecutionObservabilityOfOsrFrame(JSContext* cx,
|
|
InterpreterFrame* frame);
|
|
|
|
// Public for DebuggerScript_setBreakpoint.
|
|
static MOZ_MUST_USE bool ensureExecutionObservabilityOfScript(JSContext* cx, JSScript* script);
|
|
|
|
// Whether the Debugger instance needs to observe all non-AOT JS
|
|
// execution of its debugees.
|
|
IsObserving observesAllExecution() const;
|
|
|
|
// Whether the Debugger instance needs to observe AOT-compiled asm.js
|
|
// execution of its debuggees.
|
|
IsObserving observesAsmJS() const;
|
|
|
|
// Whether the Debugger instance needs to observe coverage of any JavaScript
|
|
// execution.
|
|
IsObserving observesCoverage() const;
|
|
|
|
private:
|
|
static MOZ_MUST_USE bool ensureExecutionObservabilityOfFrame(JSContext* cx,
|
|
AbstractFramePtr frame);
|
|
static MOZ_MUST_USE bool ensureExecutionObservabilityOfCompartment(JSContext* cx,
|
|
JSCompartment* comp);
|
|
|
|
static bool hookObservesAllExecution(Hook which);
|
|
|
|
MOZ_MUST_USE bool updateObservesAllExecutionOnDebuggees(JSContext* cx, IsObserving observing);
|
|
MOZ_MUST_USE bool updateObservesCoverageOnDebuggees(JSContext* cx, IsObserving observing);
|
|
void updateObservesAsmJSOnDebuggees(IsObserving observing);
|
|
|
|
JSObject* getHook(Hook hook) const;
|
|
bool hasAnyLiveHooks(JSRuntime* rt) const;
|
|
|
|
static MOZ_MUST_USE bool slowPathCheckNoExecute(JSContext* cx, HandleScript script);
|
|
static JSTrapStatus slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame);
|
|
static MOZ_MUST_USE bool slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame,
|
|
jsbytecode* pc, bool ok);
|
|
static JSTrapStatus slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
|
|
static JSTrapStatus slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
|
|
static void slowPathOnNewScript(JSContext* cx, HandleScript script);
|
|
static void slowPathOnNewWasmInstance(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
|
|
static void slowPathOnNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
|
|
static MOZ_MUST_USE bool slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj,
|
|
HandleSavedFrame frame, double when,
|
|
GlobalObject::DebuggerVector& dbgs);
|
|
static void slowPathPromiseHook(JSContext* cx, Hook hook, HandleObject promise);
|
|
|
|
template <typename HookIsEnabledFun /* bool (Debugger*) */,
|
|
typename FireHookFun /* JSTrapStatus (Debugger*) */>
|
|
static JSTrapStatus dispatchHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
|
|
FireHookFun fireHook);
|
|
|
|
JSTrapStatus fireDebuggerStatement(JSContext* cx, MutableHandleValue vp);
|
|
JSTrapStatus fireExceptionUnwind(JSContext* cx, MutableHandleValue vp);
|
|
JSTrapStatus fireEnterFrame(JSContext* cx, MutableHandleValue vp);
|
|
JSTrapStatus fireNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global, MutableHandleValue vp);
|
|
JSTrapStatus firePromiseHook(JSContext* cx, Hook hook, HandleObject promise, MutableHandleValue vp);
|
|
|
|
NativeObject* newVariantWrapper(JSContext* cx, Handle<DebuggerScriptReferent> referent) {
|
|
return newDebuggerScript(cx, referent);
|
|
}
|
|
NativeObject* newVariantWrapper(JSContext* cx, Handle<DebuggerSourceReferent> referent) {
|
|
return newDebuggerSource(cx, referent);
|
|
}
|
|
|
|
/*
|
|
* Helper function to help wrap Debugger objects whose referents may be
|
|
* variants. Currently Debugger.Script and Debugger.Source referents may
|
|
* be variants.
|
|
*
|
|
* Prefer using wrapScript, wrapWasmScript, wrapSource, and wrapWasmSource
|
|
* whenever possible.
|
|
*/
|
|
template <typename ReferentVariant, typename Referent, typename Map>
|
|
JSObject* wrapVariantReferent(JSContext* cx, Map& map, Handle<CrossCompartmentKey> key,
|
|
Handle<ReferentVariant> referent);
|
|
JSObject* wrapVariantReferent(JSContext* cx, Handle<DebuggerScriptReferent> referent);
|
|
JSObject* wrapVariantReferent(JSContext* cx, Handle<DebuggerSourceReferent> referent);
|
|
|
|
/*
|
|
* Allocate and initialize a Debugger.Script instance whose referent is
|
|
* |referent|.
|
|
*/
|
|
NativeObject* newDebuggerScript(JSContext* cx, Handle<DebuggerScriptReferent> referent);
|
|
|
|
/*
|
|
* Allocate and initialize a Debugger.Source instance whose referent is
|
|
* |referent|.
|
|
*/
|
|
NativeObject* newDebuggerSource(JSContext* cx, Handle<DebuggerSourceReferent> referent);
|
|
|
|
/*
|
|
* Receive a "new script" event from the engine. A new script was compiled
|
|
* or deserialized.
|
|
*/
|
|
void fireNewScript(JSContext* cx, Handle<DebuggerScriptReferent> scriptReferent);
|
|
|
|
/*
|
|
* Receive a "garbage collection" event from the engine. A GC cycle with the
|
|
* given data was recently completed.
|
|
*/
|
|
void fireOnGarbageCollectionHook(JSContext* cx,
|
|
const JS::dbg::GarbageCollectionEvent::Ptr& gcData);
|
|
|
|
/*
|
|
* 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,
|
|
MutableHandleValue vp);
|
|
MOZ_MUST_USE bool getScriptFrameWithIter(JSContext* cx, AbstractFramePtr frame,
|
|
const ScriptFrameIter* maybeIter,
|
|
MutableHandleDebuggerFrame result);
|
|
|
|
inline Breakpoint* firstBreakpoint() const;
|
|
|
|
static inline Debugger* fromOnNewGlobalObjectWatchersLink(JSCList* link);
|
|
|
|
static MOZ_MUST_USE bool replaceFrameGuts(JSContext* cx, AbstractFramePtr from,
|
|
AbstractFramePtr to,
|
|
ScriptFrameIter& iter);
|
|
|
|
public:
|
|
Debugger(JSContext* cx, NativeObject* dbg);
|
|
~Debugger();
|
|
|
|
MOZ_MUST_USE bool init(JSContext* cx);
|
|
inline const js::GCPtrNativeObject& toJSObject() const;
|
|
inline js::GCPtrNativeObject& toJSObjectRef();
|
|
static inline Debugger* fromJSObject(const JSObject* obj);
|
|
static Debugger* fromChildJSObject(JSObject* obj);
|
|
|
|
bool hasMemory() const;
|
|
DebuggerMemory& memory() const;
|
|
|
|
WeakGlobalObjectSet::Range allDebuggees() const { return debuggees.all(); }
|
|
|
|
/*********************************** Methods for interaction with the GC. */
|
|
|
|
/*
|
|
* A Debugger object is live if:
|
|
* * the Debugger JSObject is live (Debugger::trace handles this case); OR
|
|
* * it is in the middle of dispatching an event (the event dispatching
|
|
* code roots it in this case); OR
|
|
* * it is enabled, and it is debugging at least one live compartment,
|
|
* and at least one of the following is true:
|
|
* - it has a debugger hook installed
|
|
* - it has a breakpoint set on a live script
|
|
* - it has a watchpoint set on a live object.
|
|
*
|
|
* Debugger::markAllIteratively handles the last case. If it finds any
|
|
* Debugger objects that are definitely live but not yet marked, it marks
|
|
* them and returns true. If not, it returns false.
|
|
*/
|
|
static void markIncomingCrossCompartmentEdges(JSTracer* tracer);
|
|
static MOZ_MUST_USE bool markAllIteratively(GCMarker* trc);
|
|
static void markAll(JSTracer* trc);
|
|
static void sweepAll(FreeOp* fop);
|
|
static void detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global);
|
|
static void findZoneEdges(JS::Zone* v, gc::ZoneComponentFinder& finder);
|
|
|
|
// Checks it the current compartment is allowed to execute code.
|
|
static inline MOZ_MUST_USE bool checkNoExecute(JSContext* cx, HandleScript script);
|
|
|
|
/*
|
|
* JSTrapStatus Overview
|
|
* ---------------------
|
|
*
|
|
* The |onEnterFrame|, |onDebuggerStatement|, and |onExceptionUnwind|
|
|
* methods below return a JSTrapStatus code that indicates how execution
|
|
* should proceed:
|
|
*
|
|
* - JSTRAP_CONTINUE: Continue execution normally.
|
|
*
|
|
* - JSTRAP_THROW: Throw an exception. The method has set |cx|'s
|
|
* pending exception to the value to be thrown.
|
|
*
|
|
* - JSTRAP_ERROR: Terminate execution (as is done when a script is terminated
|
|
* for running too long). The method has cleared |cx|'s pending
|
|
* exception.
|
|
*
|
|
* - JSTRAP_RETURN: Return from the new frame immediately. The method has
|
|
* set the youngest JS frame's return value appropriately.
|
|
*/
|
|
|
|
/*
|
|
* Announce to the debugger that the context has entered a new JavaScript
|
|
* frame, |frame|. Call whatever hooks have been registered to observe new
|
|
* frames.
|
|
*/
|
|
static inline JSTrapStatus onEnterFrame(JSContext* cx, AbstractFramePtr frame);
|
|
|
|
/*
|
|
* Announce to the debugger a |debugger;| statement on has been
|
|
* encountered on the youngest JS frame on |cx|. Call whatever hooks have
|
|
* been registered to observe this.
|
|
*
|
|
* Note that this method is called for all |debugger;| statements,
|
|
* regardless of the frame's debuggee-ness.
|
|
*/
|
|
static inline JSTrapStatus onDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
|
|
|
|
/*
|
|
* Announce to the debugger that an exception has been thrown and propagated
|
|
* to |frame|. Call whatever hooks have been registered to observe this.
|
|
*/
|
|
static inline JSTrapStatus onExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
|
|
|
|
/*
|
|
* Announce to the debugger that the thread has exited a JavaScript frame, |frame|.
|
|
* If |ok| is true, the frame is returning normally; if |ok| is false, the frame
|
|
* is throwing an exception or terminating.
|
|
*
|
|
* Change cx's current exception and |frame|'s return value to reflect the changes
|
|
* in behavior the hooks request, if any. Return the new error/success value.
|
|
*
|
|
* This function may be called twice for the same outgoing frame; only the
|
|
* first call has any effect. (Permitting double calls simplifies some
|
|
* cases where an onPop handler's resumption value changes a return to a
|
|
* throw, or vice versa: we can redirect to a complete copy of the
|
|
* alternative path, containing its own call to onLeaveFrame.)
|
|
*/
|
|
static inline MOZ_MUST_USE bool onLeaveFrame(JSContext* cx, AbstractFramePtr frame,
|
|
jsbytecode* pc, bool ok);
|
|
|
|
static inline void onNewScript(JSContext* cx, HandleScript script);
|
|
static inline void onNewWasmInstance(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
|
|
static inline void onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
|
|
static inline MOZ_MUST_USE bool onLogAllocationSite(JSContext* cx, JSObject* obj,
|
|
HandleSavedFrame frame, double when);
|
|
static JSTrapStatus onTrap(JSContext* cx, MutableHandleValue vp);
|
|
static JSTrapStatus onSingleStep(JSContext* cx, MutableHandleValue vp);
|
|
static MOZ_MUST_USE bool handleBaselineOsr(JSContext* cx, InterpreterFrame* from,
|
|
jit::BaselineFrame* to);
|
|
static MOZ_MUST_USE bool handleIonBailout(JSContext* cx, jit::RematerializedFrame* from,
|
|
jit::BaselineFrame* to);
|
|
static void handleUnrecoverableIonBailoutError(JSContext* cx, jit::RematerializedFrame* frame);
|
|
static void propagateForcedReturn(JSContext* cx, AbstractFramePtr frame, HandleValue rval);
|
|
static bool hasLiveHook(GlobalObject* global, Hook which);
|
|
static bool inFrameMaps(AbstractFramePtr frame);
|
|
|
|
/************************************* Functions for use by Debugger.cpp. */
|
|
|
|
inline bool observesEnterFrame() const;
|
|
inline bool observesNewScript() const;
|
|
inline bool observesNewGlobalObject() const;
|
|
inline bool observesGlobal(GlobalObject* global) const;
|
|
bool observesFrame(AbstractFramePtr frame) const;
|
|
bool observesFrame(const FrameIter& iter) const;
|
|
bool observesScript(JSScript* script) const;
|
|
|
|
/*
|
|
* If env is nullptr, call vp->setNull() and return true. Otherwise, find
|
|
* or create a Debugger.Environment object for the given Env. On success,
|
|
* store the Environment object in *vp and return true.
|
|
*/
|
|
MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env, MutableHandleValue vp);
|
|
MOZ_MUST_USE bool wrapEnvironment(JSContext* cx, Handle<Env*> env,
|
|
MutableHandleDebuggerEnvironment result);
|
|
|
|
/*
|
|
* Like cx->compartment()->wrap(cx, vp), but for the debugger compartment.
|
|
*
|
|
* Preconditions: *vp is a value from a debuggee compartment; cx is in the
|
|
* debugger's compartment.
|
|
*
|
|
* If *vp is an object, this produces a (new or existing) Debugger.Object
|
|
* wrapper for it. Otherwise this is the same as JSCompartment::wrap.
|
|
*
|
|
* If *vp is a magic JS_OPTIMIZED_OUT value, this produces a plain object
|
|
* of the form { optimizedOut: true }.
|
|
*
|
|
* If *vp is a magic JS_OPTIMIZED_ARGUMENTS value signifying missing
|
|
* arguments, this produces a plain object of the form { missingArguments:
|
|
* true }.
|
|
*
|
|
* If *vp is a magic JS_UNINITIALIZED_LEXICAL value signifying an
|
|
* unaccessible uninitialized binding, this produces a plain object of the
|
|
* form { uninitialized: true }.
|
|
*/
|
|
MOZ_MUST_USE bool wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
|
|
MOZ_MUST_USE bool wrapDebuggeeObject(JSContext* cx, HandleObject obj,
|
|
MutableHandleDebuggerObject result);
|
|
|
|
/*
|
|
* Unwrap a Debug.Object, without rewrapping it for any particular debuggee
|
|
* compartment.
|
|
*
|
|
* Preconditions: cx is in the debugger compartment. *vp is a value in that
|
|
* compartment. (*vp should be a "debuggee value", meaning it is the
|
|
* debugger's reflection of a value in the debuggee.)
|
|
*
|
|
* If *vp is a Debugger.Object, store the referent in *vp. Otherwise, if *vp
|
|
* is an object, throw a TypeError, because it is not a debuggee
|
|
* value. Otherwise *vp is a primitive, so leave it alone.
|
|
*
|
|
* When passing values from the debuggee to the debugger:
|
|
* enter debugger compartment;
|
|
* call wrapDebuggeeValue; // compartment- and debugger-wrapping
|
|
*
|
|
* When passing values from the debugger to the debuggee:
|
|
* call unwrapDebuggeeValue; // debugger-unwrapping
|
|
* enter debuggee compartment;
|
|
* call cx->compartment()->wrap; // compartment-rewrapping
|
|
*
|
|
* (Extreme nerd sidebar: Unwrapping happens in two steps because there are
|
|
* two different kinds of symmetry at work: regardless of which direction
|
|
* we're going, we want any exceptions to be created and thrown in the
|
|
* debugger compartment--mirror symmetry. But compartment wrapping always
|
|
* happens in the target compartment--rotational symmetry.)
|
|
*/
|
|
MOZ_MUST_USE bool unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp);
|
|
MOZ_MUST_USE bool unwrapDebuggeeObject(JSContext* cx, MutableHandleObject obj);
|
|
MOZ_MUST_USE bool unwrapPropertyDescriptor(JSContext* cx, HandleObject obj,
|
|
MutableHandle<PropertyDescriptor> desc);
|
|
|
|
/*
|
|
* Store the Debugger.Frame object for frame in *vp.
|
|
*
|
|
* Use this if you have already access to a frame pointer without having
|
|
* to incur the cost of walking the stack.
|
|
*/
|
|
MOZ_MUST_USE bool getScriptFrame(JSContext* cx, AbstractFramePtr frame, MutableHandleValue vp) {
|
|
return getScriptFrameWithIter(cx, frame, nullptr, vp);
|
|
}
|
|
|
|
/*
|
|
* Store the Debugger.Frame object for iter in *vp/result. Eagerly copies a
|
|
* ScriptFrameIter::Data.
|
|
*
|
|
* Use this if you had to make a ScriptFrameIter to get the required
|
|
* frame, in which case the cost of walking the stack has already been
|
|
* paid.
|
|
*/
|
|
MOZ_MUST_USE bool getScriptFrame(JSContext* cx, const ScriptFrameIter& iter,
|
|
MutableHandleValue vp) {
|
|
return getScriptFrameWithIter(cx, iter.abstractFramePtr(), &iter, vp);
|
|
}
|
|
MOZ_MUST_USE bool getScriptFrame(JSContext* cx, const ScriptFrameIter& iter,
|
|
MutableHandleDebuggerFrame result);
|
|
|
|
|
|
/*
|
|
* Set |*status| and |*value| to a (JSTrapStatus, Value) pair reflecting a
|
|
* standard SpiderMonkey call state: a boolean success value |ok|, a return
|
|
* value |rv|, and a context |cx| that may or may not have an exception set.
|
|
* If an exception was pending on |cx|, it is cleared (and |ok| is asserted
|
|
* to be false).
|
|
*/
|
|
static void resultToCompletion(JSContext* cx, bool ok, const Value& rv,
|
|
JSTrapStatus* status, MutableHandleValue value);
|
|
|
|
/*
|
|
* Set |*result| to a JavaScript completion value corresponding to |status|
|
|
* and |value|. |value| should be the return value or exception value, not
|
|
* wrapped as a debuggee value. |cx| must be in the debugger compartment.
|
|
*/
|
|
MOZ_MUST_USE bool newCompletionValue(JSContext* cx, JSTrapStatus status, const Value& value,
|
|
MutableHandleValue result);
|
|
|
|
/*
|
|
* Precondition: we are in the debuggee compartment (ac is entered) and ok
|
|
* is true if the operation in the debuggee compartment succeeded, false on
|
|
* error or exception.
|
|
*
|
|
* Postcondition: we are in the debugger compartment, having called
|
|
* ac.leave() even if an error occurred.
|
|
*
|
|
* On success, a completion value is in vp and ac.context does not have a
|
|
* pending exception. (This ordinarily returns true even if the ok argument
|
|
* is false.)
|
|
*/
|
|
MOZ_MUST_USE bool receiveCompletionValue(mozilla::Maybe<AutoCompartment>& ac, bool ok,
|
|
HandleValue val,
|
|
MutableHandleValue vp);
|
|
|
|
/*
|
|
* Return the Debugger.Script object for |script|, or create a new one if
|
|
* needed. The context |cx| must be in the debugger compartment; |script|
|
|
* must be a script in a debuggee compartment.
|
|
*/
|
|
JSObject* wrapScript(JSContext* cx, HandleScript script);
|
|
|
|
/*
|
|
* Return the Debugger.Script object for |wasmInstance| (the toplevel
|
|
* script), synthesizing a new one if needed. The context |cx| must be in
|
|
* the debugger compartment; |wasmInstance| must be a WasmInstanceObject in
|
|
* the debuggee compartment.
|
|
*/
|
|
JSObject* wrapWasmScript(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
|
|
|
|
/*
|
|
* Return the Debugger.Source object for |source|, or create a new one if
|
|
* needed. The context |cx| must be in the debugger compartment; |source|
|
|
* must be a script source object in a debuggee compartment.
|
|
*/
|
|
JSObject* wrapSource(JSContext* cx, js::HandleScriptSource source);
|
|
|
|
/*
|
|
* Return the Debugger.Source object for |wasmInstance| (the entire module),
|
|
* synthesizing a new one if needed. The context |cx| must be in the
|
|
* debugger compartment; |wasmInstance| must be a WasmInstanceObject in the
|
|
* debuggee compartment.
|
|
*/
|
|
JSObject* wrapWasmSource(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
|
|
|
|
private:
|
|
Debugger(const Debugger&) = delete;
|
|
Debugger & operator=(const Debugger&) = delete;
|
|
};
|
|
|
|
enum class DebuggerEnvironmentType {
|
|
Declarative,
|
|
With,
|
|
Object
|
|
};
|
|
|
|
class DebuggerEnvironment : public NativeObject
|
|
{
|
|
public:
|
|
enum {
|
|
OWNER_SLOT
|
|
};
|
|
|
|
static const unsigned RESERVED_SLOTS = 1;
|
|
|
|
static const Class class_;
|
|
|
|
static NativeObject* initClass(JSContext* cx, HandleObject dbgCtor, HandleObject objProto);
|
|
static DebuggerEnvironment* create(JSContext* cx, HandleObject proto, HandleObject referent,
|
|
HandleNativeObject debugger);
|
|
|
|
DebuggerEnvironmentType type() const;
|
|
MOZ_MUST_USE bool getParent(JSContext* cx, MutableHandleDebuggerEnvironment result) const;
|
|
MOZ_MUST_USE bool getObject(JSContext* cx, MutableHandleDebuggerObject result) const;
|
|
MOZ_MUST_USE bool getCallee(JSContext* cx, MutableHandleDebuggerObject result) const;
|
|
bool isDebuggee() const;
|
|
bool isOptimized() const;
|
|
|
|
static MOZ_MUST_USE bool getNames(JSContext* cx, HandleDebuggerEnvironment environment,
|
|
MutableHandle<IdVector> result);
|
|
static MOZ_MUST_USE bool find(JSContext* cx, HandleDebuggerEnvironment environment,
|
|
HandleId id, MutableHandleDebuggerEnvironment result);
|
|
static MOZ_MUST_USE bool getVariable(JSContext* cx, HandleDebuggerEnvironment environment,
|
|
HandleId id, MutableHandleValue result);
|
|
static MOZ_MUST_USE bool setVariable(JSContext* cx, HandleDebuggerEnvironment environment,
|
|
HandleId id, HandleValue value);
|
|
|
|
private:
|
|
static const ClassOps classOps_;
|
|
|
|
static const JSPropertySpec properties_[];
|
|
static const JSFunctionSpec methods_[];
|
|
|
|
Env* referent() const {
|
|
Env* env = static_cast<Env*>(getPrivate());
|
|
MOZ_ASSERT(env);
|
|
return env;
|
|
}
|
|
|
|
Debugger* owner() const;
|
|
|
|
bool requireDebuggee(JSContext* cx) const;
|
|
|
|
static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static MOZ_MUST_USE bool typeGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool parentGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool objectGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool calleeGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool inspectableGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool optimizedOutGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static MOZ_MUST_USE bool namesMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool findMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool getVariableMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool setVariableMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
};
|
|
|
|
enum class DebuggerFrameType {
|
|
Eval,
|
|
Global,
|
|
Call,
|
|
Module
|
|
};
|
|
|
|
enum class DebuggerFrameImplementation {
|
|
Interpreter,
|
|
Baseline,
|
|
Ion
|
|
};
|
|
|
|
class DebuggerFrame : public NativeObject
|
|
{
|
|
public:
|
|
enum {
|
|
OWNER_SLOT
|
|
};
|
|
|
|
static const unsigned RESERVED_SLOTS = 1;
|
|
|
|
static const Class class_;
|
|
|
|
static NativeObject* initClass(JSContext* cx, HandleObject dbgCtor, HandleObject objProto);
|
|
static DebuggerFrame* create(JSContext* cx, HandleObject proto, AbstractFramePtr referent,
|
|
const ScriptFrameIter* maybeIter, HandleNativeObject debugger);
|
|
|
|
static MOZ_MUST_USE bool getCallee(JSContext* cx, HandleDebuggerFrame frame,
|
|
MutableHandleDebuggerObject result);
|
|
static MOZ_MUST_USE bool getIsConstructing(JSContext* cx, HandleDebuggerFrame frame,
|
|
bool& result);
|
|
static MOZ_MUST_USE bool getEnvironment(JSContext* cx, HandleDebuggerFrame frame,
|
|
MutableHandleDebuggerEnvironment result);
|
|
static bool getIsGenerator(HandleDebuggerFrame frame);
|
|
static MOZ_MUST_USE bool getOffset(JSContext* cx, HandleDebuggerFrame frame, size_t& result);
|
|
static MOZ_MUST_USE bool getOlder(JSContext* cx, HandleDebuggerFrame frame,
|
|
MutableHandleDebuggerFrame result);
|
|
static MOZ_MUST_USE bool getThis(JSContext* cx, HandleDebuggerFrame frame,
|
|
MutableHandleValue result);
|
|
static DebuggerFrameType getType(HandleDebuggerFrame frame);
|
|
static DebuggerFrameImplementation getImplementation(HandleDebuggerFrame frame);
|
|
|
|
static MOZ_MUST_USE bool eval(JSContext* cx, HandleDebuggerFrame frame,
|
|
mozilla::Range<const char16_t> chars, HandleObject bindings,
|
|
const EvalOptions& options, JSTrapStatus& status,
|
|
MutableHandleValue value);
|
|
|
|
bool isLive() const;
|
|
|
|
private:
|
|
static const ClassOps classOps_;
|
|
|
|
static const JSPropertySpec properties_[];
|
|
static const JSFunctionSpec methods_[];
|
|
|
|
static AbstractFramePtr getReferent(HandleDebuggerFrame frame);
|
|
static MOZ_MUST_USE bool getScriptFrameIter(JSContext* cx, HandleDebuggerFrame frame,
|
|
mozilla::Maybe<ScriptFrameIter>& result);
|
|
|
|
static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static MOZ_MUST_USE bool calleeGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool constructingGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool generatorGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool liveGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool offsetGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool olderGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool thisGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool typeGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool implementationGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
static MOZ_MUST_USE bool evalMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool evalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
Debugger* owner() const;
|
|
};
|
|
|
|
class DebuggerObject : public NativeObject
|
|
{
|
|
public:
|
|
static const Class class_;
|
|
|
|
static NativeObject* initClass(JSContext* cx, HandleObject obj, HandleObject debugCtor);
|
|
static DebuggerObject* create(JSContext* cx, HandleObject proto, HandleObject obj,
|
|
HandleNativeObject debugger);
|
|
|
|
// Properties
|
|
static MOZ_MUST_USE bool getClassName(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleString result);
|
|
static MOZ_MUST_USE bool getGlobal(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleDebuggerObject result);
|
|
static MOZ_MUST_USE bool getParameterNames(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandle<StringVector> result);
|
|
static MOZ_MUST_USE bool getBoundTargetFunction(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleDebuggerObject result);
|
|
static MOZ_MUST_USE bool getBoundThis(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleValue result);
|
|
static MOZ_MUST_USE bool getBoundArguments(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandle<ValueVector> result);
|
|
static MOZ_MUST_USE bool getAllocationSite(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleObject result);
|
|
static MOZ_MUST_USE bool getErrorMessageName(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleString result);
|
|
static MOZ_MUST_USE bool getErrorNotes(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleValue result);
|
|
static MOZ_MUST_USE bool getErrorLineNumber(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleValue result);
|
|
static MOZ_MUST_USE bool getErrorColumnNumber(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleValue result);
|
|
static MOZ_MUST_USE bool getScriptedProxyTarget(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleDebuggerObject result);
|
|
static MOZ_MUST_USE bool getScriptedProxyHandler(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleDebuggerObject result);
|
|
static MOZ_MUST_USE bool getPromiseValue(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleValue result);
|
|
static MOZ_MUST_USE bool getPromiseReason(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleValue result);
|
|
|
|
// Methods
|
|
static MOZ_MUST_USE bool isExtensible(JSContext* cx, HandleDebuggerObject object,
|
|
bool& result);
|
|
static MOZ_MUST_USE bool isSealed(JSContext* cx, HandleDebuggerObject object, bool& result);
|
|
static MOZ_MUST_USE bool isFrozen(JSContext* cx, HandleDebuggerObject object, bool& result);
|
|
static MOZ_MUST_USE bool getPrototypeOf(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleDebuggerObject result);
|
|
static MOZ_MUST_USE bool getOwnPropertyNames(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandle<IdVector> result);
|
|
static MOZ_MUST_USE bool getOwnPropertySymbols(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandle<IdVector> result);
|
|
static MOZ_MUST_USE bool getOwnPropertyDescriptor(JSContext* cx, HandleDebuggerObject object,
|
|
HandleId id,
|
|
MutableHandle<PropertyDescriptor> desc);
|
|
static MOZ_MUST_USE bool preventExtensions(JSContext* cx, HandleDebuggerObject object);
|
|
static MOZ_MUST_USE bool seal(JSContext* cx, HandleDebuggerObject object);
|
|
static MOZ_MUST_USE bool freeze(JSContext* cx, HandleDebuggerObject object);
|
|
static MOZ_MUST_USE bool defineProperty(JSContext* cx, HandleDebuggerObject object,
|
|
HandleId id, Handle<PropertyDescriptor> desc);
|
|
static MOZ_MUST_USE bool defineProperties(JSContext* cx, HandleDebuggerObject object,
|
|
Handle<IdVector> ids,
|
|
Handle<PropertyDescriptorVector> descs);
|
|
static MOZ_MUST_USE bool deleteProperty(JSContext* cx, HandleDebuggerObject object,
|
|
HandleId id, ObjectOpResult& result);
|
|
static MOZ_MUST_USE bool call(JSContext* cx, HandleDebuggerObject object, HandleValue thisv,
|
|
Handle<ValueVector> args, MutableHandleValue result);
|
|
static MOZ_MUST_USE bool forceLexicalInitializationByName(JSContext* cx,
|
|
HandleDebuggerObject object,
|
|
HandleId id, bool& result);
|
|
static MOZ_MUST_USE bool executeInGlobal(JSContext* cx, HandleDebuggerObject object,
|
|
mozilla::Range<const char16_t> chars,
|
|
HandleObject bindings, const EvalOptions& options,
|
|
JSTrapStatus& status, MutableHandleValue value);
|
|
static MOZ_MUST_USE bool makeDebuggeeValue(JSContext* cx, HandleDebuggerObject object,
|
|
HandleValue value, MutableHandleValue result);
|
|
static MOZ_MUST_USE bool unsafeDereference(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleObject result);
|
|
static MOZ_MUST_USE bool unwrap(JSContext* cx, HandleDebuggerObject object,
|
|
MutableHandleDebuggerObject result);
|
|
|
|
// Infallible properties
|
|
bool isCallable() const;
|
|
bool isFunction() const;
|
|
bool isDebuggeeFunction() const;
|
|
bool isBoundFunction() const;
|
|
bool isArrowFunction() const;
|
|
bool isGlobal() const;
|
|
bool isScriptedProxy() const;
|
|
bool isPromise() const;
|
|
JSAtom* name() const;
|
|
JSAtom* displayName() const;
|
|
JS::PromiseState promiseState() const;
|
|
double promiseLifetime() const;
|
|
double promiseTimeToResolution() const;
|
|
|
|
private:
|
|
enum {
|
|
OWNER_SLOT
|
|
};
|
|
|
|
static const unsigned RESERVED_SLOTS = 1;
|
|
|
|
static const ClassOps classOps_;
|
|
|
|
static const JSPropertySpec properties_[];
|
|
static const JSPropertySpec promiseProperties_[];
|
|
static const JSFunctionSpec methods_[];
|
|
|
|
JSObject* referent() const {
|
|
JSObject* obj = (JSObject*) getPrivate();
|
|
MOZ_ASSERT(obj);
|
|
return obj;
|
|
}
|
|
|
|
Debugger* owner() const;
|
|
|
|
PromiseObject* promise() const;
|
|
|
|
static MOZ_MUST_USE bool requireGlobal(JSContext* cx, HandleDebuggerObject object);
|
|
static MOZ_MUST_USE bool requirePromise(JSContext* cx, HandleDebuggerObject object);
|
|
|
|
static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
// JSNative properties
|
|
static MOZ_MUST_USE bool callableGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool isBoundFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool isArrowFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool protoGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool classGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool nameGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool displayNameGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool parameterNamesGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool scriptGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool boundTargetFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool boundThisGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool boundArgumentsGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool globalGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool allocationSiteGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool errorMessageNameGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool errorNotesGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool errorLineNumberGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool errorColumnNumberGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool isProxyGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool proxyTargetGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool proxyHandlerGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool isPromiseGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool promiseStateGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool promiseValueGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool promiseReasonGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool promiseLifetimeGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool promiseTimeToResolutionGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool promiseAllocationSiteGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool promiseResolutionSiteGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool promiseIDGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool promiseDependentPromisesGetter(JSContext* cx, unsigned argc, Value* vp);
|
|
|
|
// JSNative methods
|
|
static MOZ_MUST_USE bool isExtensibleMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool isSealedMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool isFrozenMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool getOwnPropertyNamesMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool getOwnPropertySymbolsMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool getOwnPropertyDescriptorMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool preventExtensionsMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool sealMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool freezeMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool definePropertyMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool definePropertiesMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool deletePropertyMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool callMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool applyMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool asEnvironmentMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool forceLexicalInitializationByNameMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool executeInGlobalMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool executeInGlobalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool makeDebuggeeValueMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool unsafeDereferenceMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool unwrapMethod(JSContext* cx, unsigned argc, Value* vp);
|
|
static MOZ_MUST_USE bool getErrorReport(JSContext* cx, HandleObject maybeError,
|
|
JSErrorReport*& report);
|
|
};
|
|
|
|
class BreakpointSite {
|
|
friend class Breakpoint;
|
|
friend struct ::JSCompartment;
|
|
friend class ::JSScript;
|
|
friend class Debugger;
|
|
|
|
public:
|
|
JSScript* script;
|
|
jsbytecode * const pc;
|
|
|
|
private:
|
|
JSCList breakpoints; /* cyclic list of all js::Breakpoints at this instruction */
|
|
size_t enabledCount; /* number of breakpoints in the list that are enabled */
|
|
|
|
void recompile(FreeOp* fop);
|
|
|
|
public:
|
|
BreakpointSite(JSScript* script, jsbytecode* pc);
|
|
Breakpoint* firstBreakpoint() const;
|
|
bool hasBreakpoint(Breakpoint* bp);
|
|
|
|
void inc(FreeOp* fop);
|
|
void dec(FreeOp* fop);
|
|
void destroyIfEmpty(FreeOp* fop);
|
|
};
|
|
|
|
/*
|
|
* Each Breakpoint is a member of two linked lists: its debugger's list and its
|
|
* site's list.
|
|
*
|
|
* GC rules:
|
|
* - script is live, breakpoint exists, and debugger is enabled
|
|
* ==> debugger is live
|
|
* - script is live, breakpoint exists, and debugger is live
|
|
* ==> retain the breakpoint and the handler object is live
|
|
*
|
|
* Debugger::markAllIteratively implements these two rules. It uses
|
|
* Debugger::hasAnyLiveHooks to check for rule 1.
|
|
*
|
|
* Nothing else causes a breakpoint to be retained, so if its script or
|
|
* debugger is collected, the breakpoint is destroyed during GC sweep phase,
|
|
* even if the debugger compartment isn't being GC'd. This is implemented in
|
|
* Zone::sweepBreakpoints.
|
|
*/
|
|
class Breakpoint {
|
|
friend struct ::JSCompartment;
|
|
friend class Debugger;
|
|
|
|
public:
|
|
Debugger * const debugger;
|
|
BreakpointSite * const site;
|
|
private:
|
|
/* |handler| is marked unconditionally during minor GC. */
|
|
js::PreBarrieredObject handler;
|
|
JSCList debuggerLinks;
|
|
JSCList siteLinks;
|
|
|
|
public:
|
|
static Breakpoint* fromDebuggerLinks(JSCList* links);
|
|
static Breakpoint* fromSiteLinks(JSCList* links);
|
|
Breakpoint(Debugger* debugger, BreakpointSite* site, JSObject* handler);
|
|
void destroy(FreeOp* fop);
|
|
Breakpoint* nextInDebugger();
|
|
Breakpoint* nextInSite();
|
|
const PreBarrieredObject& getHandler() const { return handler; }
|
|
PreBarrieredObject& getHandlerRef() { return handler; }
|
|
};
|
|
|
|
Breakpoint*
|
|
Debugger::firstBreakpoint() const
|
|
{
|
|
if (JS_CLIST_IS_EMPTY(&breakpoints))
|
|
return nullptr;
|
|
return Breakpoint::fromDebuggerLinks(JS_NEXT_LINK(&breakpoints));
|
|
}
|
|
|
|
/* static */ Debugger*
|
|
Debugger::fromOnNewGlobalObjectWatchersLink(JSCList* link) {
|
|
char* p = reinterpret_cast<char*>(link);
|
|
return reinterpret_cast<Debugger*>(p - offsetof(Debugger, onNewGlobalObjectWatchersLink));
|
|
}
|
|
|
|
const js::GCPtrNativeObject&
|
|
Debugger::toJSObject() const
|
|
{
|
|
MOZ_ASSERT(object);
|
|
return object;
|
|
}
|
|
|
|
js::GCPtrNativeObject&
|
|
Debugger::toJSObjectRef()
|
|
{
|
|
MOZ_ASSERT(object);
|
|
return object;
|
|
}
|
|
|
|
bool
|
|
Debugger::observesEnterFrame() const
|
|
{
|
|
return enabled && getHook(OnEnterFrame);
|
|
}
|
|
|
|
bool
|
|
Debugger::observesNewScript() const
|
|
{
|
|
return enabled && getHook(OnNewScript);
|
|
}
|
|
|
|
bool
|
|
Debugger::observesNewGlobalObject() const
|
|
{
|
|
return enabled && getHook(OnNewGlobalObject);
|
|
}
|
|
|
|
bool
|
|
Debugger::observesGlobal(GlobalObject* global) const
|
|
{
|
|
ReadBarriered<GlobalObject*> debuggee(global);
|
|
return debuggees.has(debuggee);
|
|
}
|
|
|
|
/* static */ void
|
|
Debugger::onNewScript(JSContext* cx, HandleScript script)
|
|
{
|
|
// We early return in slowPathOnNewScript for self-hosted scripts, so we can
|
|
// ignore those in our assertion here.
|
|
MOZ_ASSERT_IF(!script->compartment()->creationOptions().invisibleToDebugger() &&
|
|
!script->selfHosted(),
|
|
script->compartment()->firedOnNewGlobalObject);
|
|
if (script->compartment()->isDebuggee())
|
|
slowPathOnNewScript(cx, script);
|
|
}
|
|
|
|
/* static */ void
|
|
Debugger::onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global)
|
|
{
|
|
MOZ_ASSERT(!global->compartment()->firedOnNewGlobalObject);
|
|
#ifdef DEBUG
|
|
global->compartment()->firedOnNewGlobalObject = true;
|
|
#endif
|
|
if (!JS_CLIST_IS_EMPTY(&cx->runtime()->onNewGlobalObjectWatchers))
|
|
Debugger::slowPathOnNewGlobalObject(cx, global);
|
|
}
|
|
|
|
/* static */ bool
|
|
Debugger::onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame, double when)
|
|
{
|
|
GlobalObject::DebuggerVector* dbgs = cx->global()->getDebuggers();
|
|
if (!dbgs || dbgs->empty())
|
|
return true;
|
|
RootedObject hobj(cx, obj);
|
|
return Debugger::slowPathOnLogAllocationSite(cx, hobj, frame, when, *dbgs);
|
|
}
|
|
|
|
MOZ_MUST_USE bool ReportObjectRequired(JSContext* cx);
|
|
|
|
} /* namespace js */
|
|
|
|
namespace JS {
|
|
|
|
template <>
|
|
struct DeletePolicy<js::Debugger> : public js::GCManagedDeletePolicy<js::Debugger>
|
|
{};
|
|
|
|
} /* namespace JS */
|
|
|
|
#endif /* vm_Debugger_h */
|