tor-browser

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

testGCExactRooting.cpp (32504B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 */
      4 /* This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #include "mozilla/Maybe.h"
      9 #include "mozilla/Result.h"
     10 #include "mozilla/ResultVariant.h"
     11 
     12 #include "ds/TraceableFifo.h"
     13 #include "gc/Policy.h"
     14 #include "js/GCHashTable.h"
     15 #include "js/GCVector.h"
     16 #include "js/PropertyAndElement.h"  // JS_DefineProperty, JS_GetProperty, JS_SetProperty
     17 #include "js/RootingAPI.h"
     18 
     19 #include "jsapi-tests/tests.h"
     20 
     21 using namespace js;
     22 
     23 using mozilla::Maybe;
     24 using mozilla::Some;
     25 
     26 BEGIN_TEST(testGCExactRooting) {
     27  JS::RootedObject rootCx(cx, JS_NewPlainObject(cx));
     28 
     29  JS_GC(cx);
     30 
     31  /* Use the objects we just created to ensure that they are still alive. */
     32  JS_DefineProperty(cx, rootCx, "foo", JS::UndefinedHandleValue, 0);
     33 
     34  return true;
     35 }
     36 END_TEST(testGCExactRooting)
     37 
     38 BEGIN_TEST(testGCSuppressions) {
     39  JS::AutoAssertNoGC nogc;
     40  JS::AutoCheckCannotGC checkgc;
     41  JS::AutoSuppressGCAnalysis noanalysis;
     42 
     43  JS::AutoAssertNoGC nogcCx(cx);
     44  JS::AutoCheckCannotGC checkgcCx(cx);
     45  JS::AutoSuppressGCAnalysis noanalysisCx(cx);
     46 
     47  return true;
     48 }
     49 END_TEST(testGCSuppressions)
     50 
     51 struct MyContainer {
     52  int whichConstructor;
     53  HeapPtr<JSObject*> obj;
     54  HeapPtr<JSString*> str;
     55 
     56  MyContainer() : whichConstructor(1), obj(nullptr), str(nullptr) {}
     57  explicit MyContainer(double) : MyContainer() { whichConstructor = 2; }
     58  explicit MyContainer(JSContext* cx) : MyContainer() { whichConstructor = 3; }
     59  MyContainer(JSContext* cx, JSContext* cx2, JSContext* cx3) : MyContainer() {
     60    whichConstructor = 4;
     61  }
     62  MyContainer(const MyContainer& rhs)
     63      : whichConstructor(100 + rhs.whichConstructor),
     64        obj(rhs.obj),
     65        str(rhs.str) {}
     66  void trace(JSTracer* trc) {
     67    js::TraceNullableEdge(trc, &obj, "test container obj");
     68    js::TraceNullableEdge(trc, &str, "test container str");
     69  }
     70 };
     71 
     72 struct MyNonCopyableContainer {
     73  int whichConstructor;
     74  HeapPtr<JSObject*> obj;
     75  HeapPtr<JSString*> str;
     76 
     77  MyNonCopyableContainer() : whichConstructor(1), obj(nullptr), str(nullptr) {}
     78  explicit MyNonCopyableContainer(double) : MyNonCopyableContainer() {
     79    whichConstructor = 2;
     80  }
     81  explicit MyNonCopyableContainer(JSContext* cx) : MyNonCopyableContainer() {
     82    whichConstructor = 3;
     83  }
     84  explicit MyNonCopyableContainer(JSContext* cx, JSContext* cx2, JSContext* cx3)
     85      : MyNonCopyableContainer() {
     86    whichConstructor = 4;
     87  }
     88 
     89  MyNonCopyableContainer(const MyNonCopyableContainer&) = delete;
     90  MyNonCopyableContainer& operator=(const MyNonCopyableContainer&) = delete;
     91 
     92  void trace(JSTracer* trc) {
     93    js::TraceNullableEdge(trc, &obj, "test container obj");
     94    js::TraceNullableEdge(trc, &str, "test container str");
     95  }
     96 };
     97 
     98 namespace js {
     99 template <typename Wrapper>
    100 struct MutableWrappedPtrOperations<MyContainer, Wrapper> {
    101  HeapPtr<JSObject*>& obj() { return static_cast<Wrapper*>(this)->get().obj; }
    102  HeapPtr<JSString*>& str() { return static_cast<Wrapper*>(this)->get().str; }
    103  int constructor() {
    104    return static_cast<Wrapper*>(this)->get().whichConstructor;
    105  }
    106 };
    107 
    108 template <typename Wrapper>
    109 struct MutableWrappedPtrOperations<MyNonCopyableContainer, Wrapper> {
    110  HeapPtr<JSObject*>& obj() { return static_cast<Wrapper*>(this)->get().obj; }
    111  HeapPtr<JSString*>& str() { return static_cast<Wrapper*>(this)->get().str; }
    112  int constructor() {
    113    return static_cast<Wrapper*>(this)->get().whichConstructor;
    114  }
    115 };
    116 }  // namespace js
    117 
    118 BEGIN_TEST(testGCRootedStaticStructInternalStackStorageAugmented) {
    119  // Test Rooted constructors for a copyable type.
    120  JS::Rooted<MyContainer> r1(cx);
    121  JS::Rooted<MyContainer> r2(cx, 3.4);
    122  JS::Rooted<MyContainer> r3(cx, MyContainer(cx));
    123  JS::Rooted<MyContainer> r4(cx, cx);
    124  JS::Rooted<MyContainer> r5(cx, cx, cx, cx);
    125 
    126  JS::Rooted<Value> rv(cx);
    127 
    128  CHECK_EQUAL(r1.constructor(), 1);    // direct SafelyInitialized<T>
    129  CHECK_EQUAL(r2.constructor(), 2);    // direct MyContainer(3.4)
    130  CHECK_EQUAL(r3.constructor(), 103);  // copy of MyContainer(cx)
    131  CHECK_EQUAL(r4.constructor(), 3);    // direct MyContainer(cx)
    132  CHECK_EQUAL(r5.constructor(), 4);    // direct MyContainer(cx, cx, cx)
    133 
    134  // Test Rooted constructor forwarding for a non-copyable type.
    135  JS::Rooted<MyNonCopyableContainer> nc1(cx);
    136  JS::Rooted<MyNonCopyableContainer> nc2(cx, 3.4);
    137  // Compile error: cannot copy
    138  // JS::Rooted<MyNonCopyableContainer> nc3(cx, MyNonCopyableContainer(cx));
    139  JS::Rooted<MyNonCopyableContainer> nc4(cx, cx);
    140  JS::Rooted<MyNonCopyableContainer> nc5(cx, cx, cx, cx);
    141 
    142  CHECK_EQUAL(nc1.constructor(), 1);  // direct MyNonCopyableContainer()
    143  CHECK_EQUAL(nc2.constructor(), 2);  // direct MyNonCopyableContainer(3.4)
    144  CHECK_EQUAL(nc4.constructor(), 3);  // direct MyNonCopyableContainer(cx)
    145  CHECK_EQUAL(nc5.constructor(),
    146              4);  // direct MyNonCopyableContainer(cx, cx, cx)
    147 
    148  JS::Rooted<MyContainer> container(cx);
    149  container.obj() = JS_NewObject(cx, nullptr);
    150  container.str() = JS_NewStringCopyZ(cx, "Hello");
    151 
    152  JS_GC(cx);
    153  JS_GC(cx);
    154 
    155  JS::RootedObject obj(cx, container.obj());
    156  JS::RootedValue val(cx, StringValue(container.str()));
    157  CHECK(JS_SetProperty(cx, obj, "foo", val));
    158  obj = nullptr;
    159  val = UndefinedValue();
    160 
    161  {
    162    JS::RootedString actual(cx);
    163    bool same;
    164 
    165    // Automatic move from stack to heap.
    166    JS::PersistentRooted<MyContainer> heap(cx, container);
    167 
    168    // Copyable types in place.
    169    JS::PersistentRooted<MyContainer> cp1(cx);
    170    JS::PersistentRooted<MyContainer> cp2(cx, 7.8);
    171    JS::PersistentRooted<MyContainer> cp3(cx, cx);
    172    JS::PersistentRooted<MyContainer> cp4(cx, cx, cx, cx);
    173 
    174    CHECK_EQUAL(cp1.constructor(), 1);  // direct SafelyInitialized<T>
    175    CHECK_EQUAL(cp2.constructor(), 2);  // direct MyContainer(double)
    176    CHECK_EQUAL(cp3.constructor(), 3);  // direct MyContainer(cx)
    177    CHECK_EQUAL(cp4.constructor(), 4);  // direct MyContainer(cx, cx, cx)
    178 
    179    // Construct uncopyable type in place.
    180    JS::PersistentRooted<MyNonCopyableContainer> ncp1(cx);
    181    JS::PersistentRooted<MyNonCopyableContainer> ncp2(cx, 7.8);
    182 
    183    // We're not just using a 1-arg constructor, right?
    184    JS::PersistentRooted<MyNonCopyableContainer> ncp3(cx, cx);
    185    JS::PersistentRooted<MyNonCopyableContainer> ncp4(cx, cx, cx, cx);
    186 
    187    CHECK_EQUAL(ncp1.constructor(), 1);  // direct SafelyInitialized<T>
    188    CHECK_EQUAL(ncp2.constructor(), 2);  // direct Ctor(double)
    189    CHECK_EQUAL(ncp3.constructor(), 3);  // direct Ctor(cx)
    190    CHECK_EQUAL(ncp4.constructor(), 4);  // direct Ctor(cx, cx, cx)
    191 
    192    // clear prior rooting.
    193    container.obj() = nullptr;
    194    container.str() = nullptr;
    195 
    196    obj = heap.obj();
    197    CHECK(JS_GetProperty(cx, obj, "foo", &val));
    198    actual = val.toString();
    199    CHECK(JS_StringEqualsLiteral(cx, actual, "Hello", &same));
    200    CHECK(same);
    201    obj = nullptr;
    202    actual = nullptr;
    203 
    204    JS_GC(cx);
    205    JS_GC(cx);
    206 
    207    obj = heap.obj();
    208    CHECK(JS_GetProperty(cx, obj, "foo", &val));
    209    actual = val.toString();
    210    CHECK(JS_StringEqualsLiteral(cx, actual, "Hello", &same));
    211    CHECK(same);
    212    obj = nullptr;
    213    actual = nullptr;
    214  }
    215 
    216  return true;
    217 }
    218 END_TEST(testGCRootedStaticStructInternalStackStorageAugmented)
    219 
    220 MOZ_RUNINIT static JS::PersistentRooted<JSObject*> sLongLived;
    221 BEGIN_TEST(testGCPersistentRootedOutlivesRuntime) {
    222  sLongLived.init(cx, JS_NewObject(cx, nullptr));
    223  CHECK(sLongLived);
    224  return true;
    225 }
    226 END_TEST(testGCPersistentRootedOutlivesRuntime)
    227 
    228 // Unlike the above, the following test is an example of an invalid usage: for
    229 // performance and simplicity reasons, PersistentRooted<Traceable> is not
    230 // allowed to outlive the container it belongs to. The following commented out
    231 // test can be used to verify that the relevant assertion fires as expected.
    232 MOZ_RUNINIT static JS::PersistentRooted<MyContainer> sContainer;
    233 BEGIN_TEST(testGCPersistentRootedTraceableCannotOutliveRuntime) {
    234  JS::Rooted<MyContainer> container(cx);
    235  container.obj() = JS_NewObject(cx, nullptr);
    236  container.str() = JS_NewStringCopyZ(cx, "Hello");
    237  sContainer.init(cx, container);
    238 
    239  // Commenting the following line will trigger an assertion that the
    240  // PersistentRooted outlives the runtime it is attached to.
    241  sContainer.reset();
    242 
    243  return true;
    244 }
    245 END_TEST(testGCPersistentRootedTraceableCannotOutliveRuntime)
    246 
    247 using MyHashMap = js::GCHashMap<js::Shape*, JSObject*>;
    248 
    249 BEGIN_TEST(testGCRootedHashMap) {
    250  JS::Rooted<MyHashMap> map(cx, MyHashMap(cx, 15));
    251 
    252  for (size_t i = 0; i < 10; ++i) {
    253    RootedObject obj(cx, JS_NewObject(cx, nullptr));
    254    RootedValue val(cx, UndefinedValue());
    255    // Construct a unique property name to ensure that the object creates a
    256    // new shape.
    257    char buffer[2];
    258    buffer[0] = 'a' + i;
    259    buffer[1] = '\0';
    260    CHECK(JS_SetProperty(cx, obj, buffer, val));
    261    CHECK(map.putNew(obj->shape(), obj));
    262  }
    263 
    264  JS_GC(cx);
    265  JS_GC(cx);
    266 
    267  for (auto r = map.all(); !r.empty(); r.popFront()) {
    268    RootedObject obj(cx, r.front().value());
    269    CHECK(obj->shape() == r.front().key());
    270  }
    271 
    272  return true;
    273 }
    274 END_TEST(testGCRootedHashMap)
    275 
    276 // Repeat of the test above, but without rooting. This is a rooting hazard. The
    277 // JS_EXPECT_HAZARDS annotation will cause the hazard taskcluster job to fail
    278 // if the hazard below is *not* detected.
    279 BEGIN_TEST_WITH_ATTRIBUTES(testUnrootedGCHashMap, JS_EXPECT_HAZARDS) {
    280  AutoLeaveZeal noZeal(cx);
    281 
    282  MyHashMap map(cx, 15);
    283 
    284  for (size_t i = 0; i < 10; ++i) {
    285    RootedObject obj(cx, JS_NewObject(cx, nullptr));
    286    RootedValue val(cx, UndefinedValue());
    287    // Construct a unique property name to ensure that the object creates a
    288    // new shape.
    289    char buffer[2];
    290    buffer[0] = 'a' + i;
    291    buffer[1] = '\0';
    292    CHECK(JS_SetProperty(cx, obj, buffer, val));
    293    CHECK(map.putNew(obj->shape(), obj));
    294  }
    295 
    296  JS_GC(cx);
    297 
    298  // Access map to keep it live across the GC.
    299  CHECK(map.count() == 10);
    300 
    301  return true;
    302 }
    303 END_TEST(testUnrootedGCHashMap)
    304 
    305 BEGIN_TEST(testSafelyUnrootedGCHashMap) {
    306  // This is not rooted, but it doesn't use GC pointers as keys or values so
    307  // it's ok.
    308  js::GCHashMap<uint64_t, uint64_t> map(cx, 15);
    309 
    310  JS_GC(cx);
    311  CHECK(map.putNew(12, 13));
    312 
    313  return true;
    314 }
    315 END_TEST(testSafelyUnrootedGCHashMap)
    316 
    317 static bool FillMyHashMap(JSContext* cx, MutableHandle<MyHashMap> map) {
    318  for (size_t i = 0; i < 10; ++i) {
    319    RootedObject obj(cx, JS_NewObject(cx, nullptr));
    320    RootedValue val(cx, UndefinedValue());
    321    // Construct a unique property name to ensure that the object creates a
    322    // new shape.
    323    char buffer[2];
    324    buffer[0] = 'a' + i;
    325    buffer[1] = '\0';
    326    if (!JS_SetProperty(cx, obj, buffer, val)) {
    327      return false;
    328    }
    329    if (!map.putNew(obj->shape(), obj)) {
    330      return false;
    331    }
    332  }
    333  return true;
    334 }
    335 
    336 static bool CheckMyHashMap(JSContext* cx, Handle<MyHashMap> map) {
    337  for (auto r = map.all(); !r.empty(); r.popFront()) {
    338    RootedObject obj(cx, r.front().value());
    339    if (obj->shape() != r.front().key()) {
    340      return false;
    341    }
    342  }
    343  return true;
    344 }
    345 
    346 BEGIN_TEST(testGCHandleHashMap) {
    347  JS::Rooted<MyHashMap> map(cx, MyHashMap(cx, 15));
    348 
    349  CHECK(FillMyHashMap(cx, &map));
    350 
    351  JS_GC(cx);
    352  JS_GC(cx);
    353 
    354  CHECK(CheckMyHashMap(cx, map));
    355 
    356  return true;
    357 }
    358 END_TEST(testGCHandleHashMap)
    359 
    360 using ShapeVec = GCVector<NativeShape*>;
    361 
    362 BEGIN_TEST(testGCRootedVector) {
    363  JS::Rooted<ShapeVec> shapes(cx, cx);
    364 
    365  for (size_t i = 0; i < 10; ++i) {
    366    RootedObject obj(cx, JS_NewObject(cx, nullptr));
    367    RootedValue val(cx, UndefinedValue());
    368    // Construct a unique property name to ensure that the object creates a
    369    // new shape.
    370    char buffer[2];
    371    buffer[0] = 'a' + i;
    372    buffer[1] = '\0';
    373    CHECK(JS_SetProperty(cx, obj, buffer, val));
    374    CHECK(shapes.append(obj->as<NativeObject>().shape()));
    375  }
    376 
    377  JS_GC(cx);
    378  JS_GC(cx);
    379 
    380  for (size_t i = 0; i < 10; ++i) {
    381    // Check the shape to ensure it did not get collected.
    382    char letter = 'a' + i;
    383    bool match;
    384    ShapePropertyIter<NoGC> iter(shapes[i]);
    385    CHECK(JS_StringEqualsAscii(cx, iter->key().toString(), &letter, 1, &match));
    386    CHECK(match);
    387  }
    388 
    389  // Ensure iterator enumeration works through the rooted.
    390  for (auto shape : shapes) {
    391    CHECK(shape);
    392  }
    393 
    394  CHECK(receiveConstRefToShapeVector(shapes));
    395 
    396  // Ensure rooted converts to handles.
    397  CHECK(receiveHandleToShapeVector(shapes));
    398  CHECK(receiveMutableHandleToShapeVector(&shapes));
    399 
    400  return true;
    401 }
    402 
    403 bool receiveConstRefToShapeVector(
    404    const JS::Rooted<GCVector<NativeShape*>>& rooted) {
    405  // Ensure range enumeration works through the reference.
    406  for (auto shape : rooted) {
    407    CHECK(shape);
    408  }
    409  return true;
    410 }
    411 
    412 bool receiveHandleToShapeVector(JS::Handle<GCVector<NativeShape*>> handle) {
    413  // Ensure range enumeration works through the handle.
    414  for (auto shape : handle) {
    415    CHECK(shape);
    416  }
    417  return true;
    418 }
    419 
    420 bool receiveMutableHandleToShapeVector(
    421    JS::MutableHandle<GCVector<NativeShape*>> handle) {
    422  // Ensure range enumeration works through the handle.
    423  for (auto shape : handle) {
    424    CHECK(shape);
    425  }
    426  return true;
    427 }
    428 END_TEST(testGCRootedVector)
    429 
    430 BEGIN_TEST(testTraceableFifo) {
    431  using ShapeFifo = TraceableFifo<NativeShape*>;
    432  JS::Rooted<ShapeFifo> shapes(cx, ShapeFifo(cx));
    433  CHECK(shapes.empty());
    434 
    435  for (size_t i = 0; i < 10; ++i) {
    436    RootedObject obj(cx, JS_NewObject(cx, nullptr));
    437    RootedValue val(cx, UndefinedValue());
    438    // Construct a unique property name to ensure that the object creates a
    439    // new shape.
    440    char buffer[2];
    441    buffer[0] = 'a' + i;
    442    buffer[1] = '\0';
    443    CHECK(JS_SetProperty(cx, obj, buffer, val));
    444    CHECK(shapes.pushBack(obj->as<NativeObject>().shape()));
    445  }
    446 
    447  CHECK(shapes.length() == 10);
    448 
    449  JS_GC(cx);
    450  JS_GC(cx);
    451 
    452  for (size_t i = 0; i < 10; ++i) {
    453    // Check the shape to ensure it did not get collected.
    454    char letter = 'a' + i;
    455    bool match;
    456    ShapePropertyIter<NoGC> iter(shapes.front());
    457    CHECK(JS_StringEqualsAscii(cx, iter->key().toString(), &letter, 1, &match));
    458    CHECK(match);
    459    shapes.popFront();
    460  }
    461 
    462  CHECK(shapes.empty());
    463  return true;
    464 }
    465 END_TEST(testTraceableFifo)
    466 
    467 using ShapeVec = GCVector<NativeShape*>;
    468 
    469 static bool FillVector(JSContext* cx, MutableHandle<ShapeVec> shapes) {
    470  for (size_t i = 0; i < 10; ++i) {
    471    RootedObject obj(cx, JS_NewObject(cx, nullptr));
    472    RootedValue val(cx, UndefinedValue());
    473    // Construct a unique property name to ensure that the object creates a
    474    // new shape.
    475    char buffer[2];
    476    buffer[0] = 'a' + i;
    477    buffer[1] = '\0';
    478    if (!JS_SetProperty(cx, obj, buffer, val)) {
    479      return false;
    480    }
    481    if (!shapes.append(obj->as<NativeObject>().shape())) {
    482      return false;
    483    }
    484  }
    485 
    486  // Ensure iterator enumeration works through the mutable handle.
    487  for (auto shape : shapes) {
    488    if (!shape) {
    489      return false;
    490    }
    491  }
    492 
    493  return true;
    494 }
    495 
    496 static bool CheckVector(JSContext* cx, Handle<ShapeVec> shapes) {
    497  for (size_t i = 0; i < 10; ++i) {
    498    // Check the shape to ensure it did not get collected.
    499    char letter = 'a' + i;
    500    bool match;
    501    ShapePropertyIter<NoGC> iter(shapes[i]);
    502    if (!JS_StringEqualsAscii(cx, iter->key().toString(), &letter, 1, &match)) {
    503      return false;
    504    }
    505    if (!match) {
    506      return false;
    507    }
    508  }
    509 
    510  // Ensure iterator enumeration works through the handle.
    511  for (auto shape : shapes) {
    512    if (!shape) {
    513      return false;
    514    }
    515  }
    516 
    517  return true;
    518 }
    519 
    520 BEGIN_TEST(testGCHandleVector) {
    521  JS::Rooted<ShapeVec> vec(cx, ShapeVec(cx));
    522 
    523  CHECK(FillVector(cx, &vec));
    524 
    525  JS_GC(cx);
    526  JS_GC(cx);
    527 
    528  CHECK(CheckVector(cx, vec));
    529 
    530  return true;
    531 }
    532 END_TEST(testGCHandleVector)
    533 
    534 class Foo {
    535 public:
    536  Foo(int, int) {}
    537  void trace(JSTracer*) {}
    538 };
    539 
    540 using FooVector = JS::GCVector<Foo>;
    541 
    542 BEGIN_TEST(testGCVectorEmplaceBack) {
    543  JS::Rooted<FooVector> vector(cx, FooVector(cx));
    544 
    545  CHECK(vector.emplaceBack(1, 2));
    546 
    547  return true;
    548 }
    549 END_TEST(testGCVectorEmplaceBack)
    550 
    551 BEGIN_TEST(testRootedMaybeValue) {
    552  JS::Rooted<Maybe<Value>> maybeNothing(cx);
    553  CHECK(maybeNothing.isNothing());
    554  CHECK(!maybeNothing.isSome());
    555 
    556  JS::Rooted<Maybe<Value>> maybe(cx, Some(UndefinedValue()));
    557  CHECK(CheckConstOperations<Rooted<Maybe<Value>>&>(maybe));
    558  CHECK(CheckConstOperations<Handle<Maybe<Value>>>(maybe));
    559  CHECK(CheckConstOperations<MutableHandle<Maybe<Value>>>(&maybe));
    560 
    561  maybe = Some(JS::TrueValue());
    562  CHECK(CheckMutableOperations<Rooted<Maybe<Value>>&>(maybe));
    563 
    564  maybe = Some(JS::TrueValue());
    565  CHECK(CheckMutableOperations<MutableHandle<Maybe<Value>>>(&maybe));
    566 
    567  CHECK(JS::NothingHandleValue.isNothing());
    568 
    569  return true;
    570 }
    571 
    572 template <typename T>
    573 bool CheckConstOperations(T someUndefinedValue) {
    574  CHECK(someUndefinedValue.isSome());
    575  CHECK(someUndefinedValue.value().isUndefined());
    576  CHECK(someUndefinedValue->isUndefined());
    577  CHECK((*someUndefinedValue).isUndefined());
    578  return true;
    579 }
    580 
    581 template <typename T>
    582 bool CheckMutableOperations(T maybe) {
    583  CHECK(maybe->isTrue());
    584 
    585  *maybe = JS::FalseValue();
    586  CHECK(maybe->isFalse());
    587 
    588  maybe.reset();
    589  CHECK(maybe.isNothing());
    590 
    591  return true;
    592 }
    593 
    594 END_TEST(testRootedMaybeValue)
    595 
    596 struct TestErr {};
    597 struct OtherTestErr {};
    598 
    599 struct SimpleTraceable {
    600  // I'm using plain objects rather than Heap<T> because Heap<T> would get
    601  // traced via the store buffer. Heap<T> would be a more realistic example,
    602  // but would require compaction to test for tracing.
    603  JSObject* obj;
    604  JS::Value val;
    605 
    606  void trace(JSTracer* trc) {
    607    TraceRoot(trc, &obj, "obj");
    608    TraceRoot(trc, &val, "val");
    609  }
    610 };
    611 
    612 namespace JS {
    613 template <>
    614 struct GCPolicy<TestErr> : public IgnoreGCPolicy<TestErr> {};
    615 }  // namespace JS
    616 
    617 BEGIN_TEST_WITH_ATTRIBUTES(testGCRootedResult, JS_EXPECT_HAZARDS) {
    618  AutoLeaveZeal noZeal(cx);
    619 
    620  JSObject* unrootedObj = JS_NewPlainObject(cx);
    621  CHECK(js::gc::IsInsideNursery(unrootedObj));
    622  Value unrootedVal = ObjectValue(*unrootedObj);
    623 
    624  RootedObject obj(cx, unrootedObj);
    625  RootedValue val(cx, unrootedVal);
    626 
    627  Result<Value, TestErr> unrootedValerr(val);
    628  Rooted<Result<Value, TestErr>> valerr(cx, val);
    629 
    630  Result<mozilla::Ok, Value> unrootedOkval(val);
    631  Rooted<Result<mozilla::Ok, Value>> okval(cx, val);
    632 
    633  Result<mozilla::Ok, TestErr> simple{mozilla::Ok()};
    634 
    635  Result<Value, JSObject*> unrootedValobj1(val);
    636  Rooted<Result<Value, JSObject*>> valobj1(cx, val);
    637  Result<Value, JSObject*> unrootedValobj2(obj);
    638  Rooted<Result<Value, JSObject*>> valobj2(cx, obj);
    639 
    640  // Test nested traceable structures.
    641  Result<mozilla::Maybe<mozilla::Ok>, JSObject*> maybeobj(
    642      mozilla::Some(mozilla::Ok()));
    643  Rooted<Result<mozilla::Maybe<mozilla::Ok>, JSObject*>> rooted_maybeobj(
    644      cx, mozilla::Some(mozilla::Ok()));
    645 
    646  // This would fail to compile because Result<> deletes its copy constructor,
    647  // which prevents updating after tracing:
    648  //
    649  // Rooted<Result<Result<mozilla::Ok, JS::Value>, JSObject*>>
    650 
    651  // But this should be fine when no tracing is required.
    652  Result<Result<mozilla::Ok, int>, double> dummy(3.4);
    653 
    654  // One thing I didn't realize initially about Result<>: unwrap() takes
    655  // ownership of a value. In the case of Result<Maybe>, that means the
    656  // contained Maybe is reset to Nothing.
    657  Result<mozilla::Maybe<int>, int> confusing(mozilla::Some(7));
    658  CHECK(confusing.unwrap().isSome());
    659  CHECK(!confusing.unwrap().isSome());
    660 
    661  Result<mozilla::Maybe<JS::Value>, JSObject*> maybevalobj(
    662      mozilla::Some(val.get()));
    663  Rooted<Result<mozilla::Maybe<JS::Value>, JSObject*>> rooted_maybevalobj(
    664      cx, mozilla::Some(val.get()));
    665 
    666  // Custom types that haven't had GCPolicy explicitly specialized.
    667  SimpleTraceable s1{obj, val};
    668  Result<SimpleTraceable, TestErr> custom(s1);
    669  SimpleTraceable s2{obj, val};
    670  Rooted<Result<SimpleTraceable, TestErr>> rootedCustom(cx, s2);
    671 
    672  CHECK(obj == unrootedObj);
    673  CHECK(val == unrootedVal);
    674  CHECK(simple.isOk());
    675  CHECK(unrootedValerr.inspect() == unrootedVal);
    676  CHECK(valerr.get().inspect() == val);
    677  CHECK(unrootedOkval.inspectErr() == unrootedVal);
    678  CHECK(okval.get().inspectErr() == val);
    679  CHECK(unrootedValobj1.inspect() == unrootedVal);
    680  CHECK(valobj1.get().inspect() == val);
    681  CHECK(unrootedValobj2.inspectErr() == unrootedObj);
    682  CHECK(valobj2.get().inspectErr() == obj);
    683  CHECK(*maybevalobj.inspect() == unrootedVal);
    684  CHECK(*rooted_maybevalobj.get().inspect() == val);
    685  CHECK(custom.inspect().obj == unrootedObj);
    686  CHECK(custom.inspect().val == unrootedVal);
    687  CHECK(rootedCustom.get().inspect().obj == obj);
    688  CHECK(rootedCustom.get().inspect().val == val);
    689 
    690  JS_GC(cx);
    691 
    692  CHECK(obj != unrootedObj);
    693  CHECK(val != unrootedVal);
    694  CHECK(unrootedValerr.inspect() == unrootedVal);
    695  CHECK(valerr.get().inspect() == val);
    696  CHECK(unrootedOkval.inspectErr() == unrootedVal);
    697  CHECK(okval.get().inspectErr() == val);
    698  CHECK(unrootedValobj1.inspect() == unrootedVal);
    699  CHECK(valobj1.get().inspect() == val);
    700  CHECK(unrootedValobj2.inspectErr() == unrootedObj);
    701  CHECK(valobj2.get().inspectErr() == obj);
    702  CHECK(*maybevalobj.inspect() == unrootedVal);
    703  CHECK(*rooted_maybevalobj.get().inspect() == val);
    704  MOZ_ASSERT(custom.inspect().obj == unrootedObj);
    705  CHECK(custom.inspect().obj == unrootedObj);
    706  CHECK(custom.inspect().val == unrootedVal);
    707  CHECK(rootedCustom.get().inspect().obj == obj);
    708  CHECK(rootedCustom.get().inspect().val == val);
    709 
    710  mozilla::Result<OtherTestErr, mozilla::Ok> r(mozilla::Ok{});
    711  (void)r;
    712 
    713  return true;
    714 }
    715 END_TEST(testGCRootedResult)
    716 
    717 static int copies = 0;
    718 
    719 struct DontCopyMe_Variant {
    720  JSObject* obj;
    721  explicit DontCopyMe_Variant(JSObject* objArg) : obj(objArg) {}
    722  DontCopyMe_Variant(const DontCopyMe_Variant& other) : obj(other.obj) {
    723    copies++;
    724  }
    725  DontCopyMe_Variant(DontCopyMe_Variant&& other) : obj(std::move(other.obj)) {
    726    other.obj = nullptr;
    727  }
    728  void trace(JSTracer* trc) { TraceRoot(trc, &obj, "obj"); }
    729 };
    730 
    731 enum struct TestUnusedZeroEnum : int16_t { Ok = 0, NotOk = 1 };
    732 
    733 namespace mozilla::detail {
    734 template <>
    735 struct UnusedZero<TestUnusedZeroEnum> : UnusedZeroEnum<TestUnusedZeroEnum> {};
    736 }  // namespace mozilla::detail
    737 
    738 namespace JS {
    739 template <>
    740 struct GCPolicy<TestUnusedZeroEnum>
    741    : public IgnoreGCPolicy<TestUnusedZeroEnum> {};
    742 }  // namespace JS
    743 
    744 struct DontCopyMe_NullIsOk {
    745  JS::Value val;
    746  DontCopyMe_NullIsOk() : val(UndefinedValue()) {}
    747  explicit DontCopyMe_NullIsOk(const JS::Value& valArg) : val(valArg) {}
    748  DontCopyMe_NullIsOk(const DontCopyMe_NullIsOk& other) = delete;
    749  DontCopyMe_NullIsOk(DontCopyMe_NullIsOk&& other)
    750      : val(std::move(other.val)) {}
    751  DontCopyMe_NullIsOk& operator=(DontCopyMe_NullIsOk&& other) {
    752    val = std::move(other.val);
    753    other.val = UndefinedValue();
    754    return *this;
    755  }
    756  void trace(JSTracer* trc) { TraceRoot(trc, &val, "val"); }
    757 };
    758 
    759 struct Failed {};
    760 
    761 namespace mozilla::detail {
    762 template <>
    763 struct UnusedZero<Failed> {
    764  using StorageType = uintptr_t;
    765 
    766  static constexpr bool value = true;
    767  static constexpr StorageType nullValue = 0;
    768  static constexpr StorageType GetDefaultValue() { return 2; }
    769 
    770  static constexpr void AssertValid(StorageType aValue) {}
    771  static constexpr Failed Inspect(const StorageType& aValue) {
    772    return Failed{};
    773  }
    774  static constexpr Failed Unwrap(StorageType aValue) { return Failed{}; }
    775  static constexpr StorageType Store(Failed aValue) {
    776    return GetDefaultValue();
    777  }
    778 };
    779 }  // namespace mozilla::detail
    780 
    781 namespace JS {
    782 template <>
    783 struct GCPolicy<Failed> : public IgnoreGCPolicy<Failed> {};
    784 }  // namespace JS
    785 
    786 struct TriviallyCopyable_LowBitTagIsError {
    787  JSObject* obj;
    788  TriviallyCopyable_LowBitTagIsError() : obj(nullptr) {}
    789  explicit TriviallyCopyable_LowBitTagIsError(JSObject* objArg) : obj(objArg) {}
    790  TriviallyCopyable_LowBitTagIsError(
    791      const TriviallyCopyable_LowBitTagIsError& other) = default;
    792  void trace(JSTracer* trc) { TraceRoot(trc, &obj, "obj"); }
    793 };
    794 
    795 namespace mozilla::detail {
    796 template <>
    797 struct HasFreeLSB<TriviallyCopyable_LowBitTagIsError> : HasFreeLSB<JSObject*> {
    798 };
    799 }  // namespace mozilla::detail
    800 
    801 BEGIN_TEST_WITH_ATTRIBUTES(testRootedResultCtors, JS_EXPECT_HAZARDS) {
    802  JSObject* unrootedObj = JS_NewPlainObject(cx);
    803  CHECK(unrootedObj);
    804  Rooted<JSObject*> obj(cx, unrootedObj);
    805 
    806  using mozilla::detail::PackingStrategy;
    807 
    808  static_assert(Result<DontCopyMe_Variant, TestErr>::Strategy ==
    809                PackingStrategy::Variant);
    810  Rooted<Result<DontCopyMe_Variant, TestErr>> vv(cx, DontCopyMe_Variant{obj});
    811  static_assert(Result<mozilla::Ok, DontCopyMe_Variant>::Strategy ==
    812                PackingStrategy::Variant);
    813  Rooted<Result<mozilla::Ok, DontCopyMe_Variant>> ve(cx,
    814                                                     DontCopyMe_Variant{obj});
    815 
    816  static_assert(Result<DontCopyMe_NullIsOk, TestUnusedZeroEnum>::Strategy ==
    817                PackingStrategy::NullIsOk);
    818  Rooted<Result<DontCopyMe_NullIsOk, TestUnusedZeroEnum>> nv(
    819      cx, DontCopyMe_NullIsOk{JS::ObjectValue(*obj)});
    820 
    821  static_assert(Result<TriviallyCopyable_LowBitTagIsError, Failed>::Strategy ==
    822                PackingStrategy::LowBitTagIsError);
    823  Rooted<Result<TriviallyCopyable_LowBitTagIsError, Failed>> lv(
    824      cx, TriviallyCopyable_LowBitTagIsError{obj});
    825 
    826  CHECK(obj == unrootedObj);
    827 
    828  CHECK(vv.get().inspect().obj == obj);
    829  CHECK(ve.get().inspectErr().obj == obj);
    830  CHECK(nv.get().inspect().val.toObjectOrNull() == obj);
    831  CHECK(lv.get().inspect().obj == obj);
    832 
    833  JS_GC(cx);
    834  CHECK(obj != unrootedObj);
    835 
    836  CHECK(vv.get().inspect().obj == obj);
    837  CHECK(ve.get().inspectErr().obj == obj);
    838  CHECK(nv.get().inspect().val.toObjectOrNull() == obj);
    839  CHECK(lv.get().inspect().obj == obj);
    840  CHECK(copies == 0);
    841  return true;
    842 }
    843 END_TEST(testRootedResultCtors)
    844 
    845 #if defined(HAVE_64BIT_BUILD) && !defined(XP_WIN)
    846 
    847 // This depends on a pointer fitting in 48 bits, leaving space for an empty
    848 // struct and a bool in a packed struct. Windows doesn't seem to do this
    849 // packing, so we'll skip this test here. We're primarily checking whether
    850 // copy constructors get called, which should be cross-platform, and
    851 // secondarily making sure that the Rooted/tracing stuff is compiled and
    852 // executed properly. There are certainly more clever ways to do this that
    853 // would work cross-platform, but it doesn't seem worth the bother right now.
    854 
    855 struct __attribute__((packed)) DontCopyMe_PackedVariant {
    856  uintptr_t obj : 48;
    857  static JSObject* Unwrap(uintptr_t packed) {
    858    return reinterpret_cast<JSObject*>(packed);
    859  }
    860  static uintptr_t Store(JSObject* obj) {
    861    return reinterpret_cast<uintptr_t>(obj);
    862  }
    863 
    864  DontCopyMe_PackedVariant() : obj(0) {}
    865  explicit DontCopyMe_PackedVariant(JSObject* objArg)
    866      : obj(reinterpret_cast<uintptr_t>(objArg)) {}
    867  DontCopyMe_PackedVariant(const DontCopyMe_PackedVariant& other)
    868      : obj(other.obj) {
    869    copies++;
    870  }
    871  DontCopyMe_PackedVariant(DontCopyMe_PackedVariant&& other) : obj(other.obj) {
    872    other.obj = 0;
    873  }
    874  void trace(JSTracer* trc) {
    875    JSObject* realObj = Unwrap(obj);
    876    TraceRoot(trc, &realObj, "obj");
    877    obj = Store(realObj);
    878  }
    879 };
    880 
    881 static_assert(std::is_default_constructible_v<DontCopyMe_PackedVariant>);
    882 static_assert(std::is_default_constructible_v<TestErr>);
    883 static_assert(mozilla::detail::IsPackableVariant<DontCopyMe_PackedVariant,
    884                                                 TestErr>::value);
    885 
    886 BEGIN_TEST_WITH_ATTRIBUTES(testResultPackedVariant, JS_EXPECT_HAZARDS) {
    887  JSObject* unrootedObj = JS_NewPlainObject(cx);
    888  CHECK(unrootedObj);
    889  Rooted<JSObject*> obj(cx, unrootedObj);
    890 
    891  using mozilla::detail::PackingStrategy;
    892 
    893  static_assert(Result<DontCopyMe_PackedVariant, TestErr>::Strategy ==
    894                PackingStrategy::PackedVariant);
    895  Rooted<Result<DontCopyMe_PackedVariant, TestErr>> pv(
    896      cx, DontCopyMe_PackedVariant{obj});
    897  static_assert(Result<mozilla::Ok, DontCopyMe_PackedVariant>::Strategy ==
    898                PackingStrategy::PackedVariant);
    899  Rooted<Result<mozilla::Ok, DontCopyMe_PackedVariant>> pe(
    900      cx, DontCopyMe_PackedVariant{obj});
    901 
    902  CHECK(obj == unrootedObj);
    903 
    904  CHECK(DontCopyMe_PackedVariant::Unwrap(pv.get().inspect().obj) == obj);
    905  CHECK(DontCopyMe_PackedVariant::Unwrap(pe.get().inspectErr().obj) == obj);
    906 
    907  JS_GC(cx);
    908  CHECK(obj != unrootedObj);
    909 
    910  CHECK(DontCopyMe_PackedVariant::Unwrap(pv.get().inspect().obj) == obj);
    911  CHECK(DontCopyMe_PackedVariant::Unwrap(pe.get().inspectErr().obj) == obj);
    912 
    913  return true;
    914 }
    915 END_TEST(testResultPackedVariant)
    916 
    917 #endif  // HAVE_64BIT_BUILD
    918 
    919 BEGIN_TEST(testIndexOf) {
    920  // Test template metaprogramming for finding the index of a type in a
    921  // parameter pack. This is used by RootedField.
    922  //
    923  // Missing types fail to compile.
    924 
    925  CHECK((JS::detail::IndexOfTypeV<int, int> == 0));
    926  CHECK((JS::detail::IndexOfTypeV<int, float, int, float> == 1));
    927  CHECK((JS::detail::IndexOfTypeV<int, float, double, int> == 2));
    928 
    929  // IndexOfType doesn't check for duplicates.
    930  CHECK((JS::detail::IndexOfTypeV<float, float, int, float> == 0));
    931 
    932  return true;
    933 }
    934 END_TEST(testIndexOf)
    935 
    936 BEGIN_TEST(testRootedTuple) {
    937  // Tuple with a single GC thing field.
    938  {
    939    RootedTuple<JSObject*> roots(cx);
    940    RootedField<JSObject*> x(roots);
    941    CHECK(!x);
    942    CHECK(x == nullptr);
    943    x = JS_NewPlainObject(cx);
    944    CHECK(x);
    945    CHECK(IsInsideNursery(x));
    946    JS_GC(cx);
    947    CHECK(x);
    948    CHECK(!IsInsideNursery(x));
    949  }
    950 
    951  // Tuple with multiple fields of different types.
    952  {
    953    RootedTuple<JSObject*, JSString*> roots(cx);
    954    RootedField<JSObject*> x(roots);
    955    RootedField<JSString*> y(roots);
    956    CHECK(!x);
    957    CHECK(!y);
    958    x = JS_NewPlainObject(cx);
    959    y = JS_NewStringCopyZ(cx, "foobar");
    960    CHECK(x);
    961    CHECK(y);
    962    CHECK(IsInsideNursery(x));
    963    CHECK(IsInsideNursery(y));
    964    JS_GC(cx);
    965    CHECK(x);
    966    CHECK(y);
    967    CHECK(!IsInsideNursery(x));
    968    CHECK(!IsInsideNursery(y));
    969  }
    970 
    971  // Tuple with multiple fields of the same type.
    972  {
    973    RootedTuple<JSObject*, JSObject*> roots(cx);
    974    RootedField<JSObject*, 0> x(roots);
    975    RootedField<JSObject*, 1> y(roots);
    976    CHECK(!x);
    977    CHECK(!y);
    978    x = JS_NewPlainObject(cx);
    979    y = JS_NewPlainObject(cx);
    980    CHECK(x);
    981    CHECK(y);
    982    CHECK(x != y);
    983    CHECK(IsInsideNursery(x));
    984    CHECK(IsInsideNursery(y));
    985    JS_GC(cx);
    986    CHECK(x);
    987    CHECK(y);
    988    CHECK(x != y);
    989    CHECK(!IsInsideNursery(x));
    990    CHECK(!IsInsideNursery(y));
    991  }
    992 
    993  // Test initialization by RootedTuple.
    994  {
    995    Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
    996    CHECK(obj);
    997    RootedTuple<JSObject*> roots(cx, obj);
    998    RootedField<JSObject*> x(roots);
    999    CHECK(x == obj);
   1000  }
   1001 
   1002  // Test initialization by RootedField.
   1003  {
   1004    Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
   1005    CHECK(obj);
   1006    RootedTuple<JSObject*> roots(cx);
   1007    RootedField<JSObject*> x(roots, obj);
   1008    CHECK(x == obj);
   1009  }
   1010 
   1011  // Test RootedField converts to Handle.
   1012  {
   1013    RootedTuple<JSObject*> roots(cx);
   1014    RootedField<JSObject*> array(roots, JS::NewArrayObject(cx, 1));
   1015    CHECK(array);
   1016    uint32_t length = 0;
   1017    CHECK(JS::GetArrayLength(cx, array, &length));
   1018    CHECK(length == 1);
   1019  }
   1020 
   1021  // Test &RootedField converts to MutableHandle.
   1022  {
   1023    RootedTuple<JSObject*> roots(cx);
   1024    RootedField<JSObject*> obj(roots);
   1025    CHECK(!obj);
   1026    CHECK(JS_GetClassObject(cx, JSProto_Object, &obj));
   1027    CHECK(obj);
   1028  }
   1029 
   1030  // Test RootedField converts to Handle.
   1031  {
   1032    RootedTuple<JSObject*> roots(cx);
   1033    RootedField<JSObject*> array(roots, JS::NewArrayObject(cx, 1));
   1034    CHECK(array);
   1035    uint32_t length = 0;
   1036    CHECK(JS::GetArrayLength(cx, array, &length));
   1037    CHECK(length == 1);
   1038  }
   1039 
   1040  // Test &RootedField converts to MutableHandle.
   1041  {
   1042    RootedTuple<JSObject*> roots(cx);
   1043    RootedField<JSObject*> obj(roots);
   1044    CHECK(!obj);
   1045    CHECK(JS_GetClassObject(cx, JSProto_Object, &obj));
   1046    CHECK(obj);
   1047  }
   1048 
   1049  return true;
   1050 }
   1051 END_TEST(testRootedTuple)
   1052 
   1053 BEGIN_TEST(testRootedCopying) {
   1054  // Make sure that when you write JS::Rooted<T> obj(cx, some_argument)
   1055  // some_argument is not copied.
   1056 
   1057  struct NoCopy {
   1058    NoCopy() = default;
   1059    NoCopy(NoCopy&) = delete;
   1060    NoCopy(const NoCopy&) = delete;
   1061    NoCopy& operator=(const NoCopy&) = delete;
   1062  };
   1063 
   1064  struct StructWithNoCopyArg {
   1065    StructWithNoCopyArg(void* a, NoCopy& nc) {}
   1066    void trace(JSTracer* trace) {}
   1067  };
   1068 
   1069  // Two arguments to choose
   1070  //   Rooted(const RootingContext& cx, CtorArgs... args)
   1071  // over
   1072  //   Rooted(const RootingContext& cx, S&& initial)
   1073  void* a = nullptr;
   1074  NoCopy nc;
   1075 
   1076  // This compiling is a test pass.
   1077  JS::Rooted<StructWithNoCopyArg> rooted(cx, a, nc);
   1078  return true;
   1079 }
   1080 END_TEST(testRootedCopying)