Files
tubestation/toolkit/components/uniffi-js/UniFFICall.cpp
Ben Dean-Kawamura 1af4217925 Bug 1899351 - remove ScaffoldingCall.h. r=nika,markh
The C++ templates were hard to understand and modify.  Instead of using
the templates, let's monomorphize by hand and generate equivalent
classes for each FFI function signature.

I tried a slightly different approach for the template code. .  In the
template struct itself, we now generate some simple Rust structs with
all the data the templates need. I like this because it's easier to
compute these values in Rust than in an Aksama template.

Differential Revision: https://phabricator.services.mozilla.com/D212342
2024-07-26 18:38:05 +00:00

142 lines
4.7 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 "nsThreadUtils.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/UniFFICall.h"
namespace mozilla::uniffi {
using dom::GlobalObject;
using dom::RootedDictionary;
using dom::Sequence;
using dom::UniFFIScaffoldingCallResult;
using dom::UniFFIScaffoldingValue;
void UniffiHandlerBase::CallSync(
UniquePtr<UniffiHandlerBase> aHandler, const GlobalObject& aGlobal,
const Sequence<UniFFIScaffoldingValue>& aArgs,
RootedDictionary<UniFFIScaffoldingCallResult>& aReturnValue,
ErrorResult& aError) {
aHandler->PrepareRustArgs(aArgs, aError);
if (aError.Failed()) {
return;
}
aHandler->MakeRustCall();
aHandler->ExtractCallResult(aGlobal.Context(), aReturnValue, aError);
}
already_AddRefed<dom::Promise> UniffiHandlerBase::CallAsync(
UniquePtr<UniffiHandlerBase> aHandler, const dom::GlobalObject& aGlobal,
const dom::Sequence<dom::UniFFIScaffoldingValue>& aArgs,
ErrorResult& aError) {
aHandler->PrepareRustArgs(aArgs, aError);
if (aError.Failed()) {
return nullptr;
}
// Create the promise that we return to JS
nsCOMPtr<nsIGlobalObject> xpcomGlobal =
do_QueryInterface(aGlobal.GetAsSupports());
RefPtr<dom::Promise> returnPromise =
dom::Promise::Create(xpcomGlobal, aError);
if (aError.Failed()) {
return nullptr;
}
// Create a second promise that gets resolved by a background task that
// calls the scaffolding function
RefPtr taskPromise =
new MozPromise<UniquePtr<UniffiHandlerBase>, nsresult, true>::Private(
__func__);
nsresult dispatchResult = NS_DispatchBackgroundTask(
NS_NewRunnableFunction(
__func__,
[handler = std::move(aHandler), taskPromise]() mutable {
handler->MakeRustCall();
taskPromise->Resolve(std::move(handler), __func__);
}),
NS_DISPATCH_EVENT_MAY_BLOCK);
if (NS_FAILED(dispatchResult)) {
taskPromise->Reject(dispatchResult, __func__);
}
// When the background task promise completes, resolve the JS promise
taskPromise->Then(
GetCurrentSerialEventTarget(), __func__,
[xpcomGlobal, returnPromise](
typename MozPromise<UniquePtr<UniffiHandlerBase>, nsresult,
true>::ResolveOrRejectValue&& aResult) {
if (!aResult.IsResolve()) {
returnPromise->MaybeRejectWithUnknownError(__func__);
return;
}
auto handler = std::move(aResult.ResolveValue());
dom::AutoEntryScript aes(xpcomGlobal, __func__);
dom::RootedDictionary<dom::UniFFIScaffoldingCallResult> returnValue(
aes.cx());
ErrorResult error;
handler->ExtractCallResult(aes.cx(), returnValue, error);
error.WouldReportJSException();
if (error.Failed()) {
returnPromise->MaybeReject(std::move(error));
} else {
returnPromise->MaybeResolve(returnValue);
}
});
// Return the JS promise, using forget() to convert it to already_AddRefed
return returnPromise.forget();
}
void UniffiHandlerBase::ExtractCallResult(
JSContext* aCx,
dom::RootedDictionary<dom::UniFFIScaffoldingCallResult>& aDest,
ErrorResult& aError) {
switch (mUniffiCallStatusCode) {
case RUST_CALL_SUCCESS: {
aDest.mCode = dom::UniFFIScaffoldingCallCode::Success;
ExtractSuccessfulCallResult(aCx, aDest.mData, aError);
break;
}
case RUST_CALL_ERROR: {
// Rust Err() value. Populate data with the `RustBuffer` containing the
// error
aDest.mCode = dom::UniFFIScaffoldingCallCode::Error;
JS::Rooted<JSObject*> obj(aCx);
mUniffiCallStatusErrorBuf.IntoArrayBuffer(aCx, &obj, aError);
if (aError.Failed()) {
break;
}
aDest.mData.Construct().SetAsArrayBuffer().Init(obj);
break;
}
default: {
// This indicates a RustError, which should rarely happen in practice.
// The normal case is a Rust panic, but FF sets panic=abort.
aDest.mCode = dom::UniFFIScaffoldingCallCode::Internal_error;
if (mUniffiCallStatusErrorBuf.IsValid()) {
JS::Rooted<JSObject*> obj(aCx);
mUniffiCallStatusErrorBuf.IntoArrayBuffer(aCx, &obj, aError);
if (aError.Failed()) {
break;
}
aDest.mData.Construct().SetAsArrayBuffer().Init(obj);
}
break;
}
}
}
} // namespace mozilla::uniffi