summaryrefslogtreecommitdiffstats
path: root/js/src/jsapi-tests/testMappedArrayBuffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jsapi-tests/testMappedArrayBuffer.cpp')
-rw-r--r--js/src/jsapi-tests/testMappedArrayBuffer.cpp195
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