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