Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef vm_TypedArrayObject_inl_h
8 : #define vm_TypedArrayObject_inl_h
9 :
10 : /* Utilities and common inline code for TypedArray */
11 :
12 : #include "vm/TypedArrayObject.h"
13 :
14 : #include "mozilla/Assertions.h"
15 : #include "mozilla/FloatingPoint.h"
16 : #include "mozilla/PodOperations.h"
17 :
18 : #include "jsarray.h"
19 : #include "jscntxt.h"
20 : #include "jsnum.h"
21 :
22 : #include "jit/AtomicOperations.h"
23 :
24 : #include "js/Conversions.h"
25 : #include "js/Value.h"
26 :
27 : #include "vm/NativeObject.h"
28 :
29 : namespace js {
30 :
31 : template<typename To, typename From>
32 : inline To
33 : ConvertNumber(From src);
34 :
35 : template<>
36 : inline int8_t
37 0 : ConvertNumber<int8_t, float>(float src)
38 : {
39 0 : return JS::ToInt8(src);
40 : }
41 :
42 : template<>
43 : inline uint8_t
44 0 : ConvertNumber<uint8_t, float>(float src)
45 : {
46 0 : return JS::ToUint8(src);
47 : }
48 :
49 : template<>
50 : inline uint8_clamped
51 0 : ConvertNumber<uint8_clamped, float>(float src)
52 : {
53 0 : return uint8_clamped(src);
54 : }
55 :
56 : template<>
57 : inline int16_t
58 0 : ConvertNumber<int16_t, float>(float src)
59 : {
60 0 : return JS::ToInt16(src);
61 : }
62 :
63 : template<>
64 : inline uint16_t
65 0 : ConvertNumber<uint16_t, float>(float src)
66 : {
67 0 : return JS::ToUint16(src);
68 : }
69 :
70 : template<>
71 : inline int32_t
72 0 : ConvertNumber<int32_t, float>(float src)
73 : {
74 0 : return JS::ToInt32(src);
75 : }
76 :
77 : template<>
78 : inline uint32_t
79 0 : ConvertNumber<uint32_t, float>(float src)
80 : {
81 0 : return JS::ToUint32(src);
82 : }
83 :
84 : template<> inline int8_t
85 0 : ConvertNumber<int8_t, double>(double src)
86 : {
87 0 : return JS::ToInt8(src);
88 : }
89 :
90 : template<>
91 : inline uint8_t
92 0 : ConvertNumber<uint8_t, double>(double src)
93 : {
94 0 : return JS::ToUint8(src);
95 : }
96 :
97 : template<>
98 : inline uint8_clamped
99 0 : ConvertNumber<uint8_clamped, double>(double src)
100 : {
101 0 : return uint8_clamped(src);
102 : }
103 :
104 : template<>
105 : inline int16_t
106 0 : ConvertNumber<int16_t, double>(double src)
107 : {
108 0 : return JS::ToInt16(src);
109 : }
110 :
111 : template<>
112 : inline uint16_t
113 0 : ConvertNumber<uint16_t, double>(double src)
114 : {
115 0 : return JS::ToUint16(src);
116 : }
117 :
118 : template<>
119 : inline int32_t
120 0 : ConvertNumber<int32_t, double>(double src)
121 : {
122 0 : return JS::ToInt32(src);
123 : }
124 :
125 : template<>
126 : inline uint32_t
127 0 : ConvertNumber<uint32_t, double>(double src)
128 : {
129 0 : return JS::ToUint32(src);
130 : }
131 :
132 : template<typename To, typename From>
133 : inline To
134 0 : ConvertNumber(From src)
135 : {
136 : static_assert(!mozilla::IsFloatingPoint<From>::value ||
137 : (mozilla::IsFloatingPoint<From>::value && mozilla::IsFloatingPoint<To>::value),
138 : "conversion from floating point to int should have been handled by "
139 : "specializations above");
140 0 : return To(src);
141 : }
142 :
143 : template<typename NativeType> struct TypeIDOfType;
144 : template<> struct TypeIDOfType<int8_t> { static const Scalar::Type id = Scalar::Int8; };
145 : template<> struct TypeIDOfType<uint8_t> { static const Scalar::Type id = Scalar::Uint8; };
146 : template<> struct TypeIDOfType<int16_t> { static const Scalar::Type id = Scalar::Int16; };
147 : template<> struct TypeIDOfType<uint16_t> { static const Scalar::Type id = Scalar::Uint16; };
148 : template<> struct TypeIDOfType<int32_t> { static const Scalar::Type id = Scalar::Int32; };
149 : template<> struct TypeIDOfType<uint32_t> { static const Scalar::Type id = Scalar::Uint32; };
150 : template<> struct TypeIDOfType<float> { static const Scalar::Type id = Scalar::Float32; };
151 : template<> struct TypeIDOfType<double> { static const Scalar::Type id = Scalar::Float64; };
152 : template<> struct TypeIDOfType<uint8_clamped> { static const Scalar::Type id = Scalar::Uint8Clamped; };
153 :
154 : class SharedOps
155 : {
156 : public:
157 : template<typename T>
158 0 : static T load(SharedMem<T*> addr) {
159 0 : return js::jit::AtomicOperations::loadSafeWhenRacy(addr);
160 : }
161 :
162 : template<typename T>
163 0 : static void store(SharedMem<T*> addr, T value) {
164 0 : js::jit::AtomicOperations::storeSafeWhenRacy(addr, value);
165 0 : }
166 :
167 : template<typename T>
168 0 : static void memcpy(SharedMem<T*> dest, SharedMem<T*> src, size_t size) {
169 0 : js::jit::AtomicOperations::memcpySafeWhenRacy(dest, src, size);
170 0 : }
171 :
172 : template<typename T>
173 : static void memmove(SharedMem<T*> dest, SharedMem<T*> src, size_t size) {
174 : js::jit::AtomicOperations::memmoveSafeWhenRacy(dest, src, size);
175 : }
176 :
177 : template<typename T>
178 0 : static void podCopy(SharedMem<T*> dest, SharedMem<T*> src, size_t nelem) {
179 0 : js::jit::AtomicOperations::podCopySafeWhenRacy(dest, src, nelem);
180 0 : }
181 :
182 : template<typename T>
183 0 : static void podMove(SharedMem<T*> dest, SharedMem<T*> src, size_t nelem) {
184 0 : js::jit::AtomicOperations::podMoveSafeWhenRacy(dest, src, nelem);
185 0 : }
186 :
187 0 : static SharedMem<void*> extract(TypedArrayObject* obj) {
188 0 : return obj->viewDataEither();
189 : }
190 : };
191 :
192 : class UnsharedOps
193 : {
194 : public:
195 : template<typename T>
196 0 : static T load(SharedMem<T*> addr) {
197 0 : return *addr.unwrapUnshared();
198 : }
199 :
200 : template<typename T>
201 0 : static void store(SharedMem<T*> addr, T value) {
202 0 : *addr.unwrapUnshared() = value;
203 0 : }
204 :
205 : template<typename T>
206 0 : static void memcpy(SharedMem<T*> dest, SharedMem<T*> src, size_t size) {
207 0 : ::memcpy(dest.unwrapUnshared(), src.unwrapUnshared(), size);
208 0 : }
209 :
210 : template<typename T>
211 : static void memmove(SharedMem<T*> dest, SharedMem<T*> src, size_t size) {
212 : ::memmove(dest.unwrapUnshared(), src.unwrapUnshared(), size);
213 : }
214 :
215 : template<typename T>
216 0 : static void podCopy(SharedMem<T*> dest, SharedMem<T*> src, size_t nelem) {
217 0 : mozilla::PodCopy(dest.unwrapUnshared(), src.unwrapUnshared(), nelem);
218 0 : }
219 :
220 : template<typename T>
221 0 : static void podMove(SharedMem<T*> dest, SharedMem<T*> src, size_t nelem) {
222 0 : mozilla::PodMove(dest.unwrapUnshared(), src.unwrapUnshared(), nelem);
223 0 : }
224 :
225 0 : static SharedMem<void*> extract(TypedArrayObject* obj) {
226 0 : return SharedMem<void*>::unshared(obj->viewDataUnshared());
227 : }
228 : };
229 :
230 : template<typename T, typename Ops>
231 : class ElementSpecific
232 : {
233 : public:
234 : /*
235 : * Copy |source|'s elements into |target|, starting at |target[offset]|.
236 : * Act as if the assignments occurred from a fresh copy of |source|, in
237 : * case the two memory ranges overlap.
238 : */
239 : static bool
240 0 : setFromTypedArray(JSContext* cx,
241 : Handle<TypedArrayObject*> target, Handle<TypedArrayObject*> source,
242 : uint32_t offset)
243 : {
244 : // WARNING: |source| may be an unwrapped typed array from a different
245 : // compartment. Proceed with caution!
246 :
247 0 : MOZ_ASSERT(TypeIDOfType<T>::id == target->type(),
248 : "calling wrong setFromTypedArray specialization");
249 0 : MOZ_ASSERT(!target->hasDetachedBuffer(), "target isn't detached");
250 0 : MOZ_ASSERT(!source->hasDetachedBuffer(), "source isn't detached");
251 :
252 0 : MOZ_ASSERT(offset <= target->length());
253 0 : MOZ_ASSERT(source->length() <= target->length() - offset);
254 :
255 0 : if (TypedArrayObject::sameBuffer(target, source))
256 0 : return setFromOverlappingTypedArray(cx, target, source, offset);
257 :
258 0 : SharedMem<T*> dest = target->viewDataEither().template cast<T*>() + offset;
259 0 : uint32_t count = source->length();
260 :
261 0 : if (source->type() == target->type()) {
262 0 : Ops::podCopy(dest, source->viewDataEither().template cast<T*>(), count);
263 0 : return true;
264 : }
265 :
266 : // Inhibit unaligned accesses on ARM (bug 1097253, a compiler bug).
267 : #ifdef __arm__
268 : # define JS_VOLATILE_ARM volatile
269 : #else
270 : # define JS_VOLATILE_ARM
271 : #endif
272 :
273 0 : SharedMem<void*> data = Ops::extract(source);
274 0 : switch (source->type()) {
275 : case Scalar::Int8: {
276 0 : SharedMem<JS_VOLATILE_ARM int8_t*> src = data.cast<JS_VOLATILE_ARM int8_t*>();
277 0 : for (uint32_t i = 0; i < count; ++i)
278 0 : Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
279 0 : break;
280 : }
281 : case Scalar::Uint8:
282 : case Scalar::Uint8Clamped: {
283 0 : SharedMem<JS_VOLATILE_ARM uint8_t*> src = data.cast<JS_VOLATILE_ARM uint8_t*>();
284 0 : for (uint32_t i = 0; i < count; ++i)
285 0 : Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
286 0 : break;
287 : }
288 : case Scalar::Int16: {
289 0 : SharedMem<JS_VOLATILE_ARM int16_t*> src = data.cast<JS_VOLATILE_ARM int16_t*>();
290 0 : for (uint32_t i = 0; i < count; ++i)
291 0 : Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
292 0 : break;
293 : }
294 : case Scalar::Uint16: {
295 0 : SharedMem<JS_VOLATILE_ARM uint16_t*> src = data.cast<JS_VOLATILE_ARM uint16_t*>();
296 0 : for (uint32_t i = 0; i < count; ++i)
297 0 : Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
298 0 : break;
299 : }
300 : case Scalar::Int32: {
301 0 : SharedMem<JS_VOLATILE_ARM int32_t*> src = data.cast<JS_VOLATILE_ARM int32_t*>();
302 0 : for (uint32_t i = 0; i < count; ++i)
303 0 : Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
304 0 : break;
305 : }
306 : case Scalar::Uint32: {
307 0 : SharedMem<JS_VOLATILE_ARM uint32_t*> src = data.cast<JS_VOLATILE_ARM uint32_t*>();
308 0 : for (uint32_t i = 0; i < count; ++i)
309 0 : Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
310 0 : break;
311 : }
312 : case Scalar::Float32: {
313 0 : SharedMem<JS_VOLATILE_ARM float*> src = data.cast<JS_VOLATILE_ARM float*>();
314 0 : for (uint32_t i = 0; i < count; ++i)
315 0 : Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
316 0 : break;
317 : }
318 : case Scalar::Float64: {
319 0 : SharedMem<JS_VOLATILE_ARM double*> src = data.cast<JS_VOLATILE_ARM double*>();
320 0 : for (uint32_t i = 0; i < count; ++i)
321 0 : Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
322 0 : break;
323 : }
324 : default:
325 0 : MOZ_CRASH("setFromTypedArray with a typed array with bogus type");
326 : }
327 :
328 : #undef JS_VOLATILE_ARM
329 :
330 0 : return true;
331 : }
332 :
333 : /*
334 : * Copy |source[0]| to |source[len]| (exclusive) elements into the typed
335 : * array |target|, starting at index |offset|. |source| must not be a
336 : * typed array.
337 : */
338 : static bool
339 0 : setFromNonTypedArray(JSContext* cx, Handle<TypedArrayObject*> target, HandleObject source,
340 : uint32_t len, uint32_t offset = 0)
341 : {
342 0 : MOZ_ASSERT(target->type() == TypeIDOfType<T>::id,
343 : "target type and NativeType must match");
344 0 : MOZ_ASSERT(!target->hasDetachedBuffer(), "target isn't detached");
345 0 : MOZ_ASSERT(!source->is<TypedArrayObject>(),
346 : "use setFromTypedArray instead of this method");
347 :
348 0 : uint32_t i = 0;
349 0 : if (source->isNative()) {
350 : // Attempt fast-path infallible conversion of dense elements up to
351 : // the first potentially side-effectful lookup or conversion.
352 0 : uint32_t bound = Min(source->as<NativeObject>().getDenseInitializedLength(), len);
353 :
354 0 : SharedMem<T*> dest = target->viewDataEither().template cast<T*>() + offset;
355 :
356 0 : MOZ_ASSERT(!canConvertInfallibly(MagicValue(JS_ELEMENTS_HOLE)),
357 : "the following loop must abort on holes");
358 :
359 0 : const Value* srcValues = source->as<NativeObject>().getDenseElements();
360 0 : for (; i < bound; i++) {
361 0 : if (!canConvertInfallibly(srcValues[i]))
362 0 : break;
363 0 : Ops::store(dest + i, infallibleValueToNative(srcValues[i]));
364 : }
365 0 : if (i == len)
366 0 : return true;
367 : }
368 :
369 : // Convert and copy any remaining elements generically.
370 0 : RootedValue v(cx);
371 0 : for (; i < len; i++) {
372 0 : if (!GetElement(cx, source, source, i, &v))
373 0 : return false;
374 :
375 0 : T n;
376 0 : if (!valueToNative(cx, v, &n))
377 0 : return false;
378 :
379 0 : len = Min(len, target->length());
380 0 : if (i >= len)
381 0 : break;
382 :
383 : // Compute every iteration in case getElement/valueToNative
384 : // detaches the underlying array buffer or GC moves the data.
385 0 : SharedMem<T*> dest = target->viewDataEither().template cast<T*>() + offset + i;
386 0 : Ops::store(dest, n);
387 : }
388 :
389 0 : return true;
390 : }
391 :
392 : /*
393 : * Copy |source| into the typed array |target|.
394 : */
395 : static bool
396 0 : initFromIterablePackedArray(JSContext* cx, Handle<TypedArrayObject*> target,
397 : HandleArrayObject source)
398 : {
399 0 : MOZ_ASSERT(target->type() == TypeIDOfType<T>::id,
400 : "target type and NativeType must match");
401 0 : MOZ_ASSERT(!target->hasDetachedBuffer(), "target isn't detached");
402 0 : MOZ_ASSERT(IsPackedArray(source), "source array must be packed");
403 0 : MOZ_ASSERT(source->getDenseInitializedLength() <= target->length());
404 :
405 0 : uint32_t len = source->getDenseInitializedLength();
406 0 : uint32_t i = 0;
407 :
408 : // Attempt fast-path infallible conversion of dense elements up to the
409 : // first potentially side-effectful conversion.
410 :
411 0 : SharedMem<T*> dest = target->viewDataEither().template cast<T*>();
412 :
413 0 : const Value* srcValues = source->getDenseElements();
414 0 : for (; i < len; i++) {
415 0 : if (!canConvertInfallibly(srcValues[i]))
416 0 : break;
417 0 : Ops::store(dest + i, infallibleValueToNative(srcValues[i]));
418 : }
419 0 : if (i == len)
420 0 : return true;
421 :
422 : // Convert any remaining elements by first collecting them into a
423 : // temporary list, and then copying them into the typed array.
424 0 : AutoValueVector values(cx);
425 0 : if (!values.append(srcValues + i, len - i))
426 0 : return false;
427 :
428 0 : RootedValue v(cx);
429 0 : for (uint32_t j = 0; j < values.length(); i++, j++) {
430 0 : v = values[j];
431 :
432 0 : T n;
433 0 : if (!valueToNative(cx, v, &n))
434 0 : return false;
435 :
436 : // |target| is a newly allocated typed array and not yet visible to
437 : // content script, so valueToNative can't detach the underlying
438 : // buffer.
439 0 : MOZ_ASSERT(i < target->length());
440 :
441 : // Compute every iteration in case GC moves the data.
442 0 : SharedMem<T*> newDest = target->viewDataEither().template cast<T*>();
443 0 : Ops::store(newDest + i, n);
444 : }
445 :
446 0 : return true;
447 : }
448 :
449 : private:
450 : static bool
451 0 : setFromOverlappingTypedArray(JSContext* cx,
452 : Handle<TypedArrayObject*> target,
453 : Handle<TypedArrayObject*> source,
454 : uint32_t offset)
455 : {
456 : // WARNING: |source| may be an unwrapped typed array from a different
457 : // compartment. Proceed with caution!
458 :
459 0 : MOZ_ASSERT(TypeIDOfType<T>::id == target->type(),
460 : "calling wrong setFromTypedArray specialization");
461 0 : MOZ_ASSERT(!target->hasDetachedBuffer(), "target isn't detached");
462 0 : MOZ_ASSERT(!source->hasDetachedBuffer(), "source isn't detached");
463 0 : MOZ_ASSERT(TypedArrayObject::sameBuffer(target, source),
464 : "the provided arrays don't actually overlap, so it's "
465 : "undesirable to use this method");
466 :
467 0 : MOZ_ASSERT(offset <= target->length());
468 0 : MOZ_ASSERT(source->length() <= target->length() - offset);
469 :
470 0 : SharedMem<T*> dest = target->viewDataEither().template cast<T*>() + offset;
471 0 : uint32_t len = source->length();
472 :
473 0 : if (source->type() == target->type()) {
474 0 : SharedMem<T*> src = source->viewDataEither().template cast<T*>();
475 0 : Ops::podMove(dest, src, len);
476 0 : return true;
477 : }
478 :
479 : // Copy |source| in case it overlaps the target elements being set.
480 0 : size_t sourceByteLen = len * source->bytesPerElement();
481 0 : void* data = target->zone()->template pod_malloc<uint8_t>(sourceByteLen);
482 0 : if (!data)
483 0 : return false;
484 0 : Ops::memcpy(SharedMem<void*>::unshared(data),
485 0 : source->viewDataEither(),
486 : sourceByteLen);
487 :
488 0 : switch (source->type()) {
489 : case Scalar::Int8: {
490 0 : int8_t* src = static_cast<int8_t*>(data);
491 0 : for (uint32_t i = 0; i < len; ++i)
492 0 : Ops::store(dest++, ConvertNumber<T>(*src++));
493 0 : break;
494 : }
495 : case Scalar::Uint8:
496 : case Scalar::Uint8Clamped: {
497 0 : uint8_t* src = static_cast<uint8_t*>(data);
498 0 : for (uint32_t i = 0; i < len; ++i)
499 0 : Ops::store(dest++, ConvertNumber<T>(*src++));
500 0 : break;
501 : }
502 : case Scalar::Int16: {
503 0 : int16_t* src = static_cast<int16_t*>(data);
504 0 : for (uint32_t i = 0; i < len; ++i)
505 0 : Ops::store(dest++, ConvertNumber<T>(*src++));
506 0 : break;
507 : }
508 : case Scalar::Uint16: {
509 0 : uint16_t* src = static_cast<uint16_t*>(data);
510 0 : for (uint32_t i = 0; i < len; ++i)
511 0 : Ops::store(dest++, ConvertNumber<T>(*src++));
512 0 : break;
513 : }
514 : case Scalar::Int32: {
515 0 : int32_t* src = static_cast<int32_t*>(data);
516 0 : for (uint32_t i = 0; i < len; ++i)
517 0 : Ops::store(dest++, ConvertNumber<T>(*src++));
518 0 : break;
519 : }
520 : case Scalar::Uint32: {
521 0 : uint32_t* src = static_cast<uint32_t*>(data);
522 0 : for (uint32_t i = 0; i < len; ++i)
523 0 : Ops::store(dest++, ConvertNumber<T>(*src++));
524 0 : break;
525 : }
526 : case Scalar::Float32: {
527 0 : float* src = static_cast<float*>(data);
528 0 : for (uint32_t i = 0; i < len; ++i)
529 0 : Ops::store(dest++, ConvertNumber<T>(*src++));
530 0 : break;
531 : }
532 : case Scalar::Float64: {
533 0 : double* src = static_cast<double*>(data);
534 0 : for (uint32_t i = 0; i < len; ++i)
535 0 : Ops::store(dest++, ConvertNumber<T>(*src++));
536 0 : break;
537 : }
538 : default:
539 0 : MOZ_CRASH("setFromOverlappingTypedArray with a typed array with bogus type");
540 : }
541 :
542 0 : js_free(data);
543 0 : return true;
544 : }
545 :
546 : static bool
547 0 : canConvertInfallibly(const Value& v)
548 : {
549 0 : return v.isNumber() || v.isBoolean() || v.isNull() || v.isUndefined();
550 : }
551 :
552 : static T
553 0 : infallibleValueToNative(const Value& v)
554 : {
555 0 : if (v.isInt32())
556 0 : return T(v.toInt32());
557 0 : if (v.isDouble())
558 0 : return doubleToNative(v.toDouble());
559 0 : if (v.isBoolean())
560 0 : return T(v.toBoolean());
561 0 : if (v.isNull())
562 0 : return T(0);
563 :
564 0 : MOZ_ASSERT(v.isUndefined());
565 0 : return TypeIsFloatingPoint<T>() ? T(JS::GenericNaN()) : T(0);
566 : }
567 :
568 : static bool
569 0 : valueToNative(JSContext* cx, HandleValue v, T* result)
570 : {
571 0 : MOZ_ASSERT(!v.isMagic());
572 :
573 0 : if (MOZ_LIKELY(canConvertInfallibly(v))) {
574 0 : *result = infallibleValueToNative(v);
575 0 : return true;
576 : }
577 :
578 : double d;
579 0 : MOZ_ASSERT(v.isString() || v.isObject() || v.isSymbol());
580 0 : if (!(v.isString() ? StringToNumber(cx, v.toString(), &d) : ToNumber(cx, v, &d)))
581 0 : return false;
582 :
583 0 : *result = doubleToNative(d);
584 0 : return true;
585 : }
586 :
587 : static T
588 0 : doubleToNative(double d)
589 : {
590 0 : if (TypeIsFloatingPoint<T>()) {
591 : #ifdef JS_MORE_DETERMINISTIC
592 : // The JS spec doesn't distinguish among different NaN values, and
593 : // it deliberately doesn't specify the bit pattern written to a
594 : // typed array when NaN is written into it. This bit-pattern
595 : // inconsistency could confuse deterministic testing, so always
596 : // canonicalize NaN values in more-deterministic builds.
597 : d = JS::CanonicalizeNaN(d);
598 : #endif
599 0 : return T(d);
600 : }
601 0 : if (MOZ_UNLIKELY(mozilla::IsNaN(d)))
602 0 : return T(0);
603 : if (TypeIDOfType<T>::id == Scalar::Uint8Clamped)
604 0 : return T(d);
605 0 : if (TypeIsUnsigned<T>())
606 0 : return T(JS::ToUint32(d));
607 0 : return T(JS::ToInt32(d));
608 : }
609 : };
610 :
611 : } // namespace js
612 :
613 : #endif // vm_TypedArrayObject_inl_h
|