ArrayBuffer.h (15755B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* ArrayBuffer functionality. */ 7 8 #ifndef js_ArrayBuffer_h 9 #define js_ArrayBuffer_h 10 11 #include "mozilla/UniquePtr.h" 12 13 #include <stddef.h> // size_t 14 #include <stdint.h> // uint32_t 15 16 #include "jstypes.h" // JS_PUBLIC_API 17 #include "js/TypeDecls.h" 18 #include "js/Utility.h" 19 20 struct JS_PUBLIC_API JSContext; 21 class JS_PUBLIC_API JSObject; 22 23 namespace JS { 24 25 class JS_PUBLIC_API AutoRequireNoGC; 26 27 // CREATION 28 29 /** 30 * Create a new ArrayBuffer with the given byte length. 31 */ 32 extern JS_PUBLIC_API JSObject* NewArrayBuffer(JSContext* cx, size_t nbytes); 33 34 /** 35 * Create a new ArrayBuffer with the given |contents|, which may be null only 36 * if |nbytes == 0|. |contents| must be allocated compatible with deallocation 37 * by |JS_free|. 38 * 39 * Care must be taken that |nbytes| bytes of |contents| remain valid for the 40 * duration of this call. In particular, passing the length/pointer of existing 41 * typed array or ArrayBuffer data is generally unsafe: if a GC occurs during a 42 * call to this function, it could move those contents to a different location 43 * and invalidate the provided pointer. 44 */ 45 extern JS_PUBLIC_API JSObject* NewArrayBufferWithContents( 46 JSContext* cx, size_t nbytes, 47 mozilla::UniquePtr<void, JS::FreePolicy> contents); 48 49 /** 50 * Create a new ArrayBuffer with the given |contents|, which may be null only 51 * if |nbytes == 0|. |contents| must be allocated compatible with deallocation 52 * by |JS_free|. 53 * 54 * Care must be taken that |nbytes| bytes of |contents| remain valid for the 55 * duration of this call. In particular, passing the length/pointer of existing 56 * typed array or ArrayBuffer data is generally unsafe: if a GC occurs during a 57 * call to this function, it could move those contents to a different location 58 * and invalidate the provided pointer. 59 */ 60 inline JS_PUBLIC_API JSObject* NewArrayBufferWithContents( 61 JSContext* cx, size_t nbytes, 62 mozilla::UniquePtr<char[], JS::FreePolicy> contents) { 63 // As a convenience, provide an overload for UniquePtr<char[]>. 64 mozilla::UniquePtr<void, JS::FreePolicy> ptr{contents.release()}; 65 return NewArrayBufferWithContents(cx, nbytes, std::move(ptr)); 66 } 67 68 /** 69 * Create a new ArrayBuffer with the given |contents|, which may be null only 70 * if |nbytes == 0|. |contents| must be allocated compatible with deallocation 71 * by |JS_free|. 72 * 73 * Care must be taken that |nbytes| bytes of |contents| remain valid for the 74 * duration of this call. In particular, passing the length/pointer of existing 75 * typed array or ArrayBuffer data is generally unsafe: if a GC occurs during a 76 * call to this function, it could move those contents to a different location 77 * and invalidate the provided pointer. 78 */ 79 inline JS_PUBLIC_API JSObject* NewArrayBufferWithContents( 80 JSContext* cx, size_t nbytes, 81 mozilla::UniquePtr<uint8_t[], JS::FreePolicy> contents) { 82 // As a convenience, provide an overload for UniquePtr<uint8_t[]>. 83 mozilla::UniquePtr<void, JS::FreePolicy> ptr{contents.release()}; 84 return NewArrayBufferWithContents(cx, nbytes, std::move(ptr)); 85 } 86 87 /** 88 * Marker enum to notify callers that the buffer contents must be freed manually 89 * when the ArrayBuffer allocation failed. 90 */ 91 enum class NewArrayBufferOutOfMemory { CallerMustFreeMemory }; 92 93 /** 94 * Create a new ArrayBuffer with the given |contents|, which may be null only 95 * if |nbytes == 0|. |contents| must be allocated compatible with deallocation 96 * by |JS_free|. 97 * 98 * !!! IMPORTANT !!! 99 * If and only if an ArrayBuffer is successfully created and returned, 100 * ownership of |contents| is transferred to the new ArrayBuffer. 101 * 102 * Care must be taken that |nbytes| bytes of |contents| remain valid for the 103 * duration of this call. In particular, passing the length/pointer of existing 104 * typed array or ArrayBuffer data is generally unsafe: if a GC occurs during a 105 * call to this function, it could move those contents to a different location 106 * and invalidate the provided pointer. 107 */ 108 extern JS_PUBLIC_API JSObject* NewArrayBufferWithContents( 109 JSContext* cx, size_t nbytes, void* contents, NewArrayBufferOutOfMemory); 110 111 /** 112 * Create a new ArrayBuffer, whose bytes are set to the values of the bytes in 113 * the provided ArrayBuffer. 114 * 115 * |maybeArrayBuffer| is asserted to be non-null. An error is thrown if 116 * |maybeArrayBuffer| would fail the |IsArrayBufferObject| test given further 117 * below or if |maybeArrayBuffer| is detached. 118 * 119 * |maybeArrayBuffer| may store its contents in any fashion (i.e. it doesn't 120 * matter whether |maybeArrayBuffer| was allocated using |JS::NewArrayBuffer|, 121 * |JS::NewExternalArrayBuffer|, or any other ArrayBuffer-allocating function). 122 * 123 * The newly-created ArrayBuffer is effectively creatd as if by 124 * |JS::NewArrayBufferWithContents| passing in |maybeArrayBuffer|'s internal 125 * data pointer and length, in a manner safe against |maybeArrayBuffer|'s data 126 * being moved around by the GC. In particular, the new ArrayBuffer will not 127 * behave like one created for WASM or asm.js, so it *can* be detached. 128 */ 129 extern JS_PUBLIC_API JSObject* CopyArrayBuffer( 130 JSContext* cx, JS::Handle<JSObject*> maybeArrayBuffer); 131 132 using BufferContentsFreeFunc = void (*)(void* contents, void* userData); 133 134 /** 135 * UniquePtr deleter for external buffer contents. 136 */ 137 class JS_PUBLIC_API BufferContentsDeleter { 138 BufferContentsFreeFunc freeFunc_ = nullptr; 139 void* userData_ = nullptr; 140 141 public: 142 MOZ_IMPLICIT BufferContentsDeleter(BufferContentsFreeFunc freeFunc, 143 void* userData = nullptr) 144 : freeFunc_(freeFunc), userData_(userData) {} 145 146 void operator()(void* contents) const { freeFunc_(contents, userData_); } 147 148 BufferContentsFreeFunc freeFunc() const { return freeFunc_; } 149 void* userData() const { return userData_; } 150 }; 151 152 /** 153 * Create a new ArrayBuffer with the given contents. The contents must not be 154 * modified by any other code, internal or external. 155 * 156 * When the ArrayBuffer is ready to be disposed of, `freeFunc(contents, 157 * freeUserData)` will be called to release the ArrayBuffer's reference on the 158 * contents. 159 * 160 * `freeFunc()` must not call any JSAPI functions that could cause a garbage 161 * collection. 162 * 163 * The caller must keep the buffer alive until `freeFunc()` is called, or, if 164 * `freeFunc` is null, until the JSRuntime is destroyed. 165 * 166 * The caller must not access the buffer on other threads. The JS engine will 167 * not allow the buffer to be transferred to other threads. If you try to 168 * transfer an external ArrayBuffer to another thread, the data is copied to a 169 * new malloc buffer. `freeFunc()` must be threadsafe, and may be called from 170 * any thread. 171 * 172 * This allows ArrayBuffers to be used with embedder objects that use reference 173 * counting, for example. In that case the caller is responsible 174 * for incrementing the reference count before passing the contents to this 175 * function. This also allows using non-reference-counted contents that must be 176 * freed with some function other than free(). 177 */ 178 extern JS_PUBLIC_API JSObject* NewExternalArrayBuffer( 179 JSContext* cx, size_t nbytes, 180 mozilla::UniquePtr<void, BufferContentsDeleter> contents); 181 182 /** 183 * Create a new ArrayBuffer with the given non-null |contents|. 184 * 185 * Ownership of |contents| remains with the caller: it isn't transferred to the 186 * returned ArrayBuffer. Callers of this function *must* ensure that they 187 * perform these two steps, in this order, to properly relinquish ownership of 188 * |contents|: 189 * 190 * 1. Call |JS::DetachArrayBuffer| on the buffer returned by this function. 191 * (|JS::DetachArrayBuffer| is generally fallible, but a call under these 192 * circumstances is guaranteed to succeed.) 193 * 2. |contents| may be deallocated or discarded consistent with the manner 194 * in which it was allocated. 195 * 196 * Do not simply allow the returned buffer to be garbage-collected before 197 * deallocating |contents|, because in general there is no way to know *when* 198 * an object is fully garbage-collected to the point where this would be safe. 199 */ 200 extern JS_PUBLIC_API JSObject* NewArrayBufferWithUserOwnedContents( 201 JSContext* cx, size_t nbytes, void* contents); 202 203 /** 204 * Create a new mapped ArrayBuffer with the given memory mapped contents. It 205 * must be legal to free the contents pointer by unmapping it. On success, 206 * ownership is transferred to the new mapped ArrayBuffer. 207 */ 208 extern JS_PUBLIC_API JSObject* NewMappedArrayBufferWithContents(JSContext* cx, 209 size_t nbytes, 210 void* contents); 211 212 /** 213 * Create memory mapped ArrayBuffer contents. 214 * Caller must take care of closing fd after calling this function. 215 */ 216 extern JS_PUBLIC_API void* CreateMappedArrayBufferContents(int fd, 217 size_t offset, 218 size_t length); 219 220 /** 221 * Release the allocated resource of mapped ArrayBuffer contents before the 222 * object is created. 223 * If a new object has been created by JS::NewMappedArrayBufferWithContents() 224 * with this content, then JS::DetachArrayBuffer() should be used instead to 225 * release the resource used by the object. 226 */ 227 extern JS_PUBLIC_API void ReleaseMappedArrayBufferContents(void* contents, 228 size_t length); 229 230 // TYPE TESTING 231 232 /* 233 * Check whether obj supports the JS::GetArrayBuffer* APIs. Note that this may 234 * return false if a security wrapper is encountered that denies the unwrapping. 235 * If this test succeeds, then it is safe to call the various predicate and 236 * accessor JSAPI calls defined below. 237 */ 238 extern JS_PUBLIC_API bool IsArrayBufferObject(JSObject* obj); 239 240 // PREDICATES 241 242 /** 243 * Check whether the obj is a detached ArrayBufferObject. Note that this may 244 * return false if a security wrapper is encountered that denies the 245 * unwrapping. 246 */ 247 extern JS_PUBLIC_API bool IsDetachedArrayBufferObject(JSObject* obj); 248 249 /** 250 * Check whether the obj is ArrayBufferObject and memory mapped. Note that this 251 * may return false if a security wrapper is encountered that denies the 252 * unwrapping. 253 */ 254 extern JS_PUBLIC_API bool IsMappedArrayBufferObject(JSObject* obj); 255 256 /** 257 * Return true if the ArrayBuffer |obj| contains any data, i.e. it is not a 258 * detached ArrayBuffer. (ArrayBuffer.prototype is not an ArrayBuffer.) 259 * 260 * |obj| must have passed a JS::IsArrayBufferObject test, or somehow be known 261 * that it would pass such a test: it is an ArrayBuffer or a wrapper of an 262 * ArrayBuffer, and the unwrapping will succeed. 263 */ 264 extern JS_PUBLIC_API bool ArrayBufferHasData(JSObject* obj); 265 266 // ACCESSORS 267 268 extern JS_PUBLIC_API JSObject* UnwrapArrayBuffer(JSObject* obj); 269 270 /** 271 * Attempt to unwrap |obj| as an ArrayBuffer. 272 * 273 * If |obj| *is* an ArrayBuffer, return it unwrapped and set |*length| and 274 * |*data| to weakly refer to the ArrayBuffer's contents. 275 * 276 * If |obj| isn't an ArrayBuffer, return nullptr and do not modify |*length| or 277 * |*data|. 278 */ 279 extern JS_PUBLIC_API JSObject* GetObjectAsArrayBuffer(JSObject* obj, 280 size_t* length, 281 uint8_t** data); 282 283 /** 284 * Return the available byte length of an ArrayBuffer. 285 * 286 * |obj| must have passed a JS::IsArrayBufferObject test, or somehow be known 287 * that it would pass such a test: it is an ArrayBuffer or a wrapper of an 288 * ArrayBuffer, and the unwrapping will succeed. 289 */ 290 extern JS_PUBLIC_API size_t GetArrayBufferByteLength(JSObject* obj); 291 292 // This one isn't inlined because there are a bunch of different ArrayBuffer 293 // classes that would have to be individually handled here. 294 // 295 // There is an isShared out argument for API consistency (eases use from DOM). 296 // It will always be set to false. 297 extern JS_PUBLIC_API void GetArrayBufferLengthAndData(JSObject* obj, 298 size_t* length, 299 bool* isSharedMemory, 300 uint8_t** data); 301 302 /** 303 * Return a pointer to the start of the data referenced by a typed array. The 304 * data is still owned by the typed array, and should not be modified on 305 * another thread. Furthermore, the pointer can become invalid on GC (if the 306 * data is small and fits inside the array's GC header), so callers must take 307 * care not to hold on across anything that could GC. 308 * 309 * |obj| must have passed a JS::IsArrayBufferObject test, or somehow be known 310 * that it would pass such a test: it is an ArrayBuffer or a wrapper of an 311 * ArrayBuffer, and the unwrapping will succeed. 312 * 313 * |*isSharedMemory| is always set to false. The argument is present to 314 * simplify its use from code that also interacts with SharedArrayBuffer. 315 */ 316 extern JS_PUBLIC_API uint8_t* GetArrayBufferData(JSObject* obj, 317 bool* isSharedMemory, 318 const AutoRequireNoGC&); 319 320 // MUTATORS 321 322 /** 323 * Detach an ArrayBuffer, causing all associated views to no longer refer to 324 * the ArrayBuffer's original attached memory. 325 * 326 * This function throws only if it is provided a non-ArrayBuffer object or if 327 * the provided ArrayBuffer is a WASM-backed ArrayBuffer or an ArrayBuffer used 328 * in asm.js code. 329 */ 330 extern JS_PUBLIC_API bool DetachArrayBuffer(JSContext* cx, 331 Handle<JSObject*> obj); 332 333 // Indicates if an object has a defined [[ArrayBufferDetachKey]] internal slot, 334 // which indicates an ArrayBuffer cannot be detached 335 extern JS_PUBLIC_API bool HasDefinedArrayBufferDetachKey(JSContext* cx, 336 Handle<JSObject*> obj, 337 bool* isDefined); 338 339 /** 340 * Steal the contents of the given ArrayBuffer. The ArrayBuffer has its length 341 * set to 0 and its contents array cleared. The caller takes ownership of the 342 * return value and must free it or transfer ownership via 343 * JS::NewArrayBufferWithContents when done using it. 344 */ 345 extern JS_PUBLIC_API void* StealArrayBufferContents(JSContext* cx, 346 Handle<JSObject*> obj); 347 348 /** 349 * Copy data from one array buffer to another. 350 * 351 * Both fromBuffer and toBuffer must be (possibly wrapped) 352 * ArrayBufferObjectMaybeShared. 353 * 354 * This method may throw if the sizes don't match, or if unwrapping fails. 355 * 356 * The API for this is modelled on CopyDataBlockBytes from the spec: 357 * https://tc39.es/ecma262/#sec-copydatablockbytes 358 */ 359 [[nodiscard]] extern JS_PUBLIC_API bool ArrayBufferCopyData( 360 JSContext* cx, Handle<JSObject*> toBlock, size_t toIndex, 361 Handle<JSObject*> fromBlock, size_t fromIndex, size_t count); 362 363 /** 364 * Copy data from one array buffer to another. 365 * 366 * srcBuffer must be a (possibly wrapped) ArrayBufferObjectMaybeShared. 367 * 368 * This method may throw if unwrapping or allocation fails. 369 * 370 * The API for this is modelled on CloneArrayBuffer from the spec: 371 * https://tc39.es/ecma262/#sec-clonearraybuffer 372 */ 373 extern JS_PUBLIC_API JSObject* ArrayBufferClone(JSContext* cx, 374 Handle<JSObject*> srcBuffer, 375 size_t srcByteOffset, 376 size_t srcLength); 377 378 } // namespace JS 379 380 #endif /* js_ArrayBuffer_h */