Make 'Debug(w)' and 'new Debug(w)' return a new Debug object. The argument w must be a cross-compartment wrapper.
authorJason Orendorff <jorendorff@mozilla.com>
Thu, 14 Apr 2011 22:03:31 -0700
changeset 74380 090fcf731131e25c5c73525869f852cad259f7f4
parent 74379 2684c36f33e68b4cd938db38a4bef48a491490d1
child 74381 086e038cf0f48369822fc4ba3ac59b37c3009d27
push id20986
push userkhuey@mozilla.com
push dateSun, 14 Aug 2011 11:45:15 +0000
treeherdermozilla-central@2de3cff973b2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.2a1pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Make 'Debug(w)' and 'new Debug(w)' return a new Debug object. The argument w must be a cross-compartment wrapper.
js/src/js.msg
js/src/jsdbg.cpp
js/src/jsobj.h
js/src/jswrapper.cpp
js/src/tests/js1_8_5/extensions/debug-object-02.js
js/src/tests/js1_8_5/extensions/jstests.list
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -344,8 +344,9 @@ MSG_DEF(JSMSG_SC_UNSUPPORTED_TYPE,    26
 MSG_DEF(JSMSG_SC_RECURSION,           262, 0, JSEXN_INTERNALERR, "recursive object")
 MSG_DEF(JSMSG_CANT_WRAP_XML_OBJECT,   263, 0, JSEXN_TYPEERR, "can't wrap XML objects")
 MSG_DEF(JSMSG_BAD_CLONE_VERSION,      264, 0, JSEXN_ERR, "unsupported structured clone version")
 MSG_DEF(JSMSG_CANT_CLONE_OBJECT,      265, 0, JSEXN_TYPEERR, "can't clone object")
 MSG_DEF(JSMSG_NON_NATIVE_SCOPE,       266, 0, JSEXN_TYPEERR, "non-native scope object")
 MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 267, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")
 MSG_DEF(JSMSG_INVALID_FOR_IN_INIT,    268, 0, JSEXN_SYNTAXERR, "for-in loop let declaration may not have an initializer")
 MSG_DEF(JSMSG_CLEARED_SCOPE,          269, 0, JSEXN_TYPEERR, "attempt to run compile-and-go script on a cleared scope")
+MSG_DEF(JSMSG_CCW_REQUIRED,           270, 1, JSEXN_TYPEERR, "{0}: argument must be an object from a different compartment")
--- a/js/src/jsdbg.cpp
+++ b/js/src/jsdbg.cpp
@@ -34,38 +34,85 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsobj.h"
+#include "jswrapper.h"
+#include "jsobjinlines.h"
 
 using namespace js;
 
-static bool
-NotImplemented(JSContext *cx)
+bool
+ReportMoreArgsNeeded(JSContext *cx, const char *name, uintN required)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, "API");
+    JS_ASSERT(required < 10);
+    char s[2] = { '0' + required, '\0' };
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
+                         name, s, required == 1 ? "" : "s");
     return false;
 }
 
-static JSBool
-Debug(JSContext *cx, uintN argc, Value *vp)
+#define REQUIRE_ARGC(name, n) \
+    JS_BEGIN_MACRO \
+        if (argc < n) \
+            return ReportMoreArgsNeeded(cx, name, n); \
+    JS_END_MACRO
+
+bool
+ReportObjectRequired(JSContext *cx, const char *name)
 {
-    return NotImplemented(cx);
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
+    return false;
 }
 
+// === Debug objects
+
 static Class DebugClass = {
     "Debug", 0,
     PropertyStub, PropertyStub, PropertyStub, StrictPropertyStub,
     EnumerateStub, ResolveStub, ConvertStub
 };
 
+static JSBool
+Debug(JSContext *cx, uintN argc, Value *vp)
+{
+    REQUIRE_ARGC("Debug", 1);
+
+    // Confirm that the first argument is a cross-compartment wrapper.
+    const Value &arg = vp[2];
+    if (!arg.isObject())
+        return ReportObjectRequired(cx, "Debug");
+    JSObject *argobj = &arg.toObject();
+    if (!argobj->isWrapper() ||
+        (!argobj->getWrapperHandler()->flags() & JSWrapper::CROSS_COMPARTMENT))
+    {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CCW_REQUIRED, "Debug");
+        return false;
+    }
+
+    // Get Debug.prototype.
+    Value v;
+    jsid prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
+    if (!vp[0].toObject().getProperty(cx, prototypeId, &v))
+        return false;
+    JSObject *proto = &v.toObject();
+    JS_ASSERT(proto->getClass() == &DebugClass);
+
+    // Make the new Debug object.
+    JSObject *obj = NewNonFunction<WithProto::Given>(cx, &DebugClass, proto, NULL);
+    if (!obj)
+        return false;
+    vp->setObject(*obj);
+    return true;
+}
+
 extern JS_PUBLIC_API(JSBool)
 JS_DefineDebugObject(JSContext *cx, JSObject *obj)
 {
     JSObject *objProto;
     if (!js_GetClassPrototype(cx, obj, JSProto_Object, &objProto))
         return NULL;
 
     return !!js_InitClass(cx, obj, objProto, &DebugClass, Debug, 1,
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1124,16 +1124,17 @@ struct JSObject : js::gc::Cell {
      * Proxy-specific getters and setters.
      */
 
     inline js::JSProxyHandler *getProxyHandler() const;
     inline const js::Value &getProxyPrivate() const;
     inline void setProxyPrivate(const js::Value &priv);
     inline const js::Value &getProxyExtra() const;
     inline void setProxyExtra(const js::Value &extra);
+    JSWrapper *getWrapperHandler() const;
 
     /*
      * With object-specific getters and setters.
      */
     inline JSObject *getWithThis() const;
     inline void setWithThis(JSObject *thisp);
 
     /*
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -66,16 +66,23 @@ JSWrapper::getWrapperFamily()
 }
 
 bool
 JSObject::isWrapper() const
 {
     return isProxy() && getProxyHandler()->family() == &sWrapperFamily;
 }
 
+JSWrapper *
+JSObject::getWrapperHandler() const
+{
+    JS_ASSERT(isWrapper());
+    return static_cast<JSWrapper *>(getProxyHandler());
+}
+
 JSObject *
 JSObject::unwrap(uintN *flagsp)
 {
     JSObject *wrapped = this;
     uintN flags = 0;
     while (wrapped->isWrapper()) {
         flags |= static_cast<JSWrapper *>(wrapped->getProxyHandler())->flags();
         wrapped = wrapped->getProxyPrivate().toObjectOrNull();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/debug-object-02.js
@@ -0,0 +1,37 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+// Debug rejects arguments that aren't cross-compartment wrappers.
+assertThrows(function () { Debug(); }, TypeError);
+assertThrows(function () { Debug(null); }, TypeError);
+assertThrows(function () { Debug(true); }, TypeError);
+assertThrows(function () { Debug(42); }, TypeError);
+assertThrows(function () { Debug("bad"); }, TypeError);
+assertThrows(function () { Debug(function () {}); }, TypeError);
+assertThrows(function () { Debug(this); }, TypeError);
+assertThrows(function () { new Debug(); }, TypeError);
+assertThrows(function () { new Debug(null); }, TypeError);
+assertThrows(function () { new Debug(true); }, TypeError);
+assertThrows(function () { new Debug(42); }, TypeError);
+assertThrows(function () { new Debug("bad"); }, TypeError);
+assertThrows(function () { new Debug(function () {}); }, TypeError);
+assertThrows(function () { new Debug(this); }, TypeError);
+
+// Very basic tests of Debug creation.
+var g = newGlobal('new-compartment');
+var dbg = new Debug(g);
+assertEq(dbg instanceof Debug, true);
+assertEq(Object.getPrototypeOf(dbg), Debug.prototype);
+
+// The reverse.
+var g2 = newGlobal('new-compartment');
+g2.debuggeeGlobal = this;
+g2.eval("var dbg = new Debug(debuggeeGlobal);");
+assertEq(g2.eval("dbg instanceof Debug"), true);
+
+// The Debug constructor from this compartment will not accept as its argument
+// an Object from this compartment. Shenanigans won't fool the membrane.
+g2.outer = this;
+assertThrows(function () { g2.eval("outer.Debug(outer.Object())"); }, TypeError);
+
+reportCompare(0, 0, 'ok');
--- a/js/src/tests/js1_8_5/extensions/jstests.list
+++ b/js/src/tests/js1_8_5/extensions/jstests.list
@@ -35,8 +35,9 @@ script regress-627984-4.js
 script regress-627984-5.js
 script regress-627984-6.js
 script regress-627984-7.js
 script regress-630377.js
 script regress-631723.js
 skip-if(!xulRuntime.shell) script regress-636818.js
 script regress-636697.js
 skip-if(!xulRuntime.shell) script debug-object-01.js
+skip-if(!xulRuntime.shell) script debug-object-02.js