mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
Backed out 7 changesets (bug 1841314) for causing hazard failures at js.cpp CLOSED TREE
Backed out changeset becc2fa2c186 (bug 1841314) Backed out changeset e5b723317177 (bug 1841314) Backed out changeset 61ae850b25e5 (bug 1841314) Backed out changeset 9ff320c779b8 (bug 1841314) Backed out changeset debf1172f794 (bug 1841314) Backed out changeset 8ac4fa317006 (bug 1841314) Backed out changeset eccacbb3b620 (bug 1841314)
This commit is contained in:
parent
2336558a6d
commit
f77c6f3e48
@ -651,7 +651,7 @@ void BodyConsumer::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength,
|
||||
AssertIsOnTargetThread();
|
||||
|
||||
// This makes sure that we free the data correctly.
|
||||
UniquePtr<uint8_t[], JS::FreePolicy> resultPtr{aResult};
|
||||
auto autoFree = mozilla::MakeScopeExit([&] { free(aResult); });
|
||||
|
||||
if (mBodyConsumed) {
|
||||
return;
|
||||
@ -689,7 +689,7 @@ void BodyConsumer::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength,
|
||||
}
|
||||
|
||||
// Finish successfully consuming body according to type.
|
||||
MOZ_ASSERT(resultPtr);
|
||||
MOZ_ASSERT(aResult);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (!jsapi.Init(mGlobal)) {
|
||||
@ -703,14 +703,16 @@ void BodyConsumer::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength,
|
||||
switch (mConsumeType) {
|
||||
case CONSUME_ARRAYBUFFER: {
|
||||
JS::Rooted<JSObject*> arrayBuffer(cx);
|
||||
BodyUtil::ConsumeArrayBuffer(cx, &arrayBuffer, aResultLength,
|
||||
std::move(resultPtr), error);
|
||||
BodyUtil::ConsumeArrayBuffer(cx, &arrayBuffer, aResultLength, aResult,
|
||||
error);
|
||||
|
||||
if (!error.Failed()) {
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
val.setObjectOrNull(arrayBuffer);
|
||||
|
||||
localPromise->MaybeResolve(val);
|
||||
// ArrayBuffer takes over ownership.
|
||||
aResult = nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -720,7 +722,8 @@ void BodyConsumer::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength,
|
||||
}
|
||||
case CONSUME_FORMDATA: {
|
||||
nsCString data;
|
||||
data.Adopt(reinterpret_cast<char*>(resultPtr.release()), aResultLength);
|
||||
data.Adopt(reinterpret_cast<char*>(aResult), aResultLength);
|
||||
aResult = nullptr;
|
||||
|
||||
RefPtr<dom::FormData> fd = BodyUtil::ConsumeFormData(
|
||||
mGlobal, mBodyMimeType, mMixedCaseMimeType, data, error);
|
||||
@ -734,7 +737,7 @@ void BodyConsumer::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength,
|
||||
case CONSUME_JSON: {
|
||||
nsString decoded;
|
||||
if (NS_SUCCEEDED(
|
||||
BodyUtil::ConsumeText(aResultLength, resultPtr.get(), decoded))) {
|
||||
BodyUtil::ConsumeText(aResultLength, aResult, decoded))) {
|
||||
if (mConsumeType == CONSUME_TEXT) {
|
||||
localPromise->MaybeResolve(decoded);
|
||||
} else {
|
||||
|
@ -353,12 +353,11 @@ class MOZ_STACK_CLASS FormDataParser {
|
||||
// static
|
||||
void BodyUtil::ConsumeArrayBuffer(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aValue,
|
||||
uint32_t aInputLength,
|
||||
UniquePtr<uint8_t[], JS::FreePolicy> aInput,
|
||||
uint32_t aInputLength, uint8_t* aInput,
|
||||
ErrorResult& aRv) {
|
||||
JS::Rooted<JSObject*> arrayBuffer(aCx);
|
||||
arrayBuffer =
|
||||
JS::NewArrayBufferWithContents(aCx, aInputLength, std::move(aInput));
|
||||
arrayBuffer = JS::NewArrayBufferWithContents(aCx, aInputLength,
|
||||
reinterpret_cast<void*>(aInput));
|
||||
if (!arrayBuffer) {
|
||||
JS_ClearPendingException(aCx);
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
|
@ -13,8 +13,6 @@
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/FormData.h"
|
||||
|
||||
#include "js/Utility.h" // JS::FreePolicy
|
||||
|
||||
namespace mozilla {
|
||||
class ErrorResult;
|
||||
|
||||
@ -32,8 +30,7 @@ class BodyUtil final {
|
||||
*/
|
||||
static void ConsumeArrayBuffer(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aValue,
|
||||
uint32_t aInputLength,
|
||||
UniquePtr<uint8_t[], JS::FreePolicy> aInput,
|
||||
uint32_t aInputLength, uint8_t* aInput,
|
||||
ErrorResult& aRv);
|
||||
|
||||
/**
|
||||
|
@ -108,7 +108,7 @@ class CompressionStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
||||
|
||||
do {
|
||||
static uint16_t kBufferSize = 16384;
|
||||
UniquePtr<uint8_t[], JS::FreePolicy> buffer(
|
||||
UniquePtr<uint8_t> buffer(
|
||||
static_cast<uint8_t*>(JS_malloc(aCx, kBufferSize)));
|
||||
if (!buffer) {
|
||||
aRv.ThrowTypeError("Out of memory");
|
||||
@ -164,8 +164,8 @@ class CompressionStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
||||
// into Uint8Arrays.
|
||||
// (The buffer is 'split' by having a fixed sized buffer above.)
|
||||
|
||||
JS::Rooted<JSObject*> view(aCx, nsJSUtils::MoveBufferAsUint8Array(
|
||||
aCx, written, std::move(buffer)));
|
||||
JS::Rooted<JSObject*> view(
|
||||
aCx, nsJSUtils::MoveBufferAsUint8Array(aCx, written, buffer));
|
||||
if (!view || !array.append(view)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
aRv.ThrowTypeError("Out of memory");
|
||||
|
@ -107,7 +107,7 @@ class DecompressionStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
||||
|
||||
do {
|
||||
static uint16_t kBufferSize = 16384;
|
||||
UniquePtr<uint8_t[], JS::FreePolicy> buffer(
|
||||
UniquePtr<uint8_t> buffer(
|
||||
static_cast<uint8_t*>(JS_malloc(aCx, kBufferSize)));
|
||||
if (!buffer) {
|
||||
aRv.ThrowTypeError("Out of memory");
|
||||
@ -194,8 +194,8 @@ class DecompressionStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
||||
// into Uint8Arrays.
|
||||
// (The buffer is 'split' by having a fixed sized buffer above.)
|
||||
|
||||
JS::Rooted<JSObject*> view(aCx, nsJSUtils::MoveBufferAsUint8Array(
|
||||
aCx, written, std::move(buffer)));
|
||||
JS::Rooted<JSObject*> view(
|
||||
aCx, nsJSUtils::MoveBufferAsUint8Array(aCx, written, buffer));
|
||||
if (!view || !array.append(view)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
aRv.ThrowTypeError("Out of memory");
|
||||
|
@ -186,15 +186,17 @@ bool nsJSUtils::DumpEnabled() {
|
||||
#endif
|
||||
}
|
||||
|
||||
JSObject* nsJSUtils::MoveBufferAsUint8Array(
|
||||
JSContext* aCx, size_t aSize,
|
||||
UniquePtr<uint8_t[], JS::FreePolicy> aBuffer) {
|
||||
JSObject* nsJSUtils::MoveBufferAsUint8Array(JSContext* aCx, size_t aSize,
|
||||
UniquePtr<uint8_t>& aBuffer) {
|
||||
JS::Rooted<JSObject*> arrayBuffer(
|
||||
aCx, JS::NewArrayBufferWithContents(aCx, aSize, std::move(aBuffer)));
|
||||
aCx, JS::NewArrayBufferWithContents(aCx, aSize, aBuffer.get()));
|
||||
if (!arrayBuffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Now the ArrayBuffer owns the buffer, so let's release our ownership
|
||||
(void)aBuffer.release();
|
||||
|
||||
return JS_NewUint8ArrayWithBuffer(aCx, arrayBuffer, 0,
|
||||
static_cast<int64_t>(aSize));
|
||||
}
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "js/Conversions.h"
|
||||
#include "js/SourceText.h"
|
||||
#include "js/String.h" // JS::{,Lossy}CopyLinearStringChars, JS::CopyStringChars, JS::Get{,Linear}StringLength, JS::MaxStringLength, JS::StringHasLatin1Chars
|
||||
#include "js/Utility.h" // JS::FreePolicy
|
||||
#include "nsString.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
@ -88,9 +87,8 @@ class nsJSUtils {
|
||||
// Note that the buffer needs to be created by JS_malloc (or at least can be
|
||||
// freed by JS_free), as the resulting Uint8Array takes the ownership of the
|
||||
// buffer.
|
||||
static JSObject* MoveBufferAsUint8Array(
|
||||
JSContext* aCx, size_t aSize,
|
||||
mozilla::UniquePtr<uint8_t[], JS::FreePolicy> aBuffer);
|
||||
static JSObject* MoveBufferAsUint8Array(JSContext* aCx, size_t aSize,
|
||||
mozilla::UniquePtr<uint8_t>& aBuffer);
|
||||
};
|
||||
|
||||
inline void AssignFromStringBuffer(nsStringBuffer* buffer, size_t len,
|
||||
|
@ -68,7 +68,7 @@ static void EncodeNative(JSContext* aCx, mozilla::Decoder* aDecoder,
|
||||
return;
|
||||
}
|
||||
|
||||
UniquePtr<uint8_t[], JS::FreePolicy> buffer(
|
||||
UniquePtr<uint8_t> buffer(
|
||||
static_cast<uint8_t*>(JS_malloc(aCx, needed.value())));
|
||||
if (!buffer) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
@ -99,7 +99,7 @@ static void EncodeNative(JSContext* aCx, mozilla::Decoder* aDecoder,
|
||||
// Step 4.2.2.1. Let chunk be a Uint8Array object wrapping an ArrayBuffer
|
||||
// containing output.
|
||||
JS::Rooted<JSObject*> arrayBuffer(
|
||||
aCx, JS::NewArrayBufferWithContents(aCx, written, std::move(buffer)));
|
||||
aCx, JS::NewArrayBufferWithContents(aCx, written, buffer.release()));
|
||||
if (!arrayBuffer.get()) {
|
||||
JS_ClearPendingException(aCx);
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
|
@ -201,11 +201,7 @@ void FileReader::OnLoadEndArrayBuffer() {
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
// |mFileData| will be deallocated in FileReader's destructor when this
|
||||
// ArrayBuffer allocation failed.
|
||||
mResultArrayBuffer = JS::NewArrayBufferWithContents(
|
||||
cx, mDataLen, mFileData,
|
||||
JS::NewArrayBufferOutOfMemory::CallerMustFreeMemory);
|
||||
mResultArrayBuffer = JS::NewArrayBufferWithContents(cx, mDataLen, mFileData);
|
||||
if (mResultArrayBuffer) {
|
||||
mFileData = nullptr; // Transfer ownership
|
||||
FreeDataAndDispatchSuccess();
|
||||
|
@ -85,11 +85,14 @@ void FileReaderSync::ReadAsArrayBuffer(JSContext* aCx,
|
||||
}
|
||||
|
||||
JSObject* arrayBuffer =
|
||||
JS::NewArrayBufferWithContents(aCx, blobSize, std::move(bufferData));
|
||||
JS::NewArrayBufferWithContents(aCx, blobSize, bufferData.get());
|
||||
if (!arrayBuffer) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
// arrayBuffer takes the ownership when it is not null. Otherwise we
|
||||
// need to release it explicitly.
|
||||
(void)bufferData.release();
|
||||
|
||||
aRetval.set(arrayBuffer);
|
||||
}
|
||||
|
@ -855,19 +855,12 @@ JSObject* Key::DecodeBinary(const EncodedDataType*& aPos,
|
||||
DecodeStringy<eBinary, uint8_t>(
|
||||
aPos, aEnd,
|
||||
[&rv, aCx](uint8_t** out, uint32_t decodedSize) {
|
||||
UniquePtr<void, JS::FreePolicy> ptr{JS_malloc(aCx, decodedSize)};
|
||||
if (NS_WARN_IF(!ptr)) {
|
||||
*out = nullptr;
|
||||
*out = static_cast<uint8_t*>(JS_malloc(aCx, decodedSize));
|
||||
if (NS_WARN_IF(!*out)) {
|
||||
rv = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = static_cast<uint8_t*>(ptr.get());
|
||||
rv = JS::NewArrayBufferWithContents(aCx, decodedSize, std::move(ptr));
|
||||
if (NS_WARN_IF(!rv)) {
|
||||
*out = nullptr;
|
||||
return false;
|
||||
}
|
||||
rv = JS::NewArrayBufferWithContents(aCx, decodedSize, *out);
|
||||
return true;
|
||||
},
|
||||
[&rv, aCx] { rv = JS::NewArrayBuffer(aCx, 0); });
|
||||
|
@ -238,9 +238,8 @@ INSTANTIATE_TEST_SUITE_P(DOM_IndexedDB_Key, TestWithParam_LiteralString,
|
||||
|
||||
static JS::Value CreateArrayBufferValue(JSContext* const aContext,
|
||||
const size_t aSize, char* const aData) {
|
||||
mozilla::UniquePtr<void, JS::FreePolicy> ptr{aData};
|
||||
Rooted<JSObject*> arrayBuffer{aContext, JS::NewArrayBufferWithContents(
|
||||
aContext, aSize, std::move(ptr))};
|
||||
Rooted<JSObject*> arrayBuffer{
|
||||
aContext, JS::NewArrayBufferWithContents(aContext, aSize, aData)};
|
||||
EXPECT_TRUE(arrayBuffer);
|
||||
return JS::ObjectValue(*arrayBuffer);
|
||||
}
|
||||
|
@ -2166,10 +2166,9 @@ void PeerConnectionImpl::DumpPacket_m(size_t level, dom::mozPacketDumpType type,
|
||||
return;
|
||||
}
|
||||
|
||||
UniquePtr<void, JS::FreePolicy> packetPtr{packet.release()};
|
||||
JS::Rooted<JSObject*> jsobj(
|
||||
jsapi.cx(),
|
||||
JS::NewArrayBufferWithContents(jsapi.cx(), size, std::move(packetPtr)));
|
||||
JS::NewArrayBufferWithContents(jsapi.cx(), size, packet.release()));
|
||||
|
||||
RootedSpiderMonkeyInterface<ArrayBuffer> arrayBuffer(jsapi.cx());
|
||||
if (!arrayBuffer.Init(jsobj)) {
|
||||
|
@ -33,8 +33,11 @@ bool DeserializeArrayBuffer(JSContext* cx, const nsTArray<uint8_t>& aBuffer,
|
||||
memcpy(data.get(), aBuffer.Elements(), aBuffer.Length());
|
||||
|
||||
JSObject* obj =
|
||||
JS::NewArrayBufferWithContents(cx, aBuffer.Length(), std::move(data));
|
||||
JS::NewArrayBufferWithContents(cx, aBuffer.Length(), data.get());
|
||||
if (!obj) return false;
|
||||
// If JS::NewArrayBufferWithContents returns non-null, the ownership of
|
||||
// the data is transfered to obj, so we release the ownership here.
|
||||
mozilla::Unused << data.release();
|
||||
|
||||
aVal.setObject(*obj);
|
||||
return true;
|
||||
|
@ -1093,9 +1093,7 @@ void PushMessageData::ArrayBuffer(JSContext* cx,
|
||||
ErrorResult& aRv) {
|
||||
uint8_t* data = GetContentsCopy();
|
||||
if (data) {
|
||||
UniquePtr<uint8_t[], JS::FreePolicy> dataPtr(data);
|
||||
BodyUtil::ConsumeArrayBuffer(cx, aRetval, mBytes.Length(),
|
||||
std::move(dataPtr), aRv);
|
||||
BodyUtil::ConsumeArrayBuffer(cx, aRetval, mBytes.Length(), data, aRv);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,7 @@ JSObject* TransferArrayBuffer(JSContext* aCx, JS::Handle<JSObject*> aObject) {
|
||||
size_t bufferLength = JS::GetArrayBufferByteLength(aObject);
|
||||
|
||||
// Step 2 (Reordered)
|
||||
UniquePtr<void, JS::FreePolicy> bufferData{
|
||||
JS::StealArrayBufferContents(aCx, aObject)};
|
||||
void* bufferData = JS::StealArrayBufferContents(aCx, aObject);
|
||||
|
||||
// Step 4.
|
||||
if (!JS::DetachArrayBuffer(aCx, aObject)) {
|
||||
@ -35,8 +34,7 @@ JSObject* TransferArrayBuffer(JSContext* aCx, JS::Handle<JSObject*> aObject) {
|
||||
}
|
||||
|
||||
// Step 5.
|
||||
return JS::NewArrayBufferWithContents(aCx, bufferLength,
|
||||
std::move(bufferData));
|
||||
return JS::NewArrayBufferWithContents(aCx, bufferLength, bufferData);
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#can-transfer-array-buffer
|
||||
|
@ -429,8 +429,7 @@ void InputToReadableStreamAlgorithms::PullFromInputStream(JSContext* aCx,
|
||||
else {
|
||||
// Step 9.1. Set view to the result of creating a Uint8Array from pulled in
|
||||
// stream’s relevant Realm.
|
||||
UniquePtr<uint8_t[], JS::FreePolicy> buffer(
|
||||
static_cast<uint8_t*>(JS_malloc(aCx, pullSize)));
|
||||
UniquePtr<uint8_t> buffer(static_cast<uint8_t*>(JS_malloc(aCx, pullSize)));
|
||||
if (!buffer) {
|
||||
aRv.ThrowTypeError("Out of memory");
|
||||
return;
|
||||
@ -447,8 +446,8 @@ void InputToReadableStreamAlgorithms::PullFromInputStream(JSContext* aCx,
|
||||
}
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(pullSize == bytesWritten);
|
||||
JS::Rooted<JSObject*> view(aCx, nsJSUtils::MoveBufferAsUint8Array(
|
||||
aCx, bytesWritten, std::move(buffer)));
|
||||
JS::Rooted<JSObject*> view(
|
||||
aCx, nsJSUtils::MoveBufferAsUint8Array(aCx, bytesWritten, buffer));
|
||||
if (!view) {
|
||||
JS_ClearPendingException(aCx);
|
||||
aRv.ThrowTypeError("Out of memory");
|
||||
|
@ -2708,15 +2708,20 @@ JSObject* IOUtils::JsBuffer::IntoUint8Array(JSContext* aCx, JsBuffer aBuffer) {
|
||||
return JS_NewUint8Array(aCx, 0);
|
||||
}
|
||||
|
||||
MOZ_RELEASE_ASSERT(aBuffer.mBuffer);
|
||||
char* rawBuffer = aBuffer.mBuffer.release();
|
||||
MOZ_RELEASE_ASSERT(rawBuffer);
|
||||
JS::Rooted<JSObject*> arrayBuffer(
|
||||
aCx, JS::NewArrayBufferWithContents(aCx, aBuffer.mLength,
|
||||
std::move(aBuffer.mBuffer)));
|
||||
reinterpret_cast<void*>(rawBuffer)));
|
||||
|
||||
if (!arrayBuffer) {
|
||||
// The array buffer does not take ownership of the data pointer unless
|
||||
// creation succeeds. We are still on the hook to free it.
|
||||
//
|
||||
// aBuffer will be destructed at end of scope, but its destructor does not
|
||||
// take into account |mCapacity| or |mLength|, so it is OK for them to be
|
||||
// non-zero here with a null |mBuffer|.
|
||||
js_free(rawBuffer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -247,10 +247,8 @@ void Buffer::GetMappedRange(JSContext* aCx, uint64_t aOffset,
|
||||
|
||||
std::shared_ptr<ipc::WritableSharedMemoryMapping>* userData =
|
||||
new std::shared_ptr<ipc::WritableSharedMemoryMapping>(mShmem);
|
||||
UniquePtr<void, JS::BufferContentsDeleter> dataPtr{
|
||||
span.data(), {&ExternalBufferFreeCallback, userData}};
|
||||
auto* const arrayBuffer =
|
||||
JS::NewExternalArrayBuffer(aCx, size, std::move(dataPtr));
|
||||
auto* const arrayBuffer = JS::NewExternalArrayBuffer(
|
||||
aCx, size, span.data(), &ExternalBufferFreeCallback, userData);
|
||||
|
||||
if (!arrayBuffer) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
|
@ -3927,11 +3927,7 @@ JSObject* ArrayBufferBuilder::TakeArrayBuffer(JSContext* aCx) {
|
||||
}
|
||||
}
|
||||
|
||||
// |mDataPtr| will be deallocated in ArrayBufferBuilder's destructor when this
|
||||
// ArrayBuffer allocation failed.
|
||||
JSObject* obj = JS::NewArrayBufferWithContents(
|
||||
aCx, mLength, mDataPtr,
|
||||
JS::NewArrayBufferOutOfMemory::CallerMustFreeMemory);
|
||||
JSObject* obj = JS::NewArrayBufferWithContents(aCx, mLength, mDataPtr);
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -8,14 +8,11 @@
|
||||
#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;
|
||||
@ -36,77 +33,18 @@ extern JS_PUBLIC_API JSObject* NewArrayBuffer(JSContext* cx, size_t nbytes);
|
||||
* 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
|
||||
* 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, NewArrayBufferOutOfMemory);
|
||||
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
|
||||
@ -131,24 +69,6 @@ extern JS_PUBLIC_API JSObject* CopyArrayBuffer(
|
||||
|
||||
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.
|
||||
@ -176,8 +96,8 @@ class JS_PUBLIC_API BufferContentsDeleter {
|
||||
* freed with some function other than free().
|
||||
*/
|
||||
extern JS_PUBLIC_API JSObject* NewExternalArrayBuffer(
|
||||
JSContext* cx, size_t nbytes,
|
||||
mozilla::UniquePtr<void, BufferContentsDeleter> contents);
|
||||
JSContext* cx, size_t nbytes, void* contents,
|
||||
BufferContentsFreeFunc freeFunc, void* freeUserData = nullptr);
|
||||
|
||||
/**
|
||||
* Create a new ArrayBuffer with the given non-null |contents|.
|
||||
|
@ -4989,9 +4989,10 @@ class CloneBufferObject : public NativeObject {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject* arrayBuffer =
|
||||
JS::NewArrayBufferWithContents(cx, size, std::move(buffer));
|
||||
auto* rawBuffer = buffer.release();
|
||||
JSObject* arrayBuffer = JS::NewArrayBufferWithContents(cx, size, rawBuffer);
|
||||
if (!arrayBuffer) {
|
||||
js_free(rawBuffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -66,15 +66,14 @@ BEGIN_TEST(testArrayBuffer_bug720949_steal) {
|
||||
CHECK(v.isInt32(MAGIC_VALUE_2));
|
||||
|
||||
// Steal the contents
|
||||
mozilla::UniquePtr<void, JS::FreePolicy> contents{
|
||||
JS::StealArrayBufferContents(cx, obj)};
|
||||
void* contents = JS::StealArrayBufferContents(cx, obj);
|
||||
CHECK(contents != nullptr);
|
||||
|
||||
CHECK(JS::IsDetachedArrayBufferObject(obj));
|
||||
|
||||
// Transfer to a new ArrayBuffer
|
||||
JS::RootedObject dst(
|
||||
cx, JS::NewArrayBufferWithContents(cx, size, std::move(contents)));
|
||||
JS::RootedObject dst(cx,
|
||||
JS::NewArrayBufferWithContents(cx, size, contents));
|
||||
CHECK(JS::IsArrayBufferObject(dst));
|
||||
{
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
@ -172,11 +171,11 @@ END_TEST(testArrayBuffer_bug720949_viewList)
|
||||
|
||||
BEGIN_TEST(testArrayBuffer_customFreeFunc) {
|
||||
ExternalData data("One two three four");
|
||||
auto dataPointer = data.pointer();
|
||||
|
||||
// The buffer takes ownership of the data.
|
||||
JS::RootedObject buffer(
|
||||
cx, JS::NewExternalArrayBuffer(cx, data.len(), std::move(dataPointer)));
|
||||
cx, JS::NewExternalArrayBuffer(cx, data.len(), data.contents(),
|
||||
&ExternalData::freeCallback, &data));
|
||||
CHECK(buffer);
|
||||
CHECK(!data.wasFreed());
|
||||
|
||||
@ -200,8 +199,9 @@ END_TEST(testArrayBuffer_customFreeFunc)
|
||||
BEGIN_TEST(testArrayBuffer_staticContents) {
|
||||
ExternalData data("One two three four");
|
||||
|
||||
JS::RootedObject buffer(cx, JS::NewArrayBufferWithUserOwnedContents(
|
||||
cx, data.len(), data.contents()));
|
||||
// When not passing a free function, the buffer doesn't own the data.
|
||||
JS::RootedObject buffer(
|
||||
cx, JS::NewExternalArrayBuffer(cx, data.len(), data.contents(), nullptr));
|
||||
CHECK(buffer);
|
||||
CHECK(!data.wasFreed());
|
||||
|
||||
@ -226,9 +226,9 @@ END_TEST(testArrayBuffer_staticContents)
|
||||
BEGIN_TEST(testArrayBuffer_stealDetachExternal) {
|
||||
static const char dataBytes[] = "One two three four";
|
||||
ExternalData data(dataBytes);
|
||||
auto dataPointer = data.pointer();
|
||||
JS::RootedObject buffer(
|
||||
cx, JS::NewExternalArrayBuffer(cx, data.len(), std::move(dataPointer)));
|
||||
cx, JS::NewExternalArrayBuffer(cx, data.len(), data.contents(),
|
||||
&ExternalData::freeCallback, &data));
|
||||
CHECK(buffer);
|
||||
CHECK(!data.wasFreed());
|
||||
|
||||
@ -260,9 +260,9 @@ BEGIN_TEST(testArrayBuffer_serializeExternal) {
|
||||
}
|
||||
|
||||
ExternalData data("One two three four");
|
||||
auto dataPointer = data.pointer();
|
||||
JS::RootedObject externalBuffer(
|
||||
cx, JS::NewExternalArrayBuffer(cx, data.len(), std::move(dataPointer)));
|
||||
cx, JS::NewExternalArrayBuffer(cx, data.len(), data.contents(),
|
||||
&ExternalData::freeCallback, &data));
|
||||
CHECK(externalBuffer);
|
||||
CHECK(!data.wasFreed());
|
||||
|
||||
@ -308,14 +308,14 @@ END_TEST(testArrayBuffer_serializeExternal)
|
||||
|
||||
BEGIN_TEST(testArrayBuffer_copyData) {
|
||||
ExternalData data1("One two three four");
|
||||
JS::RootedObject buffer1(cx, JS::NewArrayBufferWithUserOwnedContents(
|
||||
cx, data1.len(), data1.contents()));
|
||||
JS::RootedObject buffer1(cx, JS::NewExternalArrayBuffer(
|
||||
cx, data1.len(), data1.contents(), nullptr));
|
||||
|
||||
CHECK(buffer1);
|
||||
|
||||
ExternalData data2("Six");
|
||||
JS::RootedObject buffer2(cx, JS::NewArrayBufferWithUserOwnedContents(
|
||||
cx, data2.len(), data2.contents()));
|
||||
JS::RootedObject buffer2(cx, JS::NewExternalArrayBuffer(
|
||||
cx, data2.len(), data2.contents(), nullptr));
|
||||
|
||||
CHECK(buffer2);
|
||||
|
||||
@ -367,15 +367,15 @@ BEGIN_TEST(testArrayBuffer_copyDataAcrossGlobals) {
|
||||
JS::RootedObject buffer1(cx);
|
||||
{
|
||||
js::AutoRealm realm(cx, otherGlobal);
|
||||
buffer1 = JS::NewArrayBufferWithUserOwnedContents(cx, data1.len(),
|
||||
data1.contents());
|
||||
buffer1 =
|
||||
JS::NewExternalArrayBuffer(cx, data1.len(), data1.contents(), nullptr);
|
||||
}
|
||||
CHECK(buffer1);
|
||||
CHECK(JS_WrapObject(cx, &buffer1));
|
||||
|
||||
ExternalData data2("Six");
|
||||
JS::RootedObject buffer2(cx, JS::NewArrayBufferWithUserOwnedContents(
|
||||
cx, data2.len(), data2.contents()));
|
||||
JS::RootedObject buffer2(cx, JS::NewExternalArrayBuffer(
|
||||
cx, data2.len(), data2.contents(), nullptr));
|
||||
|
||||
CHECK(buffer2);
|
||||
|
||||
@ -424,8 +424,8 @@ END_TEST(testArrayBuffer_copyDataAcrossGlobals)
|
||||
|
||||
BEGIN_TEST(testArrayBuffer_ArrayBufferClone) {
|
||||
ExternalData data("One two three four");
|
||||
JS::RootedObject externalBuffer(cx, JS::NewArrayBufferWithUserOwnedContents(
|
||||
cx, data.len(), data.contents()));
|
||||
JS::RootedObject externalBuffer(
|
||||
cx, JS::NewExternalArrayBuffer(cx, data.len(), data.contents(), nullptr));
|
||||
|
||||
CHECK(externalBuffer);
|
||||
|
||||
|
@ -103,7 +103,6 @@ END_TEST(testStructuredClone_string)
|
||||
|
||||
BEGIN_TEST(testStructuredClone_externalArrayBuffer) {
|
||||
ExternalData data("One two three four");
|
||||
auto dataPointer = data.pointer();
|
||||
JS::RootedObject g1(cx, createGlobal());
|
||||
JS::RootedObject g2(cx, createGlobal());
|
||||
CHECK(g1);
|
||||
@ -115,7 +114,8 @@ BEGIN_TEST(testStructuredClone_externalArrayBuffer) {
|
||||
JSAutoRealm ar(cx, g1);
|
||||
|
||||
JS::RootedObject obj(
|
||||
cx, JS::NewExternalArrayBuffer(cx, data.len(), std::move(dataPointer)));
|
||||
cx, JS::NewExternalArrayBuffer(cx, data.len(), data.contents(),
|
||||
&ExternalData::freeCallback, &data));
|
||||
CHECK(!data.wasFreed());
|
||||
|
||||
v1 = JS::ObjectOrNullValue(obj);
|
||||
@ -164,9 +164,9 @@ BEGIN_TEST(testStructuredClone_externalArrayBufferDifferentThreadOrProcess) {
|
||||
|
||||
bool testStructuredCloneCopy(JS::StructuredCloneScope scope) {
|
||||
ExternalData data("One two three four");
|
||||
auto dataPointer = data.pointer();
|
||||
JS::RootedObject buffer(
|
||||
cx, JS::NewExternalArrayBuffer(cx, data.len(), std::move(dataPointer)));
|
||||
cx, JS::NewExternalArrayBuffer(cx, data.len(), data.contents(),
|
||||
&ExternalData::freeCallback, &data));
|
||||
CHECK(buffer);
|
||||
CHECK(!data.wasFreed());
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
#include "gc/GC.h"
|
||||
#include "js/AllocPolicy.h"
|
||||
#include "js/ArrayBuffer.h"
|
||||
#include "js/CharacterEncoding.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "js/Equality.h" // JS::SameValue
|
||||
@ -543,7 +542,6 @@ class TestJSPrincipals : public JSPrincipals {
|
||||
class ExternalData {
|
||||
char* contents_;
|
||||
size_t len_;
|
||||
bool uniquePointerCreated_ = false;
|
||||
|
||||
public:
|
||||
explicit ExternalData(const char* str)
|
||||
@ -560,13 +558,6 @@ class ExternalData {
|
||||
contents_ = nullptr;
|
||||
}
|
||||
|
||||
mozilla::UniquePtr<void, JS::BufferContentsDeleter> pointer() {
|
||||
MOZ_ASSERT(!uniquePointerCreated_,
|
||||
"Not allowed to create multiple unique pointers to contents");
|
||||
uniquePointerCreated_ = true;
|
||||
return {contents_, {ExternalData::freeCallback, this}};
|
||||
}
|
||||
|
||||
static void freeCallback(void* contents, void* userData) {
|
||||
auto self = static_cast<ExternalData*>(userData);
|
||||
MOZ_ASSERT(self->contents() == contents);
|
||||
|
@ -1743,15 +1743,14 @@ static bool CreateExternalArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void* buffer = js_calloc(bytes);
|
||||
void* buffer = js_malloc(bytes);
|
||||
if (!buffer) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
UniquePtr<void, JS::BufferContentsDeleter> ptr{buffer,
|
||||
{&freeExternalCallback}};
|
||||
auto* arrayBuffer = JS::NewExternalArrayBuffer(cx, bytes, std::move(ptr));
|
||||
RootedObject arrayBuffer(
|
||||
cx, JS::NewExternalArrayBuffer(cx, bytes, buffer, &freeExternalCallback));
|
||||
if (!arrayBuffer) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1018,8 +1018,7 @@ void ArrayBufferObject::releaseData(JS::GCContext* gcx) {
|
||||
gcx->removeCellMemory(this, byteLength(), MemoryUse::ArrayBufferContents);
|
||||
break;
|
||||
case EXTERNAL:
|
||||
MOZ_ASSERT(freeInfo()->freeFunc);
|
||||
{
|
||||
if (freeInfo()->freeFunc) {
|
||||
// The analyzer can't know for sure whether the embedder-supplied
|
||||
// free function will GC. We give the analyzer a hint here.
|
||||
// (Doing a GC in the free function is considered a programmer
|
||||
@ -1357,10 +1356,9 @@ ArrayBufferObject* ArrayBufferObject::createForContents(
|
||||
} else if (contents.kind() == EXTERNAL) {
|
||||
// Store the FreeInfo in the inline data slots so that we
|
||||
// don't use up slots for it in non-refcounted array buffers.
|
||||
constexpr size_t freeInfoSlots = HowMany(sizeof(FreeInfo), sizeof(Value));
|
||||
static_assert(
|
||||
reservedSlots + freeInfoSlots <= NativeObject::MAX_FIXED_SLOTS,
|
||||
"FreeInfo must fit in inline slots");
|
||||
size_t freeInfoSlots = HowMany(sizeof(FreeInfo), sizeof(Value));
|
||||
MOZ_ASSERT(reservedSlots + freeInfoSlots <= NativeObject::MAX_FIXED_SLOTS,
|
||||
"FreeInfo must fit in inline slots");
|
||||
nslots += freeInfoSlots;
|
||||
} else {
|
||||
// The ABO is taking ownership, so account the bytes against the zone.
|
||||
@ -1924,22 +1922,9 @@ JS_PUBLIC_API JSObject* JS::NewArrayBuffer(JSContext* cx, size_t nbytes) {
|
||||
return ArrayBufferObject::createZeroed(cx, nbytes);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API JSObject* JS::NewArrayBufferWithContents(
|
||||
JSContext* cx, size_t nbytes,
|
||||
mozilla::UniquePtr<void, JS::FreePolicy> contents) {
|
||||
auto* result = NewArrayBufferWithContents(
|
||||
cx, nbytes, contents.get(),
|
||||
JS::NewArrayBufferOutOfMemory::CallerMustFreeMemory);
|
||||
if (result) {
|
||||
// If and only if an ArrayBuffer is successfully created, ownership of
|
||||
// |contents| is transferred to the new ArrayBuffer.
|
||||
(void)contents.release();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API JSObject* JS::NewArrayBufferWithContents(
|
||||
JSContext* cx, size_t nbytes, void* data, NewArrayBufferOutOfMemory) {
|
||||
JS_PUBLIC_API JSObject* JS::NewArrayBufferWithContents(JSContext* cx,
|
||||
size_t nbytes,
|
||||
void* data) {
|
||||
AssertHeapIsIdle();
|
||||
CHECK_THREAD(cx);
|
||||
MOZ_ASSERT_IF(!data, nbytes == 0);
|
||||
@ -1972,26 +1957,18 @@ JS_PUBLIC_API JSObject* JS::CopyArrayBuffer(JSContext* cx,
|
||||
}
|
||||
|
||||
JS_PUBLIC_API JSObject* JS::NewExternalArrayBuffer(
|
||||
JSContext* cx, size_t nbytes,
|
||||
mozilla::UniquePtr<void, JS::BufferContentsDeleter> contents) {
|
||||
JSContext* cx, size_t nbytes, void* data,
|
||||
JS::BufferContentsFreeFunc freeFunc, void* freeUserData) {
|
||||
AssertHeapIsIdle();
|
||||
CHECK_THREAD(cx);
|
||||
|
||||
MOZ_ASSERT(contents);
|
||||
MOZ_ASSERT(data);
|
||||
|
||||
using BufferContents = ArrayBufferObject::BufferContents;
|
||||
|
||||
BufferContents bufferContents = BufferContents::createExternal(
|
||||
contents.get(), contents.get_deleter().freeFunc(),
|
||||
contents.get_deleter().userData());
|
||||
auto* result =
|
||||
ArrayBufferObject::createForContents(cx, nbytes, bufferContents);
|
||||
if (result) {
|
||||
// If and only if an ArrayBuffer is successfully created, ownership of
|
||||
// |contents| is transferred to the new ArrayBuffer.
|
||||
(void)contents.release();
|
||||
}
|
||||
return result;
|
||||
BufferContents contents =
|
||||
BufferContents::createExternal(data, freeFunc, freeUserData);
|
||||
return ArrayBufferObject::createForContents(cx, nbytes, contents);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API JSObject* JS::NewArrayBufferWithUserOwnedContents(JSContext* cx,
|
||||
|
@ -308,7 +308,6 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared {
|
||||
static BufferContents createExternal(void* data,
|
||||
JS::BufferContentsFreeFunc freeFunc,
|
||||
void* freeUserData = nullptr) {
|
||||
MOZ_ASSERT(freeFunc);
|
||||
return BufferContents(static_cast<uint8_t*>(data), EXTERNAL, freeFunc,
|
||||
freeUserData);
|
||||
}
|
||||
|
@ -3261,11 +3261,7 @@ bool JSStructuredCloneReader::readTransferMap() {
|
||||
MOZ_ASSERT(data == JS::SCTAG_TMO_ALLOC_DATA ||
|
||||
data == JS::SCTAG_TMO_MAPPED_DATA);
|
||||
if (data == JS::SCTAG_TMO_ALLOC_DATA) {
|
||||
// When the ArrayBuffer can't be allocated, |content| will be free'ed
|
||||
// in `JSStructuredCloneData::discardTransferables()`.
|
||||
obj = JS::NewArrayBufferWithContents(
|
||||
cx, nbytes, content,
|
||||
JS::NewArrayBufferOutOfMemory::CallerMustFreeMemory);
|
||||
obj = JS::NewArrayBufferWithContents(cx, nbytes, content);
|
||||
} else if (data == JS::SCTAG_TMO_MAPPED_DATA) {
|
||||
obj = JS::NewMappedArrayBufferWithContents(cx, nbytes, content);
|
||||
}
|
||||
|
@ -490,9 +490,8 @@ class Span {
|
||||
/**
|
||||
* Constructor for mozilla::UniquePtr holding an array and length.
|
||||
*/
|
||||
template <class ArrayElementType = std::add_pointer<element_type>,
|
||||
class DeleterType>
|
||||
constexpr Span(const mozilla::UniquePtr<ArrayElementType, DeleterType>& aPtr,
|
||||
template <class ArrayElementType = std::add_pointer<element_type>>
|
||||
constexpr Span(const mozilla::UniquePtr<ArrayElementType>& aPtr,
|
||||
index_type aLength)
|
||||
: storage_(aPtr.get(), aLength) {}
|
||||
|
||||
|
@ -82,9 +82,8 @@ JSObject* OwnedRustBuffer::IntoArrayBuffer(JSContext* cx) {
|
||||
int32_t len = mBuf.len;
|
||||
void* data = mBuf.data;
|
||||
auto userData = MakeUnique<OwnedRustBuffer>(std::move(*this));
|
||||
UniquePtr<void, JS::BufferContentsDeleter> dataPtr{
|
||||
data, {&ArrayBufferFreeFunc, userData.release()}};
|
||||
return JS::NewExternalArrayBuffer(cx, len, std::move(dataPtr));
|
||||
return JS::NewExternalArrayBuffer(cx, len, data, &ArrayBufferFreeFunc,
|
||||
userData.release());
|
||||
}
|
||||
|
||||
void OwnedRustBuffer::ArrayBufferFreeFunc(void* contents, void* userData) {
|
||||
|
Loading…
Reference in New Issue
Block a user