2 * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
28 #include "ArrayPrototype.h"
30 #include "ExceptionHelpers.h"
31 #include "JSArrayBufferViewInlines.h"
32 #include "JSCBuiltins.h"
33 #include "JSCJSValueInlines.h"
34 #include "JSFunction.h"
35 #include "JSGenericTypedArrayViewInlines.h"
36 #include "JSGenericTypedArrayViewPrototypeInlines.h"
37 #include "JSStringJoiner.h"
38 #include "StructureInlines.h"
39 #include "TypedArrayAdaptors.h"
40 #include <wtf/StdLibExtras.h>
44 // This implements 22.2.4.7 TypedArraySpeciesCreate
45 // Note, that this function throws.
46 template<typename Functor>
47 inline JSArrayBufferView* speciesConstruct(ExecState* exec, JSObject* exemplar, MarkedArgumentBuffer& args, const Functor& defaultConstructor)
50 auto scope = DECLARE_THROW_SCOPE(vm);
52 JSValue constructor = exemplar->get(exec, vm.propertyNames->constructor);
53 RETURN_IF_EXCEPTION(scope, nullptr);
55 if (constructor.isUndefined())
56 return defaultConstructor();
57 if (!constructor.isObject()) {
58 throwTypeError(exec, scope, ASCIILiteral("constructor Property should not be null"));
62 JSValue species = constructor.get(exec, vm.propertyNames->speciesSymbol);
63 RETURN_IF_EXCEPTION(scope, nullptr);
65 if (species.isUndefinedOrNull())
66 return defaultConstructor();
68 JSValue result = construct(exec, species, args, "species is not a constructor");
69 RETURN_IF_EXCEPTION(scope, nullptr);
71 if (JSArrayBufferView* view = jsDynamicCast<JSArrayBufferView*>(result)) {
72 if (!view->isNeutered())
75 throwTypeError(exec, scope, ASCIILiteral(typedArrayBufferHasBeenDetachedErrorMessage));
79 throwTypeError(exec, scope, ASCIILiteral("species constructor did not return a TypedArray View"));
83 inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
85 JSValue value = exec->argument(argument);
86 if (value.isUndefined())
87 return undefinedValue;
89 double indexDouble = value.toInteger(exec);
90 if (indexDouble < 0) {
91 indexDouble += length;
92 return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble);
94 return indexDouble > length ? length : static_cast<unsigned>(indexDouble);
97 template<typename ViewClass>
98 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncSet(VM& vm, ExecState* exec)
100 auto scope = DECLARE_THROW_SCOPE(vm);
103 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
105 if (UNLIKELY(!exec->argumentCount()))
106 return throwVMTypeError(exec, scope, ASCIILiteral("Expected at least one argument"));
109 if (exec->argumentCount() >= 2) {
110 double offsetNumber = exec->uncheckedArgument(1).toInteger(exec);
111 RETURN_IF_EXCEPTION(scope, encodedJSValue());
112 if (UNLIKELY(offsetNumber < 0))
113 return throwVMRangeError(exec, scope, "Offset should not be negative");
114 offset = static_cast<unsigned>(std::min(offsetNumber, static_cast<double>(std::numeric_limits<unsigned>::max())));
118 if (UNLIKELY(thisObject->isNeutered()))
119 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
121 JSObject* sourceArray = jsDynamicCast<JSObject*>(exec->uncheckedArgument(0));
122 if (UNLIKELY(!sourceArray))
123 return throwVMTypeError(exec, scope, ASCIILiteral("First argument should be an object"));
126 if (isTypedView(sourceArray->classInfo()->typedArrayStorageType)) {
127 JSArrayBufferView* sourceView = jsCast<JSArrayBufferView*>(sourceArray);
128 if (UNLIKELY(sourceView->isNeutered()))
129 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
131 length = jsCast<JSArrayBufferView*>(sourceArray)->length();
133 length = sourceArray->get(exec, vm.propertyNames->length).toUInt32(exec);
135 RETURN_IF_EXCEPTION(scope, encodedJSValue());
137 thisObject->set(exec, offset, sourceArray, 0, length, CopyType::Unobservable);
138 return JSValue::encode(jsUndefined());
141 template<typename ViewClass>
142 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncCopyWithin(VM& vm, ExecState* exec)
144 auto scope = DECLARE_THROW_SCOPE(vm);
147 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
148 if (thisObject->isNeutered())
149 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
151 long length = thisObject->length();
152 long to = argumentClampedIndexFromStartOrEnd(exec, 0, length);
153 RETURN_IF_EXCEPTION(scope, encodedJSValue());
154 long from = argumentClampedIndexFromStartOrEnd(exec, 1, length);
155 RETURN_IF_EXCEPTION(scope, encodedJSValue());
156 long final = argumentClampedIndexFromStartOrEnd(exec, 2, length, length);
157 RETURN_IF_EXCEPTION(scope, encodedJSValue());
160 return JSValue::encode(exec->thisValue());
162 long count = std::min(length - std::max(to, from), final - from);
164 if (thisObject->isNeutered())
165 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
167 typename ViewClass::ElementType* array = thisObject->typedVector();
168 memmove(array + to, array + from, count * thisObject->elementSize);
170 return JSValue::encode(exec->thisValue());
173 template<typename ViewClass>
174 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncIncludes(VM& vm, ExecState* exec)
176 auto scope = DECLARE_THROW_SCOPE(vm);
178 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
179 if (thisObject->isNeutered())
180 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
182 unsigned length = thisObject->length();
185 return JSValue::encode(jsBoolean(false));
187 JSValue valueToFind = exec->argument(0);
189 unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
190 RETURN_IF_EXCEPTION(scope, encodedJSValue());
192 if (thisObject->isNeutered())
193 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
195 typename ViewClass::ElementType* array = thisObject->typedVector();
196 auto targetOption = ViewClass::toAdaptorNativeFromValueWithoutCoercion(valueToFind);
198 return JSValue::encode(jsBoolean(false));
200 ASSERT(!scope.exception());
201 RELEASE_ASSERT(!thisObject->isNeutered());
203 if (std::isnan(static_cast<double>(*targetOption))) {
204 for (; index < length; ++index) {
205 if (std::isnan(static_cast<double>(array[index])))
206 return JSValue::encode(jsBoolean(true));
209 for (; index < length; ++index) {
210 if (array[index] == targetOption)
211 return JSValue::encode(jsBoolean(true));
215 return JSValue::encode(jsBoolean(false));
218 template<typename ViewClass>
219 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncIndexOf(VM& vm, ExecState* exec)
221 auto scope = DECLARE_THROW_SCOPE(vm);
224 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
225 if (thisObject->isNeutered())
226 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
228 if (!exec->argumentCount())
229 return throwVMTypeError(exec, scope, ASCIILiteral("Expected at least one argument"));
231 unsigned length = thisObject->length();
233 JSValue valueToFind = exec->argument(0);
234 unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
235 RETURN_IF_EXCEPTION(scope, encodedJSValue());
237 if (thisObject->isNeutered())
238 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
240 typename ViewClass::ElementType* array = thisObject->typedVector();
241 auto targetOption = ViewClass::toAdaptorNativeFromValueWithoutCoercion(valueToFind);
243 return JSValue::encode(jsNumber(-1));
244 ASSERT(!scope.exception());
245 RELEASE_ASSERT(!thisObject->isNeutered());
247 for (; index < length; ++index) {
248 if (array[index] == targetOption)
249 return JSValue::encode(jsNumber(index));
252 return JSValue::encode(jsNumber(-1));
255 template<typename ViewClass>
256 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncJoin(VM& vm, ExecState* exec)
258 auto scope = DECLARE_THROW_SCOPE(vm);
260 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
261 if (thisObject->isNeutered())
262 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
265 auto joinWithSeparator = [&] (StringView separator) -> EncodedJSValue {
266 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
267 unsigned length = thisObject->length();
269 JSStringJoiner joiner(*exec, separator, length);
270 RETURN_IF_EXCEPTION(scope, encodedJSValue());
271 for (unsigned i = 0; i < length; i++) {
272 joiner.append(*exec, thisObject->getIndexQuickly(i));
273 RETURN_IF_EXCEPTION(scope, encodedJSValue());
275 return JSValue::encode(joiner.join(*exec));
278 JSValue separatorValue = exec->argument(0);
279 if (separatorValue.isUndefined()) {
280 const LChar* comma = reinterpret_cast<const LChar*>(",");
281 return joinWithSeparator({ comma, 1 });
284 JSString* separatorString = separatorValue.toString(exec);
285 RETURN_IF_EXCEPTION(scope, encodedJSValue());
287 if (thisObject->isNeutered())
288 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
289 return joinWithSeparator(separatorString->view(exec).get());
292 template<typename ViewClass>
293 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncLastIndexOf(VM& vm, ExecState* exec)
295 auto scope = DECLARE_THROW_SCOPE(vm);
298 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
299 if (thisObject->isNeutered())
300 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
302 if (!exec->argumentCount())
303 return throwVMTypeError(exec, scope, ASCIILiteral("Expected at least one argument"));
305 unsigned length = thisObject->length();
307 JSValue valueToFind = exec->argument(0);
309 int index = length - 1;
310 if (exec->argumentCount() >= 2) {
311 JSValue fromValue = exec->uncheckedArgument(1);
312 double fromDouble = fromValue.toInteger(exec);
313 if (fromDouble < 0) {
314 fromDouble += length;
316 return JSValue::encode(jsNumber(-1));
318 if (fromDouble < length)
319 index = static_cast<unsigned>(fromDouble);
322 RETURN_IF_EXCEPTION(scope, encodedJSValue());
324 if (thisObject->isNeutered())
325 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
327 auto targetOption = ViewClass::toAdaptorNativeFromValueWithoutCoercion(valueToFind);
329 return JSValue::encode(jsNumber(-1));
331 typename ViewClass::ElementType* array = thisObject->typedVector();
332 ASSERT(!scope.exception());
333 RELEASE_ASSERT(!thisObject->isNeutered());
335 for (; index >= 0; --index) {
336 if (array[index] == targetOption)
337 return JSValue::encode(jsNumber(index));
340 return JSValue::encode(jsNumber(-1));
343 template<typename ViewClass>
344 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoGetterFuncBuffer(VM&, ExecState* exec)
347 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
349 return JSValue::encode(thisObject->jsBuffer(exec));
352 template<typename ViewClass>
353 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoGetterFuncLength(VM&, ExecState* exec)
356 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
358 return JSValue::encode(jsNumber(thisObject->length()));
361 template<typename ViewClass>
362 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoGetterFuncByteLength(VM&, ExecState* exec)
365 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
367 return JSValue::encode(jsNumber(thisObject->byteLength()));
370 template<typename ViewClass>
371 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoGetterFuncByteOffset(VM&, ExecState* exec)
374 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
376 return JSValue::encode(jsNumber(thisObject->byteOffset()));
379 template<typename ViewClass>
380 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncReverse(VM& vm, ExecState* exec)
382 // VM& vm = exec->vm();
383 auto scope = DECLARE_THROW_SCOPE(vm);
386 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
387 if (thisObject->isNeutered())
388 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
390 typename ViewClass::ElementType* array = thisObject->typedVector();
391 std::reverse(array, array + thisObject->length());
393 return JSValue::encode(thisObject);
396 template<typename ViewClass>
397 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewPrivateFuncSort(VM& vm, ExecState* exec)
399 // VM& vm = exec->vm();
400 auto scope = DECLARE_THROW_SCOPE(vm);
403 ViewClass* thisObject = jsCast<ViewClass*>(exec->argument(0));
404 if (thisObject->isNeutered())
405 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
409 return JSValue::encode(thisObject);
412 template<typename ViewClass>
413 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncSlice(VM& vm, ExecState* exec)
415 auto scope = DECLARE_THROW_SCOPE(vm);
418 JSFunction* callee = jsCast<JSFunction*>(exec->callee());
420 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
421 if (thisObject->isNeutered())
422 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
424 unsigned thisLength = thisObject->length();
426 unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, thisLength);
427 RETURN_IF_EXCEPTION(scope, encodedJSValue());
428 unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, thisLength, thisLength);
429 RETURN_IF_EXCEPTION(scope, encodedJSValue());
431 if (thisObject->isNeutered())
432 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
434 // Clamp end to begin.
435 end = std::max(begin, end);
437 ASSERT(end >= begin);
438 unsigned length = end - begin;
440 MarkedArgumentBuffer args;
441 args.append(jsNumber(length));
443 JSArrayBufferView* result = speciesConstruct(exec, thisObject, args, [&]() {
444 Structure* structure = callee->globalObject()->typedArrayStructure(ViewClass::TypedArrayStorageType);
445 return ViewClass::createUninitialized(exec, structure, length);
447 RETURN_IF_EXCEPTION(scope, encodedJSValue());
449 ASSERT(!result->isNeutered());
450 if (thisObject->isNeutered())
451 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
453 // We return early here since we don't allocate a backing store if length is 0 and memmove does not like nullptrs
455 return JSValue::encode(result);
457 // The species constructor may return an array with any arbitrary length.
458 length = std::min(length, result->length());
459 switch (result->classInfo()->typedArrayStorageType) {
461 jsCast<JSInt8Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
464 jsCast<JSInt16Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
467 jsCast<JSInt32Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
470 jsCast<JSUint8Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
472 case TypeUint8Clamped:
473 jsCast<JSUint8ClampedArray*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
476 jsCast<JSUint16Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
479 jsCast<JSUint32Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
482 jsCast<JSFloat32Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
485 jsCast<JSFloat64Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
488 RELEASE_ASSERT_NOT_REACHED();
491 return JSValue::encode(result);
494 template<typename ViewClass>
495 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewPrivateFuncSubarrayCreate(VM&vm, ExecState* exec)
497 auto scope = DECLARE_THROW_SCOPE(vm);
500 JSFunction* callee = jsCast<JSFunction*>(exec->callee());
502 ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
503 if (thisObject->isNeutered())
504 return throwVMTypeError(exec, scope, typedArrayBufferHasBeenDetachedErrorMessage);
506 // Get the length here; later assert that the length didn't change.
507 unsigned thisLength = thisObject->length();
509 // I would assert that the arguments are integers here but that's not true since
510 // https://tc39.github.io/ecma262/#sec-tointeger allows the result of the operation
511 // to be +/- Infinity and -0.
512 ASSERT(exec->argument(0).isNumber());
513 ASSERT(exec->argument(1).isUndefined() || exec->argument(1).isNumber());
514 unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, thisLength);
515 ASSERT(!scope.exception());
516 unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, thisLength, thisLength);
517 ASSERT(!scope.exception());
519 RELEASE_ASSERT(!thisObject->isNeutered());
521 // Clamp end to begin.
522 end = std::max(begin, end);
524 ASSERT(end >= begin);
525 unsigned offset = begin;
526 unsigned length = end - begin;
528 RefPtr<ArrayBuffer> arrayBuffer = thisObject->buffer();
529 RELEASE_ASSERT(thisLength == thisObject->length());
531 unsigned newByteOffset = thisObject->byteOffset() + offset * ViewClass::elementSize;
533 JSObject* defaultConstructor = callee->globalObject()->typedArrayConstructor(ViewClass::TypedArrayStorageType);
534 JSValue species = exec->uncheckedArgument(2);
535 if (species == defaultConstructor) {
536 Structure* structure = callee->globalObject()->typedArrayStructure(ViewClass::TypedArrayStorageType);
538 return JSValue::encode(ViewClass::create(
539 exec, structure, arrayBuffer,
540 thisObject->byteOffset() + offset * ViewClass::elementSize,
544 MarkedArgumentBuffer args;
545 args.append(vm.m_typedArrayController->toJS(exec, thisObject->globalObject(), thisObject->buffer()));
546 args.append(jsNumber(newByteOffset));
547 args.append(jsNumber(length));
549 JSObject* result = construct(exec, species, args, "species is not a constructor");
550 RETURN_IF_EXCEPTION(scope, encodedJSValue());
552 if (jsDynamicCast<JSArrayBufferView*>(result))
553 return JSValue::encode(result);
555 throwTypeError(exec, scope, ASCIILiteral("species constructor did not return a TypedArray View"));
556 return JSValue::encode(JSValue());