summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime/ArrayBuffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/runtime/ArrayBuffer.cpp')
-rw-r--r--Source/JavaScriptCore/runtime/ArrayBuffer.cpp289
1 files changed, 278 insertions, 11 deletions
diff --git a/Source/JavaScriptCore/runtime/ArrayBuffer.cpp b/Source/JavaScriptCore/runtime/ArrayBuffer.cpp
index 2c49b6976..bb9cfc42b 100644
--- a/Source/JavaScriptCore/runtime/ArrayBuffer.cpp
+++ b/Source/JavaScriptCore/runtime/ArrayBuffer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2013, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -28,12 +28,274 @@
#include "ArrayBufferNeuteringWatchpoint.h"
#include "JSArrayBufferView.h"
-#include "Operations.h"
-#include <wtf/RefPtr.h>
+#include "JSCInlines.h"
namespace JSC {
-bool ArrayBuffer::transfer(ArrayBufferContents& result)
+SharedArrayBufferContents::SharedArrayBufferContents(void* data, ArrayBufferDestructorFunction&& destructor)
+ : m_data(data)
+ , m_destructor(WTFMove(destructor))
+{
+}
+
+SharedArrayBufferContents::~SharedArrayBufferContents()
+{
+ m_destructor(m_data);
+}
+
+ArrayBufferContents::ArrayBufferContents()
+{
+ reset();
+}
+
+ArrayBufferContents::ArrayBufferContents(ArrayBufferContents&& other)
+{
+ reset();
+ other.transferTo(*this);
+}
+
+ArrayBufferContents::ArrayBufferContents(void* data, unsigned sizeInBytes, ArrayBufferDestructorFunction&& destructor)
+ : m_data(data)
+ , m_sizeInBytes(sizeInBytes)
+{
+ m_destructor = WTFMove(destructor);
+}
+
+ArrayBufferContents& ArrayBufferContents::operator=(ArrayBufferContents&& other)
+{
+ other.transferTo(*this);
+ return *this;
+}
+
+ArrayBufferContents::~ArrayBufferContents()
+{
+ destroy();
+}
+
+void ArrayBufferContents::clear()
+{
+ destroy();
+ reset();
+}
+
+void ArrayBufferContents::destroy()
+{
+ m_destructor(m_data);
+}
+
+void ArrayBufferContents::reset()
+{
+ m_destructor = [] (void*) { };
+ m_shared = nullptr;
+ m_data = nullptr;
+ m_sizeInBytes = 0;
+}
+
+void ArrayBufferContents::tryAllocate(unsigned numElements, unsigned elementByteSize, InitializationPolicy policy)
+{
+ // Do not allow 31-bit overflow of the total size.
+ if (numElements) {
+ unsigned totalSize = numElements * elementByteSize;
+ if (totalSize / numElements != elementByteSize
+ || totalSize > static_cast<unsigned>(std::numeric_limits<int32_t>::max())) {
+ reset();
+ return;
+ }
+ }
+ bool allocationSucceeded = false;
+ if (policy == ZeroInitialize)
+ allocationSucceeded = WTF::tryFastCalloc(numElements, elementByteSize).getValue(m_data);
+ else {
+ ASSERT(policy == DontInitialize);
+ allocationSucceeded = WTF::tryFastMalloc(numElements * elementByteSize).getValue(m_data);
+ }
+
+ if (allocationSucceeded) {
+ m_sizeInBytes = numElements * elementByteSize;
+ m_destructor = [] (void* p) { fastFree(p); };
+ return;
+ }
+ reset();
+}
+
+void ArrayBufferContents::makeShared()
+{
+ m_shared = adoptRef(new SharedArrayBufferContents(m_data, WTFMove(m_destructor)));
+ m_destructor = [] (void*) { };
+}
+
+void ArrayBufferContents::transferTo(ArrayBufferContents& other)
+{
+ other.clear();
+ other.m_data = m_data;
+ other.m_sizeInBytes = m_sizeInBytes;
+ other.m_destructor = WTFMove(m_destructor);
+ other.m_shared = m_shared;
+ clear();
+}
+
+void ArrayBufferContents::copyTo(ArrayBufferContents& other)
+{
+ ASSERT(!other.m_data);
+ other.tryAllocate(m_sizeInBytes, sizeof(char), ArrayBufferContents::DontInitialize);
+ if (!other.m_data)
+ return;
+ memcpy(other.m_data, m_data, m_sizeInBytes);
+ other.m_sizeInBytes = m_sizeInBytes;
+}
+
+void ArrayBufferContents::shareWith(ArrayBufferContents& other)
+{
+ ASSERT(!other.m_data);
+ ASSERT(m_shared);
+ other.m_destructor = [] (void*) { };
+ other.m_shared = m_shared;
+ other.m_data = m_data;
+ other.m_sizeInBytes = m_sizeInBytes;
+}
+
+Ref<ArrayBuffer> ArrayBuffer::create(unsigned numElements, unsigned elementByteSize)
+{
+ auto buffer = tryCreate(numElements, elementByteSize);
+ if (!buffer)
+ CRASH();
+ return buffer.releaseNonNull();
+}
+
+Ref<ArrayBuffer> ArrayBuffer::create(ArrayBuffer& other)
+{
+ return ArrayBuffer::create(other.data(), other.byteLength());
+}
+
+Ref<ArrayBuffer> ArrayBuffer::create(const void* source, unsigned byteLength)
+{
+ auto buffer = tryCreate(source, byteLength);
+ if (!buffer)
+ CRASH();
+ return buffer.releaseNonNull();
+}
+
+Ref<ArrayBuffer> ArrayBuffer::create(ArrayBufferContents&& contents)
+{
+ return adoptRef(*new ArrayBuffer(WTFMove(contents)));
+}
+
+Ref<ArrayBuffer> ArrayBuffer::createAdopted(const void* data, unsigned byteLength)
+{
+ return createFromBytes(data, byteLength, [] (void* p) { fastFree(p); });
+}
+
+Ref<ArrayBuffer> ArrayBuffer::createFromBytes(const void* data, unsigned byteLength, ArrayBufferDestructorFunction&& destructor)
+{
+ ArrayBufferContents contents(const_cast<void*>(data), byteLength, WTFMove(destructor));
+ return create(WTFMove(contents));
+}
+
+RefPtr<ArrayBuffer> ArrayBuffer::tryCreate(unsigned numElements, unsigned elementByteSize)
+{
+ return tryCreate(numElements, elementByteSize, ArrayBufferContents::ZeroInitialize);
+}
+
+RefPtr<ArrayBuffer> ArrayBuffer::tryCreate(ArrayBuffer& other)
+{
+ return tryCreate(other.data(), other.byteLength());
+}
+
+RefPtr<ArrayBuffer> ArrayBuffer::tryCreate(const void* source, unsigned byteLength)
+{
+ ArrayBufferContents contents;
+ contents.tryAllocate(byteLength, 1, ArrayBufferContents::ZeroInitialize);
+ if (!contents.m_data)
+ return nullptr;
+ return createInternal(WTFMove(contents), source, byteLength);
+}
+
+Ref<ArrayBuffer> ArrayBuffer::createUninitialized(unsigned numElements, unsigned elementByteSize)
+{
+ return create(numElements, elementByteSize, ArrayBufferContents::DontInitialize);
+}
+
+RefPtr<ArrayBuffer> ArrayBuffer::tryCreateUninitialized(unsigned numElements, unsigned elementByteSize)
+{
+ return tryCreate(numElements, elementByteSize, ArrayBufferContents::DontInitialize);
+}
+
+Ref<ArrayBuffer> ArrayBuffer::create(unsigned numElements, unsigned elementByteSize, ArrayBufferContents::InitializationPolicy policy)
+{
+ auto buffer = tryCreate(numElements, elementByteSize, policy);
+ if (!buffer)
+ CRASH();
+ return buffer.releaseNonNull();
+}
+
+Ref<ArrayBuffer> ArrayBuffer::createInternal(ArrayBufferContents&& contents, const void* source, unsigned byteLength)
+{
+ ASSERT(!byteLength || source);
+ auto buffer = adoptRef(*new ArrayBuffer(WTFMove(contents)));
+ memcpy(buffer->data(), source, byteLength);
+ return buffer;
+}
+
+RefPtr<ArrayBuffer> ArrayBuffer::tryCreate(unsigned numElements, unsigned elementByteSize, ArrayBufferContents::InitializationPolicy policy)
+{
+ ArrayBufferContents contents;
+ contents.tryAllocate(numElements, elementByteSize, policy);
+ if (!contents.m_data)
+ return nullptr;
+ return adoptRef(*new ArrayBuffer(WTFMove(contents)));
+}
+
+ArrayBuffer::ArrayBuffer(ArrayBufferContents&& contents)
+ : m_contents(WTFMove(contents))
+ , m_pinCount(0)
+ , m_locked(false)
+{
+}
+
+RefPtr<ArrayBuffer> ArrayBuffer::slice(int begin, int end) const
+{
+ return sliceImpl(clampIndex(begin), clampIndex(end));
+}
+
+RefPtr<ArrayBuffer> ArrayBuffer::slice(int begin) const
+{
+ return sliceImpl(clampIndex(begin), byteLength());
+}
+
+RefPtr<ArrayBuffer> ArrayBuffer::sliceImpl(unsigned begin, unsigned end) const
+{
+ unsigned size = begin <= end ? end - begin : 0;
+ RefPtr<ArrayBuffer> result = ArrayBuffer::create(static_cast<const char*>(data()) + begin, size);
+ result->setSharingMode(sharingMode());
+ return result;
+}
+
+void ArrayBuffer::makeShared()
+{
+ m_contents.makeShared();
+}
+
+void ArrayBuffer::setSharingMode(ArrayBufferSharingMode newSharingMode)
+{
+ if (newSharingMode == sharingMode())
+ return;
+ RELEASE_ASSERT(!isShared()); // Cannot revert sharing.
+ RELEASE_ASSERT(newSharingMode == ArrayBufferSharingMode::Shared);
+ makeShared();
+}
+
+bool ArrayBuffer::shareWith(ArrayBufferContents& result)
+{
+ if (!m_contents.m_data || !isShared()) {
+ result.m_data = nullptr;
+ return false;
+ }
+
+ m_contents.shareWith(result);
+ return true;
+}
+
+bool ArrayBuffer::transferTo(VM& vm, ArrayBufferContents& result)
{
Ref<ArrayBuffer> protect(*this);
@@ -41,23 +303,28 @@ bool ArrayBuffer::transfer(ArrayBufferContents& result)
result.m_data = 0;
return false;
}
+
+ if (isShared()) {
+ m_contents.shareWith(result);
+ return true;
+ }
- bool isNeuterable = !m_pinCount;
+ bool isNeuterable = !m_pinCount && !m_locked;
- if (isNeuterable)
- m_contents.transfer(result);
- else {
+ if (!isNeuterable) {
m_contents.copyTo(result);
if (!result.m_data)
return false;
+ return true;
}
+ m_contents.transferTo(result);
for (size_t i = numberOfIncomingReferences(); i--;) {
JSCell* cell = incomingReferenceAt(i);
- if (JSArrayBufferView* view = jsDynamicCast<JSArrayBufferView*>(cell))
+ if (JSArrayBufferView* view = jsDynamicCast<JSArrayBufferView*>(vm, cell))
view->neuter();
- else if (ArrayBufferNeuteringWatchpoint* watchpoint = jsDynamicCast<ArrayBufferNeuteringWatchpoint*>(cell))
- watchpoint->set()->fireAll();
+ else if (ArrayBufferNeuteringWatchpoint* watchpoint = jsDynamicCast<ArrayBufferNeuteringWatchpoint*>(vm, cell))
+ watchpoint->fireAll();
}
return true;
}