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 id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersterrence
bugs1003302
milestone34.0a1
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;