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 : #include "vm/TypedArrayObject-inl.h"
8 : #include "vm/TypedArrayObject.h"
9 :
10 : #include "mozilla/Alignment.h"
11 : #include "mozilla/Casting.h"
12 : #include "mozilla/FloatingPoint.h"
13 : #include "mozilla/PodOperations.h"
14 :
15 : #include <string.h>
16 : #ifndef XP_WIN
17 : # include <sys/mman.h>
18 : #endif
19 :
20 : #include "jsapi.h"
21 : #include "jsarray.h"
22 : #include "jscntxt.h"
23 : #include "jscpucfg.h"
24 : #include "jsnum.h"
25 : #include "jsobj.h"
26 : #include "jstypes.h"
27 : #include "jsutil.h"
28 : #ifdef XP_WIN
29 : # include "jswin.h"
30 : #endif
31 : #include "jswrapper.h"
32 :
33 : #include "builtin/DataViewObject.h"
34 : #include "builtin/TypedObjectConstants.h"
35 : #include "gc/Barrier.h"
36 : #include "gc/Marking.h"
37 : #include "jit/InlinableNatives.h"
38 : #include "js/Conversions.h"
39 : #include "vm/ArrayBufferObject.h"
40 : #include "vm/GlobalObject.h"
41 : #include "vm/Interpreter.h"
42 : #include "vm/PIC.h"
43 : #include "vm/SelfHosting.h"
44 : #include "vm/SharedMem.h"
45 : #include "vm/WrapperObject.h"
46 :
47 : #include "jsatominlines.h"
48 :
49 : #include "gc/Nursery-inl.h"
50 : #include "gc/StoreBuffer-inl.h"
51 : #include "vm/ArrayBufferObject-inl.h"
52 : #include "vm/NativeObject-inl.h"
53 : #include "vm/Shape-inl.h"
54 :
55 : using namespace js;
56 : using namespace js::gc;
57 :
58 : using mozilla::AssertedCast;
59 : using JS::CanonicalizeNaN;
60 : using JS::ToInt32;
61 : using JS::ToUint32;
62 :
63 : /*
64 : * TypedArrayObject
65 : *
66 : * The non-templated base class for the specific typed implementations.
67 : * This class holds all the member variables that are used by
68 : * the subclasses.
69 : */
70 :
71 : /* static */ int
72 0 : TypedArrayObject::lengthOffset()
73 : {
74 0 : return NativeObject::getFixedSlotOffset(LENGTH_SLOT);
75 : }
76 :
77 : /* static */ int
78 0 : TypedArrayObject::dataOffset()
79 : {
80 0 : return NativeObject::getPrivateDataOffset(DATA_SLOT);
81 : }
82 :
83 : void
84 0 : TypedArrayObject::notifyBufferDetached(JSContext* cx, void* newData)
85 : {
86 0 : MOZ_ASSERT(!isSharedMemory());
87 0 : setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(0));
88 0 : setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(0));
89 :
90 : // If the object is in the nursery, the buffer will be freed by the next
91 : // nursery GC. Free the data slot pointer if the object has no inline data.
92 0 : Nursery& nursery = cx->nursery();
93 0 : if (isTenured() && !hasBuffer() && !hasInlineElements() &&
94 0 : !nursery.isInside(elements()))
95 : {
96 0 : js_free(elements());
97 : }
98 :
99 0 : setPrivate(newData);
100 0 : }
101 :
102 : /* static */ bool
103 0 : TypedArrayObject::is(HandleValue v)
104 : {
105 0 : return v.isObject() && v.toObject().is<TypedArrayObject>();
106 : }
107 :
108 : /* static */ bool
109 0 : TypedArrayObject::ensureHasBuffer(JSContext* cx, Handle<TypedArrayObject*> tarray)
110 : {
111 0 : if (tarray->hasBuffer())
112 0 : return true;
113 :
114 0 : Rooted<ArrayBufferObject*> buffer(cx, ArrayBufferObject::create(cx, tarray->byteLength()));
115 0 : if (!buffer)
116 0 : return false;
117 :
118 0 : if (!buffer->addView(cx, tarray))
119 0 : return false;
120 :
121 : // tarray is not shared, because if it were it would have a buffer.
122 0 : memcpy(buffer->dataPointer(), tarray->viewDataUnshared(), tarray->byteLength());
123 :
124 : // If the object is in the nursery, the buffer will be freed by the next
125 : // nursery GC. Free the data slot pointer if the object has no inline data.
126 0 : Nursery& nursery = cx->nursery();
127 0 : if (tarray->isTenured() && !tarray->hasInlineElements() &&
128 0 : !nursery.isInside(tarray->elements()))
129 : {
130 0 : js_free(tarray->elements());
131 : }
132 :
133 0 : tarray->setPrivate(buffer->dataPointer());
134 :
135 0 : tarray->setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*buffer));
136 :
137 : // Notify compiled jit code that the base pointer has moved.
138 0 : MarkObjectStateChange(cx, tarray);
139 :
140 0 : return true;
141 : }
142 :
143 : #ifdef DEBUG
144 : void
145 0 : TypedArrayObject::assertZeroLengthArrayData() const
146 : {
147 0 : if (length() == 0 && !hasBuffer()) {
148 0 : uint8_t* end = fixedData(TypedArrayObject::FIXED_DATA_START);
149 0 : MOZ_ASSERT(end[0] == ZeroLengthArrayData);
150 : }
151 0 : }
152 : #endif
153 :
154 : /* static */ void
155 0 : TypedArrayObject::trace(JSTracer* trc, JSObject* objArg)
156 : {
157 : // Handle all tracing required when the object has a buffer.
158 0 : ArrayBufferViewObject::trace(trc, objArg);
159 0 : }
160 :
161 : void
162 0 : TypedArrayObject::finalize(FreeOp* fop, JSObject* obj)
163 : {
164 0 : MOZ_ASSERT(!IsInsideNursery(obj));
165 0 : TypedArrayObject* curObj = &obj->as<TypedArrayObject>();
166 :
167 : // Template objects or discarded objects (which didn't have enough room
168 : // for inner elements). Don't have anything to free.
169 0 : if (!curObj->elementsRaw())
170 0 : return;
171 :
172 0 : curObj->assertZeroLengthArrayData();
173 :
174 : // Typed arrays with a buffer object do not need to be free'd
175 0 : if (curObj->hasBuffer())
176 0 : return;
177 :
178 : // Free the data slot pointer if it does not point into the old JSObject.
179 0 : if (!curObj->hasInlineElements())
180 0 : js_free(curObj->elements());
181 : }
182 :
183 : /* static */ void
184 0 : TypedArrayObject::objectMoved(JSObject* obj, const JSObject* old)
185 : {
186 0 : TypedArrayObject* newObj = &obj->as<TypedArrayObject>();
187 0 : const TypedArrayObject* oldObj = &old->as<TypedArrayObject>();
188 :
189 : // Typed arrays with a buffer object do not need an update.
190 0 : if (oldObj->hasBuffer())
191 0 : return;
192 :
193 : // Update the data slot pointer if it points to the old JSObject.
194 0 : if (oldObj->hasInlineElements())
195 0 : newObj->setInlineElements();
196 : }
197 :
198 : /* static */ size_t
199 0 : TypedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* obj, const JSObject* old,
200 : gc::AllocKind newAllocKind)
201 : {
202 0 : TypedArrayObject* newObj = &obj->as<TypedArrayObject>();
203 0 : const TypedArrayObject* oldObj = &old->as<TypedArrayObject>();
204 0 : MOZ_ASSERT(newObj->elementsRaw() == oldObj->elementsRaw());
205 0 : MOZ_ASSERT(obj->isTenured());
206 :
207 : // Typed arrays with a buffer object do not need an update.
208 0 : if (oldObj->hasBuffer())
209 0 : return 0;
210 :
211 0 : Nursery& nursery = obj->zone()->group()->nursery();
212 0 : void* buf = oldObj->elements();
213 :
214 0 : if (!nursery.isInside(buf)) {
215 0 : nursery.removeMallocedBuffer(buf);
216 0 : return 0;
217 : }
218 :
219 : // Determine if we can use inline data for the target array. If this is
220 : // possible, the nursery will have picked an allocation size that is large
221 : // enough.
222 0 : size_t nbytes = 0;
223 0 : switch (oldObj->type()) {
224 : #define OBJECT_MOVED_TYPED_ARRAY(T, N) \
225 : case Scalar::N: \
226 : nbytes = oldObj->length() * sizeof(T); \
227 : break;
228 0 : JS_FOR_EACH_TYPED_ARRAY(OBJECT_MOVED_TYPED_ARRAY)
229 : #undef OBJECT_MOVED_TYPED_ARRAY
230 : default:
231 0 : MOZ_CRASH("Unsupported TypedArray type");
232 : }
233 :
234 0 : size_t headerSize = dataOffset() + sizeof(HeapSlot);
235 :
236 : // See AllocKindForLazyBuffer.
237 0 : MOZ_ASSERT_IF(nbytes == 0, headerSize + sizeof(uint8_t) <= GetGCKindBytes(newAllocKind));
238 :
239 0 : if (headerSize + nbytes <= GetGCKindBytes(newAllocKind)) {
240 0 : MOZ_ASSERT(oldObj->hasInlineElements());
241 : #ifdef DEBUG
242 0 : if (nbytes == 0) {
243 0 : uint8_t* output = newObj->fixedData(TypedArrayObject::FIXED_DATA_START);
244 0 : output[0] = ZeroLengthArrayData;
245 : }
246 : #endif
247 0 : newObj->setInlineElements();
248 : } else {
249 0 : MOZ_ASSERT(!oldObj->hasInlineElements());
250 0 : AutoEnterOOMUnsafeRegion oomUnsafe;
251 0 : nbytes = JS_ROUNDUP(nbytes, sizeof(Value));
252 0 : void* data = newObj->zone()->pod_malloc<uint8_t>(nbytes);
253 0 : if (!data)
254 0 : oomUnsafe.crash("Failed to allocate typed array elements while tenuring.");
255 0 : MOZ_ASSERT(!nursery.isInside(data));
256 0 : newObj->initPrivate(data);
257 : }
258 :
259 0 : mozilla::PodCopy(newObj->elements(), oldObj->elements(), nbytes);
260 :
261 : // Set a forwarding pointer for the element buffers in case they were
262 : // preserved on the stack by Ion.
263 0 : nursery.maybeSetForwardingPointer(trc, oldObj->elements(), newObj->elements(),
264 0 : /* direct = */nbytes >= sizeof(uintptr_t));
265 :
266 0 : return newObj->hasInlineElements() ? 0 : nbytes;
267 : }
268 :
269 : bool
270 0 : TypedArrayObject::hasInlineElements() const
271 : {
272 0 : return elements() == this->fixedData(TypedArrayObject::FIXED_DATA_START) &&
273 0 : byteLength() <= TypedArrayObject::INLINE_BUFFER_LIMIT;
274 : }
275 :
276 : void
277 0 : TypedArrayObject::setInlineElements()
278 : {
279 0 : char* dataSlot = reinterpret_cast<char*>(this) + this->dataOffset();
280 0 : *reinterpret_cast<void**>(dataSlot) = this->fixedData(TypedArrayObject::FIXED_DATA_START);
281 0 : }
282 :
283 : /* Helper clamped uint8_t type */
284 :
285 : uint32_t JS_FASTCALL
286 0 : js::ClampDoubleToUint8(const double x)
287 : {
288 : // Not < so that NaN coerces to 0
289 0 : if (!(x >= 0))
290 0 : return 0;
291 :
292 0 : if (x > 255)
293 0 : return 255;
294 :
295 0 : double toTruncate = x + 0.5;
296 0 : uint8_t y = uint8_t(toTruncate);
297 :
298 : /*
299 : * now val is rounded to nearest, ties rounded up. We want
300 : * rounded to nearest ties to even, so check whether we had a
301 : * tie.
302 : */
303 0 : if (y == toTruncate) {
304 : /*
305 : * It was a tie (since adding 0.5 gave us the exact integer
306 : * we want). Since we rounded up, we either already have an
307 : * even number or we have an odd number but the number we
308 : * want is one less. So just unconditionally masking out the
309 : * ones bit should do the trick to get us the value we
310 : * want.
311 : */
312 0 : return y & ~1;
313 : }
314 :
315 0 : return y;
316 : }
317 :
318 : template<typename ElementType>
319 : static inline JSObject*
320 : NewArray(JSContext* cx, uint32_t nelements);
321 :
322 : namespace {
323 :
324 : enum class SpeciesConstructorOverride {
325 : None,
326 : ArrayBuffer
327 : };
328 :
329 : template<typename NativeType>
330 : class TypedArrayObjectTemplate : public TypedArrayObject
331 : {
332 : friend class TypedArrayObject;
333 :
334 : public:
335 60 : static constexpr Scalar::Type ArrayTypeID() { return TypeIDOfType<NativeType>::id; }
336 0 : static bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
337 0 : static bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); }
338 :
339 : static const size_t BYTES_PER_ELEMENT = sizeof(NativeType);
340 :
341 : static JSObject*
342 60 : createPrototype(JSContext* cx, JSProtoKey key)
343 : {
344 60 : Handle<GlobalObject*> global = cx->global();
345 120 : RootedObject typedArrayProto(cx, GlobalObject::getOrCreateTypedArrayPrototype(cx, global));
346 60 : if (!typedArrayProto)
347 0 : return nullptr;
348 :
349 60 : const Class* clasp = TypedArrayObject::protoClassForType(ArrayTypeID());
350 60 : return GlobalObject::createBlankPrototypeInheriting(cx, global, clasp, typedArrayProto);
351 : }
352 :
353 : static JSObject*
354 60 : createConstructor(JSContext* cx, JSProtoKey key)
355 : {
356 60 : Handle<GlobalObject*> global = cx->global();
357 120 : RootedFunction ctorProto(cx, GlobalObject::getOrCreateTypedArrayConstructor(cx, global));
358 60 : if (!ctorProto)
359 0 : return nullptr;
360 :
361 120 : JSFunction* fun = NewFunctionWithProto(cx, class_constructor, 3,
362 : JSFunction::NATIVE_CTOR, nullptr,
363 : ClassName(key, cx),
364 : ctorProto, gc::AllocKind::FUNCTION,
365 60 : SingletonObject);
366 :
367 60 : if (fun)
368 60 : fun->setJitInfo(&jit::JitInfo_TypedArrayConstructor);
369 :
370 60 : return fun;
371 : }
372 :
373 0 : static inline const Class* instanceClass()
374 : {
375 0 : return TypedArrayObject::classForType(ArrayTypeID());
376 : }
377 :
378 : static bool is(HandleValue v) {
379 : return v.isObject() && v.toObject().hasClass(instanceClass());
380 : }
381 :
382 : static void
383 0 : setIndexValue(TypedArrayObject& tarray, uint32_t index, double d)
384 : {
385 : // If the array is an integer array, we only handle up to
386 : // 32-bit ints from this point on. if we want to handle
387 : // 64-bit ints, we'll need some changes.
388 :
389 : // Assign based on characteristics of the destination type
390 0 : if (ArrayTypeIsFloatingPoint()) {
391 0 : setIndex(tarray, index, NativeType(d));
392 0 : } else if (ArrayTypeIsUnsigned()) {
393 0 : MOZ_ASSERT(sizeof(NativeType) <= 4);
394 0 : uint32_t n = ToUint32(d);
395 0 : setIndex(tarray, index, NativeType(n));
396 0 : } else if (ArrayTypeID() == Scalar::Uint8Clamped) {
397 : // The uint8_clamped type has a special rounding converter
398 : // for doubles.
399 0 : setIndex(tarray, index, NativeType(d));
400 : } else {
401 0 : MOZ_ASSERT(sizeof(NativeType) <= 4);
402 0 : int32_t n = ToInt32(d);
403 0 : setIndex(tarray, index, NativeType(n));
404 : }
405 0 : }
406 :
407 : static TypedArrayObject*
408 0 : makeProtoInstance(JSContext* cx, HandleObject proto, AllocKind allocKind)
409 : {
410 0 : MOZ_ASSERT(proto);
411 :
412 0 : JSObject* obj = NewObjectWithClassProto(cx, instanceClass(), proto, allocKind);
413 0 : return obj ? &obj->as<TypedArrayObject>() : nullptr;
414 : }
415 :
416 : static TypedArrayObject*
417 0 : makeTypedInstance(JSContext* cx, uint32_t len, gc::AllocKind allocKind)
418 : {
419 0 : const Class* clasp = instanceClass();
420 0 : if (len * sizeof(NativeType) >= TypedArrayObject::SINGLETON_BYTE_LENGTH) {
421 0 : JSObject* obj = NewBuiltinClassInstance(cx, clasp, allocKind, SingletonObject);
422 0 : if (!obj)
423 0 : return nullptr;
424 0 : return &obj->as<TypedArrayObject>();
425 : }
426 :
427 : jsbytecode* pc;
428 0 : RootedScript script(cx, cx->currentScript(&pc));
429 0 : NewObjectKind newKind = GenericObject;
430 0 : if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, clasp))
431 0 : newKind = SingletonObject;
432 0 : RootedObject obj(cx, NewBuiltinClassInstance(cx, clasp, allocKind, newKind));
433 0 : if (!obj)
434 0 : return nullptr;
435 :
436 0 : if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj,
437 : newKind == SingletonObject))
438 : {
439 0 : return nullptr;
440 : }
441 :
442 0 : return &obj->as<TypedArrayObject>();
443 : }
444 :
445 : static TypedArrayObject*
446 0 : makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, uint32_t byteOffset,
447 : uint32_t len, HandleObject proto)
448 : {
449 0 : MOZ_ASSERT_IF(!buffer, byteOffset == 0);
450 0 : MOZ_ASSERT_IF(buffer, !buffer->isDetached());
451 0 : MOZ_ASSERT(len < INT32_MAX / sizeof(NativeType));
452 :
453 : gc::AllocKind allocKind = buffer
454 0 : ? GetGCObjectKind(instanceClass())
455 0 : : AllocKindForLazyBuffer(len * sizeof(NativeType));
456 :
457 : // Subclassing mandates that we hand in the proto every time. Most of
458 : // the time, though, that [[Prototype]] will not be interesting. If
459 : // it isn't, we can do some more TI optimizations.
460 0 : RootedObject checkProto(cx);
461 0 : if (proto && !GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &checkProto))
462 0 : return nullptr;
463 :
464 0 : AutoSetNewObjectMetadata metadata(cx);
465 0 : Rooted<TypedArrayObject*> obj(cx);
466 0 : if (proto && proto != checkProto)
467 0 : obj = makeProtoInstance(cx, proto, allocKind);
468 : else
469 0 : obj = makeTypedInstance(cx, len, allocKind);
470 0 : if (!obj)
471 0 : return nullptr;
472 :
473 0 : bool isSharedMemory = buffer && IsSharedArrayBuffer(buffer.get());
474 :
475 0 : obj->setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectOrNullValue(buffer));
476 : // This is invariant. Self-hosting code that sets BUFFER_SLOT
477 : // (if it does) must maintain it, should it need to.
478 0 : if (isSharedMemory)
479 0 : obj->setIsSharedMemory();
480 :
481 0 : if (buffer) {
482 0 : obj->initViewData(buffer->dataPointerEither() + byteOffset);
483 :
484 : // If the buffer is for an inline typed object, the data pointer
485 : // may be in the nursery, so include a barrier to make sure this
486 : // object is updated if that typed object moves.
487 0 : auto ptr = buffer->dataPointerEither();
488 0 : if (!IsInsideNursery(obj) && cx->nursery().isInside(ptr)) {
489 : // Shared buffer data should never be nursery-allocated, so we
490 : // need to fail here if isSharedMemory. However, mmap() can
491 : // place a SharedArrayRawBuffer up against the bottom end of a
492 : // nursery chunk, and a zero-length buffer will erroneously be
493 : // perceived as being inside the nursery; sidestep that.
494 0 : if (isSharedMemory) {
495 0 : MOZ_ASSERT(buffer->byteLength() == 0 &&
496 : (uintptr_t(ptr.unwrapValue()) & gc::ChunkMask) == 0);
497 : } else {
498 0 : cx->zone()->group()->storeBuffer().putWholeCell(obj);
499 : }
500 : }
501 : } else {
502 0 : void* data = obj->fixedData(FIXED_DATA_START);
503 0 : obj->initPrivate(data);
504 0 : memset(data, 0, len * sizeof(NativeType));
505 : #ifdef DEBUG
506 0 : if (len == 0) {
507 0 : uint8_t* elements = static_cast<uint8_t*>(data);
508 0 : elements[0] = ZeroLengthArrayData;
509 : }
510 : #endif
511 : }
512 :
513 0 : obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(len));
514 0 : obj->setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
515 :
516 : #ifdef DEBUG
517 0 : if (buffer) {
518 0 : uint32_t arrayByteLength = obj->byteLength();
519 0 : uint32_t arrayByteOffset = obj->byteOffset();
520 0 : uint32_t bufferByteLength = buffer->byteLength();
521 : // Unwraps are safe: both are for the pointer value.
522 0 : if (IsArrayBuffer(buffer.get())) {
523 0 : MOZ_ASSERT_IF(!AsArrayBuffer(buffer.get()).isDetached(),
524 : buffer->dataPointerEither().unwrap(/*safe*/) <= obj->viewDataEither().unwrap(/*safe*/));
525 : }
526 0 : MOZ_ASSERT(bufferByteLength - arrayByteOffset >= arrayByteLength);
527 0 : MOZ_ASSERT(arrayByteOffset <= bufferByteLength);
528 : }
529 :
530 : // Verify that the private slot is at the expected place
531 0 : MOZ_ASSERT(obj->numFixedSlots() == TypedArrayObject::DATA_SLOT);
532 : #endif
533 :
534 : // ArrayBufferObjects track their views to support detaching.
535 0 : if (buffer && buffer->is<ArrayBufferObject>()) {
536 0 : if (!buffer->as<ArrayBufferObject>().addView(cx, obj))
537 0 : return nullptr;
538 : }
539 :
540 0 : return obj;
541 : }
542 :
543 : static TypedArrayObject*
544 0 : makeTemplateObject(JSContext* cx, int32_t len)
545 : {
546 0 : MOZ_ASSERT(len >= 0);
547 : size_t nbytes;
548 0 : MOZ_ALWAYS_TRUE(CalculateAllocSize<NativeType>(len, &nbytes));
549 0 : MOZ_ASSERT(nbytes < TypedArrayObject::SINGLETON_BYTE_LENGTH);
550 0 : NewObjectKind newKind = TenuredObject;
551 0 : bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
552 0 : const Class* clasp = instanceClass();
553 0 : gc::AllocKind allocKind = !fitsInline
554 0 : ? GetGCObjectKind(clasp)
555 0 : : AllocKindForLazyBuffer(nbytes);
556 0 : MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, clasp));
557 0 : allocKind = GetBackgroundAllocKind(allocKind);
558 :
559 0 : AutoSetNewObjectMetadata metadata(cx);
560 : jsbytecode* pc;
561 0 : RootedScript script(cx, cx->currentScript(&pc));
562 0 : if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, clasp))
563 0 : newKind = SingletonObject;
564 0 : JSObject* tmp = NewBuiltinClassInstance(cx, clasp, allocKind, newKind);
565 0 : if (!tmp)
566 0 : return nullptr;
567 :
568 0 : Rooted<TypedArrayObject*> tarray(cx, &tmp->as<TypedArrayObject>());
569 0 : initTypedArraySlots(cx, tarray, len);
570 :
571 : // Template objects do not need memory for its elements, since there
572 : // won't be any elements to store. Therefore, we set the pointer to
573 : // nullptr and avoid allocating memory that will never be used.
574 0 : tarray->initPrivate(nullptr);
575 :
576 0 : if (script && !ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, tarray,
577 : newKind == SingletonObject))
578 : {
579 0 : return nullptr;
580 : }
581 :
582 0 : return tarray;
583 : }
584 :
585 : static void
586 0 : initTypedArraySlots(JSContext* cx, TypedArrayObject* tarray, int32_t len)
587 : {
588 0 : MOZ_ASSERT(len >= 0);
589 0 : tarray->setFixedSlot(TypedArrayObject::BUFFER_SLOT, NullValue());
590 0 : tarray->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(AssertedCast<int32_t>(len)));
591 0 : tarray->setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(0));
592 :
593 : // Verify that the private slot is at the expected place.
594 0 : MOZ_ASSERT(tarray->numFixedSlots() == TypedArrayObject::DATA_SLOT);
595 :
596 : #ifdef DEBUG
597 0 : if (len == 0) {
598 0 : uint8_t* output = tarray->fixedData(TypedArrayObject::FIXED_DATA_START);
599 0 : output[0] = TypedArrayObject::ZeroLengthArrayData;
600 : }
601 : #endif
602 0 : }
603 :
604 : static void
605 0 : initTypedArrayData(JSContext* cx, TypedArrayObject* tarray, int32_t len,
606 : void* buf, AllocKind allocKind)
607 : {
608 0 : if (buf) {
609 : #ifdef DEBUG
610 0 : Nursery& nursery = cx->nursery();
611 0 : MOZ_ASSERT_IF(!nursery.isInside(buf) && !tarray->hasInlineElements(),
612 : tarray->isTenured());
613 : #endif
614 0 : tarray->initPrivate(buf);
615 : } else {
616 0 : size_t nbytes = len * sizeof(NativeType);
617 : #ifdef DEBUG
618 0 : size_t dataOffset = TypedArrayObject::dataOffset();
619 0 : size_t offset = dataOffset + sizeof(HeapSlot);
620 0 : MOZ_ASSERT(offset + nbytes <= GetGCKindBytes(allocKind));
621 : #endif
622 :
623 0 : void* data = tarray->fixedData(FIXED_DATA_START);
624 0 : tarray->initPrivate(data);
625 0 : memset(data, 0, nbytes);
626 : }
627 0 : }
628 :
629 : static TypedArrayObject*
630 0 : makeTypedArrayWithTemplate(JSContext* cx, TypedArrayObject* templateObj, int32_t len)
631 : {
632 0 : if (len < 0 || uint32_t(len) >= INT32_MAX / sizeof(NativeType)) {
633 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
634 0 : return nullptr;
635 : }
636 :
637 : size_t nbytes;
638 0 : MOZ_ALWAYS_TRUE(js::CalculateAllocSize<NativeType>(len, &nbytes));
639 :
640 0 : bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
641 :
642 0 : AutoSetNewObjectMetadata metadata(cx);
643 :
644 0 : const Class* clasp = templateObj->group()->clasp();
645 0 : gc::AllocKind allocKind = !fitsInline
646 0 : ? GetGCObjectKind(clasp)
647 0 : : AllocKindForLazyBuffer(nbytes);
648 0 : MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, clasp));
649 0 : allocKind = GetBackgroundAllocKind(allocKind);
650 0 : RootedObjectGroup group(cx, templateObj->group());
651 :
652 0 : NewObjectKind newKind = TenuredObject;
653 :
654 0 : ScopedJSFreePtr<void> buf;
655 0 : if (!fitsInline && len > 0) {
656 0 : buf = cx->zone()->pod_malloc<uint8_t>(nbytes);
657 0 : if (!buf) {
658 0 : ReportOutOfMemory(cx);
659 0 : return nullptr;
660 : }
661 :
662 0 : memset(buf, 0, nbytes);
663 : }
664 :
665 0 : RootedObject tmp(cx, NewObjectWithGroup<TypedArrayObject>(cx, group, allocKind, newKind));
666 0 : if (!tmp)
667 0 : return nullptr;
668 :
669 0 : TypedArrayObject* obj = &tmp->as<TypedArrayObject>();
670 0 : initTypedArraySlots(cx, obj, len);
671 0 : initTypedArrayData(cx, obj, len, buf.forget(), allocKind);
672 :
673 0 : return obj;
674 : }
675 :
676 : // ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
677 : // 22.2.4.1 TypedArray ( )
678 : // 22.2.4.2 TypedArray ( length )
679 : // 22.2.4.3 TypedArray ( typedArray )
680 : // 22.2.4.4 TypedArray ( object )
681 : // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
682 : static bool
683 0 : class_constructor(JSContext* cx, unsigned argc, Value* vp)
684 : {
685 0 : CallArgs args = CallArgsFromVp(argc, vp);
686 :
687 : // Step 1 (22.2.4.1) or 2 (22.2.4.2-5).
688 0 : if (!ThrowIfNotConstructing(cx, args, "typed array"))
689 0 : return false;
690 :
691 0 : JSObject* obj = create(cx, args);
692 0 : if (!obj)
693 0 : return false;
694 0 : args.rval().setObject(*obj);
695 0 : return true;
696 : }
697 :
698 : private:
699 : static JSObject*
700 0 : create(JSContext* cx, const CallArgs& args)
701 : {
702 0 : MOZ_ASSERT(args.isConstructing());
703 0 : RootedObject newTarget(cx, &args.newTarget().toObject());
704 :
705 : // 22.2.4.1 TypedArray ( )
706 : // 22.2.4.2 TypedArray ( length )
707 0 : if (args.length() == 0 || !args[0].isObject()) {
708 : // 22.2.4.2, step 3.
709 : uint64_t len;
710 0 : if (!ToIndex(cx, args.get(0), JSMSG_BAD_ARRAY_LENGTH, &len))
711 0 : return nullptr;
712 :
713 : // 22.2.4.1, step 3 and 22.2.4.2, step 5.
714 : // 22.2.4.2.1 AllocateTypedArray, step 1.
715 0 : RootedObject proto(cx);
716 0 : if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
717 0 : return nullptr;
718 :
719 0 : return fromLength(cx, len, proto);
720 : }
721 :
722 0 : RootedObject dataObj(cx, &args[0].toObject());
723 :
724 : // 22.2.4.{3,4,5}, step 4.
725 : // 22.2.4.2.1 AllocateTypedArray, step 1.
726 0 : RootedObject proto(cx);
727 0 : if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
728 0 : return nullptr;
729 :
730 : // 22.2.4.3 TypedArray ( typedArray )
731 : // 22.2.4.4 TypedArray ( object )
732 0 : if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObjectMaybeShared>())
733 0 : return fromArray(cx, dataObj, proto);
734 :
735 : // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
736 :
737 0 : uint64_t byteOffset = 0;
738 0 : if (args.hasDefined(1)) {
739 : // Step 6.
740 0 : if (!ToIndex(cx, args[1], &byteOffset))
741 0 : return nullptr;
742 :
743 : // Step 7.
744 0 : if (byteOffset % sizeof(NativeType) != 0) {
745 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
746 : JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
747 0 : return nullptr;
748 : }
749 : }
750 :
751 0 : uint64_t length = UINT64_MAX;
752 0 : if (args.hasDefined(2)) {
753 : // Step 8.a.
754 0 : if (!ToIndex(cx, args[2], &length))
755 0 : return nullptr;
756 : }
757 :
758 : // Steps 9-17.
759 0 : if (dataObj->is<ArrayBufferObjectMaybeShared>()) {
760 0 : HandleArrayBufferObjectMaybeShared buffer = dataObj.as<ArrayBufferObjectMaybeShared>();
761 0 : return fromBufferSameCompartment(cx, buffer, byteOffset, length, proto);
762 : }
763 0 : return fromBufferWrapped(cx, dataObj, byteOffset, length, proto);
764 : }
765 :
766 : // ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
767 : // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
768 : // Steps 9-12.
769 : static bool
770 0 : computeAndCheckLength(JSContext* cx, HandleArrayBufferObjectMaybeShared bufferMaybeUnwrapped,
771 : uint64_t byteOffset, uint64_t lengthIndex, uint32_t* length)
772 : {
773 0 : MOZ_ASSERT(byteOffset % sizeof(NativeType) == 0);
774 0 : MOZ_ASSERT(byteOffset < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
775 0 : MOZ_ASSERT_IF(lengthIndex != UINT64_MAX,
776 : lengthIndex < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
777 :
778 : // Step 9.
779 0 : if (bufferMaybeUnwrapped->isDetached()) {
780 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
781 0 : return false;
782 : }
783 :
784 : // Step 10.
785 0 : uint32_t bufferByteLength = bufferMaybeUnwrapped->byteLength();
786 :
787 : uint32_t len;
788 0 : if (lengthIndex == UINT64_MAX) {
789 : // Steps 11.a, 11.c.
790 0 : if (bufferByteLength % sizeof(NativeType) != 0 || byteOffset > bufferByteLength) {
791 : // The given byte array doesn't map exactly to
792 : // |sizeof(NativeType) * N| or |byteOffset| is invalid.
793 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
794 : JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
795 0 : return false;
796 : }
797 :
798 : // Step 11.b.
799 0 : uint32_t newByteLength = bufferByteLength - uint32_t(byteOffset);
800 0 : len = newByteLength / sizeof(NativeType);
801 : } else {
802 : // Step 12.a.
803 0 : uint64_t newByteLength = lengthIndex * sizeof(NativeType);
804 :
805 : // Step 12.b.
806 0 : if (byteOffset + newByteLength > bufferByteLength) {
807 : // |byteOffset + newByteLength| is too big for the arraybuffer
808 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
809 : JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
810 0 : return false;
811 : }
812 :
813 0 : len = uint32_t(lengthIndex);
814 : }
815 :
816 : // ArrayBuffer is too large for TypedArrays:
817 : // Standalone ArrayBuffers can hold up to INT32_MAX bytes, whereas
818 : // buffers in TypedArrays must have less than or equal to
819 : // |INT32_MAX - sizeof(NativeType) - INT32_MAX % sizeof(NativeType)|
820 : // bytes.
821 0 : if (len >= INT32_MAX / sizeof(NativeType)) {
822 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
823 : JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
824 0 : return false;
825 : }
826 0 : MOZ_ASSERT(byteOffset <= UINT32_MAX);
827 :
828 0 : *length = len;
829 0 : return true;
830 : }
831 :
832 : // ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
833 : // 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
834 : // Steps 9-17.
835 : static JSObject*
836 0 : fromBufferSameCompartment(JSContext* cx, HandleArrayBufferObjectMaybeShared buffer,
837 : uint64_t byteOffset, uint64_t lengthIndex, HandleObject proto)
838 : {
839 : // Steps 9-12.
840 : uint32_t length;
841 0 : if (!computeAndCheckLength(cx, buffer, byteOffset, lengthIndex, &length))
842 0 : return nullptr;
843 :
844 : // Steps 13-17.
845 0 : return makeInstance(cx, buffer, uint32_t(byteOffset), length, proto);
846 : }
847 :
848 : // Create a TypedArray object in another compartment.
849 : //
850 : // ES6 supports creating a TypedArray in global A (using global A's
851 : // TypedArray constructor) backed by an ArrayBuffer created in global B.
852 : //
853 : // Our TypedArrayObject implementation doesn't support a TypedArray in
854 : // compartment A backed by an ArrayBuffer in compartment B. So in this
855 : // case, we create the TypedArray in B (!) and return a cross-compartment
856 : // wrapper.
857 : //
858 : // Extra twist: the spec says the new TypedArray's [[Prototype]] must be
859 : // A's TypedArray.prototype. So even though we're creating the TypedArray
860 : // in B, its [[Prototype]] must be (a cross-compartment wrapper for) the
861 : // TypedArray.prototype in A.
862 : static JSObject*
863 0 : fromBufferWrapped(JSContext* cx, HandleObject bufobj, uint64_t byteOffset,
864 : uint64_t lengthIndex, HandleObject proto)
865 : {
866 0 : JSObject* unwrapped = CheckedUnwrap(bufobj);
867 0 : if (!unwrapped) {
868 0 : ReportAccessDenied(cx);
869 0 : return nullptr;
870 : }
871 :
872 0 : if (!unwrapped->is<ArrayBufferObjectMaybeShared>()) {
873 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
874 0 : return nullptr;
875 : }
876 :
877 0 : RootedArrayBufferObjectMaybeShared unwrappedBuffer(cx);
878 0 : unwrappedBuffer = &unwrapped->as<ArrayBufferObjectMaybeShared>();
879 :
880 : uint32_t length;
881 0 : if (!computeAndCheckLength(cx, unwrappedBuffer, byteOffset, lengthIndex, &length))
882 0 : return nullptr;
883 :
884 : // Make sure to get the [[Prototype]] for the created typed array from
885 : // this compartment.
886 0 : RootedObject protoRoot(cx, proto);
887 0 : if (!protoRoot) {
888 0 : if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &protoRoot))
889 0 : return nullptr;
890 : }
891 :
892 0 : RootedObject typedArray(cx);
893 : {
894 0 : JSAutoCompartment ac(cx, unwrappedBuffer);
895 :
896 0 : RootedObject wrappedProto(cx, protoRoot);
897 0 : if (!cx->compartment()->wrap(cx, &wrappedProto))
898 0 : return nullptr;
899 :
900 0 : typedArray =
901 0 : makeInstance(cx, unwrappedBuffer, uint32_t(byteOffset), length, wrappedProto);
902 0 : if (!typedArray)
903 0 : return nullptr;
904 : }
905 :
906 0 : if (!cx->compartment()->wrap(cx, &typedArray))
907 0 : return nullptr;
908 :
909 0 : return typedArray;
910 : }
911 :
912 : public:
913 : static JSObject*
914 0 : fromBuffer(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt)
915 : {
916 0 : if (byteOffset % sizeof(NativeType) != 0) {
917 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
918 : JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
919 0 : return nullptr; // invalid byteOffset
920 : }
921 :
922 0 : uint64_t lengthIndex = lengthInt >= 0 ? uint64_t(lengthInt) : UINT64_MAX;
923 0 : if (bufobj->is<ArrayBufferObjectMaybeShared>()) {
924 0 : HandleArrayBufferObjectMaybeShared buffer = bufobj.as<ArrayBufferObjectMaybeShared>();
925 0 : return fromBufferSameCompartment(cx, buffer, byteOffset, lengthIndex, nullptr);
926 : }
927 0 : return fromBufferWrapped(cx, bufobj, byteOffset, lengthIndex, nullptr);
928 : }
929 :
930 : static bool
931 0 : maybeCreateArrayBuffer(JSContext* cx, uint32_t count, uint32_t unit,
932 : HandleObject nonDefaultProto,
933 : MutableHandle<ArrayBufferObject*> buffer)
934 : {
935 0 : if (count >= INT32_MAX / unit) {
936 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
937 0 : return false;
938 : }
939 0 : uint32_t byteLength = count * unit;
940 :
941 0 : MOZ_ASSERT(byteLength < INT32_MAX);
942 : static_assert(INLINE_BUFFER_LIMIT % sizeof(NativeType) == 0,
943 : "ArrayBuffer inline storage shouldn't waste any space");
944 :
945 0 : if (!nonDefaultProto && byteLength <= INLINE_BUFFER_LIMIT) {
946 : // The array's data can be inline, and the buffer created lazily.
947 0 : return true;
948 : }
949 :
950 0 : ArrayBufferObject* buf = ArrayBufferObject::create(cx, byteLength, nonDefaultProto);
951 0 : if (!buf)
952 0 : return false;
953 :
954 0 : buffer.set(buf);
955 0 : return true;
956 : }
957 :
958 : // 22.2.4.1 TypedArray ( )
959 : // 22.2.4.2 TypedArray ( length )
960 : static JSObject*
961 0 : fromLength(JSContext* cx, uint64_t nelements, HandleObject proto = nullptr)
962 : {
963 : // 22.2.4.1, step 1 and 22.2.4.2, steps 1-3 (performed in caller).
964 : // 22.2.4.1, step 2 and 22.2.4.2, step 4 (implicit).
965 : // 22.2.4.1, step 3 and 22.2.4.2, step 5 (call AllocateTypedArray).
966 :
967 0 : if (nelements > UINT32_MAX) {
968 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
969 0 : return nullptr;
970 : }
971 :
972 0 : Rooted<ArrayBufferObject*> buffer(cx);
973 0 : if (!maybeCreateArrayBuffer(cx, uint32_t(nelements), BYTES_PER_ELEMENT, nullptr, &buffer))
974 0 : return nullptr;
975 :
976 0 : return makeInstance(cx, buffer, 0, uint32_t(nelements), proto);
977 : }
978 :
979 : static bool
980 : AllocateArrayBuffer(JSContext* cx, HandleValue ctor,
981 : uint32_t count, uint32_t unit,
982 : MutableHandle<ArrayBufferObject*> buffer);
983 :
984 : static bool
985 : CloneArrayBufferNoCopy(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> srcBuffer,
986 : bool isWrapped, uint32_t srcByteOffset, uint32_t srcLength,
987 : SpeciesConstructorOverride override,
988 : MutableHandle<ArrayBufferObject*> buffer);
989 :
990 : static JSObject*
991 : fromArray(JSContext* cx, HandleObject other, HandleObject proto = nullptr);
992 :
993 : static JSObject*
994 : fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped, HandleObject proto);
995 :
996 : static JSObject*
997 : fromObject(JSContext* cx, HandleObject other, HandleObject proto);
998 :
999 : static const NativeType
1000 0 : getIndex(JSObject* obj, uint32_t index)
1001 : {
1002 0 : TypedArrayObject& tarray = obj->as<TypedArrayObject>();
1003 0 : MOZ_ASSERT(index < tarray.length());
1004 0 : return jit::AtomicOperations::loadSafeWhenRacy(tarray.viewDataEither().cast<NativeType*>() + index);
1005 : }
1006 :
1007 : static void
1008 0 : setIndex(TypedArrayObject& tarray, uint32_t index, NativeType val)
1009 : {
1010 0 : MOZ_ASSERT(index < tarray.length());
1011 0 : jit::AtomicOperations::storeSafeWhenRacy(tarray.viewDataEither().cast<NativeType*>() + index, val);
1012 0 : }
1013 :
1014 : static Value getIndexValue(JSObject* tarray, uint32_t index);
1015 : };
1016 :
1017 : #define CREATE_TYPE_FOR_TYPED_ARRAY(T, N) \
1018 : typedef TypedArrayObjectTemplate<T> N##Array;
1019 : JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPE_FOR_TYPED_ARRAY)
1020 : #undef CREATE_TYPE_FOR_TYPED_ARRAY
1021 :
1022 : } /* anonymous namespace */
1023 :
1024 : TypedArrayObject*
1025 0 : js::TypedArrayCreateWithTemplate(JSContext* cx, HandleObject templateObj, int32_t len)
1026 : {
1027 0 : MOZ_ASSERT(templateObj->is<TypedArrayObject>());
1028 0 : TypedArrayObject* tobj = &templateObj->as<TypedArrayObject>();
1029 :
1030 0 : switch (tobj->type()) {
1031 : #define CREATE_TYPED_ARRAY(T, N) \
1032 : case Scalar::N: \
1033 : return TypedArrayObjectTemplate<T>::makeTypedArrayWithTemplate(cx, tobj, len);
1034 0 : JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
1035 : #undef CREATE_TYPED_ARRAY
1036 : default:
1037 0 : MOZ_CRASH("Unsupported TypedArray type");
1038 : }
1039 : }
1040 :
1041 : // ES 2016 draft Mar 25, 2016 24.1.1.1.
1042 : // byteLength = count * unit
1043 : template<typename T>
1044 : /* static */ bool
1045 0 : TypedArrayObjectTemplate<T>::AllocateArrayBuffer(JSContext* cx, HandleValue ctor,
1046 : uint32_t count, uint32_t unit,
1047 : MutableHandle<ArrayBufferObject*> buffer)
1048 : {
1049 : // ES 2016 draft Mar 25, 2016 24.1.1.1 step 1 (partially).
1050 : // ES 2016 draft Mar 25, 2016 9.1.14 steps 1-2.
1051 0 : MOZ_ASSERT(ctor.isObject());
1052 0 : RootedObject proto(cx);
1053 0 : RootedObject ctorObj(cx, &ctor.toObject());
1054 0 : if (!GetPrototypeFromConstructor(cx, ctorObj, &proto))
1055 0 : return false;
1056 0 : JSObject* arrayBufferProto = GlobalObject::getOrCreateArrayBufferPrototype(cx, cx->global());
1057 0 : if (!arrayBufferProto)
1058 0 : return false;
1059 0 : if (proto == arrayBufferProto)
1060 0 : proto = nullptr;
1061 :
1062 : // ES 2016 draft Mar 25, 2016 24.1.1.1 steps 1 (remaining part), 2-6.
1063 0 : if (!maybeCreateArrayBuffer(cx, count, unit, proto, buffer))
1064 0 : return false;
1065 :
1066 0 : return true;
1067 : }
1068 :
1069 : static bool
1070 0 : IsArrayBufferConstructor(const Value& v)
1071 : {
1072 0 : return v.isObject() &&
1073 0 : v.toObject().is<JSFunction>() &&
1074 0 : v.toObject().as<JSFunction>().isNative() &&
1075 0 : v.toObject().as<JSFunction>().native() == ArrayBufferObject::class_constructor;
1076 : }
1077 :
1078 : static bool
1079 0 : IsArrayBufferSpecies(JSContext* cx, HandleObject origBuffer)
1080 : {
1081 0 : RootedValue ctor(cx);
1082 0 : if (!GetPropertyPure(cx, origBuffer, NameToId(cx->names().constructor), ctor.address()))
1083 0 : return false;
1084 :
1085 0 : if (!IsArrayBufferConstructor(ctor))
1086 0 : return false;
1087 :
1088 0 : RootedObject ctorObj(cx, &ctor.toObject());
1089 0 : RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
1090 : JSFunction* getter;
1091 0 : if (!GetGetterPure(cx, ctorObj, speciesId, &getter))
1092 0 : return false;
1093 :
1094 0 : if (!getter)
1095 0 : return false;
1096 :
1097 0 : return IsSelfHostedFunctionWithName(getter, cx->names().ArrayBufferSpecies);
1098 : }
1099 :
1100 : static bool
1101 0 : GetSpeciesConstructor(JSContext* cx, HandleObject obj, bool isWrapped,
1102 : SpeciesConstructorOverride override, MutableHandleValue ctor)
1103 : {
1104 0 : if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_ArrayBuffer))
1105 0 : return false;
1106 0 : RootedValue defaultCtor(cx, cx->global()->getConstructor(JSProto_ArrayBuffer));
1107 :
1108 : // Use the current global's ArrayBuffer if the override is set.
1109 0 : if (override == SpeciesConstructorOverride::ArrayBuffer) {
1110 0 : ctor.set(defaultCtor);
1111 0 : return true;
1112 : }
1113 :
1114 0 : if (!isWrapped) {
1115 : // As an optimization, avoid calling into self-hosted code if |obj|'s
1116 : // constructor is the built-in ArrayBuffer and the constructor's
1117 : // species property is the original ArrayBuffer[@@species] function.
1118 0 : if (IsArrayBufferSpecies(cx, obj))
1119 0 : ctor.set(defaultCtor);
1120 0 : else if (!SpeciesConstructor(cx, obj, defaultCtor, ctor))
1121 0 : return false;
1122 :
1123 0 : return true;
1124 : }
1125 :
1126 0 : RootedObject wrappedObj(cx, obj);
1127 0 : if (!cx->compartment()->wrap(cx, &wrappedObj))
1128 0 : return false;
1129 :
1130 0 : if (!SpeciesConstructor(cx, wrappedObj, defaultCtor, ctor))
1131 0 : return false;
1132 :
1133 0 : return true;
1134 : }
1135 :
1136 : // ES 2017 draft rev 8633ffd9394b203b8876bb23cb79aff13eb07310 24.1.1.4.
1137 : template<typename T>
1138 : /* static */ bool
1139 0 : TypedArrayObjectTemplate<T>::CloneArrayBufferNoCopy(JSContext* cx,
1140 : Handle<ArrayBufferObjectMaybeShared*> srcBuffer,
1141 : bool isWrapped, uint32_t srcByteOffset,
1142 : uint32_t srcLength,
1143 : SpeciesConstructorOverride override,
1144 : MutableHandle<ArrayBufferObject*> buffer)
1145 : {
1146 : // Step 1 (skipped).
1147 :
1148 : // Step 2.a.
1149 0 : RootedValue cloneCtor(cx);
1150 0 : if (!GetSpeciesConstructor(cx, srcBuffer, isWrapped, override, &cloneCtor))
1151 0 : return false;
1152 :
1153 : // Step 2.b.
1154 0 : if (srcBuffer->isDetached()) {
1155 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
1156 0 : return false;
1157 : }
1158 :
1159 : // Steps 3-4 (skipped).
1160 :
1161 : // Steps 5.
1162 0 : if (!AllocateArrayBuffer(cx, cloneCtor, srcLength, 1, buffer))
1163 0 : return false;
1164 :
1165 : // Step 6.
1166 0 : if (srcBuffer->isDetached()) {
1167 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
1168 0 : return false;
1169 : }
1170 :
1171 : // Steps 7-8 (done in caller).
1172 :
1173 : // Step 9.
1174 0 : return true;
1175 : }
1176 :
1177 : template<typename T>
1178 : /* static */ JSObject*
1179 0 : TypedArrayObjectTemplate<T>::fromArray(JSContext* cx, HandleObject other,
1180 : HandleObject proto /* = nullptr */)
1181 : {
1182 : // Allow nullptr proto for FriendAPI methods, which don't care about
1183 : // subclassing.
1184 0 : if (other->is<TypedArrayObject>())
1185 0 : return fromTypedArray(cx, other, /* wrapped= */ false, proto);
1186 :
1187 0 : if (other->is<WrapperObject>() && UncheckedUnwrap(other)->is<TypedArrayObject>())
1188 0 : return fromTypedArray(cx, other, /* wrapped= */ true, proto);
1189 :
1190 0 : return fromObject(cx, other, proto);
1191 : }
1192 :
1193 : // ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
1194 : // 22.2.4.3 TypedArray ( typedArray )
1195 : template<typename T>
1196 : /* static */ JSObject*
1197 0 : TypedArrayObjectTemplate<T>::fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped,
1198 : HandleObject proto)
1199 : {
1200 : // Step 1.
1201 0 : MOZ_ASSERT_IF(!isWrapped, other->is<TypedArrayObject>());
1202 0 : MOZ_ASSERT_IF(isWrapped,
1203 : other->is<WrapperObject>() &&
1204 : UncheckedUnwrap(other)->is<TypedArrayObject>());
1205 :
1206 : // Step 2 (Already performed in caller).
1207 :
1208 : // Step 4 (Allocation deferred until later).
1209 :
1210 : // Step 5.
1211 0 : Rooted<TypedArrayObject*> srcArray(cx);
1212 0 : if (!isWrapped) {
1213 0 : srcArray = &other->as<TypedArrayObject>();
1214 0 : if (!TypedArrayObject::ensureHasBuffer(cx, srcArray))
1215 0 : return nullptr;
1216 : } else {
1217 0 : RootedObject unwrapped(cx, CheckedUnwrap(other));
1218 0 : if (!unwrapped) {
1219 0 : ReportAccessDenied(cx);
1220 0 : return nullptr;
1221 : }
1222 :
1223 0 : JSAutoCompartment ac(cx, unwrapped);
1224 :
1225 0 : srcArray = &unwrapped->as<TypedArrayObject>();
1226 0 : if (!TypedArrayObject::ensureHasBuffer(cx, srcArray))
1227 0 : return nullptr;
1228 : }
1229 :
1230 : // Step 6.
1231 0 : Rooted<ArrayBufferObjectMaybeShared*> srcData(cx, srcArray->bufferEither());
1232 :
1233 : // Step 7.
1234 0 : if (srcData->isDetached()) {
1235 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
1236 0 : return nullptr;
1237 : }
1238 :
1239 : // Step 9.
1240 0 : uint32_t elementLength = srcArray->length();
1241 :
1242 : // Steps 10-11.
1243 0 : Scalar::Type srcType = srcArray->type();
1244 :
1245 : // Step 12 (skipped).
1246 :
1247 : // Step 13.
1248 0 : uint32_t srcByteOffset = srcArray->byteOffset();
1249 :
1250 : // Steps 16-17.
1251 0 : bool isShared = srcArray->isSharedMemory();
1252 : SpeciesConstructorOverride override = isShared ? SpeciesConstructorOverride::ArrayBuffer
1253 0 : : SpeciesConstructorOverride::None;
1254 :
1255 : // Steps 8, 16-17.
1256 0 : Rooted<ArrayBufferObject*> buffer(cx);
1257 0 : if (ArrayTypeID() == srcType) {
1258 : // Step 16.a.
1259 0 : uint32_t srcLength = srcArray->byteLength();
1260 :
1261 : // Steps 16.b-c.
1262 0 : if (!CloneArrayBufferNoCopy(cx, srcData, isWrapped, srcByteOffset, srcLength, override,
1263 : &buffer))
1264 : {
1265 0 : return nullptr;
1266 : }
1267 : } else {
1268 : // Steps 17.a-b.
1269 0 : RootedValue bufferCtor(cx);
1270 0 : if (!GetSpeciesConstructor(cx, srcData, isWrapped, override, &bufferCtor))
1271 0 : return nullptr;
1272 :
1273 : // Steps 14-15, 17.c.
1274 0 : if (!AllocateArrayBuffer(cx, bufferCtor, elementLength, BYTES_PER_ELEMENT, &buffer))
1275 0 : return nullptr;
1276 :
1277 : // Step 17.d.
1278 0 : if (srcArray->hasDetachedBuffer()) {
1279 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
1280 0 : return nullptr;
1281 : }
1282 : }
1283 :
1284 : // Steps 3-4 (remaining part), 18-21.
1285 0 : Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, elementLength, proto));
1286 0 : if (!obj)
1287 0 : return nullptr;
1288 :
1289 : // Steps 17.e-h or 24.1.1.4 step 8.
1290 0 : MOZ_ASSERT(!obj->isSharedMemory());
1291 0 : if (isShared) {
1292 0 : if (!ElementSpecific<T, SharedOps>::setFromTypedArray(cx, obj, srcArray, 0))
1293 0 : return nullptr;
1294 : } else {
1295 0 : if (!ElementSpecific<T, UnsharedOps>::setFromTypedArray(cx, obj, srcArray, 0))
1296 0 : return nullptr;
1297 : }
1298 :
1299 : // Step 22.
1300 0 : return obj;
1301 : }
1302 :
1303 : static MOZ_ALWAYS_INLINE bool
1304 0 : IsOptimizableInit(JSContext* cx, HandleObject iterable, bool* optimized)
1305 : {
1306 0 : MOZ_ASSERT(!*optimized);
1307 :
1308 0 : if (!IsPackedArray(iterable))
1309 0 : return true;
1310 :
1311 0 : ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx);
1312 0 : if (!stubChain)
1313 0 : return false;
1314 :
1315 0 : return stubChain->tryOptimizeArray(cx, iterable.as<ArrayObject>(), optimized);
1316 : }
1317 :
1318 : // ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
1319 : // 22.2.4.4 TypedArray ( object )
1320 : template<typename T>
1321 : /* static */ JSObject*
1322 0 : TypedArrayObjectTemplate<T>::fromObject(JSContext* cx, HandleObject other, HandleObject proto)
1323 : {
1324 : // Steps 1-2 (Already performed in caller).
1325 :
1326 : // Steps 3-4 (Allocation deferred until later).
1327 :
1328 0 : bool optimized = false;
1329 0 : if (!IsOptimizableInit(cx, other, &optimized))
1330 0 : return nullptr;
1331 :
1332 : // Fast path when iterable is a packed array using the default iterator.
1333 0 : if (optimized) {
1334 : // Step 6.a (We don't need to call IterableToList for the fast path).
1335 0 : RootedArrayObject array(cx, &other->as<ArrayObject>());
1336 :
1337 : // Step 6.b.
1338 0 : uint32_t len = array->getDenseInitializedLength();
1339 :
1340 : // Step 6.c.
1341 0 : Rooted<ArrayBufferObject*> buffer(cx);
1342 0 : if (!maybeCreateArrayBuffer(cx, len, BYTES_PER_ELEMENT, nullptr, &buffer))
1343 0 : return nullptr;
1344 :
1345 0 : Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, len, proto));
1346 0 : if (!obj)
1347 0 : return nullptr;
1348 :
1349 : // Steps 6.d-e.
1350 0 : MOZ_ASSERT(!obj->isSharedMemory());
1351 0 : if (!ElementSpecific<T, UnsharedOps>::initFromIterablePackedArray(cx, obj, array))
1352 0 : return nullptr;
1353 :
1354 : // Step 6.f (The assertion isn't applicable for the fast path).
1355 :
1356 : // Step 6.g.
1357 0 : return obj;
1358 : }
1359 :
1360 : // Step 5.
1361 0 : RootedValue callee(cx);
1362 0 : RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
1363 0 : if (!GetProperty(cx, other, other, iteratorId, &callee))
1364 0 : return nullptr;
1365 :
1366 : // Steps 6-8.
1367 0 : RootedObject arrayLike(cx);
1368 0 : if (!callee.isNullOrUndefined()) {
1369 : // Throw if other[Symbol.iterator] isn't callable.
1370 0 : if (!callee.isObject() || !callee.toObject().isCallable()) {
1371 0 : RootedValue otherVal(cx, ObjectValue(*other));
1372 0 : UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, otherVal, nullptr);
1373 0 : if (!bytes)
1374 0 : return nullptr;
1375 0 : JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
1376 : bytes.get());
1377 0 : return nullptr;
1378 : }
1379 :
1380 0 : FixedInvokeArgs<2> args2(cx);
1381 0 : args2[0].setObject(*other);
1382 0 : args2[1].set(callee);
1383 :
1384 : // Step 6.a.
1385 0 : RootedValue rval(cx);
1386 0 : if (!CallSelfHostedFunction(cx, cx->names().IterableToList, UndefinedHandleValue, args2,
1387 : &rval))
1388 : {
1389 0 : return nullptr;
1390 : }
1391 :
1392 : // Steps 6.b-g (Implemented in steps 9-13 below).
1393 0 : arrayLike = &rval.toObject();
1394 : } else {
1395 : // Step 7 is an assertion: object is not an Iterator. Testing this is
1396 : // literally the very last thing we did, so we don't assert here.
1397 :
1398 : // Step 8.
1399 0 : arrayLike = other;
1400 : }
1401 :
1402 : // Step 9.
1403 : uint32_t len;
1404 0 : if (!GetLengthProperty(cx, arrayLike, &len))
1405 0 : return nullptr;
1406 :
1407 : // Step 10.
1408 0 : Rooted<ArrayBufferObject*> buffer(cx);
1409 0 : if (!maybeCreateArrayBuffer(cx, len, BYTES_PER_ELEMENT, nullptr, &buffer))
1410 0 : return nullptr;
1411 :
1412 0 : Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, len, proto));
1413 0 : if (!obj)
1414 0 : return nullptr;
1415 :
1416 : // Steps 11-12.
1417 0 : MOZ_ASSERT(!obj->isSharedMemory());
1418 0 : if (!ElementSpecific<T, UnsharedOps>::setFromNonTypedArray(cx, obj, arrayLike, len))
1419 0 : return nullptr;
1420 :
1421 : // Step 13.
1422 0 : return obj;
1423 : }
1424 :
1425 : bool
1426 0 : TypedArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
1427 : {
1428 0 : CallArgs args = CallArgsFromVp(argc, vp);
1429 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT,
1430 0 : args.isConstructing() ? "construct" : "call");
1431 0 : return false;
1432 : }
1433 :
1434 : /* static */ bool
1435 550 : TypedArrayObject::GetTemplateObjectForNative(JSContext* cx, Native native, uint32_t len,
1436 : MutableHandleObject res)
1437 : {
1438 : #define CHECK_TYPED_ARRAY_CONSTRUCTOR(T, N) \
1439 : if (native == &TypedArrayObjectTemplate<T>::class_constructor) { \
1440 : size_t nbytes; \
1441 : if (!js::CalculateAllocSize<T>(len, &nbytes)) \
1442 : return true; \
1443 : \
1444 : if (nbytes < TypedArrayObject::SINGLETON_BYTE_LENGTH) { \
1445 : res.set(TypedArrayObjectTemplate<T>::makeTemplateObject(cx, len)); \
1446 : return !!res; \
1447 : } \
1448 : }
1449 550 : JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR)
1450 : #undef CHECK_TYPED_ARRAY_CONSTRUCTOR
1451 550 : return true;
1452 : }
1453 :
1454 : /*
1455 : * These next 3 functions are brought to you by the buggy GCC we use to build
1456 : * B2G ICS. Older GCC versions have a bug in which they fail to compile
1457 : * reinterpret_casts of templated functions with the message: "insufficient
1458 : * contextual information to determine type". JS_PSG needs to
1459 : * reinterpret_cast<JSGetterOp>, so this causes problems for us here.
1460 : *
1461 : * We could restructure all this code to make this nicer, but since ICS isn't
1462 : * going to be around forever (and since this bug is fixed with the newer GCC
1463 : * versions we use on JB and KK), the workaround here is designed for ease of
1464 : * removal. When you stop seeing ICS Emulator builds on TBPL, remove these 3
1465 : * JSNatives and insert the templated callee directly into the JS_PSG below.
1466 : */
1467 : static bool
1468 0 : TypedArray_lengthGetter(JSContext* cx, unsigned argc, Value* vp)
1469 : {
1470 0 : return TypedArrayObject::Getter<TypedArrayObject::lengthValue>(cx, argc, vp);
1471 : }
1472 :
1473 : static bool
1474 0 : TypedArray_byteLengthGetter(JSContext* cx, unsigned argc, Value* vp)
1475 : {
1476 0 : return TypedArrayObject::Getter<TypedArrayObject::byteLengthValue>(cx, argc, vp);
1477 : }
1478 :
1479 : static bool
1480 0 : TypedArray_byteOffsetGetter(JSContext* cx, unsigned argc, Value* vp)
1481 : {
1482 0 : return TypedArrayObject::Getter<TypedArrayObject::byteOffsetValue>(cx, argc, vp);
1483 : }
1484 :
1485 : bool
1486 0 : BufferGetterImpl(JSContext* cx, const CallArgs& args)
1487 : {
1488 0 : MOZ_ASSERT(TypedArrayObject::is(args.thisv()));
1489 0 : Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
1490 0 : if (!TypedArrayObject::ensureHasBuffer(cx, tarray))
1491 0 : return false;
1492 0 : args.rval().set(TypedArrayObject::bufferValue(tarray));
1493 0 : return true;
1494 : }
1495 :
1496 : /*static*/ bool
1497 0 : js::TypedArray_bufferGetter(JSContext* cx, unsigned argc, Value* vp)
1498 : {
1499 0 : CallArgs args = CallArgsFromVp(argc, vp);
1500 0 : return CallNonGenericMethod<TypedArrayObject::is, BufferGetterImpl>(cx, args);
1501 : }
1502 :
1503 : /* static */ const JSPropertySpec
1504 : TypedArrayObject::protoAccessors[] = {
1505 : JS_PSG("length", TypedArray_lengthGetter, 0),
1506 : JS_PSG("buffer", TypedArray_bufferGetter, 0),
1507 : JS_PSG("byteLength", TypedArray_byteLengthGetter, 0),
1508 : JS_PSG("byteOffset", TypedArray_byteOffsetGetter, 0),
1509 : JS_SELF_HOSTED_SYM_GET(toStringTag, "TypedArrayToStringTag", 0),
1510 : JS_PS_END
1511 : };
1512 :
1513 : template<typename T>
1514 : static inline bool
1515 0 : SetFromTypedArray(JSContext* cx, Handle<TypedArrayObject*> target,
1516 : Handle<TypedArrayObject*> source, uint32_t offset)
1517 : {
1518 : // WARNING: |source| may be an unwrapped typed array from a different
1519 : // compartment. Proceed with caution!
1520 :
1521 0 : if (target->isSharedMemory() || source->isSharedMemory())
1522 0 : return ElementSpecific<T, SharedOps>::setFromTypedArray(cx, target, source, offset);
1523 0 : return ElementSpecific<T, UnsharedOps>::setFromTypedArray(cx, target, source, offset);
1524 : }
1525 :
1526 : template<typename T>
1527 : static inline bool
1528 0 : SetFromNonTypedArray(JSContext* cx, Handle<TypedArrayObject*> target, HandleObject source,
1529 : uint32_t len, uint32_t offset)
1530 : {
1531 0 : MOZ_ASSERT(!source->is<TypedArrayObject>(), "use SetFromTypedArray");
1532 :
1533 0 : if (target->isSharedMemory())
1534 0 : return ElementSpecific<T, SharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
1535 0 : return ElementSpecific<T, UnsharedOps>::setFromNonTypedArray(cx, target, source, len, offset);
1536 : }
1537 :
1538 : // ES2017 draft rev c57ef95c45a371f9c9485bb1c3881dbdc04524a2
1539 : // 22.2.3.23 %TypedArray%.prototype.set ( overloaded [ , offset ] )
1540 : // 22.2.3.23.1 %TypedArray%.prototype.set ( array [ , offset ] )
1541 : // 22.2.3.23.2 %TypedArray%.prototype.set( typedArray [ , offset ] )
1542 : /* static */ bool
1543 0 : TypedArrayObject::set_impl(JSContext* cx, const CallArgs& args)
1544 : {
1545 0 : MOZ_ASSERT(TypedArrayObject::is(args.thisv()));
1546 :
1547 : // Steps 1-5 (Validation performed as part of CallNonGenericMethod).
1548 0 : Rooted<TypedArrayObject*> target(cx, &args.thisv().toObject().as<TypedArrayObject>());
1549 :
1550 : // Steps 6-7.
1551 0 : double targetOffset = 0;
1552 0 : if (args.length() > 1) {
1553 : // Step 6.
1554 0 : if (!ToInteger(cx, args[1], &targetOffset))
1555 0 : return false;
1556 :
1557 : // Step 7.
1558 0 : if (targetOffset < 0) {
1559 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
1560 0 : return false;
1561 : }
1562 : }
1563 :
1564 : // Steps 8-9.
1565 0 : if (target->hasDetachedBuffer()) {
1566 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
1567 0 : return false;
1568 : }
1569 :
1570 : // 22.2.3.23.1, step 15. (22.2.3.23.2 only applies if args[0] is a typed
1571 : // array, so it doesn't make a difference there to apply ToObject here.)
1572 0 : RootedObject src(cx, ToObject(cx, args.get(0)));
1573 0 : if (!src)
1574 0 : return false;
1575 :
1576 0 : Rooted<TypedArrayObject*> srcTypedArray(cx);
1577 : {
1578 0 : JSObject* obj = CheckedUnwrap(src);
1579 0 : if (!obj) {
1580 0 : ReportAccessDenied(cx);
1581 0 : return false;
1582 : }
1583 :
1584 0 : if (obj->is<TypedArrayObject>())
1585 0 : srcTypedArray = &obj->as<TypedArrayObject>();
1586 : }
1587 :
1588 0 : if (srcTypedArray) {
1589 : // Remaining steps of 22.2.3.23.2.
1590 :
1591 : // WARNING: |srcTypedArray| may be an unwrapped typed array from a
1592 : // different compartment. Proceed with caution!
1593 :
1594 : // Steps 11-12.
1595 0 : if (srcTypedArray->hasDetachedBuffer()) {
1596 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
1597 0 : return false;
1598 : }
1599 :
1600 : // Step 10 (Reordered).
1601 0 : uint32_t targetLength = target->length();
1602 :
1603 : // Step 22 (Split into two checks to provide better error messages).
1604 0 : if (targetOffset > targetLength) {
1605 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
1606 0 : return false;
1607 : }
1608 :
1609 : // Step 22 (Cont'd).
1610 0 : uint32_t offset = uint32_t(targetOffset);
1611 0 : if (srcTypedArray->length() > targetLength - offset) {
1612 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
1613 0 : return false;
1614 : }
1615 :
1616 : // Steps 13-21, 23-28.
1617 0 : switch (target->type()) {
1618 : #define SET_FROM_TYPED_ARRAY(T, N) \
1619 : case Scalar::N: \
1620 : if (!SetFromTypedArray<T>(cx, target, srcTypedArray, offset)) \
1621 : return false; \
1622 : break;
1623 0 : JS_FOR_EACH_TYPED_ARRAY(SET_FROM_TYPED_ARRAY)
1624 : #undef SET_FROM_TYPED_ARRAY
1625 : default:
1626 0 : MOZ_CRASH("Unsupported TypedArray type");
1627 : }
1628 : } else {
1629 : // Remaining steps of 22.2.3.23.1.
1630 :
1631 : // Step 10.
1632 : // We can't reorder this step because side-effects in step 16 can
1633 : // detach the underlying array buffer from the typed array.
1634 0 : uint32_t targetLength = target->length();
1635 :
1636 : // Step 16.
1637 : uint32_t srcLength;
1638 0 : if (!GetLengthProperty(cx, src, &srcLength))
1639 0 : return false;
1640 :
1641 : // Step 17 (Split into two checks to provide better error messages).
1642 0 : if (targetOffset > targetLength) {
1643 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
1644 0 : return false;
1645 : }
1646 :
1647 : // Step 17 (Cont'd).
1648 0 : uint32_t offset = uint32_t(targetOffset);
1649 0 : if (srcLength > targetLength - offset) {
1650 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
1651 0 : return false;
1652 : }
1653 :
1654 : // Steps 11-14, 18-21.
1655 0 : if (srcLength > 0) {
1656 : // GetLengthProperty in step 16 can lead to the execution of user
1657 : // code which may detach the buffer. Handle this case here to
1658 : // ensure SetFromNonTypedArray is never called with a detached
1659 : // buffer. We still need to execute steps 21.a-b for their
1660 : // possible side-effects.
1661 0 : if (target->hasDetachedBuffer()) {
1662 : // Steps 21.a-b.
1663 0 : RootedValue v(cx);
1664 0 : if (!GetElement(cx, src, src, 0, &v))
1665 0 : return false;
1666 :
1667 : double unused;
1668 0 : if (!ToNumber(cx, v, &unused))
1669 0 : return false;
1670 :
1671 : // Step 21.c.
1672 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1673 0 : JSMSG_TYPED_ARRAY_DETACHED);
1674 0 : return false;
1675 : }
1676 :
1677 0 : switch (target->type()) {
1678 : #define SET_FROM_NON_TYPED_ARRAY(T, N) \
1679 : case Scalar::N: \
1680 : if (!SetFromNonTypedArray<T>(cx, target, src, srcLength, offset)) \
1681 : return false; \
1682 : break;
1683 0 : JS_FOR_EACH_TYPED_ARRAY(SET_FROM_NON_TYPED_ARRAY)
1684 : #undef SET_FROM_NON_TYPED_ARRAY
1685 : default:
1686 0 : MOZ_CRASH("Unsupported TypedArray type");
1687 : }
1688 :
1689 : // Step 21.c.
1690 : // SetFromNonTypedArray doesn't throw when the array buffer gets
1691 : // detached.
1692 0 : if (target->hasDetachedBuffer()) {
1693 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1694 0 : JSMSG_TYPED_ARRAY_DETACHED);
1695 0 : return false;
1696 : }
1697 : }
1698 : }
1699 :
1700 : // Step 29/22.
1701 0 : args.rval().setUndefined();
1702 0 : return true;
1703 : }
1704 :
1705 : /* static */ bool
1706 0 : TypedArrayObject::set(JSContext* cx, unsigned argc, Value* vp)
1707 : {
1708 0 : CallArgs args = CallArgsFromVp(argc, vp);
1709 0 : return CallNonGenericMethod<TypedArrayObject::is, TypedArrayObject::set_impl>(cx, args);
1710 : }
1711 :
1712 : /* static */ const JSFunctionSpec
1713 : TypedArrayObject::protoFunctions[] = {
1714 : JS_SELF_HOSTED_FN("subarray", "TypedArraySubarray", 2, 0),
1715 : #if 0 /* disabled until perf-testing is completed */
1716 : JS_SELF_HOSTED_FN("set", "TypedArraySet", 2, 0),
1717 : #else
1718 : JS_FN("set", TypedArrayObject::set, 1, 0),
1719 : #endif
1720 : JS_SELF_HOSTED_FN("copyWithin", "TypedArrayCopyWithin", 3, 0),
1721 : JS_SELF_HOSTED_FN("every", "TypedArrayEvery", 1, 0),
1722 : JS_SELF_HOSTED_FN("fill", "TypedArrayFill", 3, 0),
1723 : JS_SELF_HOSTED_FN("filter", "TypedArrayFilter", 1, 0),
1724 : JS_SELF_HOSTED_FN("find", "TypedArrayFind", 1, 0),
1725 : JS_SELF_HOSTED_FN("findIndex", "TypedArrayFindIndex", 1, 0),
1726 : JS_SELF_HOSTED_FN("forEach", "TypedArrayForEach", 1, 0),
1727 : JS_SELF_HOSTED_FN("indexOf", "TypedArrayIndexOf", 2, 0),
1728 : JS_SELF_HOSTED_FN("join", "TypedArrayJoin", 1, 0),
1729 : JS_SELF_HOSTED_FN("lastIndexOf", "TypedArrayLastIndexOf", 1, 0),
1730 : JS_SELF_HOSTED_FN("map", "TypedArrayMap", 1, 0),
1731 : JS_SELF_HOSTED_FN("reduce", "TypedArrayReduce", 1, 0),
1732 : JS_SELF_HOSTED_FN("reduceRight", "TypedArrayReduceRight", 1, 0),
1733 : JS_SELF_HOSTED_FN("reverse", "TypedArrayReverse", 0, 0),
1734 : JS_SELF_HOSTED_FN("slice", "TypedArraySlice", 2, 0),
1735 : JS_SELF_HOSTED_FN("some", "TypedArraySome", 1, 0),
1736 : JS_SELF_HOSTED_FN("sort", "TypedArraySort", 1, 0),
1737 : JS_SELF_HOSTED_FN("entries", "TypedArrayEntries", 0, 0),
1738 : JS_SELF_HOSTED_FN("keys", "TypedArrayKeys", 0, 0),
1739 : JS_SELF_HOSTED_FN("values", "TypedArrayValues", 0, 0),
1740 : JS_SELF_HOSTED_SYM_FN(iterator, "TypedArrayValues", 0, 0),
1741 : JS_SELF_HOSTED_FN("includes", "TypedArrayIncludes", 2, 0),
1742 : JS_SELF_HOSTED_FN("toString", "ArrayToString", 0, 0),
1743 : JS_SELF_HOSTED_FN("toLocaleString", "TypedArrayToLocaleString", 2, 0),
1744 : JS_FS_END
1745 : };
1746 :
1747 : /* static */ const JSFunctionSpec
1748 : TypedArrayObject::staticFunctions[] = {
1749 : JS_SELF_HOSTED_FN("from", "TypedArrayStaticFrom", 3, 0),
1750 : JS_SELF_HOSTED_FN("of", "TypedArrayStaticOf", 0, 0),
1751 : JS_FS_END
1752 : };
1753 :
1754 : /* static */ const JSPropertySpec
1755 : TypedArrayObject::staticProperties[] = {
1756 : JS_SELF_HOSTED_SYM_GET(species, "TypedArraySpecies", 0),
1757 : JS_PS_END
1758 : };
1759 :
1760 : static const ClassSpec
1761 : TypedArrayObjectSharedTypedArrayPrototypeClassSpec = {
1762 : GenericCreateConstructor<TypedArrayConstructor, 0, gc::AllocKind::FUNCTION>,
1763 : GenericCreatePrototype,
1764 : TypedArrayObject::staticFunctions,
1765 : TypedArrayObject::staticProperties,
1766 : TypedArrayObject::protoFunctions,
1767 : TypedArrayObject::protoAccessors,
1768 : nullptr,
1769 : ClassSpec::DontDefineConstructor
1770 : };
1771 :
1772 : /* static */ const Class
1773 : TypedArrayObject::sharedTypedArrayPrototypeClass = {
1774 : // Actually ({}).toString.call(%TypedArray%.prototype) should throw,
1775 : // because %TypedArray%.prototype lacks the the typed array internal
1776 : // slots. (It's not clear this is desirable -- particularly applied to
1777 : // the actual typed array prototypes, see below -- but it's what ES6
1778 : // draft 20140824 requires.) But this is about as much as we can do
1779 : // until we implement @@toStringTag.
1780 : "???",
1781 : JSCLASS_HAS_CACHED_PROTO(JSProto_TypedArray),
1782 : JS_NULL_CLASS_OPS,
1783 : &TypedArrayObjectSharedTypedArrayPrototypeClassSpec
1784 : };
1785 :
1786 : // this default implementation is only valid for integer types
1787 : // less than 32-bits in size.
1788 : template<typename NativeType>
1789 : Value
1790 0 : TypedArrayObjectTemplate<NativeType>::getIndexValue(JSObject* tarray, uint32_t index)
1791 : {
1792 : static_assert(sizeof(NativeType) < 4,
1793 : "this method must only handle NativeType values that are "
1794 : "always exact int32_t values");
1795 :
1796 0 : return Int32Value(getIndex(tarray, index));
1797 : }
1798 :
1799 : namespace {
1800 :
1801 : // and we need to specialize for 32-bit integers and floats
1802 : template<>
1803 : Value
1804 0 : TypedArrayObjectTemplate<int32_t>::getIndexValue(JSObject* tarray, uint32_t index)
1805 : {
1806 0 : return Int32Value(getIndex(tarray, index));
1807 : }
1808 :
1809 : template<>
1810 : Value
1811 0 : TypedArrayObjectTemplate<uint32_t>::getIndexValue(JSObject* tarray, uint32_t index)
1812 : {
1813 0 : uint32_t val = getIndex(tarray, index);
1814 0 : return NumberValue(val);
1815 : }
1816 :
1817 : template<>
1818 : Value
1819 0 : TypedArrayObjectTemplate<float>::getIndexValue(JSObject* tarray, uint32_t index)
1820 : {
1821 0 : float val = getIndex(tarray, index);
1822 0 : double dval = val;
1823 :
1824 : /*
1825 : * Doubles in typed arrays could be typed-punned arrays of integers. This
1826 : * could allow user code to break the engine-wide invariant that only
1827 : * canonical nans are stored into jsvals, which means user code could
1828 : * confuse the engine into interpreting a double-typed jsval as an
1829 : * object-typed jsval.
1830 : *
1831 : * This could be removed for platforms/compilers known to convert a 32-bit
1832 : * non-canonical nan to a 64-bit canonical nan.
1833 : */
1834 0 : return DoubleValue(CanonicalizeNaN(dval));
1835 : }
1836 :
1837 : template<>
1838 : Value
1839 0 : TypedArrayObjectTemplate<double>::getIndexValue(JSObject* tarray, uint32_t index)
1840 : {
1841 0 : double val = getIndex(tarray, index);
1842 :
1843 : /*
1844 : * Doubles in typed arrays could be typed-punned arrays of integers. This
1845 : * could allow user code to break the engine-wide invariant that only
1846 : * canonical nans are stored into jsvals, which means user code could
1847 : * confuse the engine into interpreting a double-typed jsval as an
1848 : * object-typed jsval.
1849 : */
1850 0 : return DoubleValue(CanonicalizeNaN(val));
1851 : }
1852 :
1853 : } /* anonymous namespace */
1854 :
1855 : Value
1856 0 : TypedArrayObject::getElement(uint32_t index)
1857 : {
1858 0 : switch (type()) {
1859 : case Scalar::Int8:
1860 0 : return Int8Array::getIndexValue(this, index);
1861 : case Scalar::Uint8:
1862 0 : return Uint8Array::getIndexValue(this, index);
1863 : case Scalar::Int16:
1864 0 : return Int16Array::getIndexValue(this, index);
1865 : case Scalar::Uint16:
1866 0 : return Uint16Array::getIndexValue(this, index);
1867 : case Scalar::Int32:
1868 0 : return Int32Array::getIndexValue(this, index);
1869 : case Scalar::Uint32:
1870 0 : return Uint32Array::getIndexValue(this, index);
1871 : case Scalar::Float32:
1872 0 : return Float32Array::getIndexValue(this, index);
1873 : case Scalar::Float64:
1874 0 : return Float64Array::getIndexValue(this, index);
1875 : case Scalar::Uint8Clamped:
1876 0 : return Uint8ClampedArray::getIndexValue(this, index);
1877 : case Scalar::Int64:
1878 : case Scalar::Float32x4:
1879 : case Scalar::Int8x16:
1880 : case Scalar::Int16x8:
1881 : case Scalar::Int32x4:
1882 : case Scalar::MaxTypedArrayViewType:
1883 0 : break;
1884 : }
1885 :
1886 0 : MOZ_CRASH("Unknown TypedArray type");
1887 : }
1888 :
1889 : void
1890 0 : TypedArrayObject::setElement(TypedArrayObject& obj, uint32_t index, double d)
1891 : {
1892 0 : MOZ_ASSERT(index < obj.length());
1893 :
1894 : #ifdef JS_MORE_DETERMINISTIC
1895 : // See the comment in ElementSpecific::doubleToNative.
1896 : d = JS::CanonicalizeNaN(d);
1897 : #endif
1898 :
1899 0 : switch (obj.type()) {
1900 : case Scalar::Int8:
1901 0 : Int8Array::setIndexValue(obj, index, d);
1902 0 : return;
1903 : case Scalar::Uint8:
1904 0 : Uint8Array::setIndexValue(obj, index, d);
1905 0 : return;
1906 : case Scalar::Uint8Clamped:
1907 0 : Uint8ClampedArray::setIndexValue(obj, index, d);
1908 0 : return;
1909 : case Scalar::Int16:
1910 0 : Int16Array::setIndexValue(obj, index, d);
1911 0 : return;
1912 : case Scalar::Uint16:
1913 0 : Uint16Array::setIndexValue(obj, index, d);
1914 0 : return;
1915 : case Scalar::Int32:
1916 0 : Int32Array::setIndexValue(obj, index, d);
1917 0 : return;
1918 : case Scalar::Uint32:
1919 0 : Uint32Array::setIndexValue(obj, index, d);
1920 0 : return;
1921 : case Scalar::Float32:
1922 0 : Float32Array::setIndexValue(obj, index, d);
1923 0 : return;
1924 : case Scalar::Float64:
1925 0 : Float64Array::setIndexValue(obj, index, d);
1926 0 : return;
1927 : case Scalar::Int64:
1928 : case Scalar::Float32x4:
1929 : case Scalar::Int8x16:
1930 : case Scalar::Int16x8:
1931 : case Scalar::Int32x4:
1932 : case Scalar::MaxTypedArrayViewType:
1933 0 : break;
1934 : }
1935 :
1936 0 : MOZ_CRASH("Unknown TypedArray type");
1937 : }
1938 :
1939 : /***
1940 : *** JS impl
1941 : ***/
1942 :
1943 : /*
1944 : * TypedArrayObject boilerplate
1945 : */
1946 :
1947 : #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType) \
1948 : JS_FRIEND_API(JSObject*) JS_New ## Name ## Array(JSContext* cx, uint32_t nelements) \
1949 : { \
1950 : return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements); \
1951 : } \
1952 : JS_FRIEND_API(JSObject*) JS_New ## Name ## ArrayFromArray(JSContext* cx, HandleObject other) \
1953 : { \
1954 : return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other); \
1955 : } \
1956 : JS_FRIEND_API(JSObject*) JS_New ## Name ## ArrayWithBuffer(JSContext* cx, \
1957 : HandleObject arrayBuffer, uint32_t byteOffset, int32_t length) \
1958 : { \
1959 : return TypedArrayObjectTemplate<NativeType>::fromBuffer(cx, arrayBuffer, byteOffset, \
1960 : length); \
1961 : } \
1962 : JS_FRIEND_API(bool) JS_Is ## Name ## Array(JSObject* obj) \
1963 : { \
1964 : if (!(obj = CheckedUnwrap(obj))) \
1965 : return false; \
1966 : const Class* clasp = obj->getClass(); \
1967 : return clasp == TypedArrayObjectTemplate<NativeType>::instanceClass(); \
1968 : } \
1969 : JS_FRIEND_API(JSObject*) js::Unwrap ## Name ## Array(JSObject* obj) \
1970 : { \
1971 : obj = CheckedUnwrap(obj); \
1972 : if (!obj) \
1973 : return nullptr; \
1974 : const Class* clasp = obj->getClass(); \
1975 : if (clasp == TypedArrayObjectTemplate<NativeType>::instanceClass()) \
1976 : return obj; \
1977 : return nullptr; \
1978 : } \
1979 : const js::Class* const js::detail::Name ## ArrayClassPtr = \
1980 : &js::TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()];
1981 :
1982 0 : IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t)
1983 0 : IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8, uint8_t)
1984 0 : IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8Clamped, uint8_clamped)
1985 0 : IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int16, int16_t)
1986 0 : IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint16, uint16_t)
1987 0 : IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int32, int32_t)
1988 0 : IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint32, uint32_t)
1989 0 : IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float32, float)
1990 0 : IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double)
1991 :
1992 : #define IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Name, ExternalType, InternalType) \
1993 : JS_FRIEND_API(JSObject*) JS_GetObjectAs ## Name ## Array(JSObject* obj, \
1994 : uint32_t* length, \
1995 : bool* isShared, \
1996 : ExternalType** data) \
1997 : { \
1998 : if (!(obj = CheckedUnwrap(obj))) \
1999 : return nullptr; \
2000 : \
2001 : const Class* clasp = obj->getClass(); \
2002 : if (clasp != TypedArrayObjectTemplate<InternalType>::instanceClass()) \
2003 : return nullptr; \
2004 : \
2005 : TypedArrayObject* tarr = &obj->as<TypedArrayObject>(); \
2006 : *length = tarr->length(); \
2007 : *isShared = tarr->isSharedMemory(); \
2008 : *data = static_cast<ExternalType*>(tarr->viewDataEither().unwrap(/*safe - caller sees isShared flag*/)); \
2009 : \
2010 : return obj; \
2011 : }
2012 :
2013 0 : IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int8, int8_t, int8_t)
2014 0 : IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8, uint8_t, uint8_t)
2015 0 : IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8Clamped, uint8_t, uint8_clamped)
2016 0 : IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int16, int16_t, int16_t)
2017 0 : IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint16, uint16_t, uint16_t)
2018 0 : IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int32, int32_t, int32_t)
2019 0 : IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint32, uint32_t, uint32_t)
2020 0 : IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float32, float, float)
2021 0 : IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double)
2022 :
2023 : static const ClassOps TypedArrayClassOps = {
2024 : nullptr, /* addProperty */
2025 : nullptr, /* delProperty */
2026 : nullptr, /* getProperty */
2027 : nullptr, /* setProperty */
2028 : nullptr, /* enumerate */
2029 : nullptr, /* newEnumerate */
2030 : nullptr, /* resolve */
2031 : nullptr, /* mayResolve */
2032 : TypedArrayObject::finalize, /* finalize */
2033 : nullptr, /* call */
2034 : nullptr, /* hasInstance */
2035 : nullptr, /* construct */
2036 : TypedArrayObject::trace, /* trace */
2037 : };
2038 :
2039 : static const ClassExtension TypedArrayClassExtension = {
2040 : nullptr,
2041 : TypedArrayObject::objectMoved,
2042 : };
2043 :
2044 : #define IMPL_TYPED_ARRAY_PROPERTIES(_type) \
2045 : { \
2046 : JS_INT32_PS("BYTES_PER_ELEMENT", _type##Array::BYTES_PER_ELEMENT, \
2047 : JSPROP_READONLY | JSPROP_PERMANENT), \
2048 : JS_PS_END \
2049 : }
2050 :
2051 : static const JSPropertySpec static_prototype_properties[Scalar::MaxTypedArrayViewType][2] = {
2052 : IMPL_TYPED_ARRAY_PROPERTIES(Int8),
2053 : IMPL_TYPED_ARRAY_PROPERTIES(Uint8),
2054 : IMPL_TYPED_ARRAY_PROPERTIES(Int16),
2055 : IMPL_TYPED_ARRAY_PROPERTIES(Uint16),
2056 : IMPL_TYPED_ARRAY_PROPERTIES(Int32),
2057 : IMPL_TYPED_ARRAY_PROPERTIES(Uint32),
2058 : IMPL_TYPED_ARRAY_PROPERTIES(Float32),
2059 : IMPL_TYPED_ARRAY_PROPERTIES(Float64),
2060 : IMPL_TYPED_ARRAY_PROPERTIES(Uint8Clamped)
2061 3 : };
2062 :
2063 : #define IMPL_TYPED_ARRAY_CLASS_SPEC(_type) \
2064 : { \
2065 : _type##Array::createConstructor, \
2066 : _type##Array::createPrototype, \
2067 : nullptr, \
2068 : static_prototype_properties[Scalar::Type::_type], \
2069 : nullptr, \
2070 : static_prototype_properties[Scalar::Type::_type], \
2071 : nullptr, \
2072 : JSProto_TypedArray \
2073 : }
2074 :
2075 : static const ClassSpec TypedArrayObjectClassSpecs[Scalar::MaxTypedArrayViewType] = {
2076 : IMPL_TYPED_ARRAY_CLASS_SPEC(Int8),
2077 : IMPL_TYPED_ARRAY_CLASS_SPEC(Uint8),
2078 : IMPL_TYPED_ARRAY_CLASS_SPEC(Int16),
2079 : IMPL_TYPED_ARRAY_CLASS_SPEC(Uint16),
2080 : IMPL_TYPED_ARRAY_CLASS_SPEC(Int32),
2081 : IMPL_TYPED_ARRAY_CLASS_SPEC(Uint32),
2082 : IMPL_TYPED_ARRAY_CLASS_SPEC(Float32),
2083 : IMPL_TYPED_ARRAY_CLASS_SPEC(Float64),
2084 : IMPL_TYPED_ARRAY_CLASS_SPEC(Uint8Clamped)
2085 : };
2086 :
2087 : #define IMPL_TYPED_ARRAY_CLASS(_type) \
2088 : { \
2089 : #_type "Array", \
2090 : JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) | \
2091 : JSCLASS_HAS_PRIVATE | \
2092 : JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array) | \
2093 : JSCLASS_DELAY_METADATA_BUILDER | \
2094 : JSCLASS_SKIP_NURSERY_FINALIZE | \
2095 : JSCLASS_BACKGROUND_FINALIZE, \
2096 : &TypedArrayClassOps, \
2097 : &TypedArrayObjectClassSpecs[Scalar::Type::_type], \
2098 : &TypedArrayClassExtension \
2099 : }
2100 :
2101 : const Class TypedArrayObject::classes[Scalar::MaxTypedArrayViewType] = {
2102 : IMPL_TYPED_ARRAY_CLASS(Int8),
2103 : IMPL_TYPED_ARRAY_CLASS(Uint8),
2104 : IMPL_TYPED_ARRAY_CLASS(Int16),
2105 : IMPL_TYPED_ARRAY_CLASS(Uint16),
2106 : IMPL_TYPED_ARRAY_CLASS(Int32),
2107 : IMPL_TYPED_ARRAY_CLASS(Uint32),
2108 : IMPL_TYPED_ARRAY_CLASS(Float32),
2109 : IMPL_TYPED_ARRAY_CLASS(Float64),
2110 : IMPL_TYPED_ARRAY_CLASS(Uint8Clamped)
2111 : };
2112 :
2113 : // The various typed array prototypes are supposed to 1) be normal objects,
2114 : // 2) stringify to "[object <name of constructor>]", and 3) (Gecko-specific)
2115 : // be xrayable. The first and second requirements mandate (in the absence of
2116 : // @@toStringTag) a custom class. The third requirement mandates that each
2117 : // prototype's class have the relevant typed array's cached JSProtoKey in them.
2118 : // Thus we need one class with cached prototype per kind of typed array, with a
2119 : // delegated ClassSpec.
2120 : #define IMPL_TYPED_ARRAY_PROTO_CLASS(_type) \
2121 : { \
2122 : /*
2123 : * Actually ({}).toString.call(Uint8Array.prototype) should throw, because
2124 : * Uint8Array.prototype lacks the the typed array internal slots. (Same as
2125 : * with %TypedArray%.prototype.) It's not clear this is desirable (see
2126 : * above), but it's what we've always done, so keep doing it till we
2127 : * implement @@toStringTag or ES6 changes.
2128 : */ \
2129 : #_type "ArrayPrototype", \
2130 : JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array), \
2131 : JS_NULL_CLASS_OPS, \
2132 : &TypedArrayObjectClassSpecs[Scalar::Type::_type] \
2133 : }
2134 :
2135 : const Class TypedArrayObject::protoClasses[Scalar::MaxTypedArrayViewType] = {
2136 : IMPL_TYPED_ARRAY_PROTO_CLASS(Int8),
2137 : IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8),
2138 : IMPL_TYPED_ARRAY_PROTO_CLASS(Int16),
2139 : IMPL_TYPED_ARRAY_PROTO_CLASS(Uint16),
2140 : IMPL_TYPED_ARRAY_PROTO_CLASS(Int32),
2141 : IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32),
2142 : IMPL_TYPED_ARRAY_PROTO_CLASS(Float32),
2143 : IMPL_TYPED_ARRAY_PROTO_CLASS(Float64),
2144 : IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Clamped)
2145 : };
2146 :
2147 : /* static */ bool
2148 0 : TypedArrayObject::isOriginalLengthGetter(Native native)
2149 : {
2150 0 : return native == TypedArray_lengthGetter;
2151 : }
2152 :
2153 : bool
2154 0 : js::IsTypedArrayConstructor(HandleValue v, uint32_t type)
2155 : {
2156 0 : switch (type) {
2157 : case Scalar::Int8:
2158 0 : return IsNativeFunction(v, Int8Array::class_constructor);
2159 : case Scalar::Uint8:
2160 0 : return IsNativeFunction(v, Uint8Array::class_constructor);
2161 : case Scalar::Int16:
2162 0 : return IsNativeFunction(v, Int16Array::class_constructor);
2163 : case Scalar::Uint16:
2164 0 : return IsNativeFunction(v, Uint16Array::class_constructor);
2165 : case Scalar::Int32:
2166 0 : return IsNativeFunction(v, Int32Array::class_constructor);
2167 : case Scalar::Uint32:
2168 0 : return IsNativeFunction(v, Uint32Array::class_constructor);
2169 : case Scalar::Float32:
2170 0 : return IsNativeFunction(v, Float32Array::class_constructor);
2171 : case Scalar::Float64:
2172 0 : return IsNativeFunction(v, Float64Array::class_constructor);
2173 : case Scalar::Uint8Clamped:
2174 0 : return IsNativeFunction(v, Uint8ClampedArray::class_constructor);
2175 : case Scalar::MaxTypedArrayViewType:
2176 0 : break;
2177 : }
2178 0 : MOZ_CRASH("unexpected typed array type");
2179 : }
2180 :
2181 : template <typename CharT>
2182 : bool
2183 0 : js::StringIsTypedArrayIndex(const CharT* s, size_t length, uint64_t* indexp)
2184 : {
2185 0 : const CharT* end = s + length;
2186 :
2187 0 : if (s == end)
2188 0 : return false;
2189 :
2190 0 : bool negative = false;
2191 0 : if (*s == '-') {
2192 0 : negative = true;
2193 0 : if (++s == end)
2194 0 : return false;
2195 : }
2196 :
2197 0 : if (!JS7_ISDEC(*s))
2198 0 : return false;
2199 :
2200 0 : uint64_t index = 0;
2201 0 : uint32_t digit = JS7_UNDEC(*s++);
2202 :
2203 : /* Don't allow leading zeros. */
2204 0 : if (digit == 0 && s != end)
2205 0 : return false;
2206 :
2207 0 : index = digit;
2208 :
2209 0 : for (; s < end; s++) {
2210 0 : if (!JS7_ISDEC(*s))
2211 0 : return false;
2212 :
2213 0 : digit = JS7_UNDEC(*s);
2214 :
2215 : /* Watch for overflows. */
2216 0 : if ((UINT64_MAX - digit) / 10 < index)
2217 0 : index = UINT64_MAX;
2218 : else
2219 0 : index = 10 * index + digit;
2220 : }
2221 :
2222 0 : if (negative)
2223 0 : *indexp = UINT64_MAX;
2224 : else
2225 0 : *indexp = index;
2226 0 : return true;
2227 : }
2228 :
2229 : template bool
2230 : js::StringIsTypedArrayIndex(const char16_t* s, size_t length, uint64_t* indexp);
2231 :
2232 : template bool
2233 : js::StringIsTypedArrayIndex(const Latin1Char* s, size_t length, uint64_t* indexp);
2234 :
2235 : /* ES6 draft rev 34 (2015 Feb 20) 9.4.5.3 [[DefineOwnProperty]] step 3.c. */
2236 : bool
2237 0 : js::DefineTypedArrayElement(JSContext* cx, HandleObject obj, uint64_t index,
2238 : Handle<PropertyDescriptor> desc, ObjectOpResult& result)
2239 : {
2240 0 : MOZ_ASSERT(obj->is<TypedArrayObject>());
2241 :
2242 : // These are all substeps of 3.b.
2243 :
2244 : // Steps i-iii are handled by the caller.
2245 :
2246 : // Steps iv-v.
2247 : // We (wrongly) ignore out of range defines with a value.
2248 0 : uint32_t length = obj->as<TypedArrayObject>().length();
2249 0 : if (index >= length)
2250 0 : return result.succeed();
2251 :
2252 : // Step vi.
2253 0 : if (desc.isAccessorDescriptor())
2254 0 : return result.fail(JSMSG_CANT_REDEFINE_PROP);
2255 :
2256 : // Step vii.
2257 0 : if (desc.hasConfigurable() && desc.configurable())
2258 0 : return result.fail(JSMSG_CANT_REDEFINE_PROP);
2259 :
2260 : // Step viii.
2261 0 : if (desc.hasEnumerable() && !desc.enumerable())
2262 0 : return result.fail(JSMSG_CANT_REDEFINE_PROP);
2263 :
2264 : // Step ix.
2265 0 : if (desc.hasWritable() && !desc.writable())
2266 0 : return result.fail(JSMSG_CANT_REDEFINE_PROP);
2267 :
2268 : // Step x.
2269 0 : if (desc.hasValue()) {
2270 : // The following step numbers refer to 9.4.5.9
2271 : // IntegerIndexedElementSet.
2272 :
2273 : // Steps 1-2 are enforced by the caller.
2274 :
2275 : // Step 3.
2276 : double numValue;
2277 0 : if (!ToNumber(cx, desc.value(), &numValue))
2278 0 : return false;
2279 :
2280 : // Steps 4-5, 8-9.
2281 0 : if (obj->as<TypedArrayObject>().hasDetachedBuffer())
2282 0 : return result.fail(JSMSG_TYPED_ARRAY_DETACHED);
2283 :
2284 : // Steps 10-16.
2285 0 : TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, numValue);
2286 : }
2287 :
2288 : // Step xii.
2289 0 : return result.succeed();
2290 : }
2291 :
2292 : /* JS Friend API */
2293 :
2294 : JS_FRIEND_API(bool)
2295 145 : JS_IsTypedArrayObject(JSObject* obj)
2296 : {
2297 145 : obj = CheckedUnwrap(obj);
2298 145 : return obj ? obj->is<TypedArrayObject>() : false;
2299 : }
2300 :
2301 : JS_FRIEND_API(uint32_t)
2302 0 : JS_GetTypedArrayLength(JSObject* obj)
2303 : {
2304 0 : obj = CheckedUnwrap(obj);
2305 0 : if (!obj)
2306 0 : return 0;
2307 0 : return obj->as<TypedArrayObject>().length();
2308 : }
2309 :
2310 : JS_FRIEND_API(uint32_t)
2311 0 : JS_GetTypedArrayByteOffset(JSObject* obj)
2312 : {
2313 0 : obj = CheckedUnwrap(obj);
2314 0 : if (!obj)
2315 0 : return 0;
2316 0 : return obj->as<TypedArrayObject>().byteOffset();
2317 : }
2318 :
2319 : JS_FRIEND_API(uint32_t)
2320 0 : JS_GetTypedArrayByteLength(JSObject* obj)
2321 : {
2322 0 : obj = CheckedUnwrap(obj);
2323 0 : if (!obj)
2324 0 : return 0;
2325 0 : return obj->as<TypedArrayObject>().byteLength();
2326 : }
2327 :
2328 : JS_FRIEND_API(bool)
2329 0 : JS_GetTypedArraySharedness(JSObject* obj)
2330 : {
2331 0 : obj = CheckedUnwrap(obj);
2332 0 : if (!obj)
2333 0 : return false;
2334 0 : return obj->as<TypedArrayObject>().isSharedMemory();
2335 : }
2336 :
2337 : JS_FRIEND_API(js::Scalar::Type)
2338 0 : JS_GetArrayBufferViewType(JSObject* obj)
2339 : {
2340 0 : obj = CheckedUnwrap(obj);
2341 0 : if (!obj)
2342 0 : return Scalar::MaxTypedArrayViewType;
2343 :
2344 0 : if (obj->is<TypedArrayObject>())
2345 0 : return obj->as<TypedArrayObject>().type();
2346 0 : if (obj->is<DataViewObject>())
2347 0 : return Scalar::MaxTypedArrayViewType;
2348 0 : MOZ_CRASH("invalid ArrayBufferView type");
2349 : }
2350 :
2351 : JS_FRIEND_API(int8_t*)
2352 0 : JS_GetInt8ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
2353 : {
2354 0 : obj = CheckedUnwrap(obj);
2355 0 : if (!obj)
2356 0 : return nullptr;
2357 0 : TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
2358 0 : MOZ_ASSERT((int32_t) tarr->type() == Scalar::Int8);
2359 0 : *isSharedMemory = tarr->isSharedMemory();
2360 0 : return static_cast<int8_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isShared*/));
2361 : }
2362 :
2363 : JS_FRIEND_API(uint8_t*)
2364 0 : JS_GetUint8ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
2365 : {
2366 0 : obj = CheckedUnwrap(obj);
2367 0 : if (!obj)
2368 0 : return nullptr;
2369 0 : TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
2370 0 : MOZ_ASSERT((int32_t) tarr->type() == Scalar::Uint8);
2371 0 : *isSharedMemory = tarr->isSharedMemory();
2372 0 : return static_cast<uint8_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
2373 : }
2374 :
2375 : JS_FRIEND_API(uint8_t*)
2376 0 : JS_GetUint8ClampedArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
2377 : {
2378 0 : obj = CheckedUnwrap(obj);
2379 0 : if (!obj)
2380 0 : return nullptr;
2381 0 : TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
2382 0 : MOZ_ASSERT((int32_t) tarr->type() == Scalar::Uint8Clamped);
2383 0 : *isSharedMemory = tarr->isSharedMemory();
2384 0 : return static_cast<uint8_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
2385 : }
2386 :
2387 : JS_FRIEND_API(int16_t*)
2388 0 : JS_GetInt16ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
2389 : {
2390 0 : obj = CheckedUnwrap(obj);
2391 0 : if (!obj)
2392 0 : return nullptr;
2393 0 : TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
2394 0 : MOZ_ASSERT((int32_t) tarr->type() == Scalar::Int16);
2395 0 : *isSharedMemory = tarr->isSharedMemory();
2396 0 : return static_cast<int16_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
2397 : }
2398 :
2399 : JS_FRIEND_API(uint16_t*)
2400 0 : JS_GetUint16ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
2401 : {
2402 0 : obj = CheckedUnwrap(obj);
2403 0 : if (!obj)
2404 0 : return nullptr;
2405 0 : TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
2406 0 : MOZ_ASSERT((int32_t) tarr->type() == Scalar::Uint16);
2407 0 : *isSharedMemory = tarr->isSharedMemory();
2408 0 : return static_cast<uint16_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
2409 : }
2410 :
2411 : JS_FRIEND_API(int32_t*)
2412 0 : JS_GetInt32ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
2413 : {
2414 0 : obj = CheckedUnwrap(obj);
2415 0 : if (!obj)
2416 0 : return nullptr;
2417 0 : TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
2418 0 : MOZ_ASSERT((int32_t) tarr->type() == Scalar::Int32);
2419 0 : *isSharedMemory = tarr->isSharedMemory();
2420 0 : return static_cast<int32_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
2421 : }
2422 :
2423 : JS_FRIEND_API(uint32_t*)
2424 0 : JS_GetUint32ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
2425 : {
2426 0 : obj = CheckedUnwrap(obj);
2427 0 : if (!obj)
2428 0 : return nullptr;
2429 0 : TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
2430 0 : MOZ_ASSERT((int32_t) tarr->type() == Scalar::Uint32);
2431 0 : *isSharedMemory = tarr->isSharedMemory();
2432 0 : return static_cast<uint32_t*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
2433 : }
2434 :
2435 : JS_FRIEND_API(float*)
2436 0 : JS_GetFloat32ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
2437 : {
2438 0 : obj = CheckedUnwrap(obj);
2439 0 : if (!obj)
2440 0 : return nullptr;
2441 0 : TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
2442 0 : MOZ_ASSERT((int32_t) tarr->type() == Scalar::Float32);
2443 0 : *isSharedMemory = tarr->isSharedMemory();
2444 0 : return static_cast<float*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
2445 : }
2446 :
2447 : JS_FRIEND_API(double*)
2448 0 : JS_GetFloat64ArrayData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
2449 : {
2450 0 : obj = CheckedUnwrap(obj);
2451 0 : if (!obj)
2452 0 : return nullptr;
2453 0 : TypedArrayObject* tarr = &obj->as<TypedArrayObject>();
2454 0 : MOZ_ASSERT((int32_t) tarr->type() == Scalar::Float64);
2455 0 : *isSharedMemory = tarr->isSharedMemory();
2456 0 : return static_cast<double*>(tarr->viewDataEither().unwrap(/*safe - caller sees isSharedMemory*/));
2457 9 : }
|