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
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#include "nsPrintfCString.h"
#include "js/GCAPI.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/dom/UniFFIPointer.h"
#include "mozilla/dom/UniFFIBinding.h"
#include "mozilla/Logging.h"
#include "UniFFIRust.h"
static mozilla::LazyLogModule sUniFFIPointerLogger("uniffi_logger");
namespace mozilla::dom {
using uniffi::RUST_CALL_SUCCESS;
using uniffi::RustCallStatus;
using uniffi::UniFFIPointerType;
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(UniFFIPointer)
NS_IMPL_CYCLE_COLLECTING_ADDREF(UniFFIPointer)
NS_IMPL_CYCLE_COLLECTING_RELEASE(UniFFIPointer)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UniFFIPointer)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
// Static function
already_AddRefed<UniFFIPointer> UniFFIPointer::Create(
void* aPtr, const UniFFIPointerType* aType) {
RefPtr<UniFFIPointer> uniFFIPointer = new UniFFIPointer(aPtr, aType);
return uniFFIPointer.forget();
}
already_AddRefed<UniFFIPointer> UniFFIPointer::Read(
const ArrayBuffer& aArrayBuff, uint32_t aPosition,
const UniFFIPointerType* aType, ErrorResult& aError) {
MOZ_LOG(sUniFFIPointerLogger, LogLevel::Info,
("[UniFFI] Reading Pointer from buffer"));
CheckedUint32 end = CheckedUint32(aPosition) + 8;
uint8_t data_ptr[8];
if (!end.isValid() ||
!aArrayBuff.CopyDataTo(
data_ptr, [&](size_t aLength) -> Maybe<std::pair<size_t, size_t>> {
if (end.value() > aLength) {
return Nothing();
}
return Some(std::make_pair(aPosition, 8));
})) {
aError.ThrowRangeError("position is out of range");
return nullptr;
}
// in Rust and Write(), a pointer is converted to a void* then written as u64
// BigEndian we do the reverse here
void* ptr = (void*)mozilla::BigEndian::readUint64(data_ptr);
return UniFFIPointer::Create(ptr, aType);
}
void UniFFIPointer::Write(const ArrayBuffer& aArrayBuff, uint32_t aPosition,
const UniFFIPointerType* aType,
ErrorResult& aError) const {
if (!this->IsSamePtrType(aType)) {
aError.ThrowUnknownError(nsPrintfCString(
"Attempt to write pointer with wrong type: %s (expected: %s)",
aType->typeName.get(), this->mType->typeName.get()));
return;
}
MOZ_LOG(sUniFFIPointerLogger, LogLevel::Info,
("[UniFFI] Writing Pointer to buffer"));
// Clone the pointer outside of ProcessData, since the JS hazard checker
// assumes the call could result in a GC pass.
//
// This means that if the code below fails, we will leak a reference to the
// pointer. This is acceptable because the code should will only fail if
// UniFFI incorrectly sizes the array buffers which should be caught by our
// unit tests. Also, there's no way to protect against this in general since
// if anything fails after writing a pointer to the array then the reference
// will leak.
void* clone = ClonePtr();
CheckedUint32 end = CheckedUint32(aPosition) + 8;
if (!end.isValid() || !aArrayBuff.ProcessData([&](const Span<uint8_t>& aData,
JS::AutoCheckCannotGC&&) {
if (end.value() > aData.Length()) {
return false;
}
// in Rust and Read(), a u64 is read as BigEndian and then converted to
// a pointer we do the reverse here
const auto& data_ptr = aData.Subspan(aPosition, 8);
mozilla::BigEndian::writeUint64(data_ptr.Elements(), (uint64_t)clone);
return true;
})) {
aError.ThrowRangeError("position is out of range");
return;
}
}
UniFFIPointer::UniFFIPointer(void* aPtr, const UniFFIPointerType* aType) {
mPtr = aPtr;
mType = aType;
}
JSObject* UniFFIPointer::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return dom::UniFFIPointer_Binding::Wrap(aCx, this, aGivenProto);
}
void* UniFFIPointer::ClonePtr() const {
MOZ_LOG(sUniFFIPointerLogger, LogLevel::Info,
("[UniFFI] Cloning raw pointer"));
RustCallStatus status{};
auto cloned = this->mType->clone(this->mPtr, &status);
MOZ_DIAGNOSTIC_ASSERT(status.code == RUST_CALL_SUCCESS,
"UniFFI clone call returned a non-success result");
return cloned;
}
bool UniFFIPointer::IsSamePtrType(const UniFFIPointerType* aType) const {
return this->mType == aType;
}
UniFFIPointer::~UniFFIPointer() {
MOZ_LOG(sUniFFIPointerLogger, LogLevel::Info,
("[UniFFI] Destroying pointer"));
RustCallStatus status{};
this->mType->destructor(this->mPtr, &status);
MOZ_DIAGNOSTIC_ASSERT(status.code == RUST_CALL_SUCCESS,
"UniFFI destructor call returned a non-success result");
}
} // namespace mozilla::dom
|