testGCUniqueId.cpp (3849B)
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 "js/GCVector.h" 9 10 #include "jsapi-tests/tests.h" 11 12 #include "gc/StableCellHasher-inl.h" 13 14 using namespace js; 15 16 static void MinimizeHeap(JSContext* cx) { 17 // The second collection is to force us to wait for the background 18 // sweeping that the first GC started to finish. 19 JS_GC(cx); 20 JS_GC(cx); 21 js::gc::FinishGC(cx); 22 } 23 24 BEGIN_TEST(testGCUID) { 25 AutoLeaveZeal nozeal(cx); 26 AutoGCParameter gcparam(cx, JSGC_SEMISPACE_NURSERY_ENABLED, 0); 27 28 uint64_t uid = 0; 29 uint64_t tmp = 0; 30 31 // Ensure the heap is as minimal as it can get. 32 MinimizeHeap(cx); 33 34 JS::RootedObject obj(cx, JS_NewPlainObject(cx)); 35 uintptr_t nurseryAddr = uintptr_t(obj.get()); 36 CHECK(obj); 37 CHECK(gc::IsInsideNursery(obj)); 38 39 // Do not start with an ID. 40 CHECK(!gc::HasUniqueId(obj)); 41 42 // Ensure we can get a new UID. 43 CHECK(gc::GetOrCreateUniqueId(obj, &uid)); 44 CHECK(uid > gc::LargestTaggedNullCellPointer); 45 46 // We should now have an id. 47 CHECK(gc::HasUniqueId(obj)); 48 49 // Calling again should get us the same thing. 50 CHECK(gc::GetOrCreateUniqueId(obj, &tmp)); 51 CHECK(uid == tmp); 52 53 // Tenure the thing and check that the UID moved with it. 54 MinimizeHeap(cx); 55 uintptr_t tenuredAddr = uintptr_t(obj.get()); 56 CHECK(tenuredAddr != nurseryAddr); 57 CHECK(!gc::IsInsideNursery(obj)); 58 CHECK(gc::HasUniqueId(obj)); 59 CHECK(gc::GetOrCreateUniqueId(obj, &tmp)); 60 CHECK(uid == tmp); 61 62 // Allocate a new nursery thing in the same location and check that we 63 // removed the prior uid that was attached to the location. 64 obj = JS_NewPlainObject(cx); 65 CHECK(obj); 66 CHECK(uintptr_t(obj.get()) == nurseryAddr); 67 CHECK(!gc::HasUniqueId(obj)); 68 69 // Try to get another tenured object in the same location and check that 70 // the uid was removed correctly. 71 obj = nullptr; 72 MinimizeHeap(cx); 73 obj = JS_NewPlainObject(cx); 74 MinimizeHeap(cx); 75 CHECK(uintptr_t(obj.get()) == tenuredAddr); 76 CHECK(!gc::HasUniqueId(obj)); 77 CHECK(gc::GetOrCreateUniqueId(obj, &tmp)); 78 CHECK(uid != tmp); 79 uid = tmp; 80 81 // Allocate a few arenas worth of objects to ensure we get some compaction. 82 const static size_t N = 2049; 83 using ObjectVector = JS::GCVector<JSObject*>; 84 JS::Rooted<ObjectVector> vec(cx, ObjectVector(cx)); 85 for (size_t i = 0; i < N; ++i) { 86 obj = JS_NewPlainObject(cx); 87 CHECK(obj); 88 CHECK(vec.append(obj)); 89 } 90 91 // Transfer our vector to tenured if it isn't there already. 92 MinimizeHeap(cx); 93 94 // Tear holes in the heap by unrooting the even objects and collecting. 95 JS::Rooted<ObjectVector> vec2(cx, ObjectVector(cx)); 96 for (size_t i = 0; i < N; ++i) { 97 if (i % 2 == 1) { 98 CHECK(vec2.append(vec[i])); 99 } 100 } 101 vec.clear(); 102 103 // Grab the last object in the vector as our object of interest. 104 obj = vec2.back(); 105 CHECK(obj); 106 CHECK(!gc::IsInsideNursery(obj)); 107 tenuredAddr = uintptr_t(obj.get()); 108 CHECK(gc::GetOrCreateUniqueId(obj, &uid)); 109 110 // Force a compaction to move the object and check that the uid moved to 111 // the new tenured heap location. 112 JS::PrepareForFullGC(cx); 113 JS::NonIncrementalGC(cx, JS::GCOptions::Shrink, JS::GCReason::API); 114 115 // There's a very low probability that this check could fail, but it is 116 // possible. If it becomes an annoying intermittent then we should make 117 // this test more robust by recording IDs of many objects and then checking 118 // that some have moved. 119 CHECK(uintptr_t(obj.get()) != tenuredAddr); 120 CHECK(gc::HasUniqueId(obj)); 121 CHECK(gc::GetOrCreateUniqueId(obj, &tmp)); 122 CHECK(uid == tmp); 123 124 return true; 125 } 126 END_TEST(testGCUID)