summaryrefslogtreecommitdiff
path: root/js/src/vm/ArrayBufferObject.h
blob: 4ff7962cfbbedc037fdc99b74fbd09681b803116 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef vm_ArrayBufferObject_h
#define vm_ArrayBufferObject_h

#include "mozilla/Maybe.h"

#include "jsobj.h"

#include "builtin/TypedObjectConstants.h"
#include "js/GCHashTable.h"
#include "vm/Runtime.h"
#include "vm/SharedMem.h"

typedef struct JSProperty JSProperty;

namespace js {

class ArrayBufferViewObject;
class WasmArrayRawBuffer;

// The inheritance hierarchy for the various classes relating to typed arrays
// is as follows.
//
// - NativeObject
//   - ArrayBufferObjectMaybeShared
//     - ArrayBufferObject
//     - SharedArrayBufferObject
//   - DataViewObject
//   - TypedArrayObject (declared in vm/TypedArrayObject.h)
//     - TypedArrayObjectTemplate
//       - Int8ArrayObject
//       - Uint8ArrayObject
//       - ...
// - JSObject
//   - ArrayBufferViewObject
//   - TypedObject (declared in builtin/TypedObject.h)
//
// Note that |TypedArrayObjectTemplate| is just an implementation
// detail that makes implementing its various subclasses easier.
//
// ArrayBufferObject and SharedArrayBufferObject are unrelated data types:
// the racy memory of the latter cannot substitute for the non-racy memory of
// the former; the non-racy memory of the former cannot be used with the atomics;
// the former can be detached and the latter not.  Hence they have been
// separated completely.
//
// Most APIs will only accept ArrayBufferObject.  ArrayBufferObjectMaybeShared
// exists as a join point to allow APIs that can take or use either, notably AsmJS.
//
// In contrast with the separation of ArrayBufferObject and
// SharedArrayBufferObject, the TypedArray types can map either.
//
// The possible data ownership and reference relationships with ArrayBuffers
// and related classes are enumerated below. These are the possible locations
// for typed data:
//
// (1) malloc'ed or mmap'ed data owned by an ArrayBufferObject.
// (2) Data allocated inline with an ArrayBufferObject.
// (3) Data allocated inline with a TypedArrayObject.
// (4) Data allocated inline with an InlineTypedObject.
//
// An ArrayBufferObject may point to any of these sources of data, except (3).
// All array buffer views may point to any of these sources of data, except
// that (3) may only be pointed to by the typed array the data is inline with.
//
// During a minor GC, (3) and (4) may move. During a compacting GC, (2), (3),
// and (4) may move.

class ArrayBufferObjectMaybeShared;

uint32_t AnyArrayBufferByteLength(const ArrayBufferObjectMaybeShared* buf);
mozilla::Maybe<uint32_t> WasmArrayBufferMaxSize(const ArrayBufferObjectMaybeShared* buf);
size_t WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf);
bool WasmArrayBufferGrowForWasm(ArrayBufferObjectMaybeShared* buf, uint32_t delta);
bool AnyArrayBufferIsPreparedForAsmJS(const ArrayBufferObjectMaybeShared* buf);
ArrayBufferObjectMaybeShared& AsAnyArrayBuffer(HandleValue val);

class ArrayBufferObjectMaybeShared : public NativeObject
{
  public:
    uint32_t byteLength() {
        return AnyArrayBufferByteLength(this);
    }

    inline bool isDetached() const;

    inline SharedMem<uint8_t*> dataPointerEither();

    // WebAssembly support:
    // Note: the eventual goal is to remove this from ArrayBuffer and have
    // (Shared)ArrayBuffers alias memory owned by some wasm::Memory object.

    mozilla::Maybe<uint32_t> wasmMaxSize() const {
        return WasmArrayBufferMaxSize(this);
    }
    size_t wasmMappedSize() const {
        return WasmArrayBufferMappedSize(this);
    }
#ifndef WASM_HUGE_MEMORY
    uint32_t wasmBoundsCheckLimit() const;
#endif

    bool isPreparedForAsmJS() const {
        return AnyArrayBufferIsPreparedForAsmJS(this);
    }
};

typedef Rooted<ArrayBufferObjectMaybeShared*> RootedArrayBufferObjectMaybeShared;
typedef Handle<ArrayBufferObjectMaybeShared*> HandleArrayBufferObjectMaybeShared;
typedef MutableHandle<ArrayBufferObjectMaybeShared*> MutableHandleArrayBufferObjectMaybeShared;

/*
 * ArrayBufferObject
 *
 * This class holds the underlying raw buffer that the various ArrayBufferViews
 * (eg DataViewObject, the TypedArrays, TypedObjects) access. It can be created
 * explicitly and used to construct an ArrayBufferView, or can be created
 * lazily when it is first accessed for a TypedArrayObject or TypedObject that
 * doesn't have an explicit buffer.
 *
 * ArrayBufferObject (or really the underlying memory) /is not racy/: the
 * memory is private to a single worker.
 */
class ArrayBufferObject : public ArrayBufferObjectMaybeShared
{
    static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
    static bool fun_slice_impl(JSContext* cx, const CallArgs& args);

  public:
    static const uint8_t DATA_SLOT = 0;
    static const uint8_t BYTE_LENGTH_SLOT = 1;
    static const uint8_t FIRST_VIEW_SLOT = 2;
    static const uint8_t FLAGS_SLOT = 3;

    static const uint8_t RESERVED_SLOTS = 4;

    static const size_t ARRAY_BUFFER_ALIGNMENT = 8;

    static_assert(FLAGS_SLOT == JS_ARRAYBUFFER_FLAGS_SLOT,
                  "self-hosted code with burned-in constants must get the "
                  "right flags slot");

  public:

    enum OwnsState {
        DoesntOwnData = 0,
        OwnsData = 1,
    };

    enum BufferKind {
        PLAIN               = 0, // malloced or inline data
        WASM                = 1,
        MAPPED              = 2,

        KIND_MASK           = 0x3
    };

  protected:

    enum ArrayBufferFlags {
        // The flags also store the BufferKind
        BUFFER_KIND_MASK    = BufferKind::KIND_MASK,

        DETACHED            = 0x4,

        // The dataPointer() is owned by this buffer and should be released
        // when no longer in use. Releasing the pointer may be done by either
        // freeing or unmapping it, and how to do this is determined by the
        // buffer's other flags.
        //
        // Array buffers which do not own their data include buffers that
        // allocate their data inline, and buffers that are created lazily for
        // typed objects with inline storage, in which case the buffer points
        // directly to the typed object's storage.
        OWNS_DATA           = 0x8,

        // This array buffer was created lazily for a typed object with inline
        // data. This implies both that the typed object owns the buffer's data
        // and that the list of views sharing this buffer's data might be
        // incomplete. Any missing views will be typed objects.
        FOR_INLINE_TYPED_OBJECT = 0x10,

        // Views of this buffer might include typed objects.
        TYPED_OBJECT_VIEWS  = 0x20,

        // This PLAIN or WASM buffer has been prepared for asm.js and cannot
        // henceforth be transferred/detached.
        FOR_ASMJS           = 0x40
    };

    static_assert(JS_ARRAYBUFFER_DETACHED_FLAG == DETACHED,
                  "self-hosted code with burned-in constants must use the "
                  "correct DETACHED bit value");
  public:

    class BufferContents {
        uint8_t* data_;
        BufferKind kind_;

        friend class ArrayBufferObject;

        BufferContents(uint8_t* data, BufferKind kind) : data_(data), kind_(kind) {
            MOZ_ASSERT((kind_ & ~KIND_MASK) == 0);
        }

      public:

        template<BufferKind Kind>
        static BufferContents create(void* data)
        {
            return BufferContents(static_cast<uint8_t*>(data), Kind);
        }

        static BufferContents createPlain(void* data)
        {
            return BufferContents(static_cast<uint8_t*>(data), PLAIN);
        }

        uint8_t* data() const { return data_; }
        BufferKind kind() const { return kind_; }

        explicit operator bool() const { return data_ != nullptr; }
        WasmArrayRawBuffer* wasmBuffer() const;
    };

    static const Class class_;

    static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp);

    static bool fun_slice(JSContext* cx, unsigned argc, Value* vp);

    static bool fun_isView(JSContext* cx, unsigned argc, Value* vp);

    static bool fun_species(JSContext* cx, unsigned argc, Value* vp);

    static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);

    static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes,
                                     BufferContents contents,
                                     OwnsState ownsState = OwnsData,
                                     HandleObject proto = nullptr,
                                     NewObjectKind newKind = GenericObject);
    static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes,
                                     HandleObject proto = nullptr,
                                     NewObjectKind newKind = GenericObject);

    // Create an ArrayBufferObject that is safely finalizable and can later be
    // initialize()d to become a real, content-visible ArrayBufferObject.
    static ArrayBufferObject* createEmpty(JSContext* cx);

    static bool createDataViewForThisImpl(JSContext* cx, const CallArgs& args);
    static bool createDataViewForThis(JSContext* cx, unsigned argc, Value* vp);

    template<typename T>
    static bool createTypedArrayFromBufferImpl(JSContext* cx, const CallArgs& args);

    template<typename T>
    static bool createTypedArrayFromBuffer(JSContext* cx, unsigned argc, Value* vp);

    static void copyData(Handle<ArrayBufferObject*> toBuffer,
                         Handle<ArrayBufferObject*> fromBuffer,
                         uint32_t fromIndex, uint32_t count);

    static void trace(JSTracer* trc, JSObject* obj);
    static void objectMoved(JSObject* obj, const JSObject* old);

    static BufferContents externalizeContents(JSContext* cx,
                                              Handle<ArrayBufferObject*> buffer,
                                              bool hasStealableContents);
    static BufferContents stealContents(JSContext* cx,
                                        Handle<ArrayBufferObject*> buffer,
                                        bool hasStealableContents);

    bool hasStealableContents() const {
        // Inline elements strictly adhere to the corresponding buffer.
        return ownsData() && !isPreparedForAsmJS() && !isWasm();
    }

    static void addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
                                       JS::ClassInfo* info);

    // ArrayBufferObjects (strongly) store the first view added to them, while
    // later views are (weakly) stored in the compartment's InnerViewTable
    // below. Buffers usually only have one view, so this slot optimizes for
    // the common case. Avoiding entries in the InnerViewTable saves memory and
    // non-incrementalized sweep time.
    ArrayBufferViewObject* firstView();

    bool addView(JSContext* cx, JSObject* view);

    void setNewData(FreeOp* fop, BufferContents newContents, OwnsState ownsState);
    void changeContents(JSContext* cx, BufferContents newContents, OwnsState ownsState);

    // Detach this buffer from its original memory.  (This necessarily makes
    // views of this buffer unusable for modifying that original memory.)
    static void
    detach(JSContext* cx, Handle<ArrayBufferObject*> buffer, BufferContents newContents);

  private:
    void changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
                            uint8_t* oldDataPointer, BufferContents newContents);
    void setFirstView(ArrayBufferViewObject* view);

    uint8_t* inlineDataPointer() const;

  public:
    uint8_t* dataPointer() const;
    SharedMem<uint8_t*> dataPointerShared() const;
    uint32_t byteLength() const;

    BufferContents contents() const {
        return BufferContents(dataPointer(), bufferKind());
    }
    bool hasInlineData() const {
        return dataPointer() == inlineDataPointer();
    }

    void releaseData(FreeOp* fop);

    /*
     * Check if the arrayBuffer contains any data. This will return false for
     * ArrayBuffer.prototype and detached ArrayBuffers.
     */
    bool hasData() const {
        return getClass() == &class_;
    }

    BufferKind bufferKind() const { return BufferKind(flags() & BUFFER_KIND_MASK); }
    bool isPlain() const { return bufferKind() == PLAIN; }
    bool isWasm() const { return bufferKind() == WASM; }
    bool isMapped() const { return bufferKind() == MAPPED; }
    bool isDetached() const { return flags() & DETACHED; }
    bool isPreparedForAsmJS() const { return flags() & FOR_ASMJS; }

    // WebAssembly support:
    static ArrayBufferObject* createForWasm(JSContext* cx, uint32_t initialSize,
                                            mozilla::Maybe<uint32_t> maxSize);
    static MOZ_MUST_USE bool prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer,
                                             bool needGuard);
    size_t wasmMappedSize() const;
    mozilla::Maybe<uint32_t> wasmMaxSize() const;
    static MOZ_MUST_USE bool wasmGrowToSizeInPlace(uint32_t newSize,
                                                   Handle<ArrayBufferObject*> oldBuf,
                                                   MutableHandle<ArrayBufferObject*> newBuf,
                                                   JSContext* cx);
#ifndef WASM_HUGE_MEMORY
    static MOZ_MUST_USE bool wasmMovingGrowToSize(uint32_t newSize,
                                                  Handle<ArrayBufferObject*> oldBuf,
                                                  MutableHandle<ArrayBufferObject*> newBuf,
                                                  JSContext* cx);
    uint32_t wasmBoundsCheckLimit() const;
#endif

    static void finalize(FreeOp* fop, JSObject* obj);

    static BufferContents createMappedContents(int fd, size_t offset, size_t length);

    static size_t offsetOfFlagsSlot() {
        return getFixedSlotOffset(FLAGS_SLOT);
    }
    static size_t offsetOfDataSlot() {
        return getFixedSlotOffset(DATA_SLOT);
    }

    void setForInlineTypedObject() {
        setFlags(flags() | FOR_INLINE_TYPED_OBJECT);
    }
    void setHasTypedObjectViews() {
        setFlags(flags() | TYPED_OBJECT_VIEWS);
    }

    bool forInlineTypedObject() const { return flags() & FOR_INLINE_TYPED_OBJECT; }

  protected:
    void setDataPointer(BufferContents contents, OwnsState ownsState);
    void setByteLength(uint32_t length);

    uint32_t flags() const;
    void setFlags(uint32_t flags);

    bool ownsData() const { return flags() & OWNS_DATA; }
    void setOwnsData(OwnsState owns) {
        setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA));
    }

    bool hasTypedObjectViews() const { return flags() & TYPED_OBJECT_VIEWS; }

    void setIsDetached() { setFlags(flags() | DETACHED); }
    void setIsPreparedForAsmJS() { setFlags(flags() | FOR_ASMJS); }

    void initialize(size_t byteLength, BufferContents contents, OwnsState ownsState) {
        setByteLength(byteLength);
        setFlags(0);
        setFirstView(nullptr);
        setDataPointer(contents, ownsState);
    }

    // Note: initialize() may be called after initEmpty(); initEmpty() must
    // only initialize the ArrayBufferObject to a safe, finalizable state.
    void initEmpty() {
        setByteLength(0);
        setFlags(0);
        setFirstView(nullptr);
        setDataPointer(BufferContents::createPlain(nullptr), DoesntOwnData);
    }
};

typedef Rooted<ArrayBufferObject*> RootedArrayBufferObject;
typedef Handle<ArrayBufferObject*> HandleArrayBufferObject;
typedef MutableHandle<ArrayBufferObject*> MutableHandleArrayBufferObject;

/*
 * ArrayBufferViewObject
 *
 * Common definitions shared by all array buffer views.
 */

class ArrayBufferViewObject : public JSObject
{
  public:
    static ArrayBufferObjectMaybeShared* bufferObject(JSContext* cx, Handle<ArrayBufferViewObject*> obj);

    void notifyBufferDetached(JSContext* cx, void* newData);

#ifdef DEBUG
    bool isSharedMemory();
#endif

    // By construction we only need unshared variants here.  See
    // comments in ArrayBufferObject.cpp.
    uint8_t* dataPointerUnshared(const JS::AutoRequireNoGC&);
    void setDataPointerUnshared(uint8_t* data);

    static void trace(JSTracer* trc, JSObject* obj);
};

bool
ToClampedIndex(JSContext* cx, HandleValue v, uint32_t length, uint32_t* out);

/*
 * Tests for ArrayBufferObject, like obj->is<ArrayBufferObject>().
 */
bool IsArrayBuffer(HandleValue v);
bool IsArrayBuffer(HandleObject obj);
bool IsArrayBuffer(JSObject* obj);
ArrayBufferObject& AsArrayBuffer(HandleObject obj);
ArrayBufferObject& AsArrayBuffer(JSObject* obj);

extern uint32_t JS_FASTCALL
ClampDoubleToUint8(const double x);

struct uint8_clamped {
    uint8_t val;

    uint8_clamped() = default;
    uint8_clamped(const uint8_clamped& other) = default;

    // invoke our assignment helpers for constructor conversion
    explicit uint8_clamped(uint8_t x)    { *this = x; }
    explicit uint8_clamped(uint16_t x)   { *this = x; }
    explicit uint8_clamped(uint32_t x)   { *this = x; }
    explicit uint8_clamped(uint64_t x)   { *this = x; }
    explicit uint8_clamped(int8_t x)     { *this = x; }
    explicit uint8_clamped(int16_t x)    { *this = x; }
    explicit uint8_clamped(int32_t x)    { *this = x; }
    explicit uint8_clamped(int64_t x)    { *this = x; }
    explicit uint8_clamped(double x)     { *this = x; }

    uint8_clamped& operator=(const uint8_clamped& x) = default;

    uint8_clamped& operator=(uint8_t x) {
        val = x;
        return *this;
    }

    uint8_clamped& operator=(uint16_t x) {
        val = (x > 255) ? 255 : uint8_t(x);
        return *this;
    }

    uint8_clamped& operator=(uint32_t x) {
        val = (x > 255) ? 255 : uint8_t(x);
        return *this;
    }

    uint8_clamped& operator=(uint64_t x) {
        val = (x > 255) ? 255 : uint8_t(x);
        return *this;
    }

    uint8_clamped& operator=(int8_t x) {
        val = (x >= 0) ? uint8_t(x) : 0;
        return *this;
    }

    uint8_clamped& operator=(int16_t x) {
        val = (x >= 0)
              ? ((x < 255)
                 ? uint8_t(x)
                 : 255)
              : 0;
        return *this;
    }

    uint8_clamped& operator=(int32_t x) {
        val = (x >= 0)
              ? ((x < 255)
                 ? uint8_t(x)
                 : 255)
              : 0;
        return *this;
    }

    uint8_clamped& operator=(int64_t x) {
        val = (x >= 0)
              ? ((x < 255)
                 ? uint8_t(x)
                 : 255)
              : 0;
        return *this;
    }

    uint8_clamped& operator=(const double x) {
        val = uint8_t(ClampDoubleToUint8(x));
        return *this;
    }

    operator uint8_t() const {
        return val;
    }

    void staticAsserts() {
        static_assert(sizeof(uint8_clamped) == 1,
                      "uint8_clamped must be layout-compatible with uint8_t");
    }
};

/* Note that we can't use std::numeric_limits here due to uint8_clamped. */
template<typename T> inline bool TypeIsFloatingPoint() { return false; }
template<> inline bool TypeIsFloatingPoint<float>() { return true; }
template<> inline bool TypeIsFloatingPoint<double>() { return true; }

template<typename T> inline bool TypeIsUnsigned() { return false; }
template<> inline bool TypeIsUnsigned<uint8_t>() { return true; }
template<> inline bool TypeIsUnsigned<uint16_t>() { return true; }
template<> inline bool TypeIsUnsigned<uint32_t>() { return true; }

// Per-compartment table that manages the relationship between array buffers
// and the views that use their storage.
class InnerViewTable
{
  public:
    typedef Vector<ArrayBufferViewObject*, 1, SystemAllocPolicy> ViewVector;

    friend class ArrayBufferObject;

  private:
    struct MapGCPolicy {
        static bool needsSweep(JSObject** key, ViewVector* value) {
            return InnerViewTable::sweepEntry(key, *value);
        }
    };

    // This key is a raw pointer and not a ReadBarriered because the post-
    // barrier would hold nursery-allocated entries live unconditionally. It is
    // a very common pattern in low-level and performance-oriented JavaScript
    // to create hundreds or thousands of very short lived temporary views on a
    // larger buffer; having to tenured all of these would be a catastrophic
    // performance regression. Thus, it is vital that nursery pointers in this
    // map not be held live. Special support is required in the minor GC,
    // implemented in sweepAfterMinorGC.
    typedef GCHashMap<JSObject*,
                      ViewVector,
                      MovableCellHasher<JSObject*>,
                      SystemAllocPolicy,
                      MapGCPolicy> Map;

    // For all objects sharing their storage with some other view, this maps
    // the object to the list of such views. All entries in this map are weak.
    Map map;

    // List of keys from innerViews where either the source or at least one
    // target is in the nursery. The raw pointer to a JSObject is allowed here
    // because this vector is cleared after every minor collection. Users in
    // sweepAfterMinorCollection must be careful to use MaybeForwarded before
    // touching these pointers.
    Vector<JSObject*, 0, SystemAllocPolicy> nurseryKeys;

    // Whether nurseryKeys is a complete list.
    bool nurseryKeysValid;

    // Sweep an entry during GC, returning whether the entry should be removed.
    static bool sweepEntry(JSObject** pkey, ViewVector& views);

    bool addView(JSContext* cx, ArrayBufferObject* obj, ArrayBufferViewObject* view);
    ViewVector* maybeViewsUnbarriered(ArrayBufferObject* obj);
    void removeViews(ArrayBufferObject* obj);

  public:
    InnerViewTable()
      : nurseryKeysValid(true)
    {}

    // Remove references to dead objects in the table and update table entries
    // to reflect moved objects.
    void sweep();
    void sweepAfterMinorGC();

    bool needsSweepAfterMinorGC() const {
        return !nurseryKeys.empty() || !nurseryKeysValid;
    }

    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
};

template <typename Wrapper>
class MutableWrappedPtrOperations<InnerViewTable, Wrapper>
    : public WrappedPtrOperations<InnerViewTable, Wrapper>
{
    InnerViewTable& table() {
        return static_cast<Wrapper*>(this)->get();
    }

  public:
    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
        return table().sizeOfExcludingThis(mallocSizeOf);
    }
};

} // namespace js

template <>
bool
JSObject::is<js::ArrayBufferViewObject>() const;

template <>
bool
JSObject::is<js::ArrayBufferObjectMaybeShared>() const;

#endif // vm_ArrayBufferObject_h