/ runtime / JSArrayBufferView.h
JSArrayBufferView.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 "AuxiliaryBarrier.h"
 29  #include "JSObject.h"
 30  #include <wtf/TaggedArrayStoragePtr.h>
 31  
 32  namespace JSC {
 33  
 34  class LLIntOffsetsExtractor;
 35  
 36  // This class serves two purposes:
 37  //
 38  // 1) It provides those parts of JSGenericTypedArrayView that don't depend
 39  //    on template parameters.
 40  //
 41  // 2) It represents the DOM/WebCore-visible "JSArrayBufferView" type, which
 42  //    C++ code uses when it wants to pass around a view of an array buffer
 43  //    without concern for the actual type of the view.
 44  //
 45  // These two roles are quite different. (1) is just a matter of optimizing
 46  // compile and link times by having as much code and data as possible not
 47  // be subject to template specialization. (2) is *almost* a matter of
 48  // semantics; indeed at the very least it is a matter of obeying a contract
 49  // that we have with WebCore right now.
 50  //
 51  // One convenient thing that saves us from too much crazy is that
 52  // ArrayBufferView is not instantiable.
 53  
 54  // Typed array views have different modes depending on how big they are and
 55  // whether the user has done anything that requires a separate backing
 56  // buffer or the DOM-specified detaching capabilities.
 57  enum TypedArrayMode : uint32_t {
 58      // Legend:
 59      // B: JSArrayBufferView::m_butterfly pointer
 60      // V: JSArrayBufferView::m_vector pointer
 61      // M: JSArrayBufferView::m_mode
 62  
 63      // Small and fast typed array. B is unused, V points to a vector
 64      // allocated in the primitive Gigacage, and M = FastTypedArray. V's
 65      // liveness is determined entirely by the view's liveness.
 66      FastTypedArray,
 67  
 68      // A large typed array that still attempts not to waste too much
 69      // memory. B is unused, V points to a vector allocated using
 70      // Gigacage::tryMalloc(), and M = OversizeTypedArray. V's liveness is
 71      // determined entirely by the view's liveness, and the view will add a
 72      // finalizer to delete V.
 73      OversizeTypedArray,
 74  
 75      // A typed array that was used in some crazy way. B's IndexingHeader
 76      // is hijacked to contain a reference to the native array buffer. The
 77      // native typed array view points back to the JS view. V points to a
 78      // vector allocated using who-knows-what, and M = WastefulTypedArray.
 79      // The view does not own the vector.
 80      WastefulTypedArray,
 81  
 82      // A data view. B is unused, V points to a vector allocated using who-
 83      // knows-what, and M = DataViewMode. The view does not own the vector.
 84      // There is an extra field (in JSDataView) that points to the
 85      // ArrayBuffer.
 86      DataViewMode
 87  };
 88  
 89  inline bool hasArrayBuffer(TypedArrayMode mode)
 90  {
 91      return mode >= WastefulTypedArray;
 92  }
 93  
 94  // When WebCore uses a JSArrayBufferView, it expects to be able to get the native
 95  // ArrayBuffer and little else. This requires slowing down and wasting memory,
 96  // and then accessing things via the Butterfly. When JS uses a JSArrayBufferView
 97  // it is always via the usual methods in the MethodTable, so this class's
 98  // implementation of those has no need to even exist - we could at any time sink
 99  // code into JSGenericTypedArrayView if it was convenient.
100  
101  class JSArrayBufferView : public JSNonFinalObject {
102  public:
103      using Base = JSNonFinalObject;
104  
105      template<typename, SubspaceAccess>
106      static void subspaceFor(VM&)
107      {
108          RELEASE_ASSERT_NOT_REACHED();
109      }
110  
111      static constexpr unsigned fastSizeLimit = 1000;
112      using VectorPtr = CagedBarrierPtr<Gigacage::Primitive, void, tagCagedPtr>;
113  
114      static void* nullVectorPtr()
115      {
116          VectorPtr null { };
117          return null.rawBits();
118      }
119      
120      static size_t sizeOf(uint32_t length, uint32_t elementSize)
121      {
122          return (static_cast<size_t>(length) * elementSize + sizeof(EncodedJSValue) - 1)
123              & ~(sizeof(EncodedJSValue) - 1);
124      }
125  
126      static size_t allocationSize(Checked<size_t> inlineCapacity)
127      {
128          ASSERT_UNUSED(inlineCapacity, !inlineCapacity);
129          return sizeof(JSArrayBufferView);
130      }
131          
132  protected:
133      class ConstructionContext {
134          WTF_MAKE_NONCOPYABLE(ConstructionContext);
135          
136      public:
137          enum InitializationMode { ZeroFill, DontInitialize };
138          
139          JS_EXPORT_PRIVATE ConstructionContext(VM&, Structure*, uint32_t length, uint32_t elementSize, InitializationMode = ZeroFill);
140          
141          // This is only for constructing fast typed arrays. It's used by the JIT's slow path.
142          ConstructionContext(Structure*, uint32_t length, void* vector);
143          
144          JS_EXPORT_PRIVATE ConstructionContext(
145              VM&, Structure*, RefPtr<ArrayBuffer>&&,
146              unsigned byteOffset, unsigned length);
147          
148          enum DataViewTag { DataView };
149          ConstructionContext(
150              Structure*, RefPtr<ArrayBuffer>&&,
151              unsigned byteOffset, unsigned length, DataViewTag);
152          
153          bool operator!() const { return !m_structure; }
154          
155          Structure* structure() const { return m_structure; }
156          void* vector() const { return m_vector.getMayBeNull(m_length); }
157          uint32_t length() const { return m_length; }
158          TypedArrayMode mode() const { return m_mode; }
159          Butterfly* butterfly() const { return m_butterfly; }
160          
161      private:
162          Structure* m_structure;
163          using VectorType = CagedPtr<Gigacage::Primitive, void, tagCagedPtr>;
164          VectorType m_vector;
165          uint32_t m_length;
166          TypedArrayMode m_mode;
167          Butterfly* m_butterfly;
168      };
169      
170      JS_EXPORT_PRIVATE JSArrayBufferView(VM&, ConstructionContext&);
171      JS_EXPORT_PRIVATE void finishCreation(VM&);
172      
173      static bool put(JSCell*, JSGlobalObject*, PropertyName, JSValue, PutPropertySlot&);
174  
175      static void visitChildren(JSCell*, SlotVisitor&);
176      
177  public:
178      TypedArrayMode mode() const { return m_mode; }
179      bool hasArrayBuffer() const { return JSC::hasArrayBuffer(mode()); }
180      
181      bool isShared();
182      JS_EXPORT_PRIVATE ArrayBuffer* unsharedBuffer();
183      inline ArrayBuffer* possiblySharedBuffer();
184      JSArrayBuffer* unsharedJSBuffer(JSGlobalObject* globalObject);
185      JSArrayBuffer* possiblySharedJSBuffer(JSGlobalObject* globalObject);
186      RefPtr<ArrayBufferView> unsharedImpl();
187      JS_EXPORT_PRIVATE RefPtr<ArrayBufferView> possiblySharedImpl();
188      bool isDetached() { return hasArrayBuffer() && !hasVector(); }
189      void detach();
190  
191      bool hasVector() const { return !!m_vector; }
192      void* vector() const { return m_vector.getMayBeNull(length()); }
193      
194      inline unsigned byteOffset();
195      inline Optional<unsigned> byteOffsetConcurrently();
196  
197      unsigned length() const { return m_length; }
198      unsigned byteLength() const;
199  
200      DECLARE_EXPORT_INFO;
201      
202      static ptrdiff_t offsetOfVector() { return OBJECT_OFFSETOF(JSArrayBufferView, m_vector); }
203      static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(JSArrayBufferView, m_length); }
204      static ptrdiff_t offsetOfMode() { return OBJECT_OFFSETOF(JSArrayBufferView, m_mode); }
205      
206      static RefPtr<ArrayBufferView> toWrapped(VM&, JSValue);
207      static RefPtr<ArrayBufferView> toWrappedAllowShared(VM&, JSValue);
208  
209  private:
210      enum Requester { Mutator, ConcurrentThread };
211      template<Requester, typename ResultType> ResultType byteOffsetImpl();
212      template<Requester> ArrayBuffer* possiblySharedBufferImpl();
213  
214      JS_EXPORT_PRIVATE ArrayBuffer* slowDownAndWasteMemory();
215      static void finalize(JSCell*);
216  
217  protected:
218      friend class LLIntOffsetsExtractor;
219  
220      ArrayBuffer* existingBufferInButterfly();
221  
222      VectorPtr m_vector;
223      uint32_t m_length;
224      TypedArrayMode m_mode;
225  };
226  
227  } // namespace JSC
228  
229  namespace WTF {
230  
231  JS_EXPORT_PRIVATE void printInternal(PrintStream&, JSC::TypedArrayMode);
232  
233  } // namespace WTF