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 | |