1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // Interfaces by which the embedding can interact with the Debugger API.
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Attributes.h"
14 #include "mozilla/Move.h"
18 #include "js/RootingAPI.h"
19 #include "js/TypeDecls.h"
28 // Helping embedding code build objects for Debugger
29 // -------------------------------------------------
31 // Some Debugger API features lean on the embedding application to construct
32 // their result values. For example, Debugger.Frame.prototype.scriptEntryReason
33 // calls hooks provided by the embedding to construct values explaining why it
34 // invoked JavaScript; if F is a frame called from a mouse click event handler,
35 // F.scriptEntryReason would return an object of the form:
37 // { eventType: "mousedown", event: <object> }
39 // where <object> is a Debugger.Object whose referent is the event being
42 // However, Debugger implements a trust boundary. Debuggee code may be
43 // considered untrusted; debugger code needs to be protected from debuggee
44 // getters, setters, proxies, Object.watch watchpoints, and any other feature
45 // that might accidentally cause debugger code to set the debuggee running. The
46 // Debugger API tries to make it easy to write safe debugger code by only
47 // offering access to debuggee objects via Debugger.Object instances, which
48 // ensure that only those operations whose explicit purpose is to invoke
49 // debuggee code do so. But this protective membrane is only helpful if we
50 // interpose Debugger.Object instances in all the necessary spots.
52 // SpiderMonkey's compartment system also implements a trust boundary. The
53 // debuggee and debugger are always in different compartments. Inter-compartment
54 // work requires carefully tracking which compartment each JSObject or JS::Value
55 // belongs to, and ensuring that is is correctly wrapped for each operation.
57 // It seems precarious to expect the embedding's hooks to implement these trust
58 // boundaries. Instead, the JS::dbg::Builder API segregates the code which
59 // constructs trusted objects from that which deals with untrusted objects.
60 // Trusted objects have an entirely different C++ type, so code that improperly
61 // mixes trusted and untrusted objects is caught at compile time.
63 // In the structure shown above, there are two trusted objects, and one
66 // - The overall object, with the 'eventType' and 'event' properties, is a
67 // trusted object. We're going to return it to D.F.p.scriptEntryReason's
68 // caller, which will handle it directly.
70 // - The Debugger.Object instance appearing as the value of the 'event' property
71 // is a trusted object. It belongs to the same Debugger instance as the
72 // Debugger.Frame instance whose scriptEntryReason accessor was called, and
73 // presents a safe reflection-oriented API for inspecting its referent, which
76 // - The actual event object, an untrusted object, and the referent of the
77 // Debugger.Object above. (Content can do things like replacing accessors on
80 // Using JS::dbg::Builder, all objects and values the embedding deals with
81 // directly are considered untrusted, and are assumed to be debuggee values. The
82 // only way to construct trusted objects is to use Builder's own methods, which
83 // return a separate Object type. The only way to set a property on a trusted
84 // object is through that Object type. The actual trusted object is never
85 // exposed to the embedding.
87 // So, for example, the embedding might use code like the following to construct
88 // the object shown above, given a Builder passed to it by Debugger:
91 // MyScriptEntryReason::explain(JSContext* cx,
93 // Builder::Object& result)
95 // JSObject* eventObject = ... obtain debuggee event object somehow ...;
98 // result = builder.newObject(cx);
100 // result.defineProperty(cx, "eventType", SafelyFetchType(eventObject)) &&
101 // result.defineProperty(cx, "event", eventObject);
105 // Object::defineProperty also accepts an Object as the value to store on the
106 // property. By its type, we know that the value is trusted, so we set it
107 // directly as the property's value, without interposing a Debugger.Object
108 // wrapper. This allows the embedding to builted nested structures of trusted
111 // The Builder and Builder::Object methods take care of doing whatever
112 // compartment switching and wrapping are necessary to construct the trusted
113 // values in the Debugger's compartment.
115 // The Object type is self-rooting. Construction, assignment, and destruction
116 // all properly root the referent object.
121 // The Debugger instance whose client we are building a value for. We build
122 // objects in this object's compartment.
123 PersistentRootedObject debuggerObject
;
125 // debuggerObject's Debugger structure, for convenience.
126 js::Debugger
* debugger
;
128 // Check that |thing| is in the same compartment as our debuggerObject. Used
129 // for assertions when constructing BuiltThings. We can overload this as we
130 // add more instantiations of BuiltThing.
132 void assertBuilt(JSObject
* obj
);
134 void assertBuilt(JSObject
* obj
) { }
138 // A reference to a trusted object or value. At the moment, we only use it
142 friend class BuilderOrigin
;
147 // The Builder to which this trusted thing belongs.
150 // A rooted reference to our value.
151 PersistentRooted
<T
> value
;
153 BuiltThing(JSContext
* cx
, Builder
& owner_
, T value_
= js::GCMethods
<T
>::initial())
154 : owner(owner_
), value(cx
, value_
)
156 owner
.assertBuilt(value_
);
159 // Forward some things from our owner, for convenience.
160 js::Debugger
* debugger() const { return owner
.debugger
; }
161 JSObject
* debuggerObject() const { return owner
.debuggerObject
; }
164 BuiltThing(const BuiltThing
& rhs
) : owner(rhs
.owner
), value(rhs
.value
) { }
165 BuiltThing
& operator=(const BuiltThing
& rhs
) {
166 MOZ_ASSERT(&owner
== &rhs
.owner
);
167 owner
.assertBuilt(rhs
.value
);
172 typedef void (BuiltThing::* ConvertibleToBool
)();
173 operator ConvertibleToBool() const {
174 // If we ever instantiate BuiltThink<Value>, this might not suffice.
175 return value
? &BuiltThing::nonNull
: 0;
179 BuiltThing() MOZ_DELETE
;
183 // A reference to a trusted object, possibly null. Instances of Object are
184 // always properly rooted. They can be copied and assigned, as if they were
186 class Object
: private BuiltThing
<JSObject
*> {
187 friend class Builder
; // for construction
188 friend class BuilderOrigin
; // for unwrapping
190 typedef BuiltThing
<JSObject
*> Base
;
192 // This is private, because only Builders can create Objects that
193 // actually point to something (hence the 'friend' declaration).
194 Object(JSContext
* cx
, Builder
& owner_
, HandleObject obj
) : Base(cx
, owner_
, obj
.get()) { }
196 bool definePropertyToTrusted(JSContext
* cx
, const char* name
,
197 JS::MutableHandleValue value
);
200 Object(JSContext
* cx
, Builder
& owner_
) : Base(cx
, owner_
, nullptr) { }
201 Object(const Object
& rhs
) : Base(rhs
) { }
203 // Our automatically-generated assignment operator can see our base
204 // class's assignment operator, so we don't need to write one out here.
206 // Set the property named |name| on this object to |value|.
208 // If |value| is a string or primitive, re-wrap it for the debugger's
211 // If |value| is an object, assume it is a debuggee object and make a
212 // Debugger.Object instance referring to it. Set that as the propery's
215 // If |value| is another trusted object, store it directly as the
218 // On error, report the problem on cx and return false.
219 bool defineProperty(JSContext
* cx
, const char* name
, JS::HandleValue value
);
220 bool defineProperty(JSContext
* cx
, const char* name
, JS::HandleObject value
);
221 bool defineProperty(JSContext
* cx
, const char* name
, Object
& value
);
223 using Base::ConvertibleToBool
;
224 using Base::operator ConvertibleToBool
;
227 // Build an empty object for direct use by debugger code, owned by this
228 // Builder. If an error occurs, report it on cx and return a false Object.
229 Object
newObject(JSContext
* cx
);
232 Builder(JSContext
* cx
, js::Debugger
* debugger
);
235 // Debugger itself instantiates this subclass of Builder, which can unwrap
236 // BuiltThings that belong to it.
237 class BuilderOrigin
: public Builder
{
239 T
unwrapAny(const BuiltThing
<T
>& thing
) {
240 MOZ_ASSERT(&thing
.owner
== this);
241 return thing
.value
.get();
245 BuilderOrigin(JSContext
* cx
, js::Debugger
* debugger_
)
246 : Builder(cx
, debugger_
)
249 JSObject
* unwrap(Object
& object
) { return unwrapAny(object
); }
256 #endif /* js_DebugAPI_h */