fune/toolkit/components/uniffi-js/UniFFIPointer.cpp
Ben Dean-Kawamura 2bb9af8156 Bug 1888683 - Upgrade UniFFI to 0.27.1. r=markh,janerik,glandium,supply-chain-reviewers,mach-reviewers,android-reviewers,kaya
Update:
  - UniFFI to 0.27.1
  - Glean to 59.0.0
  - App-services to a recent version

This removes the need for the goblin build hack, although we still have
duplicate versions of goblin since UniFFI is ahead of the moz-central
version.  I think that should be easy to resolve as a follow-up.

Updating uniffi-bindget-gecko-js based on upstream changes:
  - Clone objects before lowering them
    (https://github.com/mozilla/uniffi-rs/pull/1880)
  - Use u64 for the RustBuffer length and capacity field
    (https://github.com/mozilla/uniffi-rs/pull/1978)

I didn't implement the new callback interface VTable code.  Instead I
simply disabled the one fixture that tests it.  I'd rather implement
https://bugzilla.mozilla.org/show_bug.cgi?id=1888668 first, since that
will simplify the process a bunch. The only real-world use-case for
callbacks that I know of is Mark's logging changes, but that will
require implementing trait interfaces anyways so I'd rather wait than
write a bunch of C++ code that we then throw away.

Differential Revision: https://phabricator.services.mozilla.com/D206130
2024-04-10 17:23:00 +00:00

133 lines
4.9 KiB
C++

/* -*- 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"));
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);
// The hazard checker assumes all calls to a function pointer may result
// in a GC call and `ClonePtr` calls mType->clone. However, we know that
// mtype->clone won't make a GC call since it's essentially just a call
// to Rust's `Arc::clone()`. Use AutoSuppressGCAnalysis to tell the
// hazard checker to ignore the call.
JS::AutoSuppressGCAnalysis suppress;
mozilla::BigEndian::writeUint64(data_ptr.Elements(),
(uint64_t)ClonePtr());
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