1/* 2 * Copyright (C) 2013 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#ifndef JSGenericTypedArrayViewInlines_h 27#define JSGenericTypedArrayViewInlines_h 28 29#include "ArrayBufferView.h" 30#include "DeferGC.h" 31#include "Error.h" 32#include "ExceptionHelpers.h" 33#include "JSGenericTypedArrayView.h" 34#include "Reject.h" 35#include "TypedArrays.h" 36 37namespace JSC { 38 39template<typename Adaptor> 40JSGenericTypedArrayView<Adaptor>::JSGenericTypedArrayView( 41 VM& vm, ConstructionContext& context) 42 : Base(vm, context) 43{ 44} 45 46template<typename Adaptor> 47JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create( 48 ExecState* exec, Structure* structure, unsigned length) 49{ 50 ConstructionContext context(exec->vm(), structure, length, sizeof(typename Adaptor::Type)); 51 if (!context) { 52 exec->vm().throwException(exec, createOutOfMemoryError(structure->globalObject())); 53 return 0; 54 } 55 JSGenericTypedArrayView* result = 56 new (NotNull, allocateCell<JSGenericTypedArrayView>(exec->vm().heap)) 57 JSGenericTypedArrayView(exec->vm(), context); 58 result->finishCreation(exec->vm()); 59 return result; 60} 61 62template<typename Adaptor> 63JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::createUninitialized( 64 ExecState* exec, Structure* structure, unsigned length) 65{ 66 ConstructionContext context( 67 exec->vm(), structure, length, sizeof(typename Adaptor::Type), 68 ConstructionContext::DontInitialize); 69 if (!context) { 70 exec->vm().throwException(exec, createOutOfMemoryError(structure->globalObject())); 71 return 0; 72 } 73 JSGenericTypedArrayView* result = 74 new (NotNull, allocateCell<JSGenericTypedArrayView>(exec->vm().heap)) 75 JSGenericTypedArrayView(exec->vm(), context); 76 result->finishCreation(exec->vm()); 77 return result; 78} 79 80template<typename Adaptor> 81JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create( 82 ExecState* exec, Structure* structure, PassRefPtr<ArrayBuffer> passedBuffer, 83 unsigned byteOffset, unsigned length) 84{ 85 RefPtr<ArrayBuffer> buffer = passedBuffer; 86 if (!ArrayBufferView::verifySubRange<typename Adaptor::Type>(buffer, byteOffset, length)) { 87 exec->vm().throwException( 88 exec, createRangeError(exec, "Byte offset and length out of range of buffer")); 89 return 0; 90 } 91 ConstructionContext context(exec->vm(), structure, buffer, byteOffset, length); 92 ASSERT(context); 93 JSGenericTypedArrayView* result = 94 new (NotNull, allocateCell<JSGenericTypedArrayView>(exec->vm().heap)) 95 JSGenericTypedArrayView(exec->vm(), context); 96 result->finishCreation(exec->vm()); 97 return result; 98} 99 100template<typename Adaptor> 101JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create( 102 VM& vm, Structure* structure, PassRefPtr<typename Adaptor::ViewType> impl) 103{ 104 RefPtr<ArrayBuffer> buffer = impl->buffer(); 105 ConstructionContext context(vm, structure, buffer, impl->byteOffset(), impl->length()); 106 ASSERT(context); 107 JSGenericTypedArrayView* result = 108 new (NotNull, allocateCell<JSGenericTypedArrayView>(vm.heap)) 109 JSGenericTypedArrayView(vm, context); 110 result->finishCreation(vm); 111 return result; 112} 113 114template<typename Adaptor> 115JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create( 116 Structure* structure, JSGlobalObject* globalObject, 117 PassRefPtr<typename Adaptor::ViewType> impl) 118{ 119 return create(globalObject->vm(), structure, impl); 120} 121 122template<typename Adaptor> 123bool JSGenericTypedArrayView<Adaptor>::validateRange( 124 ExecState* exec, unsigned offset, unsigned length) 125{ 126 if (canAccessRangeQuickly(offset, length)) 127 return true; 128 129 exec->vm().throwException(exec, createRangeError(exec, "Range consisting of offset and length are out of bounds")); 130 return false; 131} 132 133template<typename Adaptor> 134template<typename OtherAdaptor> 135bool JSGenericTypedArrayView<Adaptor>::setWithSpecificType( 136 ExecState* exec, JSGenericTypedArrayView<OtherAdaptor>* other, 137 unsigned offset, unsigned length) 138{ 139 // Handle the hilarious case: the act of getting the length could have resulted 140 // in neutering. Well, no. That'll never happen because there cannot be 141 // side-effects on getting the length of a typed array. But predicting where there 142 // are, or aren't, side-effects is a fool's game so we resort to this cheap 143 // check. Worst case, if we're wrong, people start seeing less things get copied 144 // but we won't have a security vulnerability. 145 length = std::min(length, other->length()); 146 147 if (!validateRange(exec, offset, length)) 148 return false; 149 150 if (other->length() != length) { 151 exec->vm().throwException(exec, createRangeError(exec, "Length of incoming array changed unexpectedly.")); 152 return false; 153 } 154 155 // This method doesn't support copying between the same array. Note that 156 // set() will only call this if the types differ, which implicitly guarantees 157 // that we can't be the same array. This is relevant because the way we detect 158 // non-overlapping is by checking if either (a) either array doesn't have a 159 // backing buffer or (b) the backing buffers are different, but that doesn't 160 // catch the case where it's the *same* array - fortunately though, this code 161 // path never needs to worry about that case. 162 ASSERT(static_cast<JSCell*>(this) != static_cast<JSCell*>(other)); 163 164 // 1) If the two arrays are non-overlapping, we can copy in any order we like 165 // and we don't need an intermediate buffer. Arrays are definitely 166 // non-overlapping if either one of them has no backing buffer (that means 167 // that it *owns* its philosophical backing buffer) or if they have 168 // different backing buffers. 169 // 2) If the two arrays overlap but have the same element size, we can do a 170 // memmove-like copy where we flip-flop direction based on which vector 171 // starts before the other: 172 // A) If the destination vector is before the source vector, then a forward 173 // copy is in order. 174 // B) If the destination vector is after the source vector, then a backward 175 // copy is in order. 176 // 3) If we have different element sizes and there is a chance of overlap then 177 // we need an intermediate vector. 178 179 // NB. Comparisons involving elementSize will be constant-folded by template 180 // specialization. 181 182 unsigned otherElementSize = sizeof(typename OtherAdaptor::Type); 183 184 // Handle cases (1) and (2B). 185 if (!hasArrayBuffer() || !other->hasArrayBuffer() 186 || existingBuffer() != other->existingBuffer() 187 || (elementSize == otherElementSize && vector() > other->vector())) { 188 for (unsigned i = length; i--;) { 189 setIndexQuicklyToNativeValue( 190 offset + i, OtherAdaptor::template convertTo<Adaptor>( 191 other->getIndexQuicklyAsNativeValue(i))); 192 } 193 return true; 194 } 195 196 // Now we either have (2A) or (3) - so first we try to cover (2A). 197 if (elementSize == otherElementSize) { 198 for (unsigned i = 0; i < length; ++i) { 199 setIndexQuicklyToNativeValue( 200 offset + i, OtherAdaptor::template convertTo<Adaptor>( 201 other->getIndexQuicklyAsNativeValue(i))); 202 } 203 return true; 204 } 205 206 // Fail: we need an intermediate transfer buffer (i.e. case (3)). 207 Vector<typename Adaptor::Type, 32> transferBuffer(length); 208 for (unsigned i = length; i--;) { 209 transferBuffer[i] = OtherAdaptor::template convertTo<Adaptor>( 210 other->getIndexQuicklyAsNativeValue(i)); 211 } 212 for (unsigned i = length; i--;) 213 setIndexQuicklyToNativeValue(offset + i, transferBuffer[i]); 214 215 return true; 216} 217 218template<typename Adaptor> 219bool JSGenericTypedArrayView<Adaptor>::set( 220 ExecState* exec, JSObject* object, unsigned offset, unsigned length) 221{ 222 const ClassInfo* ci = object->classInfo(); 223 if (ci->typedArrayStorageType == Adaptor::typeValue) { 224 // The super fast case: we can just memcpy since we're the same type. 225 JSGenericTypedArrayView* other = jsCast<JSGenericTypedArrayView*>(object); 226 length = std::min(length, other->length()); 227 228 if (!validateRange(exec, offset, length)) 229 return false; 230 231 memmove(typedVector() + offset, other->typedVector(), other->byteLength()); 232 return true; 233 } 234 235 switch (ci->typedArrayStorageType) { 236 case TypeInt8: 237 return setWithSpecificType<Int8Adaptor>( 238 exec, jsCast<JSInt8Array*>(object), offset, length); 239 case TypeInt16: 240 return setWithSpecificType<Int16Adaptor>( 241 exec, jsCast<JSInt16Array*>(object), offset, length); 242 case TypeInt32: 243 return setWithSpecificType<Int32Adaptor>( 244 exec, jsCast<JSInt32Array*>(object), offset, length); 245 case TypeUint8: 246 return setWithSpecificType<Uint8Adaptor>( 247 exec, jsCast<JSUint8Array*>(object), offset, length); 248 case TypeUint8Clamped: 249 return setWithSpecificType<Uint8ClampedAdaptor>( 250 exec, jsCast<JSUint8ClampedArray*>(object), offset, length); 251 case TypeUint16: 252 return setWithSpecificType<Uint16Adaptor>( 253 exec, jsCast<JSUint16Array*>(object), offset, length); 254 case TypeUint32: 255 return setWithSpecificType<Uint32Adaptor>( 256 exec, jsCast<JSUint32Array*>(object), offset, length); 257 case TypeFloat32: 258 return setWithSpecificType<Float32Adaptor>( 259 exec, jsCast<JSFloat32Array*>(object), offset, length); 260 case TypeFloat64: 261 return setWithSpecificType<Float64Adaptor>( 262 exec, jsCast<JSFloat64Array*>(object), offset, length); 263 case NotTypedArray: 264 case TypeDataView: { 265 if (!validateRange(exec, offset, length)) 266 return false; 267 // We could optimize this case. But right now, we don't. 268 for (unsigned i = 0; i < length; ++i) { 269 JSValue value = object->get(exec, i); 270 if (!setIndex(exec, offset + i, value)) 271 return false; 272 } 273 return true; 274 } } 275 276 RELEASE_ASSERT_NOT_REACHED(); 277 return false; 278} 279 280template<typename Adaptor> 281ArrayBuffer* JSGenericTypedArrayView<Adaptor>::existingBuffer() 282{ 283 return existingBufferInButterfly(); 284} 285 286template<typename Adaptor> 287bool JSGenericTypedArrayView<Adaptor>::getOwnPropertySlot( 288 JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) 289{ 290 JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); 291 if (propertyName == exec->propertyNames().length) { 292 slot.setValue(thisObject, DontDelete | ReadOnly, jsNumber(thisObject->length())); 293 return true; 294 } 295 296 if (propertyName == exec->propertyNames().byteLength) { 297 slot.setValue(thisObject, DontDelete | ReadOnly, jsNumber(thisObject->byteLength())); 298 return true; 299 } 300 301 unsigned index = propertyName.asIndex(); 302 if (index != PropertyName::NotAnIndex && thisObject->canGetIndexQuickly(index)) { 303 slot.setValue(thisObject, DontDelete | ReadOnly, thisObject->getIndexQuickly(index)); 304 return true; 305 } 306 307 return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); 308} 309 310template<typename Adaptor> 311void JSGenericTypedArrayView<Adaptor>::put( 312 JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, 313 PutPropertySlot& slot) 314{ 315 JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); 316 317 if (propertyName == exec->propertyNames().length) { 318 // Firefox appears to simply ignore attempts to store to the length property. 319 // Even in strict mode. I will do the same. 320 return; 321 } 322 323 unsigned index = propertyName.asIndex(); 324 if (index != PropertyName::NotAnIndex) { 325 putByIndex(thisObject, exec, index, value, slot.isStrictMode()); 326 return; 327 } 328 329 Base::put(thisObject, exec, propertyName, value, slot); 330} 331 332template<typename Adaptor> 333bool JSGenericTypedArrayView<Adaptor>::defineOwnProperty( 334 JSObject* object, ExecState* exec, PropertyName propertyName, 335 const PropertyDescriptor& descriptor, bool shouldThrow) 336{ 337 JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); 338 339 // This is matching Firefox behavior. In particular, it rejects all attempts to 340 // defineOwnProperty for indexed properties on typed arrays, even if they're out 341 // of bounds. 342 if (propertyName == exec->propertyNames().length 343 || propertyName.asIndex() != PropertyName::NotAnIndex) 344 return reject(exec, shouldThrow, "Attempting to write to a read-only typed array property."); 345 346 return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow); 347} 348 349template<typename Adaptor> 350bool JSGenericTypedArrayView<Adaptor>::deleteProperty( 351 JSCell* cell, ExecState* exec, PropertyName propertyName) 352{ 353 JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); 354 355 if (propertyName == exec->propertyNames().length 356 || propertyName.asIndex() != PropertyName::NotAnIndex) 357 return false; 358 359 return Base::deleteProperty(thisObject, exec, propertyName); 360} 361 362template<typename Adaptor> 363bool JSGenericTypedArrayView<Adaptor>::getOwnPropertySlotByIndex( 364 JSObject* object, ExecState* exec, unsigned propertyName, PropertySlot& slot) 365{ 366 JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); 367 368 if (propertyName > MAX_ARRAY_INDEX) { 369 return thisObject->methodTable()->getOwnPropertySlot( 370 thisObject, exec, Identifier::from(exec, propertyName), slot); 371 } 372 373 if (!thisObject->canGetIndexQuickly(propertyName)) 374 return false; 375 376 slot.setValue(thisObject, None, thisObject->getIndexQuickly(propertyName)); 377 return true; 378} 379 380template<typename Adaptor> 381void JSGenericTypedArrayView<Adaptor>::putByIndex( 382 JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) 383{ 384 JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); 385 386 if (propertyName > MAX_ARRAY_INDEX) { 387 PutPropertySlot slot(JSValue(thisObject), shouldThrow); 388 thisObject->methodTable()->put( 389 thisObject, exec, Identifier::from(exec, propertyName), value, slot); 390 return; 391 } 392 393 thisObject->setIndex(exec, propertyName, value); 394} 395 396template<typename Adaptor> 397bool JSGenericTypedArrayView<Adaptor>::deletePropertyByIndex( 398 JSCell* cell, ExecState* exec, unsigned propertyName) 399{ 400 if (propertyName > MAX_ARRAY_INDEX) { 401 return cell->methodTable()->deleteProperty( 402 cell, exec, Identifier::from(exec, propertyName)); 403 } 404 405 return false; 406} 407 408template<typename Adaptor> 409void JSGenericTypedArrayView<Adaptor>::getOwnNonIndexPropertyNames( 410 JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode) 411{ 412 JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); 413 414 if (mode == IncludeDontEnumProperties) 415 array.add(exec->propertyNames().length); 416 417 Base::getOwnNonIndexPropertyNames(thisObject, exec, array, mode); 418} 419 420template<typename Adaptor> 421void JSGenericTypedArrayView<Adaptor>::getOwnPropertyNames( 422 JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode) 423{ 424 JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); 425 426 for (unsigned i = 0; i < thisObject->m_length; ++i) 427 array.add(Identifier::from(exec, i)); 428 429 return Base::getOwnPropertyNames(object, exec, array, mode); 430} 431 432template<typename Adaptor> 433void JSGenericTypedArrayView<Adaptor>::visitChildren(JSCell* cell, SlotVisitor& visitor) 434{ 435 JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); 436 437 switch (thisObject->m_mode) { 438 case FastTypedArray: { 439 if (thisObject->m_vector) 440 visitor.copyLater(thisObject, TypedArrayVectorCopyToken, thisObject->m_vector, thisObject->byteSize()); 441 break; 442 } 443 444 case OversizeTypedArray: { 445 visitor.reportExtraMemoryUsage(thisObject, thisObject->byteSize()); 446 break; 447 } 448 449 case WastefulTypedArray: 450 break; 451 452 case DataViewMode: 453 RELEASE_ASSERT_NOT_REACHED(); 454 break; 455 } 456 457 Base::visitChildren(thisObject, visitor); 458} 459 460template<typename Adaptor> 461void JSGenericTypedArrayView<Adaptor>::copyBackingStore( 462 JSCell* cell, CopyVisitor& visitor, CopyToken token) 463{ 464 JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); 465 466 if (token == TypedArrayVectorCopyToken 467 && visitor.checkIfShouldCopy(thisObject->m_vector)) { 468 ASSERT(thisObject->m_vector); 469 void* oldVector = thisObject->m_vector; 470 void* newVector = visitor.allocateNewSpace(thisObject->byteSize()); 471 memcpy(newVector, oldVector, thisObject->byteSize()); 472 thisObject->m_vector = newVector; 473 visitor.didCopy(oldVector, thisObject->byteSize()); 474 } 475 476 Base::copyBackingStore(thisObject, visitor, token); 477} 478 479template<typename Adaptor> 480ArrayBuffer* JSGenericTypedArrayView<Adaptor>::slowDownAndWasteMemory(JSArrayBufferView* object) 481{ 482 JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); 483 484 // We play this game because we want this to be callable even from places that 485 // don't have access to ExecState* or the VM, and we only allocate so little 486 // memory here that it's not necessary to trigger a GC - just accounting what 487 // we have done is good enough. The sort of bizarro exception to the "allocating 488 // little memory" is when we transfer a backing buffer into the C heap; this 489 // will temporarily get counted towards heap footprint (incorrectly, in the case 490 // of adopting an oversize typed array) but we don't GC here anyway. That's 491 // almost certainly fine. The worst case is if you created a ton of fast typed 492 // arrays, and did nothing but caused all of them to slow down and waste memory. 493 // In that case, your memory footprint will double before the GC realizes what's 494 // up. But if you do *anything* to trigger a GC watermark check, it will know 495 // that you *had* done those allocations and it will GC appropriately. 496 Heap* heap = Heap::heap(thisObject); 497 DeferGCForAWhile deferGC(*heap); 498 499 ASSERT(!thisObject->hasIndexingHeader()); 500 501 size_t size = thisObject->byteSize(); 502 503 if (thisObject->m_mode == FastTypedArray 504 && !thisObject->butterfly() && size >= sizeof(IndexingHeader)) { 505 ASSERT(thisObject->m_vector); 506 // Reuse already allocated memory if at all possible. 507 thisObject->m_butterfly.setWithoutWriteBarrier( 508 static_cast<IndexingHeader*>(thisObject->m_vector)->butterfly()); 509 } else { 510 VM& vm = *heap->vm(); 511 thisObject->m_butterfly.set(vm, thisObject, Butterfly::createOrGrowArrayRight( 512 thisObject->butterfly(), vm, thisObject, thisObject->structure(), 513 thisObject->structure()->outOfLineCapacity(), false, 0, 0)); 514 } 515 516 RefPtr<ArrayBuffer> buffer; 517 518 switch (thisObject->m_mode) { 519 case FastTypedArray: 520 buffer = ArrayBuffer::create(thisObject->m_vector, thisObject->byteLength()); 521 break; 522 523 case OversizeTypedArray: 524 // FIXME: consider doing something like "subtracting" from extra memory 525 // cost, since right now this case will cause the GC to think that we reallocated 526 // the whole buffer. 527 buffer = ArrayBuffer::createAdopted(thisObject->m_vector, thisObject->byteLength()); 528 break; 529 530 default: 531 RELEASE_ASSERT_NOT_REACHED(); 532 break; 533 } 534 535 thisObject->butterfly()->indexingHeader()->setArrayBuffer(buffer.get()); 536 thisObject->m_vector = buffer->data(); 537 thisObject->m_mode = WastefulTypedArray; 538 heap->addReference(thisObject, buffer.get()); 539 540 return buffer.get(); 541} 542 543template<typename Adaptor> 544PassRefPtr<ArrayBufferView> 545JSGenericTypedArrayView<Adaptor>::getTypedArrayImpl(JSArrayBufferView* object) 546{ 547 JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); 548 return thisObject->typedImpl(); 549} 550 551} // namespace JSC 552 553#endif // JSGenericTypedArrayViewInlines_h 554 555