Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#ifndef mozilla_UniFFIFfiConverter_h
#define mozilla_UniFFIFfiConverter_h
#include <limits>
#include <type_traits>
#include "nsString.h"
#include "mozilla/ResultVariant.h"
#include "mozilla/dom/PrimitiveConversions.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/dom/UniFFIBinding.h"
#include "mozilla/dom/UniFFIPointer.h"
#include "mozilla/dom/UniFFIScaffolding.h"
#include "mozilla/uniffi/OwnedRustBuffer.h"
#include "mozilla/uniffi/PointerType.h"
#include "mozilla/uniffi/Rust.h"
namespace mozilla::uniffi {
// This header defines the `FfiValue*` classes, which handle conversions between
// FFI values and the JS `OwningUniFFIScaffoldingValue` class.
//
// The exact signatures vary slightly, but in all FfiValue classes define these
// functions:
// - `Lower` -- Convert a `OwningUniFFIScaffoldingValue` into an `FfiValue`.
// - `Lift` -- Convert a `FfiValue` into a `OwningUniFFIScaffoldingValue`.
// - `IntoRust` -- Convert a `FfiValue` into a raw FFI type to pass to Rust
// - `FromRust` -- Convert a raw FFI type from Rust into a `FfiValue`
//
// Also, each `FfiValue` class defines a default constructor.
// For types that hold resources like `FfiValueRustBuffer`, `Lift` and
// `IntoRust` move resources out of the value, leaving behind the default.
// FfiValue class for integer values
template <typename T>
class FfiValueInt {
private:
T mValue = 0;
public:
FfiValueInt() = default;
explicit FfiValueInt(T aValue) : mValue(aValue) {}
void Lower(const dom::OwningUniFFIScaffoldingValue& aValue,
ErrorResult& aError) {
if (!aValue.IsDouble()) {
aError.ThrowTypeError("Bad argument type"_ns);
return;
}
double floatValue = aValue.GetAsDouble();
// Use PrimitiveConversionTraits_Limits rather than std::numeric_limits,
// since it handles JS-specific bounds like the 64-bit integer limits.
// (see Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER)
if (floatValue < dom::PrimitiveConversionTraits_Limits<T>::min() ||
floatValue > dom::PrimitiveConversionTraits_Limits<T>::max()) {
aError.ThrowRangeError("Integer value is out of range"_ns);
return;
}
T intValue = static_cast<T>(floatValue);
if (intValue != floatValue) {
aError.ThrowTypeError("Not an integer"_ns);
return;
}
mValue = intValue;
}
void Lift(JSContext* aContext, dom::OwningUniFFIScaffoldingValue* aDest,
ErrorResult& aError) {
if (mValue < dom::PrimitiveConversionTraits_Limits<T>::min() ||
mValue > dom::PrimitiveConversionTraits_Limits<T>::max()) {
aError.ThrowRangeError(
"64-bit value cannot be precisely represented in JS"_ns);
return;
}
aDest->SetAsDouble() = mValue;
}
T IntoRust() { return mValue; }
static FfiValueInt FromRust(T aValue) { return FfiValueInt(aValue); };
};
// FfiValue class for floating point values
template <typename T>
class FfiValueFloat {
private:
T mValue = 0.0;
public:
FfiValueFloat() = default;
explicit FfiValueFloat(T aValue) : mValue(aValue) {}
void Lower(const dom::OwningUniFFIScaffoldingValue& aValue,
ErrorResult& aError) {
if (!aValue.IsDouble()) {
aError.ThrowTypeError("Bad argument type"_ns);
return;
}
mValue = aValue.GetAsDouble();
}
void Lift(JSContext* aContext, dom::OwningUniFFIScaffoldingValue* aDest,
ErrorResult& aError) {
aDest->SetAsDouble() = mValue;
}
T IntoRust() { return mValue; }
static FfiValueFloat FromRust(T aValue) { return FfiValueFloat(aValue); }
};
class FfiValueRustBuffer {
private:
OwnedRustBuffer mValue;
public:
FfiValueRustBuffer() = default;
explicit FfiValueRustBuffer(RustBuffer aValue)
: mValue(OwnedRustBuffer(aValue)) {}
explicit FfiValueRustBuffer(OwnedRustBuffer aValue)
: mValue(std::move(aValue)) {}
void Lower(const dom::OwningUniFFIScaffoldingValue& aValue,
ErrorResult& aError) {
if (!aValue.IsArrayBuffer()) {
aError.ThrowTypeError("Expected ArrayBuffer argument"_ns);
return;
}
mValue = OwnedRustBuffer::FromArrayBuffer(aValue.GetAsArrayBuffer());
}
void Lift(JSContext* aContext, dom::OwningUniFFIScaffoldingValue* aDest,
ErrorResult& aError) {
JS::Rooted<JSObject*> obj(aContext);
mValue.IntoArrayBuffer(aContext, &obj, aError);
if (aError.Failed()) {
return;
}
aDest->SetAsArrayBuffer().Init(obj);
}
RustBuffer IntoRust() { return mValue.IntoRustBuffer(); }
static FfiValueRustBuffer FromRust(RustBuffer aValue) {
return FfiValueRustBuffer(OwnedRustBuffer(aValue));
}
bool IsSet() { return mValue.IsValid(); }
};
} // namespace mozilla::uniffi
#endif // mozilla_UniFFIFfiConverter_h