/* -*- 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/. */ #ifndef mozilla_UniFFIFfiConverter_h #define mozilla_UniFFIFfiConverter_h #include #include #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 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::min() || floatValue > dom::PrimitiveConversionTraits_Limits::max()) { aError.ThrowRangeError("Integer value is out of range"_ns); return; } T intValue = static_cast(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::min() || mValue > dom::PrimitiveConversionTraits_Limits::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 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 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