summaryrefslogtreecommitdiffstats
path: root/js/public/ArrayBuffer.h
blob: b4911c819e85e1ede9222e4b8199c786a4ac9e7a (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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */

/* ArrayBuffer functionality. */

#ifndef js_ArrayBuffer_h
#define js_ArrayBuffer_h

#include <stddef.h>  // size_t
#include <stdint.h>  // uint32_t

#include "jstypes.h"  // JS_PUBLIC_API
#include "js/TypeDecls.h"

struct JS_PUBLIC_API JSContext;
class JS_PUBLIC_API JSObject;

namespace JS {

class JS_PUBLIC_API AutoRequireNoGC;

// CREATION

/**
 * Create a new ArrayBuffer with the given byte length.
 */
extern JS_PUBLIC_API JSObject* NewArrayBuffer(JSContext* cx, size_t nbytes);

/**
 * Create a new ArrayBuffer with the given |contents|, which may be null only
 * if |nbytes == 0|.  |contents| must be allocated compatible with deallocation
 * by |JS_free|.
 *
 * If and only if an ArrayBuffer is successfully created and returned,
 * ownership of |contents| is transferred to the new ArrayBuffer.
 *
 * Care must be taken that |nbytes| bytes of |content| remain valid for the
 * duration of this call.  In particular, passing the length/pointer of existing
 * typed array or ArrayBuffer data is generally unsafe: if a GC occurs during a
 * call to this function, it could move those contents to a different location
 * and invalidate the provided pointer.
 */
extern JS_PUBLIC_API JSObject* NewArrayBufferWithContents(JSContext* cx,
                                                          size_t nbytes,
                                                          void* contents);

/**
 * Create a new ArrayBuffer, whose bytes are set to the values of the bytes in
 * the provided ArrayBuffer.
 *
 * |maybeArrayBuffer| is asserted to be non-null.  An error is thrown if
 * |maybeArrayBuffer| would fail the |IsArrayBufferObject| test given further
 * below or if |maybeArrayBuffer| is detached.
 *
 * |maybeArrayBuffer| may store its contents in any fashion (i.e. it doesn't
 * matter whether |maybeArrayBuffer| was allocated using |JS::NewArrayBuffer|,
 * |JS::NewExternalArrayBuffer|, or any other ArrayBuffer-allocating function).
 *
 * The newly-created ArrayBuffer is effectively creatd as if by
 * |JS::NewArrayBufferWithContents| passing in |maybeArrayBuffer|'s internal
 * data pointer and length, in a manner safe against |maybeArrayBuffer|'s data
 * being moved around by the GC.  In particular, the new ArrayBuffer will not
 * behave like one created for WASM or asm.js, so it *can* be detached.
 */
extern JS_PUBLIC_API JSObject* CopyArrayBuffer(
    JSContext* cx, JS::Handle<JSObject*> maybeArrayBuffer);

using BufferContentsFreeFunc = void (*)(void* contents, void* userData);

/**
 * Create a new ArrayBuffer with the given contents. The contents must not be
 * modified by any other code, internal or external.
 *
 * When the ArrayBuffer is ready to be disposed of, `freeFunc(contents,
 * freeUserData)` will be called to release the ArrayBuffer's reference on the
 * contents.
 *
 * `freeFunc()` must not call any JSAPI functions that could cause a garbage
 * collection.
 *
 * The caller must keep the buffer alive until `freeFunc()` is called, or, if
 * `freeFunc` is null, until the JSRuntime is destroyed.
 *
 * The caller must not access the buffer on other threads. The JS engine will
 * not allow the buffer to be transferred to other threads. If you try to
 * transfer an external ArrayBuffer to another thread, the data is copied to a
 * new malloc buffer. `freeFunc()` must be threadsafe, and may be called from
 * any thread.
 *
 * This allows ArrayBuffers to be used with embedder objects that use reference
 * counting, for example. In that case the caller is responsible
 * for incrementing the reference count before passing the contents to this
 * function. This also allows using non-reference-counted contents that must be
 * freed with some function other than free().
 */
extern JS_PUBLIC_API JSObject* NewExternalArrayBuffer(
    JSContext* cx, size_t nbytes, void* contents,
    BufferContentsFreeFunc freeFunc, void* freeUserData = nullptr);

/**
 * Create a new ArrayBuffer with the given non-null |contents|.
 *
 * Ownership of |contents| remains with the caller: it isn't transferred to the
 * returned ArrayBuffer.  Callers of this function *must* ensure that they
 * perform these two steps, in this order, to properly relinquish ownership of
 * |contents|:
 *
 *   1. Call |JS::DetachArrayBuffer| on the buffer returned by this function.
 *      (|JS::DetachArrayBuffer| is generally fallible, but a call under these
 *      circumstances is guaranteed to succeed.)
 *   2. |contents| may be deallocated or discarded consistent with the manner
 *      in which it was allocated.
 *
 * Do not simply allow the returned buffer to be garbage-collected before
 * deallocating |contents|, because in general there is no way to know *when*
 * an object is fully garbage-collected to the point where this would be safe.
 */
extern JS_PUBLIC_API JSObject* NewArrayBufferWithUserOwnedContents(
    JSContext* cx, size_t nbytes, void* contents);

/**
 * Create a new mapped ArrayBuffer with the given memory mapped contents. It
 * must be legal to free the contents pointer by unmapping it. On success,
 * ownership is transferred to the new mapped ArrayBuffer.
 */
extern JS_PUBLIC_API JSObject* NewMappedArrayBufferWithContents(JSContext* cx,
                                                                size_t nbytes,
                                                                void* contents);

/**
 * Create memory mapped ArrayBuffer contents.
 * Caller must take care of closing fd after calling this function.
 */
extern JS_PUBLIC_API void* CreateMappedArrayBufferContents(int fd,
                                                           size_t offset,
                                                           size_t length);

/**
 * Release the allocated resource of mapped ArrayBuffer contents before the
 * object is created.
 * If a new object has been created by JS::NewMappedArrayBufferWithContents()
 * with this content, then JS::DetachArrayBuffer() should be used instead to
 * release the resource used by the object.
 */
extern JS_PUBLIC_API void ReleaseMappedArrayBufferContents(void* contents,
                                                           size_t length);

// TYPE TESTING

/*
 * Check whether obj supports the JS::GetArrayBuffer* APIs.  Note that this may
 * return false if a security wrapper is encountered that denies the unwrapping.
 * If this test succeeds, then it is safe to call the various predicate and
 * accessor JSAPI calls defined below.
 */
extern JS_PUBLIC_API bool IsArrayBufferObject(JSObject* obj);

// PREDICATES

/**
 * Check whether the obj is a detached ArrayBufferObject. Note that this may
 * return false if a security wrapper is encountered that denies the
 * unwrapping.
 */
extern JS_PUBLIC_API bool IsDetachedArrayBufferObject(JSObject* obj);

/**
 * Check whether the obj is ArrayBufferObject and memory mapped. Note that this
 * may return false if a security wrapper is encountered that denies the
 * unwrapping.
 */
extern JS_PUBLIC_API bool IsMappedArrayBufferObject(JSObject* obj);

/**
 * Return true if the ArrayBuffer |obj| contains any data, i.e. it is not a
 * detached ArrayBuffer.  (ArrayBuffer.prototype is not an ArrayBuffer.)
 *
 * |obj| must have passed a JS::IsArrayBufferObject test, or somehow be known
 * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
 * ArrayBuffer, and the unwrapping will succeed.
 */
extern JS_PUBLIC_API bool ArrayBufferHasData(JSObject* obj);

// ACCESSORS

extern JS_PUBLIC_API JSObject* UnwrapArrayBuffer(JSObject* obj);

/**
 * Attempt to unwrap |obj| as an ArrayBuffer.
 *
 * If |obj| *is* an ArrayBuffer, return it unwrapped and set |*length| and
 * |*data| to weakly refer to the ArrayBuffer's contents.
 *
 * If |obj| isn't an ArrayBuffer, return nullptr and do not modify |*length| or
 * |*data|.
 */
extern JS_PUBLIC_API JSObject* GetObjectAsArrayBuffer(JSObject* obj,
                                                      size_t* length,
                                                      uint8_t** data);

/**
 * Return the available byte length of an ArrayBuffer.
 *
 * |obj| must have passed a JS::IsArrayBufferObject test, or somehow be known
 * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
 * ArrayBuffer, and the unwrapping will succeed.
 */
extern JS_PUBLIC_API size_t GetArrayBufferByteLength(JSObject* obj);

// This one isn't inlined because there are a bunch of different ArrayBuffer
// classes that would have to be individually handled here.
//
// There is an isShared out argument for API consistency (eases use from DOM).
// It will always be set to false.
extern JS_PUBLIC_API void GetArrayBufferLengthAndData(JSObject* obj,
                                                      size_t* length,
                                                      bool* isSharedMemory,
                                                      uint8_t** data);

/**
 * Return a pointer to the start of the data referenced by a typed array. The
 * data is still owned by the typed array, and should not be modified on
 * another thread. Furthermore, the pointer can become invalid on GC (if the
 * data is small and fits inside the array's GC header), so callers must take
 * care not to hold on across anything that could GC.
 *
 * |obj| must have passed a JS::IsArrayBufferObject test, or somehow be known
 * that it would pass such a test: it is an ArrayBuffer or a wrapper of an
 * ArrayBuffer, and the unwrapping will succeed.
 *
 * |*isSharedMemory| is always set to false.  The argument is present to
 * simplify its use from code that also interacts with SharedArrayBuffer.
 */
extern JS_PUBLIC_API uint8_t* GetArrayBufferData(JSObject* obj,
                                                 bool* isSharedMemory,
                                                 const AutoRequireNoGC&);

// MUTATORS

/**
 * Detach an ArrayBuffer, causing all associated views to no longer refer to
 * the ArrayBuffer's original attached memory.
 *
 * This function throws only if it is provided a non-ArrayBuffer object or if
 * the provided ArrayBuffer is a WASM-backed ArrayBuffer or an ArrayBuffer used
 * in asm.js code.
 */
extern JS_PUBLIC_API bool DetachArrayBuffer(JSContext* cx,
                                            Handle<JSObject*> obj);

// Indicates if an object has a defined [[ArrayBufferDetachKey]] internal slot,
// which indicates an ArrayBuffer cannot be detached
extern JS_PUBLIC_API bool HasDefinedArrayBufferDetachKey(JSContext* cx,
                                                         Handle<JSObject*> obj,
                                                         bool* isDefined);

/**
 * Steal the contents of the given ArrayBuffer. The ArrayBuffer has its length
 * set to 0 and its contents array cleared. The caller takes ownership of the
 * return value and must free it or transfer ownership via
 * JS::NewArrayBufferWithContents when done using it.
 */
extern JS_PUBLIC_API void* StealArrayBufferContents(JSContext* cx,
                                                    Handle<JSObject*> obj);

/**
 * Copy data from one array buffer to another.
 *
 * Both fromBuffer and toBuffer must be (possibly wrapped)
 * ArrayBufferObjectMaybeShared.
 *
 * This method may throw if the sizes don't match, or if unwrapping fails.
 *
 * The API for this is modelled on CopyDataBlockBytes from the spec:
 * https://tc39.es/ecma262/#sec-copydatablockbytes
 */
[[nodiscard]] extern JS_PUBLIC_API bool ArrayBufferCopyData(
    JSContext* cx, Handle<JSObject*> toBlock, size_t toIndex,
    Handle<JSObject*> fromBlock, size_t fromIndex, size_t count);

/**
 * Copy data from one array buffer to another.
 *
 * srcBuffer must be a (possibly wrapped) ArrayBufferObjectMaybeShared.
 *
 * This method may throw if unwrapping or allocation fails.
 *
 * The API for this is modelled on CloneArrayBuffer from the spec:
 * https://tc39.es/ecma262/#sec-clonearraybuffer
 */
extern JS_PUBLIC_API JSObject* ArrayBufferClone(JSContext* cx,
                                                Handle<JSObject*> srcBuffer,
                                                size_t srcByteOffset,
                                                size_t srcLength);

}  // namespace JS

#endif /* js_ArrayBuffer_h */