| 1 | /* |
| 2 | * Copyright (C) 2015-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 "Error.h" |
| 29 | #include "JSArrayBufferViewInlines.h" |
| 30 | #include "JSCBuiltins.h" |
| 31 | #include "JSCJSValueInlines.h" |
| 32 | #include "JSFunction.h" |
| 33 | #include "JSGenericTypedArrayViewInlines.h" |
| 34 | #include "JSGenericTypedArrayViewPrototypeInlines.h" |
| 35 | #include "JSStringJoiner.h" |
| 36 | #include "StructureInlines.h" |
| 37 | #include "TypedArrayAdaptors.h" |
| 38 | #include "TypedArrayController.h" |
| 39 | #include <wtf/StdLibExtras.h> |
| 40 | |
| 41 | namespace JSC { |
| 42 | |
| 43 | // This implements 22.2.4.7 TypedArraySpeciesCreate |
| 44 | // Note, that this function throws. |
| 45 | template<typename Functor> |
| 46 | inline JSArrayBufferView* speciesConstruct(ExecState* exec, JSObject* exemplar, MarkedArgumentBuffer& args, const Functor& defaultConstructor) |
| 47 | { |
| 48 | VM& vm = exec->vm(); |
| 49 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 50 | |
| 51 | JSValue constructor = exemplar->get(exec, vm.propertyNames->constructor); |
| 52 | RETURN_IF_EXCEPTION(scope, nullptr); |
| 53 | |
| 54 | if (constructor.isUndefined()) |
| 55 | RELEASE_AND_RETURN(scope, defaultConstructor()); |
| 56 | |
| 57 | if (!constructor.isObject()) { |
| 58 | throwTypeError(exec, scope, "constructor Property should not be null"_s ); |
| 59 | return nullptr; |
| 60 | } |
| 61 | |
| 62 | JSValue species = constructor.get(exec, vm.propertyNames->speciesSymbol); |
| 63 | RETURN_IF_EXCEPTION(scope, nullptr); |
| 64 | |
| 65 | if (species.isUndefinedOrNull()) |
| 66 | RELEASE_AND_RETURN(scope, defaultConstructor()); |
| 67 | |
| 68 | |
| 69 | JSValue result = construct(exec, species, args, "species is not a constructor" ); |
| 70 | RETURN_IF_EXCEPTION(scope, nullptr); |
| 71 | |
| 72 | if (JSArrayBufferView* view = jsDynamicCast<JSArrayBufferView*>(vm, result)) { |
| 73 | if (!view->isNeutered()) |
| 74 | return view; |
| 75 | |
| 76 | throwTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 77 | return nullptr; |
| 78 | } |
| 79 | |
| 80 | throwTypeError(exec, scope, "species constructor did not return a TypedArray View"_s ); |
| 81 | return nullptr; |
| 82 | } |
| 83 | |
| 84 | inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0) |
| 85 | { |
| 86 | JSValue value = exec->argument(argument); |
| 87 | if (value.isUndefined()) |
| 88 | return undefinedValue; |
| 89 | |
| 90 | double indexDouble = value.toInteger(exec); |
| 91 | if (indexDouble < 0) { |
| 92 | indexDouble += length; |
| 93 | return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble); |
| 94 | } |
| 95 | return indexDouble > length ? length : static_cast<unsigned>(indexDouble); |
| 96 | } |
| 97 | |
| 98 | template<typename ViewClass> |
| 99 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncSet(VM& vm, ExecState* exec) |
| 100 | { |
| 101 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 102 | |
| 103 | // 22.2.3.22 |
| 104 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 105 | |
| 106 | if (UNLIKELY(!exec->argumentCount())) |
| 107 | return throwVMTypeError(exec, scope, "Expected at least one argument"_s ); |
| 108 | |
| 109 | unsigned offset; |
| 110 | if (exec->argumentCount() >= 2) { |
| 111 | double offsetNumber = exec->uncheckedArgument(1).toInteger(exec); |
| 112 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 113 | if (UNLIKELY(offsetNumber < 0)) |
| 114 | return throwVMRangeError(exec, scope, "Offset should not be negative" ); |
| 115 | offset = static_cast<unsigned>(std::min(offsetNumber, static_cast<double>(std::numeric_limits<unsigned>::max()))); |
| 116 | } else |
| 117 | offset = 0; |
| 118 | |
| 119 | if (UNLIKELY(thisObject->isNeutered())) |
| 120 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 121 | |
| 122 | JSObject* sourceArray = jsDynamicCast<JSObject*>(vm, exec->uncheckedArgument(0)); |
| 123 | if (UNLIKELY(!sourceArray)) |
| 124 | return throwVMTypeError(exec, scope, "First argument should be an object"_s ); |
| 125 | |
| 126 | unsigned length; |
| 127 | if (isTypedView(sourceArray->classInfo(vm)->typedArrayStorageType)) { |
| 128 | JSArrayBufferView* sourceView = jsCast<JSArrayBufferView*>(sourceArray); |
| 129 | if (UNLIKELY(sourceView->isNeutered())) |
| 130 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 131 | |
| 132 | length = jsCast<JSArrayBufferView*>(sourceArray)->length(); |
| 133 | } else { |
| 134 | JSValue lengthValue = sourceArray->get(exec, vm.propertyNames->length); |
| 135 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 136 | length = lengthValue.toUInt32(exec); |
| 137 | } |
| 138 | |
| 139 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 140 | |
| 141 | scope.release(); |
| 142 | thisObject->set(exec, offset, sourceArray, 0, length, CopyType::Unobservable); |
| 143 | return JSValue::encode(jsUndefined()); |
| 144 | } |
| 145 | |
| 146 | template<typename ViewClass> |
| 147 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncCopyWithin(VM& vm, ExecState* exec) |
| 148 | { |
| 149 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 150 | |
| 151 | // 22.2.3.5 |
| 152 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 153 | if (thisObject->isNeutered()) |
| 154 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 155 | |
| 156 | long length = thisObject->length(); |
| 157 | long to = argumentClampedIndexFromStartOrEnd(exec, 0, length); |
| 158 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 159 | long from = argumentClampedIndexFromStartOrEnd(exec, 1, length); |
| 160 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 161 | long final = argumentClampedIndexFromStartOrEnd(exec, 2, length, length); |
| 162 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 163 | |
| 164 | if (final < from) |
| 165 | return JSValue::encode(exec->thisValue()); |
| 166 | |
| 167 | long count = std::min(length - std::max(to, from), final - from); |
| 168 | |
| 169 | if (thisObject->isNeutered()) |
| 170 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 171 | |
| 172 | typename ViewClass::ElementType* array = thisObject->typedVector(); |
| 173 | memmove(array + to, array + from, count * thisObject->elementSize); |
| 174 | |
| 175 | return JSValue::encode(exec->thisValue()); |
| 176 | } |
| 177 | |
| 178 | template<typename ViewClass> |
| 179 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncIncludes(VM& vm, ExecState* exec) |
| 180 | { |
| 181 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 182 | |
| 183 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 184 | if (thisObject->isNeutered()) |
| 185 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 186 | |
| 187 | unsigned length = thisObject->length(); |
| 188 | |
| 189 | if (!length) |
| 190 | return JSValue::encode(jsBoolean(false)); |
| 191 | |
| 192 | JSValue valueToFind = exec->argument(0); |
| 193 | |
| 194 | unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length); |
| 195 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 196 | |
| 197 | if (thisObject->isNeutered()) |
| 198 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 199 | |
| 200 | typename ViewClass::ElementType* array = thisObject->typedVector(); |
| 201 | auto targetOption = ViewClass::toAdaptorNativeFromValueWithoutCoercion(valueToFind); |
| 202 | if (!targetOption) |
| 203 | return JSValue::encode(jsBoolean(false)); |
| 204 | |
| 205 | scope.assertNoException(); |
| 206 | RELEASE_ASSERT(!thisObject->isNeutered()); |
| 207 | |
| 208 | if (std::isnan(static_cast<double>(*targetOption))) { |
| 209 | for (; index < length; ++index) { |
| 210 | if (std::isnan(static_cast<double>(array[index]))) |
| 211 | return JSValue::encode(jsBoolean(true)); |
| 212 | } |
| 213 | } else { |
| 214 | for (; index < length; ++index) { |
| 215 | if (array[index] == targetOption) |
| 216 | return JSValue::encode(jsBoolean(true)); |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | return JSValue::encode(jsBoolean(false)); |
| 221 | } |
| 222 | |
| 223 | template<typename ViewClass> |
| 224 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncIndexOf(VM& vm, ExecState* exec) |
| 225 | { |
| 226 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 227 | |
| 228 | // 22.2.3.13 |
| 229 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 230 | if (thisObject->isNeutered()) |
| 231 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 232 | |
| 233 | if (!exec->argumentCount()) |
| 234 | return throwVMTypeError(exec, scope, "Expected at least one argument"_s ); |
| 235 | |
| 236 | unsigned length = thisObject->length(); |
| 237 | |
| 238 | JSValue valueToFind = exec->argument(0); |
| 239 | unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length); |
| 240 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 241 | |
| 242 | if (thisObject->isNeutered()) |
| 243 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 244 | |
| 245 | typename ViewClass::ElementType* array = thisObject->typedVector(); |
| 246 | auto targetOption = ViewClass::toAdaptorNativeFromValueWithoutCoercion(valueToFind); |
| 247 | if (!targetOption) |
| 248 | return JSValue::encode(jsNumber(-1)); |
| 249 | scope.assertNoException(); |
| 250 | RELEASE_ASSERT(!thisObject->isNeutered()); |
| 251 | |
| 252 | for (; index < length; ++index) { |
| 253 | if (array[index] == targetOption) |
| 254 | return JSValue::encode(jsNumber(index)); |
| 255 | } |
| 256 | |
| 257 | return JSValue::encode(jsNumber(-1)); |
| 258 | } |
| 259 | |
| 260 | template<typename ViewClass> |
| 261 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncJoin(VM& vm, ExecState* exec) |
| 262 | { |
| 263 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 264 | |
| 265 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 266 | if (thisObject->isNeutered()) |
| 267 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 268 | |
| 269 | // 22.2.3.14 |
| 270 | auto joinWithSeparator = [&] (StringView separator) -> EncodedJSValue { |
| 271 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 272 | unsigned length = thisObject->length(); |
| 273 | |
| 274 | JSStringJoiner joiner(*exec, separator, length); |
| 275 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 276 | for (unsigned i = 0; i < length; i++) { |
| 277 | joiner.append(*exec, thisObject->getIndexQuickly(i)); |
| 278 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 279 | } |
| 280 | RELEASE_AND_RETURN(scope, JSValue::encode(joiner.join(*exec))); |
| 281 | }; |
| 282 | |
| 283 | JSValue separatorValue = exec->argument(0); |
| 284 | if (separatorValue.isUndefined()) { |
| 285 | const LChar* comma = reinterpret_cast<const LChar*>("," ); |
| 286 | return joinWithSeparator({ comma, 1 }); |
| 287 | } |
| 288 | |
| 289 | JSString* separatorString = separatorValue.toString(exec); |
| 290 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 291 | |
| 292 | if (thisObject->isNeutered()) |
| 293 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 294 | auto viewWithString = separatorString->viewWithUnderlyingString(exec); |
| 295 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 296 | return joinWithSeparator(viewWithString.view); |
| 297 | } |
| 298 | |
| 299 | template<typename ViewClass> |
| 300 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncLastIndexOf(VM& vm, ExecState* exec) |
| 301 | { |
| 302 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 303 | |
| 304 | // 22.2.3.16 |
| 305 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 306 | if (thisObject->isNeutered()) |
| 307 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 308 | |
| 309 | if (!exec->argumentCount()) |
| 310 | return throwVMTypeError(exec, scope, "Expected at least one argument"_s ); |
| 311 | |
| 312 | unsigned length = thisObject->length(); |
| 313 | |
| 314 | JSValue valueToFind = exec->argument(0); |
| 315 | |
| 316 | int index = length - 1; |
| 317 | if (exec->argumentCount() >= 2) { |
| 318 | JSValue fromValue = exec->uncheckedArgument(1); |
| 319 | double fromDouble = fromValue.toInteger(exec); |
| 320 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 321 | if (fromDouble < 0) { |
| 322 | fromDouble += length; |
| 323 | if (fromDouble < 0) |
| 324 | return JSValue::encode(jsNumber(-1)); |
| 325 | } |
| 326 | if (fromDouble < length) |
| 327 | index = static_cast<unsigned>(fromDouble); |
| 328 | } |
| 329 | |
| 330 | if (thisObject->isNeutered()) |
| 331 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 332 | |
| 333 | auto targetOption = ViewClass::toAdaptorNativeFromValueWithoutCoercion(valueToFind); |
| 334 | if (!targetOption) |
| 335 | return JSValue::encode(jsNumber(-1)); |
| 336 | |
| 337 | typename ViewClass::ElementType* array = thisObject->typedVector(); |
| 338 | scope.assertNoException(); |
| 339 | RELEASE_ASSERT(!thisObject->isNeutered()); |
| 340 | |
| 341 | for (; index >= 0; --index) { |
| 342 | if (array[index] == targetOption) |
| 343 | return JSValue::encode(jsNumber(index)); |
| 344 | } |
| 345 | |
| 346 | return JSValue::encode(jsNumber(-1)); |
| 347 | } |
| 348 | |
| 349 | template<typename ViewClass> |
| 350 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoGetterFuncBuffer(VM&, ExecState* exec) |
| 351 | { |
| 352 | // 22.2.3.3 |
| 353 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 354 | |
| 355 | return JSValue::encode(thisObject->possiblySharedJSBuffer(exec)); |
| 356 | } |
| 357 | |
| 358 | template<typename ViewClass> |
| 359 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoGetterFuncLength(VM&, ExecState* exec) |
| 360 | { |
| 361 | // 22.2.3.17 |
| 362 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 363 | |
| 364 | return JSValue::encode(jsNumber(thisObject->length())); |
| 365 | } |
| 366 | |
| 367 | template<typename ViewClass> |
| 368 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoGetterFuncByteLength(VM&, ExecState* exec) |
| 369 | { |
| 370 | // 22.2.3.2 |
| 371 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 372 | |
| 373 | return JSValue::encode(jsNumber(thisObject->byteLength())); |
| 374 | } |
| 375 | |
| 376 | template<typename ViewClass> |
| 377 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoGetterFuncByteOffset(VM&, ExecState* exec) |
| 378 | { |
| 379 | // 22.2.3.3 |
| 380 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 381 | |
| 382 | return JSValue::encode(jsNumber(thisObject->byteOffset())); |
| 383 | } |
| 384 | |
| 385 | template<typename ViewClass> |
| 386 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncReverse(VM& vm, ExecState* exec) |
| 387 | { |
| 388 | // VM& vm = exec->vm(); |
| 389 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 390 | |
| 391 | // 22.2.3.21 |
| 392 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 393 | if (thisObject->isNeutered()) |
| 394 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 395 | |
| 396 | typename ViewClass::ElementType* array = thisObject->typedVector(); |
| 397 | std::reverse(array, array + thisObject->length()); |
| 398 | |
| 399 | return JSValue::encode(thisObject); |
| 400 | } |
| 401 | |
| 402 | template<typename ViewClass> |
| 403 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewPrivateFuncSort(VM& vm, ExecState* exec) |
| 404 | { |
| 405 | // VM& vm = exec->vm(); |
| 406 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 407 | |
| 408 | // 22.2.3.25 |
| 409 | ViewClass* thisObject = jsCast<ViewClass*>(exec->argument(0)); |
| 410 | if (thisObject->isNeutered()) |
| 411 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 412 | |
| 413 | thisObject->sort(); |
| 414 | |
| 415 | return JSValue::encode(thisObject); |
| 416 | } |
| 417 | |
| 418 | template<typename ViewClass> |
| 419 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncSlice(VM& vm, ExecState* exec) |
| 420 | { |
| 421 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 422 | |
| 423 | // 22.2.3.26 |
| 424 | JSFunction* callee = jsCast<JSFunction*>(exec->jsCallee()); |
| 425 | |
| 426 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 427 | if (thisObject->isNeutered()) |
| 428 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 429 | |
| 430 | unsigned thisLength = thisObject->length(); |
| 431 | |
| 432 | unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, thisLength); |
| 433 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 434 | unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, thisLength, thisLength); |
| 435 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 436 | |
| 437 | if (thisObject->isNeutered()) |
| 438 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 439 | |
| 440 | // Clamp end to begin. |
| 441 | end = std::max(begin, end); |
| 442 | |
| 443 | ASSERT(end >= begin); |
| 444 | unsigned length = end - begin; |
| 445 | |
| 446 | MarkedArgumentBuffer args; |
| 447 | args.append(jsNumber(length)); |
| 448 | ASSERT(!args.hasOverflowed()); |
| 449 | |
| 450 | JSArrayBufferView* result = speciesConstruct(exec, thisObject, args, [&]() { |
| 451 | Structure* structure = callee->globalObject(vm)->typedArrayStructure(ViewClass::TypedArrayStorageType); |
| 452 | return ViewClass::createUninitialized(exec, structure, length); |
| 453 | }); |
| 454 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 455 | |
| 456 | ASSERT(!result->isNeutered()); |
| 457 | if (thisObject->isNeutered()) |
| 458 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 459 | |
| 460 | // We return early here since we don't allocate a backing store if length is 0 and memmove does not like nullptrs |
| 461 | if (!length) |
| 462 | return JSValue::encode(result); |
| 463 | |
| 464 | // The species constructor may return an array with any arbitrary length. |
| 465 | length = std::min(length, result->length()); |
| 466 | switch (result->classInfo(vm)->typedArrayStorageType) { |
| 467 | case TypeInt8: |
| 468 | scope.release(); |
| 469 | jsCast<JSInt8Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight); |
| 470 | return JSValue::encode(result); |
| 471 | case TypeInt16: |
| 472 | scope.release(); |
| 473 | jsCast<JSInt16Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight); |
| 474 | return JSValue::encode(result); |
| 475 | case TypeInt32: |
| 476 | scope.release(); |
| 477 | jsCast<JSInt32Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight); |
| 478 | return JSValue::encode(result); |
| 479 | case TypeUint8: |
| 480 | scope.release(); |
| 481 | jsCast<JSUint8Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight); |
| 482 | return JSValue::encode(result); |
| 483 | case TypeUint8Clamped: |
| 484 | scope.release(); |
| 485 | jsCast<JSUint8ClampedArray*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight); |
| 486 | return JSValue::encode(result); |
| 487 | case TypeUint16: |
| 488 | scope.release(); |
| 489 | jsCast<JSUint16Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight); |
| 490 | return JSValue::encode(result); |
| 491 | case TypeUint32: |
| 492 | scope.release(); |
| 493 | jsCast<JSUint32Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight); |
| 494 | return JSValue::encode(result); |
| 495 | case TypeFloat32: |
| 496 | scope.release(); |
| 497 | jsCast<JSFloat32Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight); |
| 498 | return JSValue::encode(result); |
| 499 | case TypeFloat64: |
| 500 | scope.release(); |
| 501 | jsCast<JSFloat64Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight); |
| 502 | return JSValue::encode(result); |
| 503 | default: |
| 504 | RELEASE_ASSERT_NOT_REACHED(); |
| 505 | } |
| 506 | } |
| 507 | |
| 508 | template<typename ViewClass> |
| 509 | EncodedJSValue JSC_HOST_CALL genericTypedArrayViewPrivateFuncSubarrayCreate(VM&vm, ExecState* exec) |
| 510 | { |
| 511 | auto scope = DECLARE_THROW_SCOPE(vm); |
| 512 | |
| 513 | // 22.2.3.23 |
| 514 | JSFunction* callee = jsCast<JSFunction*>(exec->jsCallee()); |
| 515 | |
| 516 | ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue()); |
| 517 | if (thisObject->isNeutered()) |
| 518 | return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage); |
| 519 | |
| 520 | // Get the length here; later assert that the length didn't change. |
| 521 | unsigned thisLength = thisObject->length(); |
| 522 | |
| 523 | // I would assert that the arguments are integers here but that's not true since |
| 524 | // https://tc39.github.io/ecma262/#sec-tointeger allows the result of the operation |
| 525 | // to be +/- Infinity and -0. |
| 526 | ASSERT(exec->argument(0).isNumber()); |
| 527 | ASSERT(exec->argument(1).isUndefined() || exec->argument(1).isNumber()); |
| 528 | unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, thisLength); |
| 529 | scope.assertNoException(); |
| 530 | unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, thisLength, thisLength); |
| 531 | scope.assertNoException(); |
| 532 | |
| 533 | RELEASE_ASSERT(!thisObject->isNeutered()); |
| 534 | |
| 535 | // Clamp end to begin. |
| 536 | end = std::max(begin, end); |
| 537 | |
| 538 | ASSERT(end >= begin); |
| 539 | unsigned offset = begin; |
| 540 | unsigned length = end - begin; |
| 541 | |
| 542 | RefPtr<ArrayBuffer> arrayBuffer = thisObject->possiblySharedBuffer(); |
| 543 | RELEASE_ASSERT(thisLength == thisObject->length()); |
| 544 | |
| 545 | unsigned newByteOffset = thisObject->byteOffset() + offset * ViewClass::elementSize; |
| 546 | |
| 547 | JSObject* defaultConstructor = callee->globalObject(vm)->typedArrayConstructor(ViewClass::TypedArrayStorageType); |
| 548 | JSValue species = exec->uncheckedArgument(2); |
| 549 | if (species == defaultConstructor) { |
| 550 | Structure* structure = callee->globalObject(vm)->typedArrayStructure(ViewClass::TypedArrayStorageType); |
| 551 | |
| 552 | RELEASE_AND_RETURN(scope, JSValue::encode(ViewClass::create( |
| 553 | exec, structure, WTFMove(arrayBuffer), |
| 554 | thisObject->byteOffset() + offset * ViewClass::elementSize, |
| 555 | length))); |
| 556 | } |
| 557 | |
| 558 | MarkedArgumentBuffer args; |
| 559 | args.append(vm.m_typedArrayController->toJS(exec, thisObject->globalObject(vm), arrayBuffer.get())); |
| 560 | args.append(jsNumber(newByteOffset)); |
| 561 | args.append(jsNumber(length)); |
| 562 | ASSERT(!args.hasOverflowed()); |
| 563 | |
| 564 | JSObject* result = construct(exec, species, args, "species is not a constructor" ); |
| 565 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
| 566 | |
| 567 | if (jsDynamicCast<JSArrayBufferView*>(vm, result)) |
| 568 | return JSValue::encode(result); |
| 569 | |
| 570 | throwTypeError(exec, scope, "species constructor did not return a TypedArray View"_s ); |
| 571 | return JSValue::encode(JSValue()); |
| 572 | } |
| 573 | |
| 574 | } // namespace JSC |
| 575 | |