testMappedArrayBuffer.cpp (5330B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 */ 4 5 #include <fcntl.h> 6 #include <stdio.h> 7 8 #include "js/Array.h" // JS::NewArrayObject 9 #include "js/ArrayBuffer.h" // JS::{{Create,Release}MappedArrayBufferContents,DetachArrayBuffer,GetArrayBuffer{ByteLength,Data},Is{,Detached,Mapped}ArrayBufferObject,NewMappedArrayBufferWithContents,StealArrayBufferContents} 10 #include "js/StructuredClone.h" 11 #include "jsapi-tests/tests.h" 12 #include "vm/ArrayBufferObject.h" 13 14 #ifdef XP_WIN 15 # include <io.h> 16 # define GET_OS_FD(a) int(_get_osfhandle(a)) 17 #else 18 # include <unistd.h> 19 # define GET_OS_FD(a) (a) 20 #endif 21 22 const char test_data[] = 23 "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 24 const char test_filename[] = "temp-bug945152_MappedArrayBuffer"; 25 26 BEGIN_TEST(testMappedArrayBuffer_bug945152) { 27 TempFile test_file; 28 FILE* test_stream = test_file.open(test_filename); 29 CHECK(fputs(test_data, test_stream) != EOF); 30 test_file.close(); 31 32 // Offset 0. 33 CHECK(TestCreateObject(0, 12)); 34 35 // Aligned offset. 36 CHECK(TestCreateObject(8, 12)); 37 38 // Unaligned offset. 39 CHECK(CreateNewObject(11, 12) == nullptr); 40 41 // Offset + length greater than file size. 42 CHECK(CreateNewObject(8, sizeof(test_data) - 7) == nullptr); 43 44 // Release the mapped content. 45 CHECK(TestReleaseContents()); 46 47 // Detach mapped array buffer. 48 CHECK(TestDetachObject()); 49 50 // Clone mapped array buffer. 51 CHECK(TestCloneObject()); 52 53 // Steal mapped array buffer contents. 54 CHECK(TestStealContents()); 55 56 // Transfer mapped array buffer contents. 57 CHECK(TestTransferObject()); 58 59 // GC so we can remove the file we created. 60 GC(cx); 61 62 test_file.remove(); 63 64 return true; 65 } 66 67 JSObject* CreateNewObject(const int offset, const int length) { 68 int fd = open(test_filename, O_RDONLY); 69 void* ptr = 70 JS::CreateMappedArrayBufferContents(GET_OS_FD(fd), offset, length); 71 close(fd); 72 if (!ptr) { 73 return nullptr; 74 } 75 JSObject* obj = JS::NewMappedArrayBufferWithContents(cx, length, ptr); 76 if (!obj) { 77 JS::ReleaseMappedArrayBufferContents(ptr, length); 78 return nullptr; 79 } 80 return obj; 81 } 82 83 bool VerifyObject(JS::HandleObject obj, uint32_t offset, uint32_t length, 84 const bool mapped) { 85 JS::AutoCheckCannotGC nogc; 86 87 CHECK(obj); 88 CHECK(JS::IsArrayBufferObject(obj)); 89 CHECK_EQUAL(JS::GetArrayBufferByteLength(obj), length); 90 if (mapped) { 91 CHECK(JS::IsMappedArrayBufferObject(obj)); 92 } else { 93 CHECK(!JS::IsMappedArrayBufferObject(obj)); 94 } 95 bool sharedDummy; 96 const char* data = reinterpret_cast<const char*>( 97 JS::GetArrayBufferData(obj, &sharedDummy, nogc)); 98 CHECK(data); 99 CHECK(memcmp(data, test_data + offset, length) == 0); 100 101 return true; 102 } 103 104 bool TestCreateObject(uint32_t offset, uint32_t length) { 105 JS::RootedObject obj(cx, CreateNewObject(offset, length)); 106 CHECK(VerifyObject(obj, offset, length, true)); 107 108 return true; 109 } 110 111 bool TestReleaseContents() { 112 int fd = open(test_filename, O_RDONLY); 113 void* ptr = JS::CreateMappedArrayBufferContents(GET_OS_FD(fd), 0, 12); 114 close(fd); 115 if (!ptr) { 116 return false; 117 } 118 JS::ReleaseMappedArrayBufferContents(ptr, 12); 119 120 return true; 121 } 122 123 bool TestDetachObject() { 124 JS::RootedObject obj(cx, CreateNewObject(8, 12)); 125 CHECK(obj); 126 JS::DetachArrayBuffer(cx, obj); 127 CHECK(JS::IsDetachedArrayBufferObject(obj)); 128 129 return true; 130 } 131 132 bool TestCloneObject() { 133 JS::RootedObject obj1(cx, CreateNewObject(8, 12)); 134 CHECK(obj1); 135 JSAutoStructuredCloneBuffer cloned_buffer( 136 JS::StructuredCloneScope::SameProcess, nullptr, nullptr); 137 JS::RootedValue v1(cx, JS::ObjectValue(*obj1)); 138 CHECK(cloned_buffer.write(cx, v1, nullptr, nullptr)); 139 JS::RootedValue v2(cx); 140 CHECK(cloned_buffer.read(cx, &v2, JS::CloneDataPolicy(), nullptr, nullptr)); 141 JS::RootedObject obj2(cx, v2.toObjectOrNull()); 142 CHECK(VerifyObject(obj2, 8, 12, false)); 143 144 return true; 145 } 146 147 bool TestStealContents() { 148 JS::RootedObject obj(cx, CreateNewObject(8, 12)); 149 CHECK(obj); 150 void* contents = JS::StealArrayBufferContents(cx, obj); 151 CHECK(contents); 152 CHECK(memcmp(contents, test_data + 8, 12) == 0); 153 CHECK(JS::IsDetachedArrayBufferObject(obj)); 154 155 return true; 156 } 157 158 bool TestTransferObject() { 159 JS::RootedObject obj1(cx, CreateNewObject(8, 12)); 160 CHECK(obj1); 161 JS::RootedValue v1(cx, JS::ObjectValue(*obj1)); 162 163 // Create an Array of transferable values. 164 JS::RootedValueVector argv(cx); 165 if (!argv.append(v1)) { 166 return false; 167 } 168 169 JS::RootedObject obj( 170 cx, JS::NewArrayObject(cx, JS::HandleValueArray::subarray(argv, 0, 1))); 171 CHECK(obj); 172 JS::RootedValue transferable(cx, JS::ObjectValue(*obj)); 173 174 JSAutoStructuredCloneBuffer cloned_buffer( 175 JS::StructuredCloneScope::SameProcess, nullptr, nullptr); 176 JS::CloneDataPolicy policy; 177 CHECK(cloned_buffer.write(cx, v1, transferable, policy, nullptr, nullptr)); 178 JS::RootedValue v2(cx); 179 CHECK(cloned_buffer.read(cx, &v2, policy, nullptr, nullptr)); 180 JS::RootedObject obj2(cx, v2.toObjectOrNull()); 181 CHECK(VerifyObject(obj2, 8, 12, true)); 182 CHECK(JS::IsDetachedArrayBufferObject(obj1)); 183 184 return true; 185 } 186 187 static void GC(JSContext* cx) { 188 JS_GC(cx); 189 // Trigger another to wait for background finalization to end. 190 JS_GC(cx); 191 } 192 193 END_TEST(testMappedArrayBuffer_bug945152) 194 195 #undef GET_OS_FD