tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

testPersistentRooted.cpp (5727B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "js/Class.h"
      6 #include "jsapi-tests/tests.h"
      7 
      8 using namespace JS;
      9 
     10 struct BarkWhenTracedClass {
     11  static int finalizeCount;
     12  static int traceCount;
     13 
     14  static const JSClass class_;
     15  static void finalize(JS::GCContext* gcx, JSObject* obj) { finalizeCount++; }
     16  static void trace(JSTracer* trc, JSObject* obj) { traceCount++; }
     17  static void reset() {
     18    finalizeCount = 0;
     19    traceCount = 0;
     20  }
     21 };
     22 
     23 int BarkWhenTracedClass::finalizeCount;
     24 int BarkWhenTracedClass::traceCount;
     25 
     26 static const JSClassOps BarkWhenTracedClassClassOps = {
     27    nullptr,                        // addProperty
     28    nullptr,                        // delProperty
     29    nullptr,                        // enumerate
     30    nullptr,                        // newEnumerate
     31    nullptr,                        // resolve
     32    nullptr,                        // mayResolve
     33    BarkWhenTracedClass::finalize,  // finalize
     34    nullptr,                        // call
     35    nullptr,                        // construct
     36    BarkWhenTracedClass::trace,     // trace
     37 };
     38 
     39 const JSClass BarkWhenTracedClass::class_ = {
     40    "BarkWhenTracedClass",
     41    JSCLASS_FOREGROUND_FINALIZE,
     42    &BarkWhenTracedClassClassOps,
     43 };
     44 
     45 struct Kennel {
     46  PersistentRootedObject obj;
     47  Kennel() {}
     48  explicit Kennel(JSContext* cx) : obj(cx) {}
     49  Kennel(JSContext* cx, const HandleObject& woof) : obj(cx, woof) {}
     50  void init(JSContext* cx, const HandleObject& woof) { obj.init(cx, woof); }
     51  void clear() { obj = nullptr; }
     52 };
     53 
     54 // A function for allocating a Kennel and a barker. Only allocating
     55 // PersistentRooteds on the heap, and in this function, helps ensure that the
     56 // conservative GC doesn't find stray references to the barker. Ugh.
     57 MOZ_NEVER_INLINE static Kennel* Allocate(JSContext* cx) {
     58  RootedObject barker(cx, JS_NewObject(cx, &BarkWhenTracedClass::class_));
     59  if (!barker) {
     60    return nullptr;
     61  }
     62 
     63  return new Kennel(cx, barker);
     64 }
     65 
     66 // Do a GC, expecting |n| barkers to be finalized.
     67 static bool GCFinalizesNBarkers(JSContext* cx, int n) {
     68  int preGCTrace = BarkWhenTracedClass::traceCount;
     69  int preGCFinalize = BarkWhenTracedClass::finalizeCount;
     70 
     71  JS_GC(cx);
     72 
     73  return (BarkWhenTracedClass::finalizeCount == preGCFinalize + n &&
     74          BarkWhenTracedClass::traceCount > preGCTrace);
     75 }
     76 
     77 // PersistentRooted instances protect their contents from being recycled.
     78 BEGIN_TEST(test_PersistentRooted) {
     79  BarkWhenTracedClass::reset();
     80 
     81  mozilla::UniquePtr<Kennel> kennel(Allocate(cx));
     82  CHECK(kennel.get());
     83 
     84  // GC should be able to find our barker.
     85  CHECK(GCFinalizesNBarkers(cx, 0));
     86 
     87  kennel = nullptr;
     88 
     89  // Now GC should not be able to find the barker.
     90  JS_GC(cx);
     91  CHECK(BarkWhenTracedClass::finalizeCount == 1);
     92 
     93  return true;
     94 }
     95 END_TEST(test_PersistentRooted)
     96 
     97 // GC should not be upset by null PersistentRooteds.
     98 BEGIN_TEST(test_PersistentRootedNull) {
     99  BarkWhenTracedClass::reset();
    100 
    101  Kennel kennel(cx);
    102  CHECK(!kennel.obj);
    103 
    104  JS_GC(cx);
    105  CHECK(BarkWhenTracedClass::finalizeCount == 0);
    106 
    107  return true;
    108 }
    109 END_TEST(test_PersistentRootedNull)
    110 
    111 // Copy construction works.
    112 BEGIN_TEST(test_PersistentRootedCopy) {
    113  BarkWhenTracedClass::reset();
    114 
    115  mozilla::UniquePtr<Kennel> kennel(Allocate(cx));
    116  CHECK(kennel.get());
    117 
    118  CHECK(GCFinalizesNBarkers(cx, 0));
    119 
    120  // Copy construction! AMAZING!
    121  mozilla::UniquePtr<Kennel> newKennel(new Kennel(*kennel));
    122 
    123  CHECK(GCFinalizesNBarkers(cx, 0));
    124 
    125  kennel = nullptr;
    126 
    127  CHECK(GCFinalizesNBarkers(cx, 0));
    128 
    129  newKennel = nullptr;
    130 
    131  // Now that kennel and nowKennel are both deallocated, GC should not be
    132  // able to find the barker.
    133  JS_GC(cx);
    134  CHECK(BarkWhenTracedClass::finalizeCount == 1);
    135 
    136  return true;
    137 }
    138 END_TEST(test_PersistentRootedCopy)
    139 
    140 // Assignment works.
    141 BEGIN_TEST(test_PersistentRootedAssign) {
    142  BarkWhenTracedClass::reset();
    143 
    144  mozilla::UniquePtr<Kennel> kennel(Allocate(cx));
    145  CHECK(kennel.get());
    146 
    147  CHECK(GCFinalizesNBarkers(cx, 0));
    148 
    149  // Allocate a new, empty kennel.
    150  mozilla::UniquePtr<Kennel> kennel2(new Kennel(cx));
    151 
    152  // Assignment! ASTONISHING!
    153  *kennel2 = *kennel;
    154 
    155  // With both kennels referring to the same barker, it is held alive.
    156  CHECK(GCFinalizesNBarkers(cx, 0));
    157 
    158  kennel2 = nullptr;
    159 
    160  // The destination of the assignment alone holds the barker alive.
    161  CHECK(GCFinalizesNBarkers(cx, 0));
    162 
    163  // Allocate a second barker.
    164  kennel2 = mozilla::UniquePtr<Kennel>(Allocate(cx));
    165  CHECK(kennel2.get());
    166 
    167  *kennel = *kennel2;
    168 
    169  // Nothing refers to the first kennel any more.
    170  CHECK(GCFinalizesNBarkers(cx, 1));
    171 
    172  kennel = nullptr;
    173  kennel2 = nullptr;
    174 
    175  // Now that kennel and kennel2 are both deallocated, GC should not be
    176  // able to find the barker.
    177  JS_GC(cx);
    178  CHECK(BarkWhenTracedClass::finalizeCount == 2);
    179 
    180  return true;
    181 }
    182 END_TEST(test_PersistentRootedAssign)
    183 
    184 MOZ_RUNINIT static PersistentRootedObject gGlobalRoot;
    185 
    186 // PersistentRooted instances can initialized in a separate step to allow for
    187 // global PersistentRooteds.
    188 BEGIN_TEST(test_GlobalPersistentRooted) {
    189  BarkWhenTracedClass::reset();
    190 
    191  CHECK(!gGlobalRoot.initialized());
    192 
    193  {
    194    RootedObject barker(cx, JS_NewObject(cx, &BarkWhenTracedClass::class_));
    195    CHECK(barker);
    196 
    197    gGlobalRoot.init(cx, barker);
    198  }
    199 
    200  CHECK(gGlobalRoot.initialized());
    201 
    202  // GC should be able to find our barker.
    203  CHECK(GCFinalizesNBarkers(cx, 0));
    204 
    205  gGlobalRoot.reset();
    206  CHECK(!gGlobalRoot.initialized());
    207 
    208  // Now GC should not be able to find the barker.
    209  JS_GC(cx);
    210  CHECK(BarkWhenTracedClass::finalizeCount == 1);
    211 
    212  return true;
    213 }
    214 END_TEST(test_GlobalPersistentRooted)