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 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
/* -*- 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_UniFFICall_h
#define mozilla_UniFFICall_h
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/UniFFIBindingFwd.h"
#include "mozilla/dom/UniFFIScaffolding.h"
#include "mozilla/uniffi/OwnedRustBuffer.h"
#include "mozilla/uniffi/FfiValue.h"
#include "mozilla/uniffi/ResultPromise.h"
#include "mozilla/uniffi/Rust.h"
namespace mozilla::uniffi {
// Call Rust scaffolding functions
//
// This is the base of a class hierarchy for Rust call handling:
// - UniffiCallHandlerBase contains the shared code for both async and sync
// calls.
// - UniffiSyncCallHandler and UniffiAsyncCallHandler contain generalized
// code for sync/async calls
// - The generated code creates subclasses one of the those and implements
// the specialized code for the call.
//
// In all cases, a new instance is created each time the scaffolding function
// is called.
class UniffiCallHandlerBase {
public:
// Extract the call result when the status code is `RUST_CALL_SUCCESS`.
//
// On success, set aDest with the converted return value. If there is a
// conversion error, set `aError`. This can happen for example when a u64
// value doesn't fit into a JS number.
//
// Called on the main thread.
virtual void LiftSuccessfulCallResult(
JSContext* aCx, dom::Optional<dom::OwningUniFFIScaffoldingValue>& aDest,
ErrorResult& aError) = 0;
// Extract the result of making a call, and store it into aDest
//
// Errors are handled in several different ways:
// - If the Rust code returned an `Err` value, then `aDest.code` will be set
// to "error" and `aDest.data` will be set to the serialized error value.
// - If some other error happens in the Rust layer, then `aDest.code` will
// be set to "internal-error" and `aDest.data` will contain a serialized
// error string if possible and be empty otherwise. This should be fairly
// rare, since the main case is a caught Rust panic, but FF sets
// panic=abort.
// - If some other error happens in the C++ layer, then `aError` will be set
// to the error.
void LiftCallResult(
JSContext* aCx,
dom::RootedDictionary<dom::UniFFIScaffoldingCallResult>& aDest,
ErrorResult& aError);
virtual ~UniffiCallHandlerBase() = default;
protected:
// Call status from the rust call
int8_t mUniffiCallStatusCode = RUST_CALL_SUCCESS;
FfiValueRustBuffer mUniffiCallStatusErrorBuf;
};
// Call scaffolding functions for synchronous Rust calls
class UniffiSyncCallHandler : public UniffiCallHandlerBase {
protected:
// ---- Overridden by subclasses for each call ----
// Convert a sequence of JS arguments and store them in this
// UniffiSyncCallHandler. Called on the main thread.
virtual void LowerRustArgs(
const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs,
ErrorResult& aError) = 0;
// Call the underlying rust scaffolding function, using the arguments
// stored in this UniffiHandler, and store the result as a member.
// Potentially called on a background thread.
//
// aOutStatus is the out pointer passed to Rust. The caller is responible
// for using it to update `mUniffiCallStatusCode` and
// `mUniffiCallStatusErrorBuf`.
virtual void MakeRustCall(RustCallStatus* aOutStatus) = 0;
public:
virtual ~UniffiSyncCallHandler() = default;
// ---- Generic entry points ----
// Call the function synchronously
static void CallSync(
UniquePtr<UniffiSyncCallHandler> aHandler,
const dom::GlobalObject& aGlobal,
const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs,
dom::RootedDictionary<dom::UniFFIScaffoldingCallResult>& aReturnValue,
ErrorResult& aError);
// Call the function asynchronously, in a worker queue.
static already_AddRefed<dom::Promise> CallAsyncWrapper(
UniquePtr<UniffiSyncCallHandler> aHandler,
const dom::GlobalObject& aGlobal,
const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs,
ErrorResult& aError);
};
// Call scaffolding functions for asynchronous Rust calls
class UniffiAsyncCallHandler : public UniffiCallHandlerBase {
public:
UniffiAsyncCallHandler(PollFutureFn aPollFn, FreeFutureFn aFreeFn)
: mPollFn(aPollFn), mFreeFn(aFreeFn) {}
protected:
// ---- Overridden by subclasses for each call ----
// Convert a sequence of JS arguments and call the Rust scaffolding function.
//
// Always called on the main thread since async Rust calls don't block, they
// return a future. Because of this, there's no reason to split out the
// `LowerRustArgs` and `MakeRustCall` like in the sync
// case.
virtual void LowerArgsAndMakeRustCall(
const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs,
ErrorResult& aError) = 0;
// Handle to the future we're polling, set by MakeRustCall
uint64_t mFutureHandle;
// Rust future function pointers
PollFutureFn mPollFn;
FreeFutureFn mFreeFn;
// Call the complete function.
//
// This can't be a function pointer like poll/free since the complete
// function signature varies based on the return type.
//
// aOutStatus is the out pointer passed to Rust. The caller is responible
// for using it to update `mUniffiCallStatusCode` and
// `mUniffiCallStatusErrorBuf`.
virtual void CallCompleteFn(RustCallStatus* aOutStatus) = 0;
// Call mPollFn to poll the future
static void Poll(UniquePtr<UniffiAsyncCallHandler> aHandler);
public:
virtual ~UniffiAsyncCallHandler();
// ---- Generic entry points ----
// Call the function asynchronously
static already_AddRefed<dom::Promise> CallAsync(
UniquePtr<UniffiAsyncCallHandler> aHandler,
const dom::GlobalObject& aGlobal,
const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs,
ErrorResult& aError);
private:
// Promise created by CallAsync
ResultPromise mPromise;
// Callback function for Rust async calls
//
// This is passed to Rust when we call the poll function. The callback is
// called when the future is ready or it's waker is invoked. aCode is used to
// distinguish the two cases.
//
// This is static so we can pass it as a function pointer to Rust.
static void FutureCallback(uint64_t aCallHandlerHandle, int8_t aCode);
};
} // namespace mozilla::uniffi
#endif // mozilla_UniFFICall_h
|