1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsapi-tests/testArrayBuffer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,162 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + */ 1.7 + 1.8 +#include "jsfriendapi.h" 1.9 + 1.10 +#include "jsapi-tests/tests.h" 1.11 + 1.12 +BEGIN_TEST(testArrayBuffer_bug720949_steal) 1.13 +{ 1.14 + static const unsigned NUM_TEST_BUFFERS = 2; 1.15 + static const unsigned MAGIC_VALUE_1 = 3; 1.16 + static const unsigned MAGIC_VALUE_2 = 17; 1.17 + 1.18 + JS::RootedObject buf_len1(cx), buf_len200(cx); 1.19 + JS::RootedObject tarray_len1(cx), tarray_len200(cx); 1.20 + 1.21 + uint32_t sizes[NUM_TEST_BUFFERS] = { sizeof(uint32_t), 200 * sizeof(uint32_t) }; 1.22 + JS::HandleObject testBuf[NUM_TEST_BUFFERS] = { buf_len1, buf_len200 }; 1.23 + JS::HandleObject testArray[NUM_TEST_BUFFERS] = { tarray_len1, tarray_len200 }; 1.24 + 1.25 + // Single-element ArrayBuffer (uses fixed slots for storage) 1.26 + CHECK(buf_len1 = JS_NewArrayBuffer(cx, sizes[0])); 1.27 + CHECK(tarray_len1 = JS_NewInt32ArrayWithBuffer(cx, testBuf[0], 0, -1)); 1.28 + 1.29 + JS_SetElement(cx, testArray[0], 0, MAGIC_VALUE_1); 1.30 + 1.31 + // Many-element ArrayBuffer (uses dynamic storage) 1.32 + CHECK(buf_len200 = JS_NewArrayBuffer(cx, 200 * sizeof(uint32_t))); 1.33 + CHECK(tarray_len200 = JS_NewInt32ArrayWithBuffer(cx, testBuf[1], 0, -1)); 1.34 + 1.35 + for (unsigned i = 0; i < NUM_TEST_BUFFERS; i++) { 1.36 + JS::HandleObject obj = testBuf[i]; 1.37 + JS::HandleObject view = testArray[i]; 1.38 + uint32_t size = sizes[i]; 1.39 + JS::RootedValue v(cx); 1.40 + 1.41 + // Byte lengths should all agree 1.42 + CHECK(JS_IsArrayBufferObject(obj)); 1.43 + CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), size); 1.44 + JS_GetProperty(cx, obj, "byteLength", &v); 1.45 + CHECK_SAME(v, INT_TO_JSVAL(size)); 1.46 + JS_GetProperty(cx, view, "byteLength", &v); 1.47 + CHECK_SAME(v, INT_TO_JSVAL(size)); 1.48 + 1.49 + // Modifying the underlying data should update the value returned through the view 1.50 + uint8_t *data = JS_GetStableArrayBufferData(cx, obj); 1.51 + CHECK(data != nullptr); 1.52 + *reinterpret_cast<uint32_t*>(data) = MAGIC_VALUE_2; 1.53 + CHECK(JS_GetElement(cx, view, 0, &v)); 1.54 + CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2)); 1.55 + 1.56 + // Steal the contents 1.57 + void *contents = JS_StealArrayBufferContents(cx, obj); 1.58 + CHECK(contents != nullptr); 1.59 + CHECK(data != nullptr); 1.60 + 1.61 + // Check that the original ArrayBuffer is neutered 1.62 + CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), 0); 1.63 + CHECK(JS_GetProperty(cx, obj, "byteLength", &v)); 1.64 + CHECK_SAME(v, INT_TO_JSVAL(0)); 1.65 + CHECK(JS_GetProperty(cx, view, "byteLength", &v)); 1.66 + CHECK_SAME(v, INT_TO_JSVAL(0)); 1.67 + CHECK(JS_GetProperty(cx, view, "byteOffset", &v)); 1.68 + CHECK_SAME(v, INT_TO_JSVAL(0)); 1.69 + CHECK(JS_GetProperty(cx, view, "length", &v)); 1.70 + CHECK_SAME(v, INT_TO_JSVAL(0)); 1.71 + CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), 0); 1.72 + v = JSVAL_VOID; 1.73 + JS_GetElement(cx, obj, 0, &v); 1.74 + CHECK_SAME(v, JSVAL_VOID); 1.75 + 1.76 + // Transfer to a new ArrayBuffer 1.77 + JS::RootedObject dst(cx, JS_NewArrayBufferWithContents(cx, size, contents)); 1.78 + CHECK(JS_IsArrayBufferObject(dst)); 1.79 + data = JS_GetStableArrayBufferData(cx, obj); 1.80 + 1.81 + JS::RootedObject dstview(cx, JS_NewInt32ArrayWithBuffer(cx, dst, 0, -1)); 1.82 + CHECK(dstview != nullptr); 1.83 + 1.84 + CHECK_EQUAL(JS_GetArrayBufferByteLength(dst), size); 1.85 + data = JS_GetStableArrayBufferData(cx, dst); 1.86 + CHECK(data != nullptr); 1.87 + CHECK_EQUAL(*reinterpret_cast<uint32_t*>(data), MAGIC_VALUE_2); 1.88 + CHECK(JS_GetElement(cx, dstview, 0, &v)); 1.89 + CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2)); 1.90 + } 1.91 + 1.92 + return true; 1.93 +} 1.94 +END_TEST(testArrayBuffer_bug720949_steal) 1.95 + 1.96 +// Varying number of views of a buffer, to test the neutering weak pointers 1.97 +BEGIN_TEST(testArrayBuffer_bug720949_viewList) 1.98 +{ 1.99 + JS::RootedObject buffer(cx); 1.100 + 1.101 + // No views 1.102 + buffer = JS_NewArrayBuffer(cx, 2000); 1.103 + buffer = nullptr; 1.104 + GC(cx); 1.105 + 1.106 + // One view. 1.107 + { 1.108 + buffer = JS_NewArrayBuffer(cx, 2000); 1.109 + JS::RootedObject view(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, -1)); 1.110 + void *contents = JS_StealArrayBufferContents(cx, buffer); 1.111 + CHECK(contents != nullptr); 1.112 + JS_free(nullptr, contents); 1.113 + GC(cx); 1.114 + CHECK(isNeutered(view)); 1.115 + CHECK(isNeutered(buffer)); 1.116 + view = nullptr; 1.117 + GC(cx); 1.118 + buffer = nullptr; 1.119 + GC(cx); 1.120 + } 1.121 + 1.122 + // Two views 1.123 + { 1.124 + buffer = JS_NewArrayBuffer(cx, 2000); 1.125 + 1.126 + JS::RootedObject view1(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, -1)); 1.127 + JS::RootedObject view2(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 1, 200)); 1.128 + 1.129 + // Remove, re-add a view 1.130 + view2 = nullptr; 1.131 + GC(cx); 1.132 + view2 = JS_NewUint8ArrayWithBuffer(cx, buffer, 1, 200); 1.133 + 1.134 + // Neuter 1.135 + void *contents = JS_StealArrayBufferContents(cx, buffer); 1.136 + CHECK(contents != nullptr); 1.137 + JS_free(nullptr, contents); 1.138 + 1.139 + CHECK(isNeutered(view1)); 1.140 + CHECK(isNeutered(view2)); 1.141 + CHECK(isNeutered(buffer)); 1.142 + 1.143 + view1 = nullptr; 1.144 + GC(cx); 1.145 + view2 = nullptr; 1.146 + GC(cx); 1.147 + buffer = nullptr; 1.148 + GC(cx); 1.149 + } 1.150 + 1.151 + return true; 1.152 +} 1.153 + 1.154 +static void GC(JSContext *cx) 1.155 +{ 1.156 + JS_GC(JS_GetRuntime(cx)); 1.157 + JS_GC(JS_GetRuntime(cx)); // Trigger another to wait for background finalization to end 1.158 +} 1.159 + 1.160 +bool isNeutered(JS::HandleObject obj) { 1.161 + JS::RootedValue v(cx); 1.162 + return JS_GetProperty(cx, obj, "byteLength", &v) && v.toInt32() == 0; 1.163 +} 1.164 + 1.165 +END_TEST(testArrayBuffer_bug720949_viewList)