|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 */ |
|
4 |
|
5 #include "jsfriendapi.h" |
|
6 |
|
7 #include "jsapi-tests/tests.h" |
|
8 |
|
9 BEGIN_TEST(testArrayBuffer_bug720949_steal) |
|
10 { |
|
11 static const unsigned NUM_TEST_BUFFERS = 2; |
|
12 static const unsigned MAGIC_VALUE_1 = 3; |
|
13 static const unsigned MAGIC_VALUE_2 = 17; |
|
14 |
|
15 JS::RootedObject buf_len1(cx), buf_len200(cx); |
|
16 JS::RootedObject tarray_len1(cx), tarray_len200(cx); |
|
17 |
|
18 uint32_t sizes[NUM_TEST_BUFFERS] = { sizeof(uint32_t), 200 * sizeof(uint32_t) }; |
|
19 JS::HandleObject testBuf[NUM_TEST_BUFFERS] = { buf_len1, buf_len200 }; |
|
20 JS::HandleObject testArray[NUM_TEST_BUFFERS] = { tarray_len1, tarray_len200 }; |
|
21 |
|
22 // Single-element ArrayBuffer (uses fixed slots for storage) |
|
23 CHECK(buf_len1 = JS_NewArrayBuffer(cx, sizes[0])); |
|
24 CHECK(tarray_len1 = JS_NewInt32ArrayWithBuffer(cx, testBuf[0], 0, -1)); |
|
25 |
|
26 JS_SetElement(cx, testArray[0], 0, MAGIC_VALUE_1); |
|
27 |
|
28 // Many-element ArrayBuffer (uses dynamic storage) |
|
29 CHECK(buf_len200 = JS_NewArrayBuffer(cx, 200 * sizeof(uint32_t))); |
|
30 CHECK(tarray_len200 = JS_NewInt32ArrayWithBuffer(cx, testBuf[1], 0, -1)); |
|
31 |
|
32 for (unsigned i = 0; i < NUM_TEST_BUFFERS; i++) { |
|
33 JS::HandleObject obj = testBuf[i]; |
|
34 JS::HandleObject view = testArray[i]; |
|
35 uint32_t size = sizes[i]; |
|
36 JS::RootedValue v(cx); |
|
37 |
|
38 // Byte lengths should all agree |
|
39 CHECK(JS_IsArrayBufferObject(obj)); |
|
40 CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), size); |
|
41 JS_GetProperty(cx, obj, "byteLength", &v); |
|
42 CHECK_SAME(v, INT_TO_JSVAL(size)); |
|
43 JS_GetProperty(cx, view, "byteLength", &v); |
|
44 CHECK_SAME(v, INT_TO_JSVAL(size)); |
|
45 |
|
46 // Modifying the underlying data should update the value returned through the view |
|
47 uint8_t *data = JS_GetStableArrayBufferData(cx, obj); |
|
48 CHECK(data != nullptr); |
|
49 *reinterpret_cast<uint32_t*>(data) = MAGIC_VALUE_2; |
|
50 CHECK(JS_GetElement(cx, view, 0, &v)); |
|
51 CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2)); |
|
52 |
|
53 // Steal the contents |
|
54 void *contents = JS_StealArrayBufferContents(cx, obj); |
|
55 CHECK(contents != nullptr); |
|
56 CHECK(data != nullptr); |
|
57 |
|
58 // Check that the original ArrayBuffer is neutered |
|
59 CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), 0); |
|
60 CHECK(JS_GetProperty(cx, obj, "byteLength", &v)); |
|
61 CHECK_SAME(v, INT_TO_JSVAL(0)); |
|
62 CHECK(JS_GetProperty(cx, view, "byteLength", &v)); |
|
63 CHECK_SAME(v, INT_TO_JSVAL(0)); |
|
64 CHECK(JS_GetProperty(cx, view, "byteOffset", &v)); |
|
65 CHECK_SAME(v, INT_TO_JSVAL(0)); |
|
66 CHECK(JS_GetProperty(cx, view, "length", &v)); |
|
67 CHECK_SAME(v, INT_TO_JSVAL(0)); |
|
68 CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), 0); |
|
69 v = JSVAL_VOID; |
|
70 JS_GetElement(cx, obj, 0, &v); |
|
71 CHECK_SAME(v, JSVAL_VOID); |
|
72 |
|
73 // Transfer to a new ArrayBuffer |
|
74 JS::RootedObject dst(cx, JS_NewArrayBufferWithContents(cx, size, contents)); |
|
75 CHECK(JS_IsArrayBufferObject(dst)); |
|
76 data = JS_GetStableArrayBufferData(cx, obj); |
|
77 |
|
78 JS::RootedObject dstview(cx, JS_NewInt32ArrayWithBuffer(cx, dst, 0, -1)); |
|
79 CHECK(dstview != nullptr); |
|
80 |
|
81 CHECK_EQUAL(JS_GetArrayBufferByteLength(dst), size); |
|
82 data = JS_GetStableArrayBufferData(cx, dst); |
|
83 CHECK(data != nullptr); |
|
84 CHECK_EQUAL(*reinterpret_cast<uint32_t*>(data), MAGIC_VALUE_2); |
|
85 CHECK(JS_GetElement(cx, dstview, 0, &v)); |
|
86 CHECK_SAME(v, INT_TO_JSVAL(MAGIC_VALUE_2)); |
|
87 } |
|
88 |
|
89 return true; |
|
90 } |
|
91 END_TEST(testArrayBuffer_bug720949_steal) |
|
92 |
|
93 // Varying number of views of a buffer, to test the neutering weak pointers |
|
94 BEGIN_TEST(testArrayBuffer_bug720949_viewList) |
|
95 { |
|
96 JS::RootedObject buffer(cx); |
|
97 |
|
98 // No views |
|
99 buffer = JS_NewArrayBuffer(cx, 2000); |
|
100 buffer = nullptr; |
|
101 GC(cx); |
|
102 |
|
103 // One view. |
|
104 { |
|
105 buffer = JS_NewArrayBuffer(cx, 2000); |
|
106 JS::RootedObject view(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, -1)); |
|
107 void *contents = JS_StealArrayBufferContents(cx, buffer); |
|
108 CHECK(contents != nullptr); |
|
109 JS_free(nullptr, contents); |
|
110 GC(cx); |
|
111 CHECK(isNeutered(view)); |
|
112 CHECK(isNeutered(buffer)); |
|
113 view = nullptr; |
|
114 GC(cx); |
|
115 buffer = nullptr; |
|
116 GC(cx); |
|
117 } |
|
118 |
|
119 // Two views |
|
120 { |
|
121 buffer = JS_NewArrayBuffer(cx, 2000); |
|
122 |
|
123 JS::RootedObject view1(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, -1)); |
|
124 JS::RootedObject view2(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 1, 200)); |
|
125 |
|
126 // Remove, re-add a view |
|
127 view2 = nullptr; |
|
128 GC(cx); |
|
129 view2 = JS_NewUint8ArrayWithBuffer(cx, buffer, 1, 200); |
|
130 |
|
131 // Neuter |
|
132 void *contents = JS_StealArrayBufferContents(cx, buffer); |
|
133 CHECK(contents != nullptr); |
|
134 JS_free(nullptr, contents); |
|
135 |
|
136 CHECK(isNeutered(view1)); |
|
137 CHECK(isNeutered(view2)); |
|
138 CHECK(isNeutered(buffer)); |
|
139 |
|
140 view1 = nullptr; |
|
141 GC(cx); |
|
142 view2 = nullptr; |
|
143 GC(cx); |
|
144 buffer = nullptr; |
|
145 GC(cx); |
|
146 } |
|
147 |
|
148 return true; |
|
149 } |
|
150 |
|
151 static void GC(JSContext *cx) |
|
152 { |
|
153 JS_GC(JS_GetRuntime(cx)); |
|
154 JS_GC(JS_GetRuntime(cx)); // Trigger another to wait for background finalization to end |
|
155 } |
|
156 |
|
157 bool isNeutered(JS::HandleObject obj) { |
|
158 JS::RootedValue v(cx); |
|
159 return JS_GetProperty(cx, obj, "byteLength", &v) && v.toInt32() == 0; |
|
160 } |
|
161 |
|
162 END_TEST(testArrayBuffer_bug720949_viewList) |