summaryrefslogtreecommitdiffstats
path: root/js/src/jsapi-tests/testGCUniqueId.cpp
blob: 9502e307d993ece0808f4f9f81958b70f9156035 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "gc/GCInternals.h"
#include "gc/Zone.h"
#include "js/GCVector.h"

#include "jsapi-tests/tests.h"

static void
MinimizeHeap(JSContext* cx)
{
    // The second collection is to force us to wait for the background
    // sweeping that the first GC started to finish.
    JS_GC(cx);
    JS_GC(cx);
    js::gc::FinishGC(cx);
}

BEGIN_TEST(testGCUID)
{
    uint64_t uid = 0;
    uint64_t tmp = 0;

    // Ensure the heap is as minimal as it can get.
    MinimizeHeap(cx);

    JS::RootedObject obj(cx, JS_NewPlainObject(cx));
    uintptr_t nurseryAddr = uintptr_t(obj.get());
    CHECK(obj);
    CHECK(js::gc::IsInsideNursery(obj));

    // Do not start with an ID.
    CHECK(!obj->zone()->hasUniqueId(obj));

    // Ensure we can get a new UID.
    CHECK(obj->zone()->getUniqueId(obj, &uid));
    CHECK(uid > js::gc::LargestTaggedNullCellPointer);

    // We should now have an id.
    CHECK(obj->zone()->hasUniqueId(obj));

    // Calling again should get us the same thing.
    CHECK(obj->zone()->getUniqueId(obj, &tmp));
    CHECK(uid == tmp);

    // Tenure the thing and check that the UID moved with it.
    MinimizeHeap(cx);
    uintptr_t tenuredAddr = uintptr_t(obj.get());
    CHECK(tenuredAddr != nurseryAddr);
    CHECK(!js::gc::IsInsideNursery(obj));
    CHECK(obj->zone()->hasUniqueId(obj));
    CHECK(obj->zone()->getUniqueId(obj, &tmp));
    CHECK(uid == tmp);

    // Allocate a new nursery thing in the same location and check that we
    // removed the prior uid that was attached to the location.
    obj = JS_NewPlainObject(cx);
    CHECK(obj);
    CHECK(uintptr_t(obj.get()) == nurseryAddr);
    CHECK(!obj->zone()->hasUniqueId(obj));

    // Try to get another tenured object in the same location and check that
    // the uid was removed correctly.
    obj = nullptr;
    MinimizeHeap(cx);
    obj = JS_NewPlainObject(cx);
    MinimizeHeap(cx);
    CHECK(uintptr_t(obj.get()) == tenuredAddr);
    CHECK(!obj->zone()->hasUniqueId(obj));
    CHECK(obj->zone()->getUniqueId(obj, &tmp));
    CHECK(uid != tmp);
    uid = tmp;

    // Allocate a few arenas worth of objects to ensure we get some compaction.
    const static size_t N = 2049;
    using ObjectVector = JS::GCVector<JSObject*>;
    JS::Rooted<ObjectVector> vec(cx, ObjectVector(cx));
    for (size_t i = 0; i < N; ++i) {
        obj = JS_NewPlainObject(cx);
        CHECK(obj);
        CHECK(vec.append(obj));
    }

    // Transfer our vector to tenured if it isn't there already.
    MinimizeHeap(cx);

    // Tear holes in the heap by unrooting the even objects and collecting.
    JS::Rooted<ObjectVector> vec2(cx, ObjectVector(cx));
    for (size_t i = 0; i < N; ++i) {
        if (i % 2 == 1)
            vec2.append(vec[i]);
    }
    vec.clear();
    MinimizeHeap(cx);

    // Grab the last object in the vector as our object of interest.
    obj = vec2.back();
    CHECK(obj);
    tenuredAddr = uintptr_t(obj.get());
    CHECK(obj->zone()->getUniqueId(obj, &uid));

    // Force a compaction to move the object and check that the uid moved to
    // the new tenured heap location.
    JS::PrepareForFullGC(cx);
    JS::GCForReason(cx, GC_SHRINK, JS::gcreason::API);
    MinimizeHeap(cx);
    CHECK(uintptr_t(obj.get()) != tenuredAddr);
    CHECK(obj->zone()->hasUniqueId(obj));
    CHECK(obj->zone()->getUniqueId(obj, &tmp));
    CHECK(uid == tmp);

    return true;
}
END_TEST(testGCUID)