| 1 | /* |
| 2 | * Copyright (C) 2013-2019 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 "ArrayBufferView.h" |
| 29 | #include "DeferGC.h" |
| 30 | #include "Error.h" |
| 31 | #include "ExceptionHelpers.h" |
| 32 | #include "JSArrayBuffer.h" |
| 33 | #include "JSGenericTypedArrayView.h" |
| 34 | #include "TypeError.h" |
| 35 | #include "TypedArrays.h" |
| 36 | #include <wtf/text/StringConcatenateNumbers.h> |
| 37 | |
| 38 | namespace JSC { |
| 39 | |
| 40 | template<typename Adaptor> |
| 41 | JSGenericTypedArrayView<Adaptor>::JSGenericTypedArrayView( |
| 42 | VM& vm, ConstructionContext& context) |
| 43 | : Base(vm, context) |
| 44 | { |
| 45 | } |
| 46 | |
| 47 | template<typename Adaptor> |
| 48 | JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create( |
| 49 | ExecState* exec, Structure* structure, unsigned length) |
| 50 | { |
| 51 | VM& vm = exec->vm(); |
| 52 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 53 | ConstructionContext context(vm, structure, length, sizeof(typename Adaptor::Type)); |
| 54 | if (!context) { |
| 55 | throwOutOfMemoryError(exec, scope); |
| 56 | return nullptr; |
| 57 | } |
| 58 | JSGenericTypedArrayView* result = |
| 59 | new (NotNull, allocateCell<JSGenericTypedArrayView>(vm.heap)) |
| 60 | JSGenericTypedArrayView(vm, context); |
| 61 | result->finishCreation(vm); |
| 62 | return result; |
| 63 | } |
| 64 | |
| 65 | template<typename Adaptor> |
| 66 | JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::createWithFastVector( |
| 67 | ExecState* exec, Structure* structure, unsigned length, void* vector) |
| 68 | { |
| 69 | VM& vm = exec->vm(); |
| 70 | ConstructionContext context(structure, length, vector); |
| 71 | RELEASE_ASSERT(context); |
| 72 | JSGenericTypedArrayView* result = |
| 73 | new (NotNull, allocateCell<JSGenericTypedArrayView>(vm.heap)) |
| 74 | JSGenericTypedArrayView(vm, context); |
| 75 | result->finishCreation(vm); |
| 76 | return result; |
| 77 | } |
| 78 | |
| 79 | template<typename Adaptor> |
| 80 | JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::createUninitialized(ExecState* exec, Structure* structure, unsigned length) |
| 81 | { |
| 82 | VM& vm = exec->vm(); |
| 83 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 84 | ConstructionContext context( |
| 85 | vm, structure, length, sizeof(typename Adaptor::Type), |
| 86 | ConstructionContext::DontInitialize); |
| 87 | if (!context) { |
| 88 | throwOutOfMemoryError(exec, scope); |
| 89 | return nullptr; |
| 90 | } |
| 91 | JSGenericTypedArrayView* result = |
| 92 | new (NotNull, allocateCell<JSGenericTypedArrayView>(vm.heap)) |
| 93 | JSGenericTypedArrayView(vm, context); |
| 94 | result->finishCreation(vm); |
| 95 | return result; |
| 96 | } |
| 97 | |
| 98 | template<typename Adaptor> |
| 99 | JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create( |
| 100 | ExecState* exec, Structure* structure, RefPtr<ArrayBuffer>&& buffer, |
| 101 | unsigned byteOffset, unsigned length) |
| 102 | { |
| 103 | VM& vm = exec->vm(); |
| 104 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 105 | size_t size = sizeof(typename Adaptor::Type); |
| 106 | ASSERT(buffer); |
| 107 | if (!ArrayBufferView::verifySubRangeLength(*buffer, byteOffset, length, size)) { |
| 108 | throwException(exec, scope, createRangeError(exec, "Length out of range of buffer" )); |
| 109 | return nullptr; |
| 110 | } |
| 111 | if (!ArrayBufferView::verifyByteOffsetAlignment(byteOffset, size)) { |
| 112 | throwException(exec, scope, createRangeError(exec, "Byte offset is not aligned" )); |
| 113 | return nullptr; |
| 114 | } |
| 115 | ConstructionContext context(vm, structure, WTFMove(buffer), byteOffset, length); |
| 116 | ASSERT(context); |
| 117 | JSGenericTypedArrayView* result = |
| 118 | new (NotNull, allocateCell<JSGenericTypedArrayView>(vm.heap)) |
| 119 | JSGenericTypedArrayView(vm, context); |
| 120 | result->finishCreation(vm); |
| 121 | return result; |
| 122 | } |
| 123 | |
| 124 | template<typename Adaptor> |
| 125 | JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create( |
| 126 | VM& vm, Structure* structure, RefPtr<typename Adaptor::ViewType>&& impl) |
| 127 | { |
| 128 | ConstructionContext context(vm, structure, impl->possiblySharedBuffer(), impl->byteOffset(), impl->length()); |
| 129 | ASSERT(context); |
| 130 | JSGenericTypedArrayView* result = |
| 131 | new (NotNull, allocateCell<JSGenericTypedArrayView>(vm.heap)) |
| 132 | JSGenericTypedArrayView(vm, context); |
| 133 | result->finishCreation(vm); |
| 134 | return result; |
| 135 | } |
| 136 | |
| 137 | template<typename Adaptor> |
| 138 | JSGenericTypedArrayView<Adaptor>* JSGenericTypedArrayView<Adaptor>::create( |
| 139 | Structure* structure, JSGlobalObject* globalObject, |
| 140 | RefPtr<typename Adaptor::ViewType>&& impl) |
| 141 | { |
| 142 | return create(globalObject->vm(), structure, WTFMove(impl)); |
| 143 | } |
| 144 | |
| 145 | template<typename Adaptor> |
| 146 | bool JSGenericTypedArrayView<Adaptor>::validateRange( |
| 147 | ExecState* exec, unsigned offset, unsigned length) |
| 148 | { |
| 149 | VM& vm = exec->vm(); |
| 150 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 151 | if (canAccessRangeQuickly(offset, length)) |
| 152 | return true; |
| 153 | |
| 154 | throwException(exec, scope, createRangeError(exec, "Range consisting of offset and length are out of bounds" )); |
| 155 | return false; |
| 156 | } |
| 157 | |
| 158 | template<typename Adaptor> |
| 159 | template<typename OtherAdaptor> |
| 160 | bool JSGenericTypedArrayView<Adaptor>::setWithSpecificType( |
| 161 | ExecState* exec, unsigned offset, JSGenericTypedArrayView<OtherAdaptor>* other, |
| 162 | unsigned otherOffset, unsigned length, CopyType type) |
| 163 | { |
| 164 | // Handle the hilarious case: the act of getting the length could have resulted |
| 165 | // in neutering. Well, no. That'll never happen because there cannot be |
| 166 | // side-effects on getting the length of a typed array. But predicting where there |
| 167 | // are, or aren't, side-effects is a fool's game so we resort to this cheap |
| 168 | // check. Worst case, if we're wrong, people start seeing less things get copied |
| 169 | // but we won't have a security vulnerability. |
| 170 | length = std::min(length, other->length()); |
| 171 | |
| 172 | RELEASE_ASSERT(other->canAccessRangeQuickly(otherOffset, length)); |
| 173 | if (!validateRange(exec, offset, length)) |
| 174 | return false; |
| 175 | |
| 176 | // This method doesn't support copying between the same array. Note that |
| 177 | // set() will only call this if the types differ, which implicitly guarantees |
| 178 | // that we can't be the same array. This is relevant because the way we detect |
| 179 | // non-overlapping is by checking if either (a) either array doesn't have a |
| 180 | // backing buffer or (b) the backing buffers are different, but that doesn't |
| 181 | // catch the case where it's the *same* array - fortunately though, this code |
| 182 | // path never needs to worry about that case. |
| 183 | ASSERT(static_cast<JSCell*>(this) != static_cast<JSCell*>(other)); |
| 184 | |
| 185 | // 1) If the two arrays are non-overlapping, we can copy in any order we like |
| 186 | // and we don't need an intermediate buffer. Arrays are definitely |
| 187 | // non-overlapping if either one of them has no backing buffer (that means |
| 188 | // that it *owns* its philosophical backing buffer) or if they have |
| 189 | // different backing buffers. |
| 190 | // 2) If the two arrays overlap but have the same element size, we can do a |
| 191 | // memmove-like copy where we flip-flop direction based on which vector |
| 192 | // starts before the other: |
| 193 | // A) If the destination vector is before the source vector, then a forward |
| 194 | // copy is in order. |
| 195 | // B) If the destination vector is after the source vector, then a backward |
| 196 | // copy is in order. |
| 197 | // 3) If we have different element sizes and there is a chance of overlap then |
| 198 | // we need an intermediate vector. |
| 199 | |
| 200 | // NB. Comparisons involving elementSize will be constant-folded by template |
| 201 | // specialization. |
| 202 | |
| 203 | unsigned otherElementSize = sizeof(typename OtherAdaptor::Type); |
| 204 | |
| 205 | // Handle cases (1) and (2A). |
| 206 | if (!hasArrayBuffer() || !other->hasArrayBuffer() |
| 207 | || existingBuffer() != other->existingBuffer() |
| 208 | || (elementSize == otherElementSize && vector() <= other->vector()) |
| 209 | || type == CopyType::LeftToRight) { |
| 210 | for (unsigned i = 0; i < length; ++i) { |
| 211 | setIndexQuicklyToNativeValue( |
| 212 | offset + i, OtherAdaptor::template convertTo<Adaptor>( |
| 213 | other->getIndexQuicklyAsNativeValue(i + otherOffset))); |
| 214 | } |
| 215 | return true; |
| 216 | } |
| 217 | |
| 218 | // Now we either have (2B) or (3) - so first we try to cover (2B). |
| 219 | if (elementSize == otherElementSize) { |
| 220 | for (unsigned i = length; i--;) { |
| 221 | setIndexQuicklyToNativeValue( |
| 222 | offset + i, OtherAdaptor::template convertTo<Adaptor>( |
| 223 | other->getIndexQuicklyAsNativeValue(i + otherOffset))); |
| 224 | } |
| 225 | return true; |
| 226 | } |
| 227 | |
| 228 | // Fail: we need an intermediate transfer buffer (i.e. case (3)). |
| 229 | Vector<typename Adaptor::Type, 32> transferBuffer(length); |
| 230 | for (unsigned i = length; i--;) { |
| 231 | transferBuffer[i] = OtherAdaptor::template convertTo<Adaptor>( |
| 232 | other->getIndexQuicklyAsNativeValue(i + otherOffset)); |
| 233 | } |
| 234 | for (unsigned i = length; i--;) |
| 235 | setIndexQuicklyToNativeValue(offset + i, transferBuffer[i]); |
| 236 | |
| 237 | return true; |
| 238 | } |
| 239 | |
| 240 | template<typename Adaptor> |
| 241 | bool JSGenericTypedArrayView<Adaptor>::set( |
| 242 | ExecState* exec, unsigned offset, JSObject* object, unsigned objectOffset, unsigned length, CopyType type) |
| 243 | { |
| 244 | VM& vm = exec->vm(); |
| 245 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 246 | |
| 247 | const ClassInfo* ci = object->classInfo(vm); |
| 248 | if (ci->typedArrayStorageType == Adaptor::typeValue) { |
| 249 | // The super fast case: we can just memcpy since we're the same type. |
| 250 | JSGenericTypedArrayView* other = jsCast<JSGenericTypedArrayView*>(object); |
| 251 | length = std::min(length, other->length()); |
| 252 | |
| 253 | RELEASE_ASSERT(other->canAccessRangeQuickly(objectOffset, length)); |
| 254 | bool success = validateRange(exec, offset, length); |
| 255 | EXCEPTION_ASSERT(!scope.exception() == success); |
| 256 | if (!success) |
| 257 | return false; |
| 258 | |
| 259 | memmove(typedVector() + offset, other->typedVector() + objectOffset, length * elementSize); |
| 260 | return true; |
| 261 | } |
| 262 | |
| 263 | switch (ci->typedArrayStorageType) { |
| 264 | case TypeInt8: |
| 265 | RELEASE_AND_RETURN(scope, setWithSpecificType<Int8Adaptor>( |
| 266 | exec, offset, jsCast<JSInt8Array*>(object), objectOffset, length, type)); |
| 267 | case TypeInt16: |
| 268 | RELEASE_AND_RETURN(scope, setWithSpecificType<Int16Adaptor>( |
| 269 | exec, offset, jsCast<JSInt16Array*>(object), objectOffset, length, type)); |
| 270 | case TypeInt32: |
| 271 | RELEASE_AND_RETURN(scope, setWithSpecificType<Int32Adaptor>( |
| 272 | exec, offset, jsCast<JSInt32Array*>(object), objectOffset, length, type)); |
| 273 | case TypeUint8: |
| 274 | RELEASE_AND_RETURN(scope, setWithSpecificType<Uint8Adaptor>( |
| 275 | exec, offset, jsCast<JSUint8Array*>(object), objectOffset, length, type)); |
| 276 | case TypeUint8Clamped: |
| 277 | RELEASE_AND_RETURN(scope, setWithSpecificType<Uint8ClampedAdaptor>( |
| 278 | exec, offset, jsCast<JSUint8ClampedArray*>(object), objectOffset, length, type)); |
| 279 | case TypeUint16: |
| 280 | RELEASE_AND_RETURN(scope, setWithSpecificType<Uint16Adaptor>( |
| 281 | exec, offset, jsCast<JSUint16Array*>(object), objectOffset, length, type)); |
| 282 | case TypeUint32: |
| 283 | RELEASE_AND_RETURN(scope, setWithSpecificType<Uint32Adaptor>( |
| 284 | exec, offset, jsCast<JSUint32Array*>(object), objectOffset, length, type)); |
| 285 | case TypeFloat32: |
| 286 | RELEASE_AND_RETURN(scope, setWithSpecificType<Float32Adaptor>( |
| 287 | exec, offset, jsCast<JSFloat32Array*>(object), objectOffset, length, type)); |
| 288 | case TypeFloat64: |
| 289 | RELEASE_AND_RETURN(scope, setWithSpecificType<Float64Adaptor>( |
| 290 | exec, offset, jsCast<JSFloat64Array*>(object), objectOffset, length, type)); |
| 291 | case NotTypedArray: |
| 292 | case TypeDataView: { |
| 293 | bool success = validateRange(exec, offset, length); |
| 294 | EXCEPTION_ASSERT(!scope.exception() == success); |
| 295 | if (!success) |
| 296 | return false; |
| 297 | |
| 298 | // We could optimize this case. But right now, we don't. |
| 299 | for (unsigned i = 0; i < length; ++i) { |
| 300 | JSValue value = object->get(exec, i + objectOffset); |
| 301 | RETURN_IF_EXCEPTION(scope, false); |
| 302 | bool success = setIndex(exec, offset + i, value); |
| 303 | EXCEPTION_ASSERT(!scope.exception() || !success); |
| 304 | if (!success) |
| 305 | return false; |
| 306 | } |
| 307 | return true; |
| 308 | } } |
| 309 | |
| 310 | RELEASE_ASSERT_NOT_REACHED(); |
| 311 | return false; |
| 312 | } |
| 313 | |
| 314 | template<typename Adaptor> |
| 315 | RefPtr<typename Adaptor::ViewType> JSGenericTypedArrayView<Adaptor>::possiblySharedTypedImpl() |
| 316 | { |
| 317 | return Adaptor::ViewType::tryCreate(possiblySharedBuffer(), byteOffset(), length()); |
| 318 | } |
| 319 | |
| 320 | template<typename Adaptor> |
| 321 | RefPtr<typename Adaptor::ViewType> JSGenericTypedArrayView<Adaptor>::unsharedTypedImpl() |
| 322 | { |
| 323 | return Adaptor::ViewType::tryCreate(unsharedBuffer(), byteOffset(), length()); |
| 324 | } |
| 325 | |
| 326 | template<typename Adaptor> |
| 327 | ArrayBuffer* JSGenericTypedArrayView<Adaptor>::existingBuffer() |
| 328 | { |
| 329 | return existingBufferInButterfly(); |
| 330 | } |
| 331 | |
| 332 | template<typename Adaptor> |
| 333 | EncodedJSValue JSGenericTypedArrayView<Adaptor>::throwNeuteredTypedArrayTypeError(ExecState* exec, EncodedJSValue object, PropertyName) |
| 334 | { |
| 335 | VM& vm = exec->vm(); |
| 336 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 337 | ASSERT_UNUSED(object, jsCast<JSGenericTypedArrayView*>(JSValue::decode(object))->isNeutered()); |
| 338 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 339 | } |
| 340 | |
| 341 | template<typename Adaptor> |
| 342 | bool JSGenericTypedArrayView<Adaptor>::getOwnPropertySlot( |
| 343 | JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) |
| 344 | { |
| 345 | JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); |
| 346 | |
| 347 | if (Optional<uint32_t> index = parseIndex(propertyName)) { |
| 348 | if (thisObject->isNeutered()) { |
| 349 | slot.setCustom(thisObject, static_cast<unsigned>(PropertyAttribute::None), throwNeuteredTypedArrayTypeError); |
| 350 | return true; |
| 351 | } |
| 352 | |
| 353 | if (thisObject->canGetIndexQuickly(index.value())) { |
| 354 | slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::DontDelete), thisObject->getIndexQuickly(index.value())); |
| 355 | return true; |
| 356 | } |
| 357 | |
| 358 | return false; |
| 359 | } |
| 360 | |
| 361 | if (isCanonicalNumericIndexString(propertyName)) { |
| 362 | if (thisObject->isNeutered()) { |
| 363 | slot.setCustom(thisObject, static_cast<unsigned>(PropertyAttribute::None), throwNeuteredTypedArrayTypeError); |
| 364 | return true; |
| 365 | } |
| 366 | |
| 367 | return false; |
| 368 | } |
| 369 | |
| 370 | return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); |
| 371 | } |
| 372 | |
| 373 | template<typename Adaptor> |
| 374 | bool JSGenericTypedArrayView<Adaptor>::put( |
| 375 | JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, |
| 376 | PutPropertySlot& slot) |
| 377 | { |
| 378 | VM& vm = exec->vm(); |
| 379 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 380 | |
| 381 | JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); |
| 382 | |
| 383 | // https://tc39.github.io/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver |
| 384 | // Ignore the receiver even if the receiver is altered to non base value. |
| 385 | // 9.4.5.5-2-b-i Return ? IntegerIndexedElementSet(O, numericIndex, V). |
| 386 | if (Optional<uint32_t> index = parseIndex(propertyName)) |
| 387 | RELEASE_AND_RETURN(scope, putByIndex(thisObject, exec, index.value(), value, slot.isStrictMode())); |
| 388 | |
| 389 | if (isCanonicalNumericIndexString(propertyName)) { |
| 390 | if (thisObject->isNeutered()) |
| 391 | throwTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 392 | return false; |
| 393 | } |
| 394 | |
| 395 | RELEASE_AND_RETURN(scope, Base::put(thisObject, exec, propertyName, value, slot)); |
| 396 | } |
| 397 | |
| 398 | template<typename Adaptor> |
| 399 | bool JSGenericTypedArrayView<Adaptor>::defineOwnProperty( |
| 400 | JSObject* object, ExecState* exec, PropertyName propertyName, |
| 401 | const PropertyDescriptor& descriptor, bool shouldThrow) |
| 402 | { |
| 403 | VM& vm = exec->vm(); |
| 404 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 405 | JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); |
| 406 | |
| 407 | if (Optional<uint32_t> index = parseIndex(propertyName)) { |
| 408 | auto throwTypeErrorIfNeeded = [&] (const char* errorMessage) -> bool { |
| 409 | if (shouldThrow) |
| 410 | throwTypeError(exec, scope, makeString(errorMessage, *index)); |
| 411 | return false; |
| 412 | }; |
| 413 | |
| 414 | if (index.value() >= thisObject->m_length) |
| 415 | return false; |
| 416 | |
| 417 | if (descriptor.isAccessorDescriptor()) |
| 418 | return throwTypeErrorIfNeeded("Attempting to store accessor property on a typed array at index: " ); |
| 419 | |
| 420 | if (descriptor.configurable()) |
| 421 | return throwTypeErrorIfNeeded("Attempting to configure non-configurable property on a typed array at index: " ); |
| 422 | |
| 423 | if (!descriptor.enumerable() || !descriptor.writable()) |
| 424 | return throwTypeErrorIfNeeded("Attempting to store non-enumerable or non-writable property on a typed array at index: " ); |
| 425 | |
| 426 | if (descriptor.value()) |
| 427 | RELEASE_AND_RETURN(scope, thisObject->putByIndex(thisObject, exec, index.value(), descriptor.value(), shouldThrow)); |
| 428 | |
| 429 | return true; |
| 430 | } |
| 431 | |
| 432 | if (isCanonicalNumericIndexString(propertyName)) |
| 433 | return false; |
| 434 | |
| 435 | RELEASE_AND_RETURN(scope, Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow)); |
| 436 | } |
| 437 | |
| 438 | template<typename Adaptor> |
| 439 | bool JSGenericTypedArrayView<Adaptor>::deleteProperty( |
| 440 | JSCell* cell, ExecState* exec, PropertyName propertyName) |
| 441 | { |
| 442 | VM& vm = exec->vm(); |
| 443 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 444 | JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); |
| 445 | |
| 446 | if (thisObject->isNeutered()) |
| 447 | return typeError(exec, scope, true, typedArrayBufferHasBeenDetachedErrorMessage); |
| 448 | |
| 449 | if (parseIndex(propertyName)) |
| 450 | return false; |
| 451 | |
| 452 | return Base::deleteProperty(thisObject, exec, propertyName); |
| 453 | } |
| 454 | |
| 455 | template<typename Adaptor> |
| 456 | bool JSGenericTypedArrayView<Adaptor>::getOwnPropertySlotByIndex( |
| 457 | JSObject* object, ExecState*, unsigned propertyName, PropertySlot& slot) |
| 458 | { |
| 459 | JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); |
| 460 | |
| 461 | if (thisObject->isNeutered()) { |
| 462 | slot.setCustom(thisObject, static_cast<unsigned>(PropertyAttribute::None), throwNeuteredTypedArrayTypeError); |
| 463 | return true; |
| 464 | } |
| 465 | |
| 466 | if (!thisObject->canGetIndexQuickly(propertyName)) |
| 467 | return false; |
| 468 | |
| 469 | slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::DontDelete), thisObject->getIndexQuickly(propertyName)); |
| 470 | return true; |
| 471 | } |
| 472 | |
| 473 | template<typename Adaptor> |
| 474 | bool JSGenericTypedArrayView<Adaptor>::putByIndex( |
| 475 | JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool) |
| 476 | { |
| 477 | JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); |
| 478 | return thisObject->setIndex(exec, propertyName, value); |
| 479 | } |
| 480 | |
| 481 | template<typename Adaptor> |
| 482 | bool JSGenericTypedArrayView<Adaptor>::deletePropertyByIndex( |
| 483 | JSCell* cell, ExecState* exec, unsigned propertyName) |
| 484 | { |
| 485 | return cell->methodTable(exec->vm())->deleteProperty(cell, exec, Identifier::from(exec, propertyName)); |
| 486 | } |
| 487 | |
| 488 | template<typename Adaptor> |
| 489 | void JSGenericTypedArrayView<Adaptor>::getOwnPropertyNames( |
| 490 | JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode) |
| 491 | { |
| 492 | JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object); |
| 493 | |
| 494 | if (array.includeStringProperties()) { |
| 495 | for (unsigned i = 0; i < thisObject->m_length; ++i) |
| 496 | array.add(Identifier::from(exec, i)); |
| 497 | } |
| 498 | |
| 499 | return Base::getOwnPropertyNames(object, exec, array, mode); |
| 500 | } |
| 501 | |
| 502 | template<typename Adaptor> |
| 503 | size_t JSGenericTypedArrayView<Adaptor>::estimatedSize(JSCell* cell, VM& vm) |
| 504 | { |
| 505 | JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); |
| 506 | |
| 507 | if (thisObject->m_mode == OversizeTypedArray) |
| 508 | return Base::estimatedSize(thisObject, vm) + thisObject->byteSize(); |
| 509 | if (thisObject->m_mode == FastTypedArray && thisObject->hasVector()) |
| 510 | return Base::estimatedSize(thisObject, vm) + thisObject->byteSize(); |
| 511 | |
| 512 | return Base::estimatedSize(thisObject, vm); |
| 513 | } |
| 514 | |
| 515 | template<typename Adaptor> |
| 516 | void JSGenericTypedArrayView<Adaptor>::visitChildren(JSCell* cell, SlotVisitor& visitor) |
| 517 | { |
| 518 | JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell); |
| 519 | Base::visitChildren(thisObject, visitor); |
| 520 | |
| 521 | TypedArrayMode mode; |
| 522 | void* vector; |
| 523 | size_t byteSize; |
| 524 | |
| 525 | { |
| 526 | auto locker = holdLock(thisObject->cellLock()); |
| 527 | mode = thisObject->m_mode; |
| 528 | vector = thisObject->vector(); |
| 529 | byteSize = thisObject->byteSize(); |
| 530 | } |
| 531 | |
| 532 | switch (mode) { |
| 533 | case FastTypedArray: { |
| 534 | if (vector) |
| 535 | visitor.markAuxiliary(vector); |
| 536 | break; |
| 537 | } |
| 538 | |
| 539 | case OversizeTypedArray: { |
| 540 | visitor.reportExtraMemoryVisited(byteSize); |
| 541 | break; |
| 542 | } |
| 543 | |
| 544 | case WastefulTypedArray: |
| 545 | break; |
| 546 | |
| 547 | case DataViewMode: |
| 548 | RELEASE_ASSERT_NOT_REACHED(); |
| 549 | break; |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | } // namespace JSC |
| 554 | |