DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (d8847129d134)

VCS Links

Line Code
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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 */

#include "jsfriendapi.h"

#include "jsapi-tests/tests.h"

BEGIN_TEST(testArrayBuffer_bug720949_steal)
{
    static const unsigned NUM_TEST_BUFFERS  = 2;
    static const unsigned MAGIC_VALUE_1 = 3;
    static const unsigned MAGIC_VALUE_2 = 17;

    JS::RootedObject buf_len1(cx), buf_len200(cx);
    JS::RootedObject tarray_len1(cx), tarray_len200(cx);

    uint32_t sizes[NUM_TEST_BUFFERS] = { sizeof(uint32_t), 200 * sizeof(uint32_t) };
    JS::HandleObject testBuf[NUM_TEST_BUFFERS] = { buf_len1, buf_len200 };
    JS::HandleObject testArray[NUM_TEST_BUFFERS] = { tarray_len1, tarray_len200 };

    // Single-element ArrayBuffer (uses fixed slots for storage)
    CHECK(buf_len1 = JS_NewArrayBuffer(cx, sizes[0]));
    CHECK(tarray_len1 = JS_NewInt32ArrayWithBuffer(cx, testBuf[0], 0, -1));

    JS_SetElement(cx, testArray[0], 0, MAGIC_VALUE_1);

    // Many-element ArrayBuffer (uses dynamic storage)
    CHECK(buf_len200 = JS_NewArrayBuffer(cx, 200 * sizeof(uint32_t)));
    CHECK(tarray_len200 = JS_NewInt32ArrayWithBuffer(cx, testBuf[1], 0, -1));

    for (unsigned i = 0; i < NUM_TEST_BUFFERS; i++) {
        JS::HandleObject obj = testBuf[i];
        JS::HandleObject view = testArray[i];
        uint32_t size = sizes[i];
        JS::RootedValue v(cx);

        // Byte lengths should all agree
        CHECK(JS_IsArrayBufferObject(obj));
        CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), size);
        JS_GetProperty(cx, obj, "byteLength", &v);
        CHECK_SAME(v, INT_TO_JSVAL(size));
        JS_GetProperty(cx, view, "byteLength", &v);
        CHECK_SAME(v, INT_TO_JSVAL(size));

        // Modifying the underlying data should update the value returned through the view
        {
            JS::AutoCheckCannotGC nogc;
            uint8_t* data = JS_GetArrayBufferData(obj, nogc);
            CHECK(data != nullptr);
            *reinterpret_cast<uint32_t*>(data) = MAGIC_VALUE_2;
        }
        CHECK(JS_GetElement(cx, view, 0, &v));
        CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2));

        // Steal the contents
        void* contents = JS_StealArrayBufferContents(cx, obj);
        CHECK(contents != nullptr);

        // Check that the original ArrayBuffer is neutered
        CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), 0u);
        CHECK(JS_GetProperty(cx, obj, "byteLength", &v));
        CHECK_SAME(v, INT_TO_JSVAL(0));
        CHECK(JS_GetProperty(cx, view, "byteLength", &v));
        CHECK_SAME(v, INT_TO_JSVAL(0));
        CHECK(JS_GetProperty(cx, view, "byteOffset", &v));
        CHECK_SAME(v, INT_TO_JSVAL(0));
        CHECK(JS_GetProperty(cx, view, "length", &v));
        CHECK_SAME(v, INT_TO_JSVAL(0));
        CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), 0u);
        v = JSVAL_VOID;
        JS_GetElement(cx, obj, 0, &v);
        CHECK_SAME(v, JSVAL_VOID);

        // Transfer to a new ArrayBuffer
        JS::RootedObject dst(cx, JS_NewArrayBufferWithContents(cx, size, contents));
        CHECK(JS_IsArrayBufferObject(dst));
        {
            JS::AutoCheckCannotGC nogc;
            (void) JS_GetArrayBufferData(obj, nogc);
        }

        JS::RootedObject dstview(cx, JS_NewInt32ArrayWithBuffer(cx, dst, 0, -1));
        CHECK(dstview != nullptr);

        CHECK_EQUAL(JS_GetArrayBufferByteLength(dst), size);
        {
            JS::AutoCheckCannotGC nogc;
            uint8_t* data = JS_GetArrayBufferData(dst, nogc);
            CHECK(data != nullptr);
            CHECK_EQUAL(*reinterpret_cast<uint32_t*>(data), MAGIC_VALUE_2);
        }
        CHECK(JS_GetElement(cx, dstview, 0, &v));
        CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2));
    }

    return true;
}
END_TEST(testArrayBuffer_bug720949_steal)

// Varying number of views of a buffer, to test the neutering weak pointers
BEGIN_TEST(testArrayBuffer_bug720949_viewList)
{
    JS::RootedObject buffer(cx);

    // No views
    buffer = JS_NewArrayBuffer(cx, 2000);
    buffer = nullptr;
    GC(cx);

    // One view.
    {
        buffer = JS_NewArrayBuffer(cx, 2000);
        JS::RootedObject view(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, -1));
        void* contents = JS_StealArrayBufferContents(cx, buffer);
        CHECK(contents != nullptr);
        JS_free(nullptr, contents);
        GC(cx);
        CHECK(isNeutered(view));
        CHECK(isNeutered(buffer));
        view = nullptr;
        GC(cx);
        buffer = nullptr;
        GC(cx);
    }

    // Two views
    {
        buffer = JS_NewArrayBuffer(cx, 2000);

        JS::RootedObject view1(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, -1));
        JS::RootedObject view2(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 1, 200));

        // Remove, re-add a view
        view2 = nullptr;
        GC(cx);
        view2 = JS_NewUint8ArrayWithBuffer(cx, buffer, 1, 200);

        // Neuter
        void* contents = JS_StealArrayBufferContents(cx, buffer);
        CHECK(contents != nullptr);
        JS_free(nullptr, contents);

        CHECK(isNeutered(view1));
        CHECK(isNeutered(view2));
        CHECK(isNeutered(buffer));

        view1 = nullptr;
        GC(cx);
        view2 = nullptr;
        GC(cx);
        buffer = nullptr;
        GC(cx);
    }

    return true;
}

static void GC(JSContext* cx)
{
    JS_GC(JS_GetRuntime(cx));
    JS_GC(JS_GetRuntime(cx)); // Trigger another to wait for background finalization to end
}

bool isNeutered(JS::HandleObject obj) {
    JS::RootedValue v(cx);
    return JS_GetProperty(cx, obj, "byteLength", &v) && v.toInt32() == 0;
}

END_TEST(testArrayBuffer_bug720949_viewList)