/ runtime / JSGenericTypedArrayView.h
JSGenericTypedArrayView.h
  1  /*
  2   * Copyright (C) 2013-2020 Apple Inc. All rights reserved.
  3   *
  4   * Redistribution and use in source and binary forms, with or without
  5   * modification, are permitted provided that the following conditions
  6   * are met:
  7   * 1. Redistributions of source code must retain the above copyright
  8   *    notice, this list of conditions and the following disclaimer.
  9   * 2. Redistributions in binary form must reproduce the above copyright
 10   *    notice, this list of conditions and the following disclaimer in the
 11   *    documentation and/or other materials provided with the distribution.
 12   *
 13   * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 14   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 15   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 17   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 18   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 19   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 20   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 21   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 23   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 24   */
 25  
 26  #pragma once
 27  
 28  #include "JSArrayBufferView.h"
 29  #include "ThrowScope.h"
 30  #include "ToNativeFromValue.h"
 31  
 32  namespace JSC {
 33  
 34  JS_EXPORT_PRIVATE const ClassInfo* getInt8ArrayClassInfo();
 35  JS_EXPORT_PRIVATE const ClassInfo* getInt16ArrayClassInfo();
 36  JS_EXPORT_PRIVATE const ClassInfo* getInt32ArrayClassInfo();
 37  JS_EXPORT_PRIVATE const ClassInfo* getUint8ArrayClassInfo();
 38  JS_EXPORT_PRIVATE const ClassInfo* getUint8ClampedArrayClassInfo();
 39  JS_EXPORT_PRIVATE const ClassInfo* getUint16ArrayClassInfo();
 40  JS_EXPORT_PRIVATE const ClassInfo* getUint32ArrayClassInfo();
 41  JS_EXPORT_PRIVATE const ClassInfo* getFloat32ArrayClassInfo();
 42  JS_EXPORT_PRIVATE const ClassInfo* getFloat64ArrayClassInfo();
 43  
 44  // A typed array view is our representation of a typed array object as seen
 45  // from JavaScript. For example:
 46  //
 47  // var o = new Int8Array(100);
 48  //
 49  // Here, 'o' points to a JSGenericTypedArrayView<int8_t>.
 50  //
 51  // Views contain five fields:
 52  //
 53  //     Structure* S     // from JSCell
 54  //     Butterfly* B     // from JSObject
 55  //     ElementType* V
 56  //     uint32_t L
 57  //     TypedArrayMode M
 58  //
 59  // These fields take up a total of four pointer-width words. FIXME: Make
 60  // it take less words!
 61  //
 62  // B is usually unused but may stored some additional "overflow" data for
 63  // one of the modes. V always points to the base of the typed array's data,
 64  // and may point to either GC-managed copied space, or data in the C heap;
 65  // which of those things it points to is governed by the mode although for
 66  // simple accesses to the view you can just read from the pointer either
 67  // way. M specifies the mode of the view. L is the length, in units that
 68  // depend on the view's type.
 69  
 70  // The JSGenericTypedArrayView is templatized by an Adaptor that controls
 71  // the element type and how it's converted; it should obey the following
 72  // interface; I use int8_t as an example:
 73  //
 74  // struct Adaptor {
 75  //     typedef int8_t Type;
 76  //     typedef Int8Array ViewType;
 77  //     typedef JSInt8Array JSViewType;
 78  //     static int8_t toNativeFromInt32(int32_t);
 79  //     static int8_t toNativeFromUint32(uint32_t);
 80  //     static int8_t toNativeFromDouble(double);
 81  //     static JSValue toJSValue(int8_t);
 82  //     static double toDouble(int8_t);
 83  //     template<T> static T::Type convertTo(uint8_t);
 84  // };
 85  
 86  enum class CopyType {
 87      LeftToRight,
 88      Unobservable,
 89  };
 90  
 91  extern const ASCIILiteral typedArrayBufferHasBeenDetachedErrorMessage;
 92  
 93  template<typename Adaptor>
 94  class JSGenericTypedArrayView final : public JSArrayBufferView {
 95  public:
 96      using Base = JSArrayBufferView;
 97      typedef typename Adaptor::Type ElementType;
 98  
 99      static constexpr unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetOwnPropertyNames | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero;
100  
101      static constexpr unsigned elementSize = sizeof(typename Adaptor::Type);
102  
103      static JSGenericTypedArrayView* create(JSGlobalObject*, Structure*, unsigned length);
104      static JSGenericTypedArrayView* createWithFastVector(JSGlobalObject*, Structure*, unsigned length, void* vector);
105      static JSGenericTypedArrayView* createUninitialized(JSGlobalObject*, Structure*, unsigned length);
106      static JSGenericTypedArrayView* create(JSGlobalObject*, Structure*, RefPtr<ArrayBuffer>&&, unsigned byteOffset, unsigned length);
107      static JSGenericTypedArrayView* create(VM&, Structure*, RefPtr<typename Adaptor::ViewType>&& impl);
108      static JSGenericTypedArrayView* create(Structure*, JSGlobalObject*, RefPtr<typename Adaptor::ViewType>&& impl);
109      
110      unsigned byteLength() const { return m_length * sizeof(typename Adaptor::Type); }
111      size_t byteSize() const { return sizeOf(m_length, sizeof(typename Adaptor::Type)); }
112      
113      const typename Adaptor::Type* typedVector() const
114      {
115          return bitwise_cast<const typename Adaptor::Type*>(vector());
116      }
117      typename Adaptor::Type* typedVector()
118      {
119          return bitwise_cast<typename Adaptor::Type*>(vector());
120      }
121  
122      // These methods are meant to match indexed access methods that JSObject
123      // supports - hence the slight redundancy.
124      bool canGetIndexQuickly(unsigned i) const
125      {
126          return i < m_length;
127      }
128      bool canSetIndexQuickly(unsigned i, JSValue value) const
129      {
130          return i < m_length && value.isNumber();
131      }
132      
133      typename Adaptor::Type getIndexQuicklyAsNativeValue(unsigned i) const
134      {
135          ASSERT(i < m_length);
136          return typedVector()[i];
137      }
138      
139      double getIndexQuicklyAsDouble(unsigned i)
140      {
141          return Adaptor::toDouble(getIndexQuicklyAsNativeValue(i));
142      }
143      
144      JSValue getIndexQuickly(unsigned i) const
145      {
146          return Adaptor::toJSValue(getIndexQuicklyAsNativeValue(i));
147      }
148      
149      void setIndexQuicklyToNativeValue(unsigned i, typename Adaptor::Type value)
150      {
151          ASSERT(i < m_length);
152          typedVector()[i] = value;
153      }
154      
155      void setIndexQuicklyToDouble(unsigned i, double value)
156      {
157          setIndexQuicklyToNativeValue(i, toNativeFromValue<Adaptor>(jsNumber(value)));
158      }
159      
160      void setIndexQuickly(unsigned i, JSValue value)
161      {
162          ASSERT(!value.isObject());
163          setIndexQuicklyToNativeValue(i, toNativeFromValue<Adaptor>(value));
164      }
165      
166      bool setIndex(JSGlobalObject* globalObject, unsigned i, JSValue jsValue)
167      {
168          VM& vm = getVM(globalObject);
169          auto scope = DECLARE_THROW_SCOPE(vm);
170  
171          typename Adaptor::Type value = toNativeFromValue<Adaptor>(globalObject, jsValue);
172          RETURN_IF_EXCEPTION(scope, false);
173  
174          if (isDetached() || i >= m_length)
175              return false;
176  
177          setIndexQuicklyToNativeValue(i, value);
178          return true;
179      }
180  
181      static ElementType toAdaptorNativeFromValue(JSGlobalObject* globalObject, JSValue jsValue) { return toNativeFromValue<Adaptor>(globalObject, jsValue); }
182  
183      static Optional<ElementType> toAdaptorNativeFromValueWithoutCoercion(JSValue jsValue) { return toNativeFromValueWithoutCoercion<Adaptor>(jsValue); }
184  
185      void sort()
186      {
187          RELEASE_ASSERT(!isDetached());
188          switch (Adaptor::typeValue) {
189          case TypeFloat32:
190              sortFloat<int32_t>();
191              break;
192          case TypeFloat64:
193              sortFloat<int64_t>();
194              break;
195          default: {
196              ElementType* array = typedVector();
197              std::sort(array, array + m_length);
198              break;
199          }
200          }
201      }
202  
203      bool canAccessRangeQuickly(unsigned offset, unsigned length)
204      {
205          return offset <= m_length
206              && offset + length <= m_length
207              // check overflow
208              && offset + length >= offset;
209      }
210      
211      // Like canSetQuickly, except: if it returns false, it will throw the
212      // appropriate exception.
213      bool validateRange(JSGlobalObject*, unsigned offset, unsigned length);
214  
215      // Returns true if successful, and false on error; if it returns false
216      // then it will have thrown an exception.
217      bool set(JSGlobalObject*, unsigned offset, JSObject*, unsigned objectOffset, unsigned length, CopyType type = CopyType::Unobservable);
218      
219      RefPtr<typename Adaptor::ViewType> possiblySharedTypedImpl();
220      RefPtr<typename Adaptor::ViewType> unsharedTypedImpl();
221  
222      static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
223      {
224          return Structure::create(vm, globalObject, prototype, TypeInfo(typeForTypedArrayType(Adaptor::typeValue), StructureFlags), info(), NonArray);
225      }
226      
227      static const ClassInfo s_info; // This is never accessed directly, since that would break linkage on some compilers.
228      
229      static const ClassInfo* info()
230      {
231          switch (Adaptor::typeValue) {
232          case TypeInt8:
233              return getInt8ArrayClassInfo();
234          case TypeInt16:
235              return getInt16ArrayClassInfo();
236          case TypeInt32:
237              return getInt32ArrayClassInfo();
238          case TypeUint8:
239              return getUint8ArrayClassInfo();
240          case TypeUint8Clamped:
241              return getUint8ClampedArrayClassInfo();
242          case TypeUint16:
243              return getUint16ArrayClassInfo();
244          case TypeUint32:
245              return getUint32ArrayClassInfo();
246          case TypeFloat32:
247              return getFloat32ArrayClassInfo();
248          case TypeFloat64:
249              return getFloat64ArrayClassInfo();
250          default:
251              RELEASE_ASSERT_NOT_REACHED();
252              return nullptr;
253          }
254      }
255  
256      template<typename CellType, SubspaceAccess mode>
257      static IsoSubspace* subspaceFor(VM& vm)
258      {
259          switch (Adaptor::typeValue) {
260          case TypeInt8:
261              return vm.int8ArraySpace<mode>();
262          case TypeInt16:
263              return vm.int16ArraySpace<mode>();
264          case TypeInt32:
265              return vm.int32ArraySpace<mode>();
266          case TypeUint8:
267              return vm.uint8ArraySpace<mode>();
268          case TypeUint8Clamped:
269              return vm.uint8ClampedArraySpace<mode>();
270          case TypeUint16:
271              return vm.uint16ArraySpace<mode>();
272          case TypeUint32:
273              return vm.uint32ArraySpace<mode>();
274          case TypeFloat32:
275              return vm.float32ArraySpace<mode>();
276          case TypeFloat64:
277              return vm.float64ArraySpace<mode>();
278          default:
279              RELEASE_ASSERT_NOT_REACHED();
280              return nullptr;
281          }
282      }
283      
284      ArrayBuffer* existingBuffer();
285  
286      static const TypedArrayType TypedArrayStorageType = Adaptor::typeValue;
287  
288      // This is the default DOM unwrapping. It calls toUnsharedNativeTypedView().
289      static RefPtr<typename Adaptor::ViewType> toWrapped(VM&, JSValue);
290      // [AllowShared] annotation allows accepting TypedArray originated from SharedArrayBuffer.
291      static RefPtr<typename Adaptor::ViewType> toWrappedAllowShared(VM&, JSValue);
292      
293  private:
294      friend struct TypedArrayClassInfos;
295  
296      JSGenericTypedArrayView(VM&, ConstructionContext&);
297  
298      static bool getOwnPropertySlot(JSObject*, JSGlobalObject*, PropertyName, PropertySlot&);
299      static bool put(JSCell*, JSGlobalObject*, PropertyName, JSValue, PutPropertySlot&);
300      static bool defineOwnProperty(JSObject*, JSGlobalObject*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
301      static bool deleteProperty(JSCell*, JSGlobalObject*, PropertyName, DeletePropertySlot&);
302  
303      static bool getOwnPropertySlotByIndex(JSObject*, JSGlobalObject*, unsigned propertyName, PropertySlot&);
304      static bool putByIndex(JSCell*, JSGlobalObject*, unsigned propertyName, JSValue, bool shouldThrow);
305      static bool deletePropertyByIndex(JSCell*, JSGlobalObject*, unsigned propertyName);
306      
307      static void getOwnPropertyNames(JSObject*, JSGlobalObject*, PropertyNameArray&, DontEnumPropertiesMode);
308  
309      static size_t estimatedSize(JSCell*, VM&);
310      static void visitChildren(JSCell*, SlotVisitor&);
311  
312      // Returns true if successful, and false on error; it will throw on error.
313      template<typename OtherAdaptor>
314      bool setWithSpecificType(
315          JSGlobalObject*, unsigned offset, JSGenericTypedArrayView<OtherAdaptor>*,
316          unsigned objectOffset, unsigned length, CopyType);
317  
318      // The ECMA 6 spec states that floating point Typed Arrays should have the following ordering:
319      //
320      // -Inifinity < negative finite numbers < -0.0 < 0.0 < positive finite numbers < Infinity < NaN
321      // Note: regardless of the sign or exact representation of a NaN it is greater than all other values.
322      //
323      // An interesting fact about IEEE 754 floating point numbers is that have an adjacent representation
324      // i.e. for any finite floating point x there does not exist a finite floating point y such that
325      // ((float) ((int) x + 1)) > y > x (where int represents a signed bit integer with the same number
326      // of bits as float). Thus, if we have an array of floating points if we view it as an
327      // array of signed bit integers it will sort in the format we desire. Note, denormal
328      // numbers fit this property as they are floating point numbers with a exponent field of all
329      // zeros so they will be closer to the signed zeros than any normalized number.
330      //
331      // All the processors we support, however, use twos complement. Fortunately, if you compare a signed
332      // bit number as if it were twos complement the result will be correct assuming both numbers are not
333      // negative. e.g.
334      //
335      //    - <=> - = reversed (-30 > -20 = true)
336      //    + <=> + = ordered (30 > 20 = true)
337      //    - <=> + = ordered (-30 > 20 = false)
338      //    + <=> - = ordered (30 > -20 = true)
339      //
340      // For NaN, we normalize the NaN to a peticular representation; the sign bit is 0, all exponential bits
341      // are 1 and only the MSB of the mantissa is 1. So, NaN is recognized as the largest integral numbers.
342  
343      void purifyArray()
344      {
345          ElementType* array = typedVector();
346          for (unsigned i = 0; i < m_length; i++)
347              array[i] = purifyNaN(array[i]);
348      }
349  
350      template<typename IntegralType>
351      void sortFloat()
352      {
353          ASSERT(sizeof(IntegralType) == sizeof(ElementType));
354  
355          // Since there might be another view that sets the bits of
356          // our floats to NaNs with negative sign bits we need to
357          // purify the array.
358          // We use a separate function here to avoid the strict aliasing rule.
359          // We could use a union but ASAN seems to frown upon that.
360          purifyArray();
361  
362          IntegralType* array = reinterpret_cast_ptr<IntegralType*>(typedVector());
363          std::sort(array, array + m_length, [] (IntegralType a, IntegralType b) {
364              if (a >= 0 || b >= 0)
365                  return a < b;
366              return a > b;
367          });
368  
369      }
370  
371  };
372  
373  template<typename Adaptor>
374  inline RefPtr<typename Adaptor::ViewType> toPossiblySharedNativeTypedView(VM& vm, JSValue value)
375  {
376      typename Adaptor::JSViewType* wrapper = jsDynamicCast<typename Adaptor::JSViewType*>(vm, value);
377      if (!wrapper)
378          return nullptr;
379      return wrapper->possiblySharedTypedImpl();
380  }
381  
382  template<typename Adaptor>
383  inline RefPtr<typename Adaptor::ViewType> toUnsharedNativeTypedView(VM& vm, JSValue value)
384  {
385      RefPtr<typename Adaptor::ViewType> result = toPossiblySharedNativeTypedView<Adaptor>(vm, value);
386      if (!result || result->isShared())
387          return nullptr;
388      return result;
389  }
390  
391  template<typename Adaptor>
392  RefPtr<typename Adaptor::ViewType> JSGenericTypedArrayView<Adaptor>::toWrapped(VM& vm, JSValue value)
393  {
394      return JSC::toUnsharedNativeTypedView<Adaptor>(vm, value);
395  }
396  
397  template<typename Adaptor>
398  RefPtr<typename Adaptor::ViewType> JSGenericTypedArrayView<Adaptor>::toWrappedAllowShared(VM& vm, JSValue value)
399  {
400      return JSC::toPossiblySharedNativeTypedView<Adaptor>(vm, value);
401  }
402  
403  
404  } // namespace JSC