diff options
Diffstat (limited to 'js/public/ArrayBuffer.h')
-rw-r--r-- | js/public/ArrayBuffer.h | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/js/public/ArrayBuffer.h b/js/public/ArrayBuffer.h new file mode 100644 index 0000000000..ddb8ba3932 --- /dev/null +++ b/js/public/ArrayBuffer.h @@ -0,0 +1,380 @@ +/* -*- 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 "mozilla/UniquePtr.h" + +#include <stddef.h> // size_t +#include <stdint.h> // uint32_t + +#include "jstypes.h" // JS_PUBLIC_API +#include "js/TypeDecls.h" +#include "js/Utility.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|. + * + * Care must be taken that |nbytes| bytes of |contents| 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, + mozilla::UniquePtr<void, JS::FreePolicy> contents); + +/** + * 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|. + * + * Care must be taken that |nbytes| bytes of |contents| 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. + */ +inline JS_PUBLIC_API JSObject* NewArrayBufferWithContents( + JSContext* cx, size_t nbytes, + mozilla::UniquePtr<char[], JS::FreePolicy> contents) { + // As a convenience, provide an overload for UniquePtr<char[]>. + mozilla::UniquePtr<void, JS::FreePolicy> ptr{contents.release()}; + return NewArrayBufferWithContents(cx, nbytes, std::move(ptr)); +} + +/** + * 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|. + * + * Care must be taken that |nbytes| bytes of |contents| 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. + */ +inline JS_PUBLIC_API JSObject* NewArrayBufferWithContents( + JSContext* cx, size_t nbytes, + mozilla::UniquePtr<uint8_t[], JS::FreePolicy> contents) { + // As a convenience, provide an overload for UniquePtr<uint8_t[]>. + mozilla::UniquePtr<void, JS::FreePolicy> ptr{contents.release()}; + return NewArrayBufferWithContents(cx, nbytes, std::move(ptr)); +} + +/** + * Marker enum to notify callers that the buffer contents must be freed manually + * when the ArrayBuffer allocation failed. + */ +enum class NewArrayBufferOutOfMemory { CallerMustFreeMemory }; + +/** + * 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|. + * + * !!! IMPORTANT !!! + * 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 |contents| 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, NewArrayBufferOutOfMemory); + +/** + * 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); + +/** + * UniquePtr deleter for external buffer contents. + */ +class JS_PUBLIC_API BufferContentsDeleter { + BufferContentsFreeFunc freeFunc_ = nullptr; + void* userData_ = nullptr; + + public: + MOZ_IMPLICIT BufferContentsDeleter(BufferContentsFreeFunc freeFunc, + void* userData = nullptr) + : freeFunc_(freeFunc), userData_(userData) {} + + void operator()(void* contents) const { freeFunc_(contents, userData_); } + + BufferContentsFreeFunc freeFunc() const { return freeFunc_; } + void* userData() const { return 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, + mozilla::UniquePtr<void, BufferContentsDeleter> contents); + +/** + * 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 */ |