JSGenericTypedArrayViewInlines.h [plain text]
#ifndef JSGenericTypedArrayViewInlines_h
#define JSGenericTypedArrayViewInlines_h
#include "ArrayBufferView.h"
#include "DeferGC.h"
#include "Error.h"
#include "ExceptionHelpers.h"
#include "JSArrayBuffer.h"
#include "JSGenericTypedArrayView.h"
#include "Reject.h"
#include "TypedArrays.h"
namespace JSC {
template<typename Adaptor>
JSGenericTypedArrayView<Adaptor>::JSGenericTypedArrayView(
VM& vm, ConstructionContext& context)
: Base(vm, context)
{
}
template<typename Adaptor>
JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create(
ExecState* exec, Structure* structure, unsigned length)
{
ConstructionContext context(exec->vm(), structure, length, sizeof(typename Adaptor::Type));
if (!context) {
throwOutOfMemoryError(exec);
return nullptr;
}
JSGenericTypedArrayView* result =
new (NotNull, allocateCell<JSGenericTypedArrayView>(exec->vm().heap))
JSGenericTypedArrayView(exec->vm(), context);
result->finishCreation(exec->vm());
return result;
}
template<typename Adaptor>
JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::createUninitialized(
ExecState* exec, Structure* structure, unsigned length)
{
ConstructionContext context(
exec->vm(), structure, length, sizeof(typename Adaptor::Type),
ConstructionContext::DontInitialize);
if (!context) {
throwOutOfMemoryError(exec);
return nullptr;
}
JSGenericTypedArrayView* result =
new (NotNull, allocateCell<JSGenericTypedArrayView>(exec->vm().heap))
JSGenericTypedArrayView(exec->vm(), context);
result->finishCreation(exec->vm());
return result;
}
template<typename Adaptor>
JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create(
ExecState* exec, Structure* structure, PassRefPtr<ArrayBuffer> passedBuffer,
unsigned byteOffset, unsigned length)
{
RefPtr<ArrayBuffer> buffer = passedBuffer;
size_t size = sizeof(typename Adaptor::Type);
if (!ArrayBufferView::verifySubRangeLength(buffer, byteOffset, length, size)) {
exec->vm().throwException(exec, createRangeError(exec, "Length out of range of buffer"));
return nullptr;
}
if (!ArrayBufferView::verifyByteOffsetAlignment(byteOffset, size)) {
exec->vm().throwException(exec, createRangeError(exec, "Byte offset is not aligned"));
return nullptr;
}
ConstructionContext context(exec->vm(), structure, buffer, byteOffset, length);
ASSERT(context);
JSGenericTypedArrayView* result =
new (NotNull, allocateCell<JSGenericTypedArrayView>(exec->vm().heap))
JSGenericTypedArrayView(exec->vm(), context);
result->finishCreation(exec->vm());
return result;
}
template<typename Adaptor>
JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create(
VM& vm, Structure* structure, PassRefPtr<typename Adaptor::ViewType> impl)
{
RefPtr<ArrayBuffer> buffer = impl->buffer();
ConstructionContext context(vm, structure, buffer, impl->byteOffset(), impl->length());
ASSERT(context);
JSGenericTypedArrayView* result =
new (NotNull, allocateCell<JSGenericTypedArrayView>(vm.heap))
JSGenericTypedArrayView(vm, context);
result->finishCreation(vm);
return result;
}
template<typename Adaptor>
JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create(
Structure* structure, JSGlobalObject* globalObject,
PassRefPtr<typename Adaptor::ViewType> impl)
{
return create(globalObject->vm(), structure, impl);
}
template<typename Adaptor>
bool JSGenericTypedArrayView<Adaptor>::validateRange(
ExecState* exec, unsigned offset, unsigned length)
{
if (canAccessRangeQuickly(offset, length))
return true;
exec->vm().throwException(exec, createRangeError(exec, "Range consisting of offset and length are out of bounds"));
return false;
}
template<typename Adaptor>
template<typename OtherAdaptor>
bool JSGenericTypedArrayView<Adaptor>::setWithSpecificType(
ExecState* exec, unsigned offset, JSGenericTypedArrayView<OtherAdaptor>* other,
unsigned otherOffset, unsigned length, CopyType type)
{
length = std::min(length, other->length());
RELEASE_ASSERT(other->canAccessRangeQuickly(otherOffset, length));
if (!validateRange(exec, offset, length))
return false;
ASSERT(static_cast<JSCell*>(this) != static_cast<JSCell*>(other));
unsigned otherElementSize = sizeof(typename OtherAdaptor::Type);
if (!hasArrayBuffer() || !other->hasArrayBuffer()
|| existingBuffer() != other->existingBuffer()
|| (elementSize == otherElementSize && vector() <= other->vector())
|| type == CopyType::LeftToRight) {
for (unsigned i = 0; i < length; ++i) {
setIndexQuicklyToNativeValue(
offset + i, OtherAdaptor::template convertTo<Adaptor>(
other->getIndexQuicklyAsNativeValue(i + otherOffset)));
}
return true;
}
if (elementSize == otherElementSize) {
for (unsigned i = length; i--;) {
setIndexQuicklyToNativeValue(
offset + i, OtherAdaptor::template convertTo<Adaptor>(
other->getIndexQuicklyAsNativeValue(i + otherOffset)));
}
return true;
}
Vector<typename Adaptor::Type, 32> transferBuffer(length);
for (unsigned i = length; i--;) {
transferBuffer[i] = OtherAdaptor::template convertTo<Adaptor>(
other->getIndexQuicklyAsNativeValue(i + otherOffset));
}
for (unsigned i = length; i--;)
setIndexQuicklyToNativeValue(offset + i, transferBuffer[i]);
return true;
}
template<typename Adaptor>
bool JSGenericTypedArrayView<Adaptor>::set(
ExecState* exec, unsigned offset, JSObject* object, unsigned objectOffset, unsigned length, CopyType type)
{
const ClassInfo* ci = object->classInfo();
if (ci->typedArrayStorageType == Adaptor::typeValue) {
JSGenericTypedArrayView* other = jsCast<JSGenericTypedArrayView*>(object);
length = std::min(length, other->length());
RELEASE_ASSERT(other->canAccessRangeQuickly(objectOffset, length));
if (!validateRange(exec, offset, length))
return false;
memmove(typedVector() + offset, other->typedVector() + objectOffset, length * elementSize);
return true;
}
switch (ci->typedArrayStorageType) {
case TypeInt8:
return setWithSpecificType<Int8Adaptor>(
exec, offset, jsCast<JSInt8Array*>(object), objectOffset, length, type);
case TypeInt16:
return setWithSpecificType<Int16Adaptor>(
exec, offset, jsCast<JSInt16Array*>(object), objectOffset, length, type);
case TypeInt32:
return setWithSpecificType<Int32Adaptor>(
exec, offset, jsCast<JSInt32Array*>(object), objectOffset, length, type);
case TypeUint8:
return setWithSpecificType<Uint8Adaptor>(
exec, offset, jsCast<JSUint8Array*>(object), objectOffset, length, type);
case TypeUint8Clamped:
return setWithSpecificType<Uint8ClampedAdaptor>(
exec, offset, jsCast<JSUint8ClampedArray*>(object), objectOffset, length, type);
case TypeUint16:
return setWithSpecificType<Uint16Adaptor>(
exec, offset, jsCast<JSUint16Array*>(object), objectOffset, length, type);
case TypeUint32:
return setWithSpecificType<Uint32Adaptor>(
exec, offset, jsCast<JSUint32Array*>(object), objectOffset, length, type);
case TypeFloat32:
return setWithSpecificType<Float32Adaptor>(
exec, offset, jsCast<JSFloat32Array*>(object), objectOffset, length, type);
case TypeFloat64:
return setWithSpecificType<Float64Adaptor>(
exec, offset, jsCast<JSFloat64Array*>(object), objectOffset, length, type);
case NotTypedArray:
case TypeDataView: {
if (!validateRange(exec, offset, length))
return false;
for (unsigned i = 0; i < length; ++i) {
JSValue value = object->get(exec, i + objectOffset);
if (!setIndex(exec, offset + i, value))
return false;
}
return true;
} }
RELEASE_ASSERT_NOT_REACHED();
return false;
}
template<typename Adaptor>
ArrayBuffer* JSGenericTypedArrayView<Adaptor>::existingBuffer()
{
return existingBufferInButterfly();
}
template<typename Adaptor>
EncodedJSValue JSGenericTypedArrayView<Adaptor>::throwNeuteredTypedArrayTypeError(ExecState* exec, EncodedJSValue object, PropertyName)
{
ASSERT_UNUSED(object, jsCast<JSGenericTypedArrayView*>(JSValue::decode(object))->isNeutered());
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
}
template<typename Adaptor>
bool JSGenericTypedArrayView<Adaptor>::getOwnPropertySlot(
JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
{
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
if (Optional<uint32_t> index = parseIndex(propertyName)) {
if (thisObject->isNeutered()) {
slot.setCustom(thisObject, None, throwNeuteredTypedArrayTypeError);
return true;
}
if (thisObject->canGetIndexQuickly(index.value()))
slot.setValue(thisObject, DontDelete | ReadOnly, thisObject->getIndexQuickly(index.value()));
else
slot.setValue(thisObject, DontDelete | ReadOnly, jsUndefined());
return true;
}
return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
}
template<typename Adaptor>
bool JSGenericTypedArrayView<Adaptor>::put(
JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value,
PutPropertySlot& slot)
{
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
if (Optional<uint32_t> index = parseIndex(propertyName))
return putByIndex(thisObject, exec, index.value(), value, slot.isStrictMode());
return Base::put(thisObject, exec, propertyName, value, slot);
}
template<typename Adaptor>
bool JSGenericTypedArrayView<Adaptor>::defineOwnProperty(
JSObject* object, ExecState* exec, PropertyName propertyName,
const PropertyDescriptor& descriptor, bool shouldThrow)
{
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
if (parseIndex(propertyName)) {
if (descriptor.isAccessorDescriptor())
return reject(exec, shouldThrow, "Attempting to store accessor indexed property on a typed array.");
if (descriptor.configurable())
return reject(exec, shouldThrow, "Attempting to configure non-configurable property.");
if (!descriptor.enumerable() || !descriptor.writable())
return reject(exec, shouldThrow, "Attempting to store non-enumerable or non-writable indexed property on a typed array.");
if (descriptor.value()) {
PutPropertySlot unused(JSValue(thisObject), shouldThrow);
return thisObject->put(thisObject, exec, propertyName, descriptor.value(), unused);
}
return true;
}
return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
}
template<typename Adaptor>
bool JSGenericTypedArrayView<Adaptor>::deleteProperty(
JSCell* cell, ExecState* exec, PropertyName propertyName)
{
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
if (thisObject->isNeutered())
return reject(exec, true, typedArrayBufferHasBeenDetachedErrorMessage);
if (parseIndex(propertyName))
return false;
return Base::deleteProperty(thisObject, exec, propertyName);
}
template<typename Adaptor>
bool JSGenericTypedArrayView<Adaptor>::getOwnPropertySlotByIndex(
JSObject* object, ExecState* exec, unsigned propertyName, PropertySlot& slot)
{
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
if (thisObject->isNeutered()) {
slot.setCustom(thisObject, None, throwNeuteredTypedArrayTypeError);
return true;
}
if (propertyName > MAX_ARRAY_INDEX) {
return thisObject->methodTable()->getOwnPropertySlot(
thisObject, exec, Identifier::from(exec, propertyName), slot);
}
if (!thisObject->canGetIndexQuickly(propertyName))
return false;
slot.setValue(thisObject, None, thisObject->getIndexQuickly(propertyName));
return true;
}
template<typename Adaptor>
bool JSGenericTypedArrayView<Adaptor>::putByIndex(
JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
{
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
if (propertyName > MAX_ARRAY_INDEX) {
PutPropertySlot slot(JSValue(thisObject), shouldThrow);
return thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
}
return thisObject->setIndex(exec, propertyName, value);
}
template<typename Adaptor>
bool JSGenericTypedArrayView<Adaptor>::deletePropertyByIndex(
JSCell* cell, ExecState* exec, unsigned propertyName)
{
return cell->methodTable()->deleteProperty(cell, exec, Identifier::from(exec, propertyName));
}
template<typename Adaptor>
void JSGenericTypedArrayView<Adaptor>::getOwnPropertyNames(
JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode)
{
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
if (array.includeStringProperties()) {
for (unsigned i = 0; i < thisObject->m_length; ++i)
array.add(Identifier::from(exec, i));
}
return Base::getOwnPropertyNames(object, exec, array, mode);
}
template<typename Adaptor>
size_t JSGenericTypedArrayView<Adaptor>::estimatedSize(JSCell* cell)
{
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
if (thisObject->m_mode == OversizeTypedArray)
return Base::estimatedSize(thisObject) + thisObject->byteSize();
if (thisObject->m_mode == FastTypedArray && thisObject->m_vector)
return Base::estimatedSize(thisObject) + thisObject->byteSize();
return Base::estimatedSize(thisObject);
}
template<typename Adaptor>
void JSGenericTypedArrayView<Adaptor>::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
switch (thisObject->m_mode) {
case FastTypedArray: {
if (thisObject->m_vector)
visitor.copyLater(thisObject, TypedArrayVectorCopyToken, thisObject->m_vector.get(), thisObject->byteSize());
break;
}
case OversizeTypedArray: {
visitor.reportExtraMemoryVisited(thisObject->byteSize());
break;
}
case WastefulTypedArray:
break;
case DataViewMode:
RELEASE_ASSERT_NOT_REACHED();
break;
}
Base::visitChildren(thisObject, visitor);
}
template<typename Adaptor>
void JSGenericTypedArrayView<Adaptor>::copyBackingStore(
JSCell* cell, CopyVisitor& visitor, CopyToken token)
{
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
if (token == TypedArrayVectorCopyToken
&& visitor.checkIfShouldCopy(thisObject->m_vector.get())) {
ASSERT(thisObject->m_vector);
void* oldVector = thisObject->vector();
void* newVector = visitor.allocateNewSpace(thisObject->byteSize());
memcpy(newVector, oldVector, thisObject->byteSize());
thisObject->m_vector.setWithoutBarrier(static_cast<char*>(newVector));
visitor.didCopy(oldVector, thisObject->byteSize());
}
Base::copyBackingStore(thisObject, visitor, token);
}
template<typename Adaptor>
ArrayBuffer* JSGenericTypedArrayView<Adaptor>::slowDownAndWasteMemory(JSArrayBufferView* object)
{
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
Heap* heap = Heap::heap(thisObject);
DeferGCForAWhile deferGC(*heap);
ASSERT(!thisObject->hasIndexingHeader());
size_t size = thisObject->byteSize();
if (thisObject->m_mode == FastTypedArray
&& !thisObject->butterfly() && size >= sizeof(IndexingHeader)) {
ASSERT(thisObject->m_vector);
thisObject->m_butterfly.setWithoutBarrier(
bitwise_cast<IndexingHeader*>(thisObject->vector())->butterfly());
} else {
RELEASE_ASSERT(!thisObject->hasIndexingHeader());
VM& vm = *heap->vm();
thisObject->m_butterfly.set(vm, thisObject, Butterfly::createOrGrowArrayRight(
thisObject->butterfly(), vm, thisObject, thisObject->structure(),
thisObject->structure()->outOfLineCapacity(), false, 0, 0));
}
RefPtr<ArrayBuffer> buffer;
switch (thisObject->m_mode) {
case FastTypedArray:
buffer = ArrayBuffer::create(thisObject->vector(), thisObject->byteLength());
break;
case OversizeTypedArray:
buffer = ArrayBuffer::createAdopted(thisObject->vector(), thisObject->byteLength());
break;
default:
RELEASE_ASSERT_NOT_REACHED();
break;
}
thisObject->butterfly()->indexingHeader()->setArrayBuffer(buffer.get());
thisObject->m_vector.setWithoutBarrier(static_cast<char*>(buffer->data()));
thisObject->m_mode = WastefulTypedArray;
heap->addReference(thisObject, buffer.get());
return buffer.get();
}
template<typename Adaptor>
PassRefPtr<ArrayBufferView>
JSGenericTypedArrayView<Adaptor>::getTypedArrayImpl(JSArrayBufferView* object)
{
JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
return thisObject->typedImpl();
}
}
#endif // JSGenericTypedArrayViewInlines_h