|  | /* -*- 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 */ |