summaryrefslogtreecommitdiffstats
path: root/toolkit/components/uniffi-js/ScaffoldingConverter.h
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/uniffi-js/ScaffoldingConverter.h')
-rw-r--r--toolkit/components/uniffi-js/ScaffoldingConverter.h206
1 files changed, 206 insertions, 0 deletions
diff --git a/toolkit/components/uniffi-js/ScaffoldingConverter.h b/toolkit/components/uniffi-js/ScaffoldingConverter.h
new file mode 100644
index 0000000000..59829938c2
--- /dev/null
+++ b/toolkit/components/uniffi-js/ScaffoldingConverter.h
@@ -0,0 +1,206 @@
+/* -*- 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_ScaffoldingConverter_h
+#define mozilla_ScaffoldingConverter_h
+
+#include <limits>
+#include <type_traits>
+#include "nsString.h"
+#include "mozilla/ResultVariant.h"
+#include "mozilla/dom/OwnedRustBuffer.h"
+#include "mozilla/dom/PrimitiveConversions.h"
+#include "mozilla/dom/TypedArray.h"
+#include "mozilla/dom/UniFFIBinding.h"
+#include "mozilla/dom/UniFFIPointer.h"
+#include "mozilla/dom/UniFFIPointerType.h"
+#include "mozilla/dom/UniFFIRust.h"
+#include "mozilla/dom/UniFFIScaffolding.h"
+
+namespace mozilla::uniffi {
+
+class ScaffoldingConverterTagDefault {};
+
+// Handle converting types between JS and Rust
+//
+// Scaffolding conversions are done using a 2 step process:
+// - Call FromJs/FromRust to convert to an intermediate type
+// - Call IntoJs/IntoRust to convert from that type to the target type
+//
+// The main reason for this is handling RustBuffers when other arguments fail
+// to convert. By using OwnedRustBuffer as the intermediate type, we can
+// ensure those buffers get freed in that case. Note that we can't use
+// OwnedRustBuffer as the Rust type. Passing the buffer into Rust transfers
+// ownership so we shouldn't free the buffer in this case.
+//
+// For most other types, we just use the Rust type as the intermediate type.
+template <typename T, typename Tag = ScaffoldingConverterTagDefault>
+class ScaffoldingConverter {
+ public:
+ using RustType = T;
+ using IntermediateType = T;
+
+ // Convert a JS value to an intermedate type
+ //
+ // This inputs a const ref, because that's what the WebIDL bindings send to
+ // us.
+ //
+ // If this succeeds then IntoRust is also guaranteed to succeed
+ static mozilla::Result<IntermediateType, nsCString> FromJs(
+ const dom::ScaffoldingType& aValue) {
+ if (!aValue.IsDouble()) {
+ return Err("Bad argument type"_ns);
+ }
+ double value = aValue.GetAsDouble();
+
+ if (std::isnan(value)) {
+ return Err("NaN not allowed"_ns);
+ }
+
+ if constexpr (std::is_integral<RustType>::value) {
+ // 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 (value < dom::PrimitiveConversionTraits_Limits<RustType>::min() ||
+ value > dom::PrimitiveConversionTraits_Limits<RustType>::max()) {
+ return Err("Out of bounds"_ns);
+ }
+ }
+
+ // Don't check float bounds for a few reasons.
+ // - It's difficult because
+ // PrimitiveConversionTraits_Limits<float>::min() is the smallest
+ // positive value, rather than the most negative.
+ // - A float value unlikely to overflow
+ // - It's also likely that we can't do an exact conversion because the
+ // float doesn't have enough precision, but it doesn't seem correct
+ // to error out in that case.
+
+ RustType rv = static_cast<RustType>(value);
+ if constexpr (std::is_integral<RustType>::value) {
+ if (rv != value) {
+ return Err("Not an integer"_ns);
+ }
+ }
+
+ return rv;
+ }
+
+ // Convert an intermediate type to a Rust type
+ //
+ // IntoRust doesn't touch the JS data, so it's safe to call in a worker thread
+ static RustType IntoRust(IntermediateType aValue) { return aValue; }
+
+ // Convert an Rust type to an intermediate type
+ //
+ // This inputs a value since RustTypes are POD types
+ //
+ // If this succeeds then IntoJs is also guaranteed to succeed
+ static mozilla::Result<IntermediateType, nsCString> FromRust(
+ RustType aValue) {
+ if constexpr (std::is_same<RustType, int64_t>::value ||
+ std::is_same<RustType, uint64_t>::value) {
+ // Check that the value can fit in a double (only needed for 64 bit types)
+ if (aValue < dom::PrimitiveConversionTraits_Limits<RustType>::min() ||
+ aValue > dom::PrimitiveConversionTraits_Limits<RustType>::max()) {
+ return Err("Out of bounds"_ns);
+ }
+ }
+ if constexpr (std::is_floating_point<RustType>::value) {
+ if (std::isnan(aValue)) {
+ return Err("NaN not allowed"_ns);
+ }
+ }
+ return aValue;
+ }
+
+ // Convert an intermedate type to a JS type
+ //
+ // This inputs an r-value reference since we may want to move data out of
+ // this type.
+ static void IntoJs(JSContext* aContext, IntermediateType&& aValue,
+ dom::ScaffoldingType& aDest) {
+ aDest.SetAsDouble() = aValue;
+ }
+};
+
+template <>
+class ScaffoldingConverter<RustBuffer> {
+ public:
+ using RustType = RustBuffer;
+ using IntermediateType = OwnedRustBuffer;
+
+ static mozilla::Result<OwnedRustBuffer, nsCString> FromJs(
+ const dom::ScaffoldingType& aValue) {
+ if (!aValue.IsArrayBuffer()) {
+ return Err("Bad argument type"_ns);
+ }
+
+ const dom::ArrayBuffer& arrayBuf = aValue.GetAsArrayBuffer();
+ arrayBuf.ComputeState();
+ return OwnedRustBuffer::FromArrayBuffer(arrayBuf);
+ }
+
+ static RustBuffer IntoRust(OwnedRustBuffer&& aValue) {
+ return aValue.IntoRustBuffer();
+ }
+
+ static mozilla::Result<OwnedRustBuffer, nsCString> FromRust(
+ RustBuffer aValue) {
+ return OwnedRustBuffer(aValue);
+ }
+
+ static void IntoJs(JSContext* aContext, OwnedRustBuffer&& aValue,
+ dom::ScaffoldingType& aDest) {
+ aDest.SetAsArrayBuffer().Init(aValue.IntoArrayBuffer(aContext));
+ }
+};
+
+// ScaffoldingConverter for object pointers
+template <const UniFFIPointerType* PointerType>
+class ScaffoldingObjectConverter {
+ public:
+ using RustType = void*;
+ using IntermediateType = void*;
+
+ static mozilla::Result<void*, nsCString> FromJs(
+ const dom::ScaffoldingType& aValue) {
+ if (!aValue.IsUniFFIPointer()) {
+ return Err("Bad argument type"_ns);
+ }
+ dom::UniFFIPointer& value = aValue.GetAsUniFFIPointer();
+ if (!value.IsSamePtrType(PointerType)) {
+ return Err("Bad pointer type"_ns);
+ }
+ return value.GetPtr();
+ }
+
+ static void* IntoRust(void* aValue) { return aValue; }
+
+ static mozilla::Result<void*, nsCString> FromRust(void* aValue) {
+ return aValue;
+ }
+
+ static void IntoJs(JSContext* aContext, void* aValue,
+ dom::ScaffoldingType& aDest) {
+ aDest.SetAsUniFFIPointer() =
+ dom::UniFFIPointer::Create(aValue, PointerType);
+ }
+};
+
+// ScaffoldingConverter for void returns
+//
+// This doesn't implement the normal interface, it's only use is a the
+// ReturnConverter parameter of ScaffoldingCallHandler.
+template <>
+class ScaffoldingConverter<void> {
+ public:
+ using RustType = void;
+};
+
+} // namespace mozilla::uniffi
+
+#endif // mozilla_ScaffoldingConverter_h