michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: */ michael@0: michael@0: #ifdef XP_UNIX michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "jsfriendapi.h" michael@0: #include "js/StructuredClone.h" michael@0: #include "jsapi-tests/tests.h" michael@0: #include "vm/ArrayBufferObject.h" michael@0: michael@0: const char test_data[] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; michael@0: const char test_filename[] = "temp-bug945152_MappedArrayBuffer"; michael@0: michael@0: BEGIN_TEST(testMappedArrayBuffer_bug945152) michael@0: { michael@0: TempFile test_file; michael@0: FILE *test_stream = test_file.open(test_filename); michael@0: CHECK(fputs(test_data, test_stream) != EOF); michael@0: test_file.close(); michael@0: michael@0: // Offset 0. michael@0: CHECK(TestCreateObject(0, 12)); michael@0: michael@0: // Aligned offset. michael@0: CHECK(TestCreateObject(8, 12)); michael@0: michael@0: // Unaligned offset. michael@0: CHECK(CreateNewObject(11, 12) == nullptr); michael@0: michael@0: // Offset + length greater than file size. michael@0: CHECK(CreateNewObject(8, sizeof(test_data) - 7) == nullptr); michael@0: michael@0: // Release the mapped content. michael@0: CHECK(TestReleaseContents()); michael@0: michael@0: // Neuter mapped array buffer. michael@0: CHECK(TestNeuterObject()); michael@0: michael@0: // Clone mapped array buffer. michael@0: CHECK(TestCloneObject()); michael@0: michael@0: // Steal mapped array buffer contents. michael@0: CHECK(TestStealContents()); michael@0: michael@0: // Transfer mapped array buffer contents. michael@0: CHECK(TestTransferObject()); michael@0: michael@0: test_file.remove(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: JSObject *CreateNewObject(const int offset, const int length) michael@0: { michael@0: int fd = open(test_filename, O_RDONLY); michael@0: void *ptr = JS_CreateMappedArrayBufferContents(fd, offset, length); michael@0: close(fd); michael@0: if (!ptr) michael@0: return nullptr; michael@0: JSObject *obj = JS_NewMappedArrayBufferWithContents(cx, length, ptr); michael@0: if (!obj) { michael@0: JS_ReleaseMappedArrayBufferContents(ptr, length); michael@0: return nullptr; michael@0: } michael@0: return obj; michael@0: } michael@0: michael@0: bool VerifyObject(JS::HandleObject obj, const int offset, const int length, const bool mapped) michael@0: { michael@0: CHECK(obj); michael@0: CHECK(JS_IsArrayBufferObject(obj)); michael@0: CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), length); michael@0: if (mapped) michael@0: CHECK(JS_IsMappedArrayBufferObject(obj)); michael@0: else michael@0: CHECK(!JS_IsMappedArrayBufferObject(obj)); michael@0: const char *data = reinterpret_cast(JS_GetArrayBufferData(obj)); michael@0: CHECK(data); michael@0: CHECK(memcmp(data, test_data + offset, length) == 0); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool TestCreateObject(const int offset, const int length) michael@0: { michael@0: JS::RootedObject obj(cx, CreateNewObject(offset, length)); michael@0: CHECK(VerifyObject(obj, offset, length, true)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool TestReleaseContents() michael@0: { michael@0: int fd = open(test_filename, O_RDONLY); michael@0: void *ptr = JS_CreateMappedArrayBufferContents(fd, 0, 12); michael@0: close(fd); michael@0: if (!ptr) michael@0: return false; michael@0: JS_ReleaseMappedArrayBufferContents(ptr, 12); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool TestNeuterObject() michael@0: { michael@0: JS::RootedObject obj(cx, CreateNewObject(8, 12)); michael@0: CHECK(obj); michael@0: JS_NeuterArrayBuffer(cx, obj, ChangeData); michael@0: CHECK(isNeutered(obj)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool TestCloneObject() michael@0: { michael@0: JS::RootedObject obj1(cx, CreateNewObject(8, 12)); michael@0: CHECK(obj1); michael@0: JSAutoStructuredCloneBuffer cloned_buffer; michael@0: JS::RootedValue v1(cx, OBJECT_TO_JSVAL(obj1)); michael@0: const JSStructuredCloneCallbacks *callbacks = js::GetContextStructuredCloneCallbacks(cx); michael@0: CHECK(cloned_buffer.write(cx, v1, callbacks, nullptr)); michael@0: JS::RootedValue v2(cx); michael@0: CHECK(cloned_buffer.read(cx, &v2, callbacks, nullptr)); michael@0: JS::RootedObject obj2(cx, JSVAL_TO_OBJECT(v2)); michael@0: CHECK(VerifyObject(obj2, 8, 12, false)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool TestStealContents() michael@0: { michael@0: JS::RootedObject obj(cx, CreateNewObject(8, 12)); michael@0: CHECK(obj); michael@0: void *contents = JS_StealArrayBufferContents(cx, obj); michael@0: CHECK(contents); michael@0: CHECK(memcmp(contents, test_data + 8, 12) == 0); michael@0: CHECK(isNeutered(obj)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool TestTransferObject() michael@0: { michael@0: JS::RootedObject obj1(cx, CreateNewObject(8, 12)); michael@0: CHECK(obj1); michael@0: JS::RootedValue v1(cx, OBJECT_TO_JSVAL(obj1)); michael@0: michael@0: // Create an Array of transferable values. michael@0: JS::AutoValueVector argv(cx); michael@0: argv.append(v1); michael@0: JS::RootedObject obj(cx, JS_NewArrayObject(cx, JS::HandleValueArray::subarray(argv, 0, 1))); michael@0: CHECK(obj); michael@0: JS::RootedValue transferable(cx, OBJECT_TO_JSVAL(obj)); michael@0: michael@0: JSAutoStructuredCloneBuffer cloned_buffer; michael@0: const JSStructuredCloneCallbacks *callbacks = js::GetContextStructuredCloneCallbacks(cx); michael@0: CHECK(cloned_buffer.write(cx, v1, transferable, callbacks, nullptr)); michael@0: JS::RootedValue v2(cx); michael@0: CHECK(cloned_buffer.read(cx, &v2, callbacks, nullptr)); michael@0: JS::RootedObject obj2(cx, JSVAL_TO_OBJECT(v2)); michael@0: CHECK(VerifyObject(obj2, 8, 12, true)); michael@0: CHECK(isNeutered(obj1)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool isNeutered(JS::HandleObject obj) michael@0: { michael@0: JS::RootedValue v(cx); michael@0: return JS_GetProperty(cx, obj, "byteLength", &v) && v.toInt32() == 0; michael@0: } michael@0: michael@0: static void GC(JSContext *cx) michael@0: { michael@0: JS_GC(JS_GetRuntime(cx)); michael@0: // Trigger another to wait for background finalization to end. michael@0: JS_GC(JS_GetRuntime(cx)); michael@0: } michael@0: michael@0: END_TEST(testMappedArrayBuffer_bug945152) michael@0: #endif