JSTypedArray.cpp
1 /* 2 * Copyright (C) 2015 Dominic Szablewski (dominic@phoboslab.org) 3 * Copyright (C) 2016 Apple Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include "config.h" 28 #include "JSTypedArray.h" 29 30 #include "APICast.h" 31 #include "APIUtils.h" 32 #include "ClassInfo.h" 33 #include "JSCInlines.h" 34 #include "JSGenericTypedArrayViewInlines.h" 35 #include "JSTypedArrays.h" 36 #include "TypedArrayController.h" 37 #include <wtf/RefPtr.h> 38 39 using namespace JSC; 40 41 // Helper functions. 42 43 inline JSTypedArrayType toJSTypedArrayType(TypedArrayType type) 44 { 45 switch (type) { 46 case JSC::TypeDataView: 47 case NotTypedArray: 48 return kJSTypedArrayTypeNone; 49 case TypeInt8: 50 return kJSTypedArrayTypeInt8Array; 51 case TypeUint8: 52 return kJSTypedArrayTypeUint8Array; 53 case TypeUint8Clamped: 54 return kJSTypedArrayTypeUint8ClampedArray; 55 case TypeInt16: 56 return kJSTypedArrayTypeInt16Array; 57 case TypeUint16: 58 return kJSTypedArrayTypeUint16Array; 59 case TypeInt32: 60 return kJSTypedArrayTypeInt32Array; 61 case TypeUint32: 62 return kJSTypedArrayTypeUint32Array; 63 case TypeFloat32: 64 return kJSTypedArrayTypeFloat32Array; 65 case TypeFloat64: 66 return kJSTypedArrayTypeFloat64Array; 67 } 68 RELEASE_ASSERT_NOT_REACHED(); 69 } 70 71 inline TypedArrayType toTypedArrayType(JSTypedArrayType type) 72 { 73 switch (type) { 74 case kJSTypedArrayTypeArrayBuffer: 75 case kJSTypedArrayTypeNone: 76 return NotTypedArray; 77 case kJSTypedArrayTypeInt8Array: 78 return TypeInt8; 79 case kJSTypedArrayTypeUint8Array: 80 return TypeUint8; 81 case kJSTypedArrayTypeUint8ClampedArray: 82 return TypeUint8Clamped; 83 case kJSTypedArrayTypeInt16Array: 84 return TypeInt16; 85 case kJSTypedArrayTypeUint16Array: 86 return TypeUint16; 87 case kJSTypedArrayTypeInt32Array: 88 return TypeInt32; 89 case kJSTypedArrayTypeUint32Array: 90 return TypeUint32; 91 case kJSTypedArrayTypeFloat32Array: 92 return TypeFloat32; 93 case kJSTypedArrayTypeFloat64Array: 94 return TypeFloat64; 95 } 96 RELEASE_ASSERT_NOT_REACHED(); 97 } 98 99 static JSObject* createTypedArray(JSGlobalObject* globalObject, JSTypedArrayType type, RefPtr<ArrayBuffer>&& buffer, size_t offset, size_t length) 100 { 101 VM& vm = globalObject->vm(); 102 auto scope = DECLARE_THROW_SCOPE(vm); 103 if (!buffer) { 104 throwOutOfMemoryError(globalObject, scope); 105 return nullptr; 106 } 107 switch (type) { 108 case kJSTypedArrayTypeInt8Array: 109 return JSInt8Array::create(globalObject, globalObject->typedArrayStructure(TypeInt8), WTFMove(buffer), offset, length); 110 case kJSTypedArrayTypeInt16Array: 111 return JSInt16Array::create(globalObject, globalObject->typedArrayStructure(TypeInt16), WTFMove(buffer), offset, length); 112 case kJSTypedArrayTypeInt32Array: 113 return JSInt32Array::create(globalObject, globalObject->typedArrayStructure(TypeInt32), WTFMove(buffer), offset, length); 114 case kJSTypedArrayTypeUint8Array: 115 return JSUint8Array::create(globalObject, globalObject->typedArrayStructure(TypeUint8), WTFMove(buffer), offset, length); 116 case kJSTypedArrayTypeUint8ClampedArray: 117 return JSUint8ClampedArray::create(globalObject, globalObject->typedArrayStructure(TypeUint8Clamped), WTFMove(buffer), offset, length); 118 case kJSTypedArrayTypeUint16Array: 119 return JSUint16Array::create(globalObject, globalObject->typedArrayStructure(TypeUint16), WTFMove(buffer), offset, length); 120 case kJSTypedArrayTypeUint32Array: 121 return JSUint32Array::create(globalObject, globalObject->typedArrayStructure(TypeUint32), WTFMove(buffer), offset, length); 122 case kJSTypedArrayTypeFloat32Array: 123 return JSFloat32Array::create(globalObject, globalObject->typedArrayStructure(TypeFloat32), WTFMove(buffer), offset, length); 124 case kJSTypedArrayTypeFloat64Array: 125 return JSFloat64Array::create(globalObject, globalObject->typedArrayStructure(TypeFloat64), WTFMove(buffer), offset, length); 126 case kJSTypedArrayTypeArrayBuffer: 127 case kJSTypedArrayTypeNone: 128 RELEASE_ASSERT_NOT_REACHED(); 129 } 130 return nullptr; 131 } 132 133 // Implementations of the API functions. 134 135 JSTypedArrayType JSValueGetTypedArrayType(JSContextRef ctx, JSValueRef valueRef, JSValueRef*) 136 { 137 138 JSGlobalObject* globalObject = toJS(ctx); 139 VM& vm = globalObject->vm(); 140 JSLockHolder locker(vm); 141 142 JSValue value = toJS(globalObject, valueRef); 143 if (!value.isObject()) 144 return kJSTypedArrayTypeNone; 145 JSObject* object = value.getObject(); 146 147 if (jsDynamicCast<JSArrayBuffer*>(vm, object)) 148 return kJSTypedArrayTypeArrayBuffer; 149 150 return toJSTypedArrayType(object->classInfo(vm)->typedArrayStorageType); 151 } 152 153 JSObjectRef JSObjectMakeTypedArray(JSContextRef ctx, JSTypedArrayType arrayType, size_t length, JSValueRef* exception) 154 { 155 JSGlobalObject* globalObject = toJS(ctx); 156 VM& vm = globalObject->vm(); 157 JSLockHolder locker(vm); 158 auto scope = DECLARE_CATCH_SCOPE(vm); 159 160 if (arrayType == kJSTypedArrayTypeNone || arrayType == kJSTypedArrayTypeArrayBuffer) 161 return nullptr; 162 163 unsigned elementByteSize = elementSize(toTypedArrayType(arrayType)); 164 165 auto buffer = ArrayBuffer::tryCreate(length, elementByteSize); 166 JSObject* result = createTypedArray(globalObject, arrayType, WTFMove(buffer), 0, length); 167 if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow) 168 return nullptr; 169 return toRef(result); 170 } 171 172 JSObjectRef JSObjectMakeTypedArrayWithBytesNoCopy(JSContextRef ctx, JSTypedArrayType arrayType, void* bytes, size_t length, JSTypedArrayBytesDeallocator destructor, void* destructorContext, JSValueRef* exception) 173 { 174 JSGlobalObject* globalObject = toJS(ctx); 175 VM& vm = globalObject->vm(); 176 JSLockHolder locker(vm); 177 auto scope = DECLARE_CATCH_SCOPE(vm); 178 179 if (arrayType == kJSTypedArrayTypeNone || arrayType == kJSTypedArrayTypeArrayBuffer) 180 return nullptr; 181 182 unsigned elementByteSize = elementSize(toTypedArrayType(arrayType)); 183 184 auto buffer = ArrayBuffer::createFromBytes(bytes, length, createSharedTask<void(void*)>([=](void* p) { 185 if (destructor) 186 destructor(p, destructorContext); 187 })); 188 JSObject* result = createTypedArray(globalObject, arrayType, WTFMove(buffer), 0, length / elementByteSize); 189 if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow) 190 return nullptr; 191 return toRef(result); 192 } 193 194 JSObjectRef JSObjectMakeTypedArrayWithArrayBuffer(JSContextRef ctx, JSTypedArrayType arrayType, JSObjectRef jsBufferRef, JSValueRef* exception) 195 { 196 JSGlobalObject* globalObject = toJS(ctx); 197 VM& vm = globalObject->vm(); 198 JSLockHolder locker(vm); 199 auto scope = DECLARE_CATCH_SCOPE(vm); 200 201 if (arrayType == kJSTypedArrayTypeNone || arrayType == kJSTypedArrayTypeArrayBuffer) 202 return nullptr; 203 204 JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(vm, toJS(jsBufferRef)); 205 if (!jsBuffer) { 206 setException(ctx, exception, createTypeError(globalObject, "JSObjectMakeTypedArrayWithArrayBuffer expects buffer to be an Array Buffer object")); 207 return nullptr; 208 } 209 210 RefPtr<ArrayBuffer> buffer = jsBuffer->impl(); 211 unsigned elementByteSize = elementSize(toTypedArrayType(arrayType)); 212 213 JSObject* result = createTypedArray(globalObject, arrayType, WTFMove(buffer), 0, buffer->byteLength() / elementByteSize); 214 if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow) 215 return nullptr; 216 return toRef(result); 217 } 218 219 JSObjectRef JSObjectMakeTypedArrayWithArrayBufferAndOffset(JSContextRef ctx, JSTypedArrayType arrayType, JSObjectRef jsBufferRef, size_t offset, size_t length, JSValueRef* exception) 220 { 221 JSGlobalObject* globalObject = toJS(ctx); 222 VM& vm = globalObject->vm(); 223 JSLockHolder locker(vm); 224 auto scope = DECLARE_CATCH_SCOPE(vm); 225 226 if (arrayType == kJSTypedArrayTypeNone || arrayType == kJSTypedArrayTypeArrayBuffer) 227 return nullptr; 228 229 JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(vm, toJS(jsBufferRef)); 230 if (!jsBuffer) { 231 setException(ctx, exception, createTypeError(globalObject, "JSObjectMakeTypedArrayWithArrayBuffer expects buffer to be an Array Buffer object")); 232 return nullptr; 233 } 234 235 JSObject* result = createTypedArray(globalObject, arrayType, jsBuffer->impl(), offset, length); 236 if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow) 237 return nullptr; 238 return toRef(result); 239 } 240 241 void* JSObjectGetTypedArrayBytesPtr(JSContextRef ctx, JSObjectRef objectRef, JSValueRef* exception) 242 { 243 JSGlobalObject* globalObject = toJS(ctx); 244 VM& vm = globalObject->vm(); 245 JSLockHolder locker(vm); 246 JSObject* object = toJS(objectRef); 247 248 if (JSArrayBufferView* typedArray = jsDynamicCast<JSArrayBufferView*>(vm, object)) { 249 if (ArrayBuffer* buffer = typedArray->possiblySharedBuffer()) { 250 buffer->pinAndLock(); 251 return buffer->data(); 252 } 253 254 setException(ctx, exception, createOutOfMemoryError(globalObject)); 255 } 256 return nullptr; 257 } 258 259 size_t JSObjectGetTypedArrayLength(JSContextRef ctx, JSObjectRef objectRef, JSValueRef*) 260 { 261 JSGlobalObject* globalObject = toJS(ctx); 262 VM& vm = globalObject->vm(); 263 JSObject* object = toJS(objectRef); 264 265 if (JSArrayBufferView* typedArray = jsDynamicCast<JSArrayBufferView*>(vm, object)) 266 return typedArray->length(); 267 268 return 0; 269 } 270 271 size_t JSObjectGetTypedArrayByteLength(JSContextRef ctx, JSObjectRef objectRef, JSValueRef*) 272 { 273 JSGlobalObject* globalObject = toJS(ctx); 274 VM& vm = globalObject->vm(); 275 JSObject* object = toJS(objectRef); 276 277 if (JSArrayBufferView* typedArray = jsDynamicCast<JSArrayBufferView*>(vm, object)) 278 return typedArray->length() * elementSize(typedArray->classInfo(vm)->typedArrayStorageType); 279 280 return 0; 281 } 282 283 size_t JSObjectGetTypedArrayByteOffset(JSContextRef ctx, JSObjectRef objectRef, JSValueRef*) 284 { 285 JSGlobalObject* globalObject = toJS(ctx); 286 VM& vm = globalObject->vm(); 287 JSObject* object = toJS(objectRef); 288 289 if (JSArrayBufferView* typedArray = jsDynamicCast<JSArrayBufferView*>(vm, object)) 290 return typedArray->byteOffset(); 291 292 return 0; 293 } 294 295 JSObjectRef JSObjectGetTypedArrayBuffer(JSContextRef ctx, JSObjectRef objectRef, JSValueRef* exception) 296 { 297 JSGlobalObject* globalObject = toJS(ctx); 298 VM& vm = globalObject->vm(); 299 JSLockHolder locker(vm); 300 JSObject* object = toJS(objectRef); 301 302 303 if (JSArrayBufferView* typedArray = jsDynamicCast<JSArrayBufferView*>(vm, object)) { 304 if (ArrayBuffer* buffer = typedArray->possiblySharedBuffer()) 305 return toRef(vm.m_typedArrayController->toJS(globalObject, typedArray->globalObject(vm), buffer)); 306 307 setException(ctx, exception, createOutOfMemoryError(globalObject)); 308 } 309 310 return nullptr; 311 } 312 313 JSObjectRef JSObjectMakeArrayBufferWithBytesNoCopy(JSContextRef ctx, void* bytes, size_t byteLength, JSTypedArrayBytesDeallocator bytesDeallocator, void* deallocatorContext, JSValueRef* exception) 314 { 315 JSGlobalObject* globalObject = toJS(ctx); 316 VM& vm = globalObject->vm(); 317 JSLockHolder locker(vm); 318 auto scope = DECLARE_CATCH_SCOPE(vm); 319 320 auto buffer = ArrayBuffer::createFromBytes(bytes, byteLength, createSharedTask<void(void*)>([=](void* p) { 321 if (bytesDeallocator) 322 bytesDeallocator(p, deallocatorContext); 323 })); 324 325 JSArrayBuffer* jsBuffer = JSArrayBuffer::create(vm, globalObject->arrayBufferStructure(ArrayBufferSharingMode::Default), WTFMove(buffer)); 326 if (handleExceptionIfNeeded(scope, ctx, exception) == ExceptionStatus::DidThrow) 327 return nullptr; 328 329 return toRef(jsBuffer); 330 } 331 332 void* JSObjectGetArrayBufferBytesPtr(JSContextRef ctx, JSObjectRef objectRef, JSValueRef* exception) 333 { 334 JSGlobalObject* globalObject = toJS(ctx); 335 VM& vm = globalObject->vm(); 336 JSLockHolder locker(vm); 337 JSObject* object = toJS(objectRef); 338 339 if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(vm, object)) { 340 ArrayBuffer* buffer = jsBuffer->impl(); 341 if (buffer->isWasmMemory()) { 342 setException(ctx, exception, createTypeError(globalObject, "Cannot get the backing buffer for a WebAssembly.Memory"_s)); 343 return nullptr; 344 } 345 346 buffer->pinAndLock(); 347 return buffer->data(); 348 } 349 return nullptr; 350 } 351 352 size_t JSObjectGetArrayBufferByteLength(JSContextRef ctx, JSObjectRef objectRef, JSValueRef*) 353 { 354 JSGlobalObject* globalObject = toJS(ctx); 355 VM& vm = globalObject->vm(); 356 JSObject* object = toJS(objectRef); 357 358 if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(vm, object)) 359 return jsBuffer->impl()->byteLength(); 360 361 return 0; 362 }