/* -*- 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 #include #include "jsfriendapi.h" #include "js/StructuredClone.h" #include "jsapi-tests/tests.h" #include "vm/ArrayBufferObject.h" #ifdef XP_WIN # include # define GET_OS_FD(a) int(_get_osfhandle(a)) #else # include # define GET_OS_FD(a) (a) #endif const char test_data[] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; const char test_filename[] = "temp-bug945152_MappedArrayBuffer"; BEGIN_TEST(testMappedArrayBuffer_bug945152) { TempFile test_file; FILE* test_stream = test_file.open(test_filename); CHECK(fputs(test_data, test_stream) != EOF); test_file.close(); // Offset 0. CHECK(TestCreateObject(0, 12)); // Aligned offset. CHECK(TestCreateObject(8, 12)); // Unaligned offset. CHECK(CreateNewObject(11, 12) == nullptr); // Offset + length greater than file size. CHECK(CreateNewObject(8, sizeof(test_data) - 7) == nullptr); // Release the mapped content. CHECK(TestReleaseContents()); // Detach mapped array buffer. CHECK(TestDetachObject()); // Clone mapped array buffer. CHECK(TestCloneObject()); // Steal mapped array buffer contents. CHECK(TestStealContents()); // Transfer mapped array buffer contents. CHECK(TestTransferObject()); // GC so we can remove the file we created. GC(cx); test_file.remove(); return true; } JSObject* CreateNewObject(const int offset, const int length) { int fd = open(test_filename, O_RDONLY); void* ptr = JS_CreateMappedArrayBufferContents(GET_OS_FD(fd), offset, length); close(fd); if (!ptr) return nullptr; JSObject* obj = JS_NewMappedArrayBufferWithContents(cx, length, ptr); if (!obj) { JS_ReleaseMappedArrayBufferContents(ptr, length); return nullptr; } return obj; } bool VerifyObject(JS::HandleObject obj, uint32_t offset, uint32_t length, const bool mapped) { JS::AutoCheckCannotGC nogc; CHECK(obj); CHECK(JS_IsArrayBufferObject(obj)); CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), length); if (mapped) CHECK(JS_IsMappedArrayBufferObject(obj)); else CHECK(!JS_IsMappedArrayBufferObject(obj)); bool sharedDummy; const char* data = reinterpret_cast(JS_GetArrayBufferData(obj, &sharedDummy, nogc)); CHECK(data); CHECK(memcmp(data, test_data + offset, length) == 0); return true; } bool TestCreateObject(uint32_t offset, uint32_t length) { JS::RootedObject obj(cx, CreateNewObject(offset, length)); CHECK(VerifyObject(obj, offset, length, true)); return true; } bool TestReleaseContents() { int fd = open(test_filename, O_RDONLY); void* ptr = JS_CreateMappedArrayBufferContents(GET_OS_FD(fd), 0, 12); close(fd); if (!ptr) return false; JS_ReleaseMappedArrayBufferContents(ptr, 12); return true; } bool TestDetachObject() { JS::RootedObject obj(cx, CreateNewObject(8, 12)); CHECK(obj); JS_DetachArrayBuffer(cx, obj); CHECK(JS_IsDetachedArrayBufferObject(obj)); return true; } bool TestCloneObject() { JS::RootedObject obj1(cx, CreateNewObject(8, 12)); CHECK(obj1); JSAutoStructuredCloneBuffer cloned_buffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr); JS::RootedValue v1(cx, JS::ObjectValue(*obj1)); CHECK(cloned_buffer.write(cx, v1, nullptr, nullptr)); JS::RootedValue v2(cx); CHECK(cloned_buffer.read(cx, &v2, nullptr, nullptr)); JS::RootedObject obj2(cx, v2.toObjectOrNull()); CHECK(VerifyObject(obj2, 8, 12, false)); return true; } bool TestStealContents() { JS::RootedObject obj(cx, CreateNewObject(8, 12)); CHECK(obj); void* contents = JS_StealArrayBufferContents(cx, obj); CHECK(contents); CHECK(memcmp(contents, test_data + 8, 12) == 0); CHECK(JS_IsDetachedArrayBufferObject(obj)); return true; } bool TestTransferObject() { JS::RootedObject obj1(cx, CreateNewObject(8, 12)); CHECK(obj1); JS::RootedValue v1(cx, JS::ObjectValue(*obj1)); // Create an Array of transferable values. JS::AutoValueVector argv(cx); if (!argv.append(v1)) return false; JS::RootedObject obj(cx, JS_NewArrayObject(cx, JS::HandleValueArray::subarray(argv, 0, 1))); CHECK(obj); JS::RootedValue transferable(cx, JS::ObjectValue(*obj)); JSAutoStructuredCloneBuffer cloned_buffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr, nullptr); CHECK(cloned_buffer.write(cx, v1, transferable, JS::CloneDataPolicy().denySharedArrayBuffer(), nullptr, nullptr)); JS::RootedValue v2(cx); CHECK(cloned_buffer.read(cx, &v2, nullptr, nullptr)); JS::RootedObject obj2(cx, v2.toObjectOrNull()); CHECK(VerifyObject(obj2, 8, 12, true)); CHECK(JS_IsDetachedArrayBufferObject(obj1)); return true; } static void GC(JSContext* cx) { JS_GC(cx); // Trigger another to wait for background finalization to end. JS_GC(cx); } END_TEST(testMappedArrayBuffer_bug945152) #undef GET_OS_FD