blob: eb9dc306b44ff3501ff649d29ffe1294122d85bc [file] [log] [blame]
/*
* Copyright (C) 2015 Dominic Szablewski (dominic@phoboslab.org)
* Copyright (C) 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
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "JSTypedArray.h"
#include "APICast.h"
#include "APIUtils.h"
#include "ClassInfo.h"
#include "Error.h"
#include "JSArrayBufferViewInlines.h"
#include "JSCInlines.h"
#include "JSDataView.h"
#include "JSGenericTypedArrayViewInlines.h"
#include "JSTypedArrays.h"
#include "TypedArrayController.h"
#include <wtf/RefPtr.h>
using namespace JSC;
// Helper functions.
inline JSTypedArrayType toJSTypedArrayType(TypedArrayType type)
{
switch (type) {
case JSC::TypeDataView:
case NotTypedArray:
return kJSTypedArrayTypeNone;
case TypeInt8:
return kJSTypedArrayTypeInt8Array;
case TypeUint8:
return kJSTypedArrayTypeUint8Array;
case TypeUint8Clamped:
return kJSTypedArrayTypeUint8ClampedArray;
case TypeInt16:
return kJSTypedArrayTypeInt16Array;
case TypeUint16:
return kJSTypedArrayTypeUint16Array;
case TypeInt32:
return kJSTypedArrayTypeInt32Array;
case TypeUint32:
return kJSTypedArrayTypeUint32Array;
case TypeFloat32:
return kJSTypedArrayTypeFloat32Array;
case TypeFloat64:
return kJSTypedArrayTypeFloat64Array;
}
RELEASE_ASSERT_NOT_REACHED();
}
inline TypedArrayType toTypedArrayType(JSTypedArrayType type)
{
switch (type) {
case kJSTypedArrayTypeArrayBuffer:
case kJSTypedArrayTypeNone:
return NotTypedArray;
case kJSTypedArrayTypeInt8Array:
return TypeInt8;
case kJSTypedArrayTypeUint8Array:
return TypeUint8;
case kJSTypedArrayTypeUint8ClampedArray:
return TypeUint8Clamped;
case kJSTypedArrayTypeInt16Array:
return TypeInt16;
case kJSTypedArrayTypeUint16Array:
return TypeUint16;
case kJSTypedArrayTypeInt32Array:
return TypeInt32;
case kJSTypedArrayTypeUint32Array:
return TypeUint32;
case kJSTypedArrayTypeFloat32Array:
return TypeFloat32;
case kJSTypedArrayTypeFloat64Array:
return TypeFloat64;
}
RELEASE_ASSERT_NOT_REACHED();
}
static JSObject* createTypedArray(JSGlobalObject* globalObject, JSTypedArrayType type, RefPtr<ArrayBuffer>&& buffer, size_t offset, size_t length)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (!buffer) {
throwOutOfMemoryError(globalObject, scope);
return nullptr;
}
switch (type) {
case kJSTypedArrayTypeInt8Array:
return JSInt8Array::create(globalObject, globalObject->typedArrayStructure(TypeInt8), WTFMove(buffer), offset, length);
case kJSTypedArrayTypeInt16Array:
return JSInt16Array::create(globalObject, globalObject->typedArrayStructure(TypeInt16), WTFMove(buffer), offset, length);
case kJSTypedArrayTypeInt32Array:
return JSInt32Array::create(globalObject, globalObject->typedArrayStructure(TypeInt32), WTFMove(buffer), offset, length);
case kJSTypedArrayTypeUint8Array:
return JSUint8Array::create(globalObject, globalObject->typedArrayStructure(TypeUint8), WTFMove(buffer), offset, length);
case kJSTypedArrayTypeUint8ClampedArray:
return JSUint8ClampedArray::create(globalObject, globalObject->typedArrayStructure(TypeUint8Clamped), WTFMove(buffer), offset, length);
case kJSTypedArrayTypeUint16Array:
return JSUint16Array::create(globalObject, globalObject->typedArrayStructure(TypeUint16), WTFMove(buffer), offset, length);
case kJSTypedArrayTypeUint32Array:
return JSUint32Array::create(globalObject, globalObject->typedArrayStructure(TypeUint32), WTFMove(buffer), offset, length);
case kJSTypedArrayTypeFloat32Array:
return JSFloat32Array::create(globalObject, globalObject->typedArrayStructure(TypeFloat32), WTFMove(buffer), offset, length);
case kJSTypedArrayTypeFloat64Array:
return JSFloat64Array::create(globalObject, globalObject->typedArrayStructure(TypeFloat64), WTFMove(buffer), offset, length);
case kJSTypedArrayTypeArrayBuffer:
case kJSTypedArrayTypeNone:
RELEASE_ASSERT_NOT_REACHED();
}
return nullptr;
}
// Implementations of the API functions.
JSTypedArrayType JSValueGetTypedArrayType(JSContextRef ctx, JSValueRef valueRef, JSValueRef*)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSLockHolder locker(vm);
JSValue value = toJS(globalObject, valueRef);
if (!value.isObject())
return kJSTypedArrayTypeNone;
JSObject* object = value.getObject();
if (jsDynamicCast<JSArrayBuffer*>(vm, object))
return kJSTypedArrayTypeArrayBuffer;
return toJSTypedArrayType(object->classInfo(vm)->typedArrayStorageType);
}
JSObjectRef JSObjectMakeTypedArray(JSContextRef ctx, JSTypedArrayType arrayType, size_t length, JSValueRef* exception)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSLockHolder locker(vm);
auto scope = DECLARE_CATCH_SCOPE(vm);
if (arrayType == kJSTypedArrayTypeNone || arrayType == kJSTypedArrayTypeArrayBuffer)
return nullptr;
unsigned elementByteSize = elementSize(toTypedArrayType(arrayType));
auto buffer = ArrayBuffer::tryCreate(length, elementByteSize);
JSObject* result = createTypedArray(globalObject, arrayType, WTFMove(buffer), 0, length);
if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
return nullptr;
return toRef(result);
}
JSObjectRef JSObjectMakeTypedArrayWithBytesNoCopy(JSContextRef ctx, JSTypedArrayType arrayType, void* bytes, size_t length, JSTypedArrayBytesDeallocator destructor, void* destructorContext, JSValueRef* exception)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSLockHolder locker(vm);
auto scope = DECLARE_CATCH_SCOPE(vm);
if (arrayType == kJSTypedArrayTypeNone || arrayType == kJSTypedArrayTypeArrayBuffer)
return nullptr;
unsigned elementByteSize = elementSize(toTypedArrayType(arrayType));
auto buffer = ArrayBuffer::createFromBytes(bytes, length, [=](void* p) {
if (destructor)
destructor(p, destructorContext);
});
JSObject* result = createTypedArray(globalObject, arrayType, WTFMove(buffer), 0, length / elementByteSize);
if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
return nullptr;
return toRef(result);
}
JSObjectRef JSObjectMakeTypedArrayWithArrayBuffer(JSContextRef ctx, JSTypedArrayType arrayType, JSObjectRef jsBufferRef, JSValueRef* exception)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSLockHolder locker(vm);
auto scope = DECLARE_CATCH_SCOPE(vm);
if (arrayType == kJSTypedArrayTypeNone || arrayType == kJSTypedArrayTypeArrayBuffer)
return nullptr;
JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(vm, toJS(jsBufferRef));
if (!jsBuffer) {
setException(ctx, exception, createTypeError(globalObject, "JSObjectMakeTypedArrayWithArrayBuffer expects buffer to be an Array Buffer object"));
return nullptr;
}
RefPtr<ArrayBuffer> buffer = jsBuffer->impl();
unsigned elementByteSize = elementSize(toTypedArrayType(arrayType));
JSObject* result = createTypedArray(globalObject, arrayType, WTFMove(buffer), 0, buffer->byteLength() / elementByteSize);
if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
return nullptr;
return toRef(result);
}
JSObjectRef JSObjectMakeTypedArrayWithArrayBufferAndOffset(JSContextRef ctx, JSTypedArrayType arrayType, JSObjectRef jsBufferRef, size_t offset, size_t length, JSValueRef* exception)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSLockHolder locker(vm);
auto scope = DECLARE_CATCH_SCOPE(vm);
if (arrayType == kJSTypedArrayTypeNone || arrayType == kJSTypedArrayTypeArrayBuffer)
return nullptr;
JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(vm, toJS(jsBufferRef));
if (!jsBuffer) {
setException(ctx, exception, createTypeError(globalObject, "JSObjectMakeTypedArrayWithArrayBuffer expects buffer to be an Array Buffer object"));
return nullptr;
}
JSObject* result = createTypedArray(globalObject, arrayType, jsBuffer->impl(), offset, length);
if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
return nullptr;
return toRef(result);
}
void* JSObjectGetTypedArrayBytesPtr(JSContextRef ctx, JSObjectRef objectRef, JSValueRef*)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSLockHolder locker(vm);
JSObject* object = toJS(objectRef);
if (JSArrayBufferView* typedArray = jsDynamicCast<JSArrayBufferView*>(vm, object)) {
ArrayBuffer* buffer = typedArray->possiblySharedBuffer();
buffer->pinAndLock();
return buffer->data();
}
return nullptr;
}
size_t JSObjectGetTypedArrayLength(JSContextRef ctx, JSObjectRef objectRef, JSValueRef*)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSObject* object = toJS(objectRef);
if (JSArrayBufferView* typedArray = jsDynamicCast<JSArrayBufferView*>(vm, object))
return typedArray->length();
return 0;
}
size_t JSObjectGetTypedArrayByteLength(JSContextRef ctx, JSObjectRef objectRef, JSValueRef*)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSObject* object = toJS(objectRef);
if (JSArrayBufferView* typedArray = jsDynamicCast<JSArrayBufferView*>(vm, object))
return typedArray->length() * elementSize(typedArray->classInfo(vm)->typedArrayStorageType);
return 0;
}
size_t JSObjectGetTypedArrayByteOffset(JSContextRef ctx, JSObjectRef objectRef, JSValueRef*)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSObject* object = toJS(objectRef);
if (JSArrayBufferView* typedArray = jsDynamicCast<JSArrayBufferView*>(vm, object))
return typedArray->byteOffset();
return 0;
}
JSObjectRef JSObjectGetTypedArrayBuffer(JSContextRef ctx, JSObjectRef objectRef, JSValueRef*)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSLockHolder locker(vm);
JSObject* object = toJS(objectRef);
if (JSArrayBufferView* typedArray = jsDynamicCast<JSArrayBufferView*>(vm, object))
return toRef(vm.m_typedArrayController->toJS(globalObject, typedArray->globalObject(vm), typedArray->possiblySharedBuffer()));
return nullptr;
}
JSObjectRef JSObjectMakeArrayBufferWithBytesNoCopy(JSContextRef ctx, void* bytes, size_t byteLength, JSTypedArrayBytesDeallocator bytesDeallocator, void* deallocatorContext, JSValueRef* exception)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSLockHolder locker(vm);
auto scope = DECLARE_CATCH_SCOPE(vm);
auto buffer = ArrayBuffer::createFromBytes(bytes, byteLength, [=](void* p) {
if (bytesDeallocator)
bytesDeallocator(p, deallocatorContext);
});
JSArrayBuffer* jsBuffer = JSArrayBuffer::create(vm, globalObject->arrayBufferStructure(ArrayBufferSharingMode::Default), WTFMove(buffer));
if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow)
return nullptr;
return toRef(jsBuffer);
}
void* JSObjectGetArrayBufferBytesPtr(JSContextRef ctx, JSObjectRef objectRef, JSValueRef* exception)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSLockHolder locker(vm);
JSObject* object = toJS(objectRef);
if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(vm, object)) {
ArrayBuffer* buffer = jsBuffer->impl();
if (buffer->isWasmMemory()) {
setException(ctx, exception, createTypeError(globalObject, "Cannot get the backing buffer for a WebAssembly.Memory"_s));
return nullptr;
}
buffer->pinAndLock();
return buffer->data();
}
return nullptr;
}
size_t JSObjectGetArrayBufferByteLength(JSContextRef ctx, JSObjectRef objectRef, JSValueRef*)
{
JSGlobalObject* globalObject = toJS(ctx);
VM& vm = globalObject->vm();
JSObject* object = toJS(objectRef);
if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(vm, object))
return jsBuffer->impl()->byteLength();
return 0;
}