/ API / JSTypedArray.cpp
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  }