| /* -*- 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/. */ |
| |
| // Interfaces by which the embedding can interact with the Debugger API. |
| |
| #ifndef js_Debug_h |
| #define js_Debug_h |
| |
| #include "mozilla/Assertions.h" |
| #include "mozilla/Attributes.h" |
| #include "mozilla/MemoryReporting.h" |
| #include "mozilla/UniquePtr.h" |
| |
| #include "jsapi.h" |
| #include "jspubtd.h" |
| |
| #include "js/GCAPI.h" |
| #include "js/RootingAPI.h" |
| #include "js/TypeDecls.h" |
| |
| namespace js { |
| class Debugger; |
| } // namespace js |
| |
| namespace JS { |
| |
| using mozilla::UniquePtr; |
| |
| namespace dbg { |
| |
| // Helping embedding code build objects for Debugger |
| // ------------------------------------------------- |
| // |
| // Some Debugger API features lean on the embedding application to construct |
| // their result values. For example, Debugger.Frame.prototype.scriptEntryReason |
| // calls hooks provided by the embedding to construct values explaining why it |
| // invoked JavaScript; if F is a frame called from a mouse click event handler, |
| // F.scriptEntryReason would return an object of the form: |
| // |
| // { eventType: "mousedown", event: <object> } |
| // |
| // where <object> is a Debugger.Object whose referent is the event being |
| // dispatched. |
| // |
| // However, Debugger implements a trust boundary. Debuggee code may be |
| // considered untrusted; debugger code needs to be protected from debuggee |
| // getters, setters, proxies, Object.watch watchpoints, and any other feature |
| // that might accidentally cause debugger code to set the debuggee running. The |
| // Debugger API tries to make it easy to write safe debugger code by only |
| // offering access to debuggee objects via Debugger.Object instances, which |
| // ensure that only those operations whose explicit purpose is to invoke |
| // debuggee code do so. But this protective membrane is only helpful if we |
| // interpose Debugger.Object instances in all the necessary spots. |
| // |
| // SpiderMonkey's compartment system also implements a trust boundary. The |
| // debuggee and debugger are always in different compartments. Inter-compartment |
| // work requires carefully tracking which compartment each JSObject or JS::Value |
| // belongs to, and ensuring that is is correctly wrapped for each operation. |
| // |
| // It seems precarious to expect the embedding's hooks to implement these trust |
| // boundaries. Instead, the JS::dbg::Builder API segregates the code which |
| // constructs trusted objects from that which deals with untrusted objects. |
| // Trusted objects have an entirely different C++ type, so code that improperly |
| // mixes trusted and untrusted objects is caught at compile time. |
| // |
| // In the structure shown above, there are two trusted objects, and one |
| // untrusted object: |
| // |
| // - The overall object, with the 'eventType' and 'event' properties, is a |
| // trusted object. We're going to return it to D.F.p.scriptEntryReason's |
| // caller, which will handle it directly. |
| // |
| // - The Debugger.Object instance appearing as the value of the 'event' property |
| // is a trusted object. It belongs to the same Debugger instance as the |
| // Debugger.Frame instance whose scriptEntryReason accessor was called, and |
| // presents a safe reflection-oriented API for inspecting its referent, which |
| // is: |
| // |
| // - The actual event object, an untrusted object, and the referent of the |
| // Debugger.Object above. (Content can do things like replacing accessors on |
| // Event.prototype.) |
| // |
| // Using JS::dbg::Builder, all objects and values the embedding deals with |
| // directly are considered untrusted, and are assumed to be debuggee values. The |
| // only way to construct trusted objects is to use Builder's own methods, which |
| // return a separate Object type. The only way to set a property on a trusted |
| // object is through that Object type. The actual trusted object is never |
| // exposed to the embedding. |
| // |
| // So, for example, the embedding might use code like the following to construct |
| // the object shown above, given a Builder passed to it by Debugger: |
| // |
| // bool |
| // MyScriptEntryReason::explain(JSContext* cx, |
| // Builder& builder, |
| // Builder::Object& result) |
| // { |
| // JSObject* eventObject = ... obtain debuggee event object somehow ...; |
| // if (!eventObject) |
| // return false; |
| // result = builder.newObject(cx); |
| // return result && |
| // result.defineProperty(cx, "eventType", SafelyFetchType(eventObject)) && |
| // result.defineProperty(cx, "event", eventObject); |
| // } |
| // |
| // |
| // Object::defineProperty also accepts an Object as the value to store on the |
| // property. By its type, we know that the value is trusted, so we set it |
| // directly as the property's value, without interposing a Debugger.Object |
| // wrapper. This allows the embedding to builted nested structures of trusted |
| // objects. |
| // |
| // The Builder and Builder::Object methods take care of doing whatever |
| // compartment switching and wrapping are necessary to construct the trusted |
| // values in the Debugger's compartment. |
| // |
| // The Object type is self-rooting. Construction, assignment, and destruction |
| // all properly root the referent object. |
| |
| class BuilderOrigin; |
| |
| class Builder { |
| // The Debugger instance whose client we are building a value for. We build |
| // objects in this object's compartment. |
| PersistentRootedObject debuggerObject; |
| |
| // debuggerObject's Debugger structure, for convenience. |
| js::Debugger* debugger; |
| |
| // Check that |thing| is in the same compartment as our debuggerObject. Used |
| // for assertions when constructing BuiltThings. We can overload this as we |
| // add more instantiations of BuiltThing. |
| #if DEBUG |
| void assertBuilt(JSObject* obj); |
| #else |
| void assertBuilt(JSObject* obj) { } |
| #endif |
| |
| protected: |
| // A reference to a trusted object or value. At the moment, we only use it |
| // with JSObject*. |
| template<typename T> |
| class BuiltThing { |
| friend class BuilderOrigin; |
| |
| protected: |
| // The Builder to which this trusted thing belongs. |
| Builder& owner; |
| |
| // A rooted reference to our value. |
| PersistentRooted<T> value; |
| |
| BuiltThing(JSContext* cx, Builder& owner_, T value_ = js::GCMethods<T>::initial()) |
| : owner(owner_), value(cx, value_) |
| { |
| owner.assertBuilt(value_); |
| } |
| |
| // Forward some things from our owner, for convenience. |
| js::Debugger* debugger() const { return owner.debugger; } |
| JSObject* debuggerObject() const { return owner.debuggerObject; } |
| |
| public: |
| BuiltThing(const BuiltThing& rhs) : owner(rhs.owner), value(rhs.value) { } |
| BuiltThing& operator=(const BuiltThing& rhs) { |
| MOZ_ASSERT(&owner == &rhs.owner); |
| owner.assertBuilt(rhs.value); |
| value = rhs.value; |
| return *this; |
| } |
| |
| explicit operator bool() const { |
| // If we ever instantiate BuiltThing<Value>, this might not suffice. |
| return value; |
| } |
| |
| private: |
| BuiltThing() = delete; |
| }; |
| |
| public: |
| // A reference to a trusted object, possibly null. Instances of Object are |
| // always properly rooted. They can be copied and assigned, as if they were |
| // pointers. |
| class Object: private BuiltThing<JSObject*> { |
| friend class Builder; // for construction |
| friend class BuilderOrigin; // for unwrapping |
| |
| typedef BuiltThing<JSObject*> Base; |
| |
| // This is private, because only Builders can create Objects that |
| // actually point to something (hence the 'friend' declaration). |
| Object(JSContext* cx, Builder& owner_, HandleObject obj) : Base(cx, owner_, obj.get()) { } |
| |
| bool definePropertyToTrusted(JSContext* cx, const char* name, |
| JS::MutableHandleValue value); |
| |
| public: |
| Object(JSContext* cx, Builder& owner_) : Base(cx, owner_, nullptr) { } |
| Object(const Object& rhs) : Base(rhs) { } |
| |
| // Our automatically-generated assignment operator can see our base |
| // class's assignment operator, so we don't need to write one out here. |
| |
| // Set the property named |name| on this object to |value|. |
| // |
| // If |value| is a string or primitive, re-wrap it for the debugger's |
| // compartment. |
| // |
| // If |value| is an object, assume it is a debuggee object and make a |
| // Debugger.Object instance referring to it. Set that as the propery's |
| // value. |
| // |
| // If |value| is another trusted object, store it directly as the |
| // property's value. |
| // |
| // On error, report the problem on cx and return false. |
| bool defineProperty(JSContext* cx, const char* name, JS::HandleValue value); |
| bool defineProperty(JSContext* cx, const char* name, JS::HandleObject value); |
| bool defineProperty(JSContext* cx, const char* name, Object& value); |
| |
| using Base::operator bool; |
| }; |
| |
| // Build an empty object for direct use by debugger code, owned by this |
| // Builder. If an error occurs, report it on cx and return a false Object. |
| Object newObject(JSContext* cx); |
| |
| protected: |
| Builder(JSContext* cx, js::Debugger* debugger); |
| }; |
| |
| // Debugger itself instantiates this subclass of Builder, which can unwrap |
| // BuiltThings that belong to it. |
| class BuilderOrigin : public Builder { |
| template<typename T> |
| T unwrapAny(const BuiltThing<T>& thing) { |
| MOZ_ASSERT(&thing.owner == this); |
| return thing.value.get(); |
| } |
| |
| public: |
| BuilderOrigin(JSContext* cx, js::Debugger* debugger_) |
| : Builder(cx, debugger_) |
| { } |
| |
| JSObject* unwrap(Object& object) { return unwrapAny(object); } |
| }; |
| |
| |
| |
| // Finding the size of blocks allocated with malloc |
| // ------------------------------------------------ |
| // |
| // Debugger.Memory wants to be able to report how many bytes items in memory are |
| // consuming. To do this, it needs a function that accepts a pointer to a block, |
| // and returns the number of bytes allocated to that block. SpiderMonkey itself |
| // doesn't know which function is appropriate to use, but the embedding does. |
| |
| // Tell Debuggers in |runtime| to use |mallocSizeOf| to find the size of |
| // malloc'd blocks. |
| JS_PUBLIC_API(void) |
| SetDebuggerMallocSizeOf(JSRuntime* runtime, mozilla::MallocSizeOf mallocSizeOf); |
| |
| // Get the MallocSizeOf function that the given runtime is using to find the |
| // size of malloc'd blocks. |
| JS_PUBLIC_API(mozilla::MallocSizeOf) |
| GetDebuggerMallocSizeOf(JSRuntime* runtime); |
| |
| |
| |
| // Debugger and Garbage Collection Events |
| // -------------------------------------- |
| // |
| // The Debugger wants to report about its debuggees' GC cycles, however entering |
| // JS after a GC is troublesome since SpiderMonkey will often do something like |
| // force a GC and then rely on the nursery being empty. If we call into some |
| // Debugger's hook after the GC, then JS runs and the nursery won't be |
| // empty. Instead, we rely on embedders to call back into SpiderMonkey after a |
| // GC and notify Debuggers to call their onGarbageCollection hook. |
| |
| |
| // For each Debugger that observed a debuggee involved in the given GC event, |
| // call its `onGarbageCollection` hook. |
| JS_PUBLIC_API(bool) |
| FireOnGarbageCollectionHook(JSContext* cx, GarbageCollectionEvent::Ptr&& data); |
| |
| |
| |
| // Handlers for observing Promises |
| // ------------------------------- |
| // |
| // The Debugger wants to observe behavior of promises, which are implemented by |
| // Gecko with webidl and which SpiderMonkey knows nothing about. On the other |
| // hand, Gecko knows nothing about which (if any) debuggers are observing a |
| // promise's global. The compromise is that Gecko is responsible for calling |
| // these handlers at the appropriate times, and SpiderMonkey will handle |
| // notifying any Debugger instances that are observing the given promise's |
| // global. |
| |
| // Notify any Debugger instances observing this promise's global that a new |
| // promise was allocated. |
| JS_PUBLIC_API(void) |
| onNewPromise(JSContext* cx, HandleObject promise); |
| |
| // Notify any Debugger instances observing this promise's global that the |
| // promise has settled (ie, it has either been fulfilled or rejected). Note that |
| // this is *not* equivalent to the promise resolution (ie, the promise's fate |
| // getting locked in) because you can resolve a promise with another pending |
| // promise, in which case neither promise has settled yet. |
| // |
| // It is Gecko's responsibility to ensure that this is never called on the same |
| // promise more than once (because a promise can only make the transition from |
| // unsettled to settled once). |
| JS_PUBLIC_API(void) |
| onPromiseSettled(JSContext* cx, HandleObject promise); |
| |
| |
| |
| // Return true if the given value is a Debugger object, false otherwise. |
| JS_PUBLIC_API(bool) |
| IsDebugger(JSObject& obj); |
| |
| // Append each of the debuggee global objects observed by the Debugger object |
| // |dbgObj| to |vector|. Returns true on success, false on failure. |
| JS_PUBLIC_API(bool) |
| GetDebuggeeGlobals(JSContext* cx, JSObject& dbgObj, AutoObjectVector& vector); |
| |
| |
| // Hooks for reporting where JavaScript execution began. |
| // |
| // Our performance tools would like to be able to label blocks of JavaScript |
| // execution with the function name and source location where execution began: |
| // the event handler, the callback, etc. |
| // |
| // Construct an instance of this class on the stack, providing a JSContext |
| // belonging to the runtime in which execution will occur. Each time we enter |
| // JavaScript --- specifically, each time we push a JavaScript stack frame that |
| // has no older JS frames younger than this AutoEntryMonitor --- we will |
| // call the appropriate |Entry| member function to indicate where we've begun |
| // execution. |
| |
| class MOZ_STACK_CLASS AutoEntryMonitor { |
| JSRuntime* runtime_; |
| AutoEntryMonitor* savedMonitor_; |
| |
| public: |
| explicit AutoEntryMonitor(JSContext* cx); |
| ~AutoEntryMonitor(); |
| |
| // SpiderMonkey reports the JavaScript entry points occuring within this |
| // AutoEntryMonitor's scope to the following member functions, which the |
| // embedding is expected to override. |
| |
| // We have begun executing |function|. Note that |function| may not be the |
| // actual closure we are running, but only the canonical function object to |
| // which the script refers. |
| virtual void Entry(JSContext* cx, JSFunction* function, |
| HandleValue asyncStack, |
| HandleString asyncCause) = 0; |
| |
| // Execution has begun at the entry point of |script|, which is not a |
| // function body. (This is probably being executed by 'eval' or some |
| // JSAPI equivalent.) |
| virtual void Entry(JSContext* cx, JSScript* script, |
| HandleValue asyncStack, |
| HandleString asyncCause) = 0; |
| |
| // Execution of the function or script has ended. |
| virtual void Exit(JSContext* cx) { } |
| }; |
| |
| |
| |
| } // namespace dbg |
| } // namespace JS |
| |
| |
| #endif /* js_Debug_h */ |