|
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 #ifdef XP_UNIX |
|
6 #include <fcntl.h> |
|
7 #include <stdio.h> |
|
8 #include <string.h> |
|
9 #include <sys/stat.h> |
|
10 #include <sys/types.h> |
|
11 #include <unistd.h> |
|
12 |
|
13 #include "jsfriendapi.h" |
|
14 #include "js/StructuredClone.h" |
|
15 #include "jsapi-tests/tests.h" |
|
16 #include "vm/ArrayBufferObject.h" |
|
17 |
|
18 const char test_data[] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; |
|
19 const char test_filename[] = "temp-bug945152_MappedArrayBuffer"; |
|
20 |
|
21 BEGIN_TEST(testMappedArrayBuffer_bug945152) |
|
22 { |
|
23 TempFile test_file; |
|
24 FILE *test_stream = test_file.open(test_filename); |
|
25 CHECK(fputs(test_data, test_stream) != EOF); |
|
26 test_file.close(); |
|
27 |
|
28 // Offset 0. |
|
29 CHECK(TestCreateObject(0, 12)); |
|
30 |
|
31 // Aligned offset. |
|
32 CHECK(TestCreateObject(8, 12)); |
|
33 |
|
34 // Unaligned offset. |
|
35 CHECK(CreateNewObject(11, 12) == nullptr); |
|
36 |
|
37 // Offset + length greater than file size. |
|
38 CHECK(CreateNewObject(8, sizeof(test_data) - 7) == nullptr); |
|
39 |
|
40 // Release the mapped content. |
|
41 CHECK(TestReleaseContents()); |
|
42 |
|
43 // Neuter mapped array buffer. |
|
44 CHECK(TestNeuterObject()); |
|
45 |
|
46 // Clone mapped array buffer. |
|
47 CHECK(TestCloneObject()); |
|
48 |
|
49 // Steal mapped array buffer contents. |
|
50 CHECK(TestStealContents()); |
|
51 |
|
52 // Transfer mapped array buffer contents. |
|
53 CHECK(TestTransferObject()); |
|
54 |
|
55 test_file.remove(); |
|
56 |
|
57 return true; |
|
58 } |
|
59 |
|
60 JSObject *CreateNewObject(const int offset, const int length) |
|
61 { |
|
62 int fd = open(test_filename, O_RDONLY); |
|
63 void *ptr = JS_CreateMappedArrayBufferContents(fd, offset, length); |
|
64 close(fd); |
|
65 if (!ptr) |
|
66 return nullptr; |
|
67 JSObject *obj = JS_NewMappedArrayBufferWithContents(cx, length, ptr); |
|
68 if (!obj) { |
|
69 JS_ReleaseMappedArrayBufferContents(ptr, length); |
|
70 return nullptr; |
|
71 } |
|
72 return obj; |
|
73 } |
|
74 |
|
75 bool VerifyObject(JS::HandleObject obj, const int offset, const int length, const bool mapped) |
|
76 { |
|
77 CHECK(obj); |
|
78 CHECK(JS_IsArrayBufferObject(obj)); |
|
79 CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), length); |
|
80 if (mapped) |
|
81 CHECK(JS_IsMappedArrayBufferObject(obj)); |
|
82 else |
|
83 CHECK(!JS_IsMappedArrayBufferObject(obj)); |
|
84 const char *data = reinterpret_cast<const char *>(JS_GetArrayBufferData(obj)); |
|
85 CHECK(data); |
|
86 CHECK(memcmp(data, test_data + offset, length) == 0); |
|
87 |
|
88 return true; |
|
89 } |
|
90 |
|
91 bool TestCreateObject(const int offset, const int length) |
|
92 { |
|
93 JS::RootedObject obj(cx, CreateNewObject(offset, length)); |
|
94 CHECK(VerifyObject(obj, offset, length, true)); |
|
95 |
|
96 return true; |
|
97 } |
|
98 |
|
99 bool TestReleaseContents() |
|
100 { |
|
101 int fd = open(test_filename, O_RDONLY); |
|
102 void *ptr = JS_CreateMappedArrayBufferContents(fd, 0, 12); |
|
103 close(fd); |
|
104 if (!ptr) |
|
105 return false; |
|
106 JS_ReleaseMappedArrayBufferContents(ptr, 12); |
|
107 |
|
108 return true; |
|
109 } |
|
110 |
|
111 bool TestNeuterObject() |
|
112 { |
|
113 JS::RootedObject obj(cx, CreateNewObject(8, 12)); |
|
114 CHECK(obj); |
|
115 JS_NeuterArrayBuffer(cx, obj, ChangeData); |
|
116 CHECK(isNeutered(obj)); |
|
117 |
|
118 return true; |
|
119 } |
|
120 |
|
121 bool TestCloneObject() |
|
122 { |
|
123 JS::RootedObject obj1(cx, CreateNewObject(8, 12)); |
|
124 CHECK(obj1); |
|
125 JSAutoStructuredCloneBuffer cloned_buffer; |
|
126 JS::RootedValue v1(cx, OBJECT_TO_JSVAL(obj1)); |
|
127 const JSStructuredCloneCallbacks *callbacks = js::GetContextStructuredCloneCallbacks(cx); |
|
128 CHECK(cloned_buffer.write(cx, v1, callbacks, nullptr)); |
|
129 JS::RootedValue v2(cx); |
|
130 CHECK(cloned_buffer.read(cx, &v2, callbacks, nullptr)); |
|
131 JS::RootedObject obj2(cx, JSVAL_TO_OBJECT(v2)); |
|
132 CHECK(VerifyObject(obj2, 8, 12, false)); |
|
133 |
|
134 return true; |
|
135 } |
|
136 |
|
137 bool TestStealContents() |
|
138 { |
|
139 JS::RootedObject obj(cx, CreateNewObject(8, 12)); |
|
140 CHECK(obj); |
|
141 void *contents = JS_StealArrayBufferContents(cx, obj); |
|
142 CHECK(contents); |
|
143 CHECK(memcmp(contents, test_data + 8, 12) == 0); |
|
144 CHECK(isNeutered(obj)); |
|
145 |
|
146 return true; |
|
147 } |
|
148 |
|
149 bool TestTransferObject() |
|
150 { |
|
151 JS::RootedObject obj1(cx, CreateNewObject(8, 12)); |
|
152 CHECK(obj1); |
|
153 JS::RootedValue v1(cx, OBJECT_TO_JSVAL(obj1)); |
|
154 |
|
155 // Create an Array of transferable values. |
|
156 JS::AutoValueVector argv(cx); |
|
157 argv.append(v1); |
|
158 JS::RootedObject obj(cx, JS_NewArrayObject(cx, JS::HandleValueArray::subarray(argv, 0, 1))); |
|
159 CHECK(obj); |
|
160 JS::RootedValue transferable(cx, OBJECT_TO_JSVAL(obj)); |
|
161 |
|
162 JSAutoStructuredCloneBuffer cloned_buffer; |
|
163 const JSStructuredCloneCallbacks *callbacks = js::GetContextStructuredCloneCallbacks(cx); |
|
164 CHECK(cloned_buffer.write(cx, v1, transferable, callbacks, nullptr)); |
|
165 JS::RootedValue v2(cx); |
|
166 CHECK(cloned_buffer.read(cx, &v2, callbacks, nullptr)); |
|
167 JS::RootedObject obj2(cx, JSVAL_TO_OBJECT(v2)); |
|
168 CHECK(VerifyObject(obj2, 8, 12, true)); |
|
169 CHECK(isNeutered(obj1)); |
|
170 |
|
171 return true; |
|
172 } |
|
173 |
|
174 bool isNeutered(JS::HandleObject obj) |
|
175 { |
|
176 JS::RootedValue v(cx); |
|
177 return JS_GetProperty(cx, obj, "byteLength", &v) && v.toInt32() == 0; |
|
178 } |
|
179 |
|
180 static void GC(JSContext *cx) |
|
181 { |
|
182 JS_GC(JS_GetRuntime(cx)); |
|
183 // Trigger another to wait for background finalization to end. |
|
184 JS_GC(JS_GetRuntime(cx)); |
|
185 } |
|
186 |
|
187 END_TEST(testMappedArrayBuffer_bug945152) |
|
188 #endif |