/ runtime / JSArrayBufferView.cpp
JSArrayBufferView.cpp
  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  #include "config.h"
 27  #include "JSArrayBufferView.h"
 28  
 29  #include "GenericTypedArrayViewInlines.h"
 30  #include "JSCInlines.h"
 31  #include "JSGenericTypedArrayViewInlines.h"
 32  #include "JSTypedArrays.h"
 33  #include "TypedArrayController.h"
 34  #include "TypedArrays.h"
 35  #include <wtf/Gigacage.h>
 36  
 37  namespace JSC {
 38  
 39  const ClassInfo JSArrayBufferView::s_info = {
 40      "ArrayBufferView", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArrayBufferView)
 41  };
 42  
 43  JSArrayBufferView::ConstructionContext::ConstructionContext(
 44      Structure* structure, uint32_t length, void* vector)
 45      : m_structure(structure)
 46      , m_vector(vector, length)
 47      , m_length(length)
 48      , m_mode(FastTypedArray)
 49      , m_butterfly(nullptr)
 50  {
 51      ASSERT(!Gigacage::isEnabled() || (Gigacage::contains(vector) && Gigacage::contains(static_cast<const uint8_t*>(vector) + length - 1)));
 52      ASSERT(vector == removeArrayPtrTag(vector));
 53      RELEASE_ASSERT(length <= fastSizeLimit);
 54  }
 55  
 56  JSArrayBufferView::ConstructionContext::ConstructionContext(
 57      VM& vm, Structure* structure, uint32_t length, uint32_t elementSize,
 58      InitializationMode mode)
 59      : m_structure(nullptr)
 60      , m_length(length)
 61      , m_butterfly(nullptr)
 62  {
 63      if (length <= fastSizeLimit) {
 64          // Attempt GC allocation.
 65          void* temp;
 66          size_t size = sizeOf(length, elementSize);
 67          temp = vm.primitiveGigacageAuxiliarySpace.allocateNonVirtual(vm, size, nullptr, AllocationFailureMode::ReturnNull);
 68          if (!temp)
 69              return;
 70  
 71          m_structure = structure;
 72          m_vector = VectorType(temp, length);
 73          m_mode = FastTypedArray;
 74  
 75          if (mode == ZeroFill) {
 76              uint64_t* asWords = static_cast<uint64_t*>(vector());
 77              for (unsigned i = size / sizeof(uint64_t); i--;)
 78                  asWords[i] = 0;
 79          }
 80          
 81          return;
 82      }
 83  
 84      // Don't allow a typed array to use more than 2GB.
 85      if (length > static_cast<unsigned>(INT_MAX) / elementSize)
 86          return;
 87      
 88      size_t size = static_cast<size_t>(length) * static_cast<size_t>(elementSize);
 89      m_vector = VectorType(Gigacage::tryMalloc(Gigacage::Primitive, size), length);
 90      if (!m_vector)
 91          return;
 92      if (mode == ZeroFill)
 93          memset(vector(), 0, size);
 94      
 95      vm.heap.reportExtraMemoryAllocated(static_cast<size_t>(length) * elementSize);
 96      
 97      m_structure = structure;
 98      m_mode = OversizeTypedArray;
 99  }
100  
101  JSArrayBufferView::ConstructionContext::ConstructionContext(
102      VM& vm, Structure* structure, RefPtr<ArrayBuffer>&& arrayBuffer,
103      unsigned byteOffset, unsigned length)
104      : m_structure(structure)
105      , m_length(length)
106      , m_mode(WastefulTypedArray)
107  {
108      ASSERT(arrayBuffer->data() == removeArrayPtrTag(arrayBuffer->data()));
109      m_vector = VectorType(static_cast<uint8_t*>(arrayBuffer->data()) + byteOffset, length);
110      IndexingHeader indexingHeader;
111      indexingHeader.setArrayBuffer(arrayBuffer.get());
112      m_butterfly = Butterfly::create(vm, nullptr, 0, 0, true, indexingHeader, 0);
113  }
114  
115  JSArrayBufferView::ConstructionContext::ConstructionContext(
116      Structure* structure, RefPtr<ArrayBuffer>&& arrayBuffer,
117      unsigned byteOffset, unsigned length, DataViewTag)
118      : m_structure(structure)
119      , m_length(length)
120      , m_mode(DataViewMode)
121      , m_butterfly(nullptr)
122  {
123      ASSERT(arrayBuffer->data() == removeArrayPtrTag(arrayBuffer->data()));
124      m_vector = VectorType(static_cast<uint8_t*>(arrayBuffer->data()) + byteOffset, length);
125  }
126  
127  JSArrayBufferView::JSArrayBufferView(VM& vm, ConstructionContext& context)
128      : Base(vm, context.structure(), nullptr)
129      , m_length(context.length())
130      , m_mode(context.mode())
131  {
132      setButterfly(vm, context.butterfly());
133      ASSERT(context.vector() == removeArrayPtrTag(context.vector()));
134      m_vector.setWithoutBarrier(context.vector(), m_length);
135  }
136  
137  void JSArrayBufferView::finishCreation(VM& vm)
138  {
139      Base::finishCreation(vm);
140      ASSERT(jsDynamicCast<JSArrayBufferView*>(vm, this));
141      switch (m_mode) {
142      case FastTypedArray:
143          return;
144      case OversizeTypedArray:
145          vm.heap.addFinalizer(this, finalize);
146          return;
147      case WastefulTypedArray:
148          vm.heap.addReference(this, butterfly()->indexingHeader()->arrayBuffer());
149          return;
150      case DataViewMode:
151          ASSERT(!butterfly());
152          vm.heap.addReference(this, jsCast<JSDataView*>(this)->possiblySharedBuffer());
153          return;
154      }
155      RELEASE_ASSERT_NOT_REACHED();
156  }
157  
158  void JSArrayBufferView::visitChildren(JSCell* cell, SlotVisitor& visitor)
159  {
160      JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(cell);
161      ASSERT_GC_OBJECT_INHERITS(thisObject, info());
162      Base::visitChildren(cell, visitor);
163  
164      if (thisObject->hasArrayBuffer()) {
165          WTF::loadLoadFence();
166          ArrayBuffer* buffer = thisObject->possiblySharedBuffer();
167          RELEASE_ASSERT(buffer);
168          visitor.addOpaqueRoot(buffer);
169      }
170  }
171  
172  bool JSArrayBufferView::put(
173      JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, JSValue value,
174      PutPropertySlot& slot)
175  {
176      JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(cell);
177  
178      if (UNLIKELY(isThisValueAltered(slot, thisObject)))
179          return ordinarySetSlow(globalObject, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
180      
181      return Base::put(thisObject, globalObject, propertyName, value, slot);
182  }
183  
184  ArrayBuffer* JSArrayBufferView::unsharedBuffer()
185  {
186      ArrayBuffer* result = possiblySharedBuffer();
187      RELEASE_ASSERT(!result || !result->isShared());
188      return result;
189  }
190      
191  void JSArrayBufferView::finalize(JSCell* cell)
192  {
193      JSArrayBufferView* thisObject = static_cast<JSArrayBufferView*>(cell);
194  
195      // This JSArrayBufferView could be an OversizeTypedArray that was converted
196      // to a WastefulTypedArray via slowDownAndWasteMemory(). Hence, it is possible
197      // to get to this finalizer and found the mode to be WastefulTypedArray.
198      ASSERT(thisObject->m_mode == OversizeTypedArray || thisObject->m_mode == WastefulTypedArray);
199      if (thisObject->m_mode == OversizeTypedArray)
200          Gigacage::free(Gigacage::Primitive, thisObject->vector());
201  }
202  
203  JSArrayBuffer* JSArrayBufferView::unsharedJSBuffer(JSGlobalObject* globalObject)
204  {
205      VM& vm = globalObject->vm();
206      auto scope = DECLARE_THROW_SCOPE(vm);
207      if (ArrayBuffer* buffer = unsharedBuffer())
208          return vm.m_typedArrayController->toJS(globalObject, this->globalObject(vm), buffer);
209      scope.throwException(globalObject, createOutOfMemoryError(globalObject));
210      return nullptr;
211  }
212  
213  JSArrayBuffer* JSArrayBufferView::possiblySharedJSBuffer(JSGlobalObject* globalObject)
214  {
215      VM& vm = globalObject->vm();
216      auto scope = DECLARE_THROW_SCOPE(vm);
217      if (ArrayBuffer* buffer = possiblySharedBuffer())
218          return vm.m_typedArrayController->toJS(globalObject, this->globalObject(vm), buffer);
219      scope.throwException(globalObject, createOutOfMemoryError(globalObject));
220      return nullptr;
221  }
222  
223  void JSArrayBufferView::detach()
224  {
225      auto locker = holdLock(cellLock());
226      RELEASE_ASSERT(hasArrayBuffer());
227      RELEASE_ASSERT(!isShared());
228      m_length = 0;
229      m_vector.clear();
230  }
231  
232  static const constexpr size_t ElementSizeData[] = {
233  #define FACTORY(type) sizeof(typename type ## Adaptor::Type),
234      FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
235  #undef FACTORY
236      1, // DataViewType
237  };
238  
239  #define FACTORY(type) static_assert(std::is_final<JS ## type ## Array>::value, "");
240  FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
241  #undef FACTORY
242  
243  static inline size_t elementSize(JSType type)
244  {
245      ASSERT(type >= Int8ArrayType && type <= DataViewType);
246      static_assert(Float64ArrayType + 1 == DataViewType);
247      return ElementSizeData[type - Int8ArrayType];
248  }
249  
250  unsigned JSArrayBufferView::byteLength() const
251  {
252      return length() * elementSize(type());
253  }
254  
255  ArrayBuffer* JSArrayBufferView::slowDownAndWasteMemory()
256  {
257      ASSERT(m_mode == FastTypedArray || m_mode == OversizeTypedArray);
258  
259      // We play this game because we want this to be callable even from places that
260      // don't have access to CallFrame* or the VM, and we only allocate so little
261      // memory here that it's not necessary to trigger a GC - just accounting what
262      // we have done is good enough. The sort of bizarre exception to the "allocating
263      // little memory" is when we transfer a backing buffer into the C heap; this
264      // will temporarily get counted towards heap footprint (incorrectly, in the case
265      // of adopting an oversize typed array) but we don't GC here anyway. That's
266      // almost certainly fine. The worst case is if you created a ton of fast typed
267      // arrays, and did nothing but caused all of them to slow down and waste memory.
268      // In that case, your memory footprint will double before the GC realizes what's
269      // up. But if you do *anything* to trigger a GC watermark check, it will know
270      // that you *had* done those allocations and it will GC appropriately.
271      Heap* heap = Heap::heap(this);
272      VM& vm = heap->vm();
273      DeferGCForAWhile deferGC(*heap);
274  
275      RELEASE_ASSERT(!hasIndexingHeader(vm));
276      Structure* structure = this->structure(vm);
277  
278      RefPtr<ArrayBuffer> buffer;
279      unsigned byteLength = this->byteLength();
280  
281      switch (m_mode) {
282      case FastTypedArray: {
283          buffer = ArrayBuffer::tryCreate(vector(), byteLength);
284          if (!buffer)
285              return nullptr;
286          break;
287      }
288  
289      case OversizeTypedArray: {
290          // FIXME: consider doing something like "subtracting" from extra memory
291          // cost, since right now this case will cause the GC to think that we reallocated
292          // the whole buffer.
293          buffer = ArrayBuffer::createAdopted(vector(), byteLength);
294          break;
295      }
296  
297      default:
298          RELEASE_ASSERT_NOT_REACHED();
299          break;
300      }
301  
302      RELEASE_ASSERT(buffer);
303      // Don't create bufferfly until we know we have an ArrayBuffer.
304      setButterfly(vm, Butterfly::createOrGrowArrayRight(
305          butterfly(), vm, this, structure,
306          structure->outOfLineCapacity(), false, 0, 0));
307  
308      {
309          auto locker = holdLock(cellLock());
310          butterfly()->indexingHeader()->setArrayBuffer(buffer.get());
311          m_vector.setWithoutBarrier(buffer->data(), m_length);
312          WTF::storeStoreFence();
313          m_mode = WastefulTypedArray;
314      }
315      heap->addReference(this, buffer.get());
316  
317      return buffer.get();
318  }
319  
320  // Allocates the full-on native buffer and moves data into the C heap if
321  // necessary. Note that this never allocates in the GC heap.
322  RefPtr<ArrayBufferView> JSArrayBufferView::possiblySharedImpl()
323  {
324      ArrayBuffer* buffer = possiblySharedBuffer();
325      if (!buffer)
326          return nullptr;
327      unsigned byteOffset = this->byteOffset();
328      unsigned length = this->length();
329      switch (type()) {
330  #define FACTORY(type) \
331      case type ## ArrayType: \
332          return type ## Array::tryCreate(buffer, byteOffset, length);
333      FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY)
334  #undef FACTORY
335      case DataViewType:
336          return DataView::create(buffer, byteOffset, length);
337      default:
338          RELEASE_ASSERT_NOT_REACHED();
339          return nullptr;
340      }
341  }
342  
343  } // namespace JSC
344  
345  namespace WTF {
346  
347  using namespace JSC;
348  
349  void printInternal(PrintStream& out, TypedArrayMode mode)
350  {
351      switch (mode) {
352      case FastTypedArray:
353          out.print("FastTypedArray");
354          return;
355      case OversizeTypedArray:
356          out.print("OversizeTypedArray");
357          return;
358      case WastefulTypedArray:
359          out.print("WastefulTypedArray");
360          return;
361      case DataViewMode:
362          out.print("DataViewMode");
363          return;
364      }
365      RELEASE_ASSERT_NOT_REACHED();
366  }
367  
368  } // namespace WTF
369