diff options
Diffstat (limited to 'js/src/jsapi-tests/testMappedArrayBuffer.cpp')
-rw-r--r-- | js/src/jsapi-tests/testMappedArrayBuffer.cpp | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/js/src/jsapi-tests/testMappedArrayBuffer.cpp b/js/src/jsapi-tests/testMappedArrayBuffer.cpp new file mode 100644 index 0000000000..b07303f1bd --- /dev/null +++ b/js/src/jsapi-tests/testMappedArrayBuffer.cpp @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + */ + +#include <fcntl.h> +#include <stdio.h> + +#include "js/Array.h" // JS::NewArrayObject +#include "js/ArrayBuffer.h" // JS::{{Create,Release}MappedArrayBufferContents,DetachArrayBuffer,GetArrayBuffer{ByteLength,Data},Is{,Detached,Mapped}ArrayBufferObject,NewMappedArrayBufferWithContents,StealArrayBufferContents} +#include "js/StructuredClone.h" +#include "jsapi-tests/tests.h" +#include "vm/ArrayBufferObject.h" + +#ifdef XP_WIN +# include <io.h> +# define GET_OS_FD(a) int(_get_osfhandle(a)) +#else +# include <unistd.h> +# 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<const char*>( + 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::SameProcess, 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, JS::CloneDataPolicy(), 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::RootedValueVector 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::SameProcess, nullptr, nullptr); + JS::CloneDataPolicy policy; + CHECK(cloned_buffer.write(cx, v1, transferable, policy, nullptr, nullptr)); + JS::RootedValue v2(cx); + CHECK(cloned_buffer.read(cx, &v2, policy, 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 |