Bug 1003302: Implement ubi::Node::compartment. r=terrence
authorJim Blandy <jimb@mozilla.com>
Mon, 11 Aug 2014 12:46:39 -0700
changeset 198974 a65c5d0c752db9db704f8d95d3743fc112e584b3
parent 198973 02fd424797c9e64172de7d4a8e29a1ea253050aa
child 198975 1b5b25f53e2eb7440c2a80ded2a97ebfece5c277
push id27293
push useremorley@mozilla.com
push dateTue, 12 Aug 2014 14:29:39 +0000
treeherdermozilla-central@ee1ad12a3939 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1003302
milestone34.0a1
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
Bug 1003302: Implement ubi::Node::compartment. r=terrence
js/public/UbiNode.h
js/src/jsapi-tests/testUbiNode.cpp
js/src/vm/UbiNode.cpp
--- a/js/public/UbiNode.h
+++ b/js/public/UbiNode.h
@@ -199,16 +199,22 @@ class Base {
     // ScopedDJSeletePtr<EdgeRange> to manage it.) On OOM, report an exception
     // on |cx| and return nullptr.
     virtual EdgeRange *edges(JSContext *cx) const = 0;
 
     // Return the Zone to which this node's referent belongs, or nullptr if the
     // referent is not of a type allocated in SpiderMonkey Zones.
     virtual JS::Zone *zone() const = 0;
 
+    // Return the compartment for this node. Some ubi::Node referents are not
+    // associated with JSCompartments, such as JSStrings (which are associated
+    // with Zones). When the referent is not associated with a compartment,
+    // nullptr is returned.
+    virtual JSCompartment *compartment() const = 0;
+
   private:
     Base(const Base &rhs) MOZ_DELETE;
     Base &operator=(const Base &rhs) MOZ_DELETE;
 };
 
 // A traits template with a specialization for each referent type that
 // ubi::Node supports. The specialization must be the concrete subclass of
 // Base that represents a pointer to the referent type. It must also
@@ -324,16 +330,17 @@ class Node {
     // Otherwise return UndefinedValue(). JSStrings, JS::Symbols, and some (but
     // not all!) JSObjects can be exposed.
     JS::Value exposeToJS() const;
 
     const jschar *typeName()        const { return base()->typeName(); }
     size_t size()                   const { return base()->size(); }
     EdgeRange *edges(JSContext *cx) const { return base()->edges(cx); }
     JS::Zone *zone()                const { return base()->zone(); }
+    JSCompartment *compartment()    const { return base()->compartment(); }
 
     // A hash policy for ubi::Nodes.
     // This simply uses the stock PointerHasher on the ubi::Node's pointer.
     // We specialize DefaultHasher below to make this the default.
     class HashPolicy {
         typedef js::PointerHasher<void *, mozilla::tl::FloorLog2<sizeof(void *)>::value> PtrHash;
 
       public:
@@ -418,42 +425,61 @@ class EdgeRange {
 // A reusable ubi::Concrete specialization base class for types supported by
 // JS_TraceChildren.
 template<typename Referent>
 class TracerConcrete : public Base {
     const jschar *typeName() const MOZ_OVERRIDE { return concreteTypeName; }
     size_t size() const MOZ_OVERRIDE { return 0; } // not implemented yet; bug 1011300
     EdgeRange *edges(JSContext *) const MOZ_OVERRIDE;
     JS::Zone *zone() const MOZ_OVERRIDE { return get().zone(); }
+    JSCompartment *compartment() const MOZ_OVERRIDE { return nullptr; }
 
+  protected:
     TracerConcrete(Referent *ptr) : Base(ptr) { }
     Referent &get() const { return *static_cast<Referent *>(ptr); }
 
   public:
     static const jschar concreteTypeName[];
     static void construct(void *storage, Referent *ptr) { new (storage) TracerConcrete(ptr); };
 };
 
-template<> struct Concrete<JSObject> : TracerConcrete<JSObject> { };
+// For JS_TraceChildren-based types that have a 'compartment' method.
+template<typename Referent>
+class TracerConcreteWithCompartment : public TracerConcrete<Referent> {
+    typedef TracerConcrete<Referent> TracerBase;
+    JSCompartment *compartment() const MOZ_OVERRIDE {
+        return TracerBase::get().compartment();
+    }
+
+    TracerConcreteWithCompartment(Referent *ptr) : TracerBase(ptr) { }
+
+  public:
+    static void construct(void *storage, Referent *ptr) {
+        new (storage) TracerConcreteWithCompartment(ptr);
+    };
+};
+
+template<> struct Concrete<JSObject> : TracerConcreteWithCompartment<JSObject> { };
 template<> struct Concrete<JSString> : TracerConcrete<JSString> { };
 template<> struct Concrete<JS::Symbol> : TracerConcrete<JS::Symbol> { };
-template<> struct Concrete<JSScript> : TracerConcrete<JSScript> { };
+template<> struct Concrete<JSScript> : TracerConcreteWithCompartment<JSScript> { };
 template<> struct Concrete<js::LazyScript> : TracerConcrete<js::LazyScript> { };
 template<> struct Concrete<js::jit::JitCode> : TracerConcrete<js::jit::JitCode> { };
-template<> struct Concrete<js::Shape> : TracerConcrete<js::Shape> { };
-template<> struct Concrete<js::BaseShape> : TracerConcrete<js::BaseShape> { };
+template<> struct Concrete<js::Shape> : TracerConcreteWithCompartment<js::Shape> { };
+template<> struct Concrete<js::BaseShape> : TracerConcreteWithCompartment<js::BaseShape> { };
 template<> struct Concrete<js::types::TypeObject> : TracerConcrete<js::types::TypeObject> { };
 
 // The ubi::Node null pointer. Any attempt to operate on a null ubi::Node asserts.
 template<>
 class Concrete<void> : public Base {
     const jschar *typeName() const MOZ_OVERRIDE;
     size_t size() const MOZ_OVERRIDE;
     EdgeRange *edges(JSContext *cx) const MOZ_OVERRIDE;
     JS::Zone *zone() const MOZ_OVERRIDE;
+    JSCompartment *compartment() const MOZ_OVERRIDE;
 
     Concrete(void *ptr) : Base(ptr) { }
 
   public:
     static void construct(void *storage, void *ptr) { new (storage) Concrete(ptr); }
     static const jschar concreteTypeName[];
 };
 
--- a/js/src/jsapi-tests/testUbiNode.cpp
+++ b/js/src/jsapi-tests/testUbiNode.cpp
@@ -47,8 +47,44 @@ BEGIN_TEST(test_ubiNodeZone)
 
         CHECK(JS::ubi::Node(string2).zone() == global2->zone());
         CHECK(JS::ubi::Node(script2).zone() == global2->zone());
     }
 
     return true;
 }
 END_TEST(test_ubiNodeZone)
+
+// ubi::Node::compartment works
+BEGIN_TEST(test_ubiNodeCompartment)
+{
+    RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
+    CHECK(global1);
+    CHECK(JS::ubi::Node(global1).compartment() == cx->compartment());
+
+    RootedObject global2(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
+                                                JS::FireOnNewGlobalHook));
+    CHECK(global2);
+    CHECK(global1->compartment() != global2->compartment());
+    CHECK(JS::ubi::Node(global2).compartment() == global2->compartment());
+    CHECK(JS::ubi::Node(global2).compartment() != global1->compartment());
+
+    JS::CompileOptions options(cx);
+
+    // Create a script in the original compartment...
+    RootedScript script1(cx);
+    CHECK(JS::Compile(cx, global1, options, "", 0, &script1));
+
+    {
+        // ... and then enter global2's compartment and create a script
+        // there, too.
+        JSAutoCompartment ac(cx, global2);
+
+        RootedScript script2(cx);
+        CHECK(JS::Compile(cx, global2, options, "", 0, &script2));
+
+        CHECK(JS::ubi::Node(script1).compartment() == global1->compartment());
+        CHECK(JS::ubi::Node(script2).compartment() == global2->compartment());
+    }
+
+    return true;
+}
+END_TEST(test_ubiNodeCompartment)
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -27,16 +27,17 @@ using JS::ubi::EdgeRange;
 using JS::ubi::Node;
 using JS::ubi::TracerConcrete;
 
 // All operations on null ubi::Nodes crash.
 const jschar *Concrete<void>::typeName() const      { MOZ_CRASH("null ubi::Node"); }
 size_t Concrete<void>::size() const                 { MOZ_CRASH("null ubi::Node"); }
 EdgeRange *Concrete<void>::edges(JSContext *) const { MOZ_CRASH("null ubi::Node"); }
 JS::Zone *Concrete<void>::zone() const              { MOZ_CRASH("null ubi::Node"); }
+JSCompartment *Concrete<void>::compartment() const  { MOZ_CRASH("null ubi::Node"); }
 
 Node::Node(JSGCTraceKind kind, void *ptr)
 {
     switch (kind) {
       case JSTRACE_OBJECT:      construct(static_cast<JSObject *>(ptr));              break;
       case JSTRACE_STRING:      construct(static_cast<JSString *>(ptr));              break;
       case JSTRACE_SYMBOL:      construct(static_cast<JS::Symbol *>(ptr));            break;
       case JSTRACE_SCRIPT:      construct(static_cast<JSScript *>(ptr));              break;