forked from mirrors/gecko-dev
Started callback interface functionality to UniFFI. Currently this only
supports the async fire-and-forget use case, where Rust queues a JS
function to run, but doesn't wait (or `await`) for the response.
The basic system is:
- The JS code registers a callback interface handler with the C++
code. This handler is responsible for the specifics of invoking the
callback.
- The C++ code defines a function to call a JS handler. Once the JS
handler registers itself with C++, the C++ registers it's function
with Rust.
- The C++ code queues the call to the JS main thread.
- Because of how UniFFI handles callback interfaces, the C++ code can
be "dumb". UniFFI sends a object id, method id, and RustBuffer
encoding all arguments. This means C++ doesn't need to care about
the specific arguments, they get unpacked by JS.
I tried to keep the generated code as simple as possible by moving the
complexity to static code. For JS this meant writing a generic
`UniFFICallbackHandler` class in the static code that the generated code
constructs. For C++ this meant the generated code defines a
`UniFFIGetCallbackInterfaceInfo` function that returns a struct with all
the data specific to a callback interface (it's name, the UniFFI
scaffolding init function, etc). The static code can then define a
generic `QueueCallback` function that looks up the callback interface
info using the interface ID and then makes the call.
Allow UniFFI functions to run on the main thread rather than always
being dispatched to a worker thread. This allows us to test invoking
callback interfaces from the main thread thread. I don't think we will
use this much currently, since we don't want to block the main thread
for any significant amount of time. However, this will pair well with
the next step in the project which is async -- allowing async Rust
functions to call async JS functions. In that scenario, dispatching to
the worker thread is unnecessary.
Callback interface objects present a potential memory leak, since you
can easily create a cycle between a JS Callback object and a UniFFIed
Rust object, and the GC has no way of detecting it. To try to detect
these there's some shutdown code that checks that there are no callbacks
registered during shutdown and prevents any future callbacks from being
registered.
Added a `config.toml` file and code to parse it. This is needed to
specify which functions should run on the main thread.
Updated the git commits for the several UniFFI examples/fixtures.
Differential Revision: https://phabricator.services.mozilla.com/D156116
162 lines
6.1 KiB
C++
162 lines
6.1 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 <inttypes.h>
|
|
#include "nsError.h"
|
|
#include "nsString.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "mozilla/Maybe.h"
|
|
#include "mozilla/dom/UniFFIScaffolding.h"
|
|
#include "mozilla/dom/UniFFICallbacks.h"
|
|
|
|
// This file implements the UniFFI WebIDL interface by leveraging the generate
|
|
// code in UniFFIScaffolding.cpp and UniFFIFixtureScaffolding.cpp. It's main
|
|
// purpose is to check if MOZ_UNIFFI_FIXTURES is set and only try calling the
|
|
// scaffolding code if it is.
|
|
|
|
using mozilla::dom::ArrayBuffer;
|
|
using mozilla::dom::GlobalObject;
|
|
using mozilla::dom::Promise;
|
|
using mozilla::dom::RootedDictionary;
|
|
using mozilla::dom::ScaffoldingType;
|
|
using mozilla::dom::Sequence;
|
|
using mozilla::dom::UniFFICallbackHandler;
|
|
using mozilla::dom::UniFFIPointer;
|
|
using mozilla::dom::UniFFIScaffoldingCallResult;
|
|
|
|
namespace mozilla::uniffi {
|
|
|
|
// Prototypes for the generated functions
|
|
Maybe<already_AddRefed<Promise>> UniFFICallAsync(
|
|
const GlobalObject& aGlobal, uint64_t aId,
|
|
const Sequence<ScaffoldingType>& aArgs, ErrorResult& aError);
|
|
bool UniFFICallSync(const GlobalObject& aGlobal, uint64_t aId,
|
|
const Sequence<ScaffoldingType>& aArgs,
|
|
RootedDictionary<UniFFIScaffoldingCallResult>& aReturnValue,
|
|
ErrorResult& aError);
|
|
Maybe<already_AddRefed<UniFFIPointer>> UniFFIReadPointer(
|
|
const GlobalObject& aGlobal, uint64_t aId, const ArrayBuffer& aArrayBuff,
|
|
long aPosition, ErrorResult& aError);
|
|
bool UniFFIWritePointer(const GlobalObject& aGlobal, uint64_t aId,
|
|
const UniFFIPointer& aPtr,
|
|
const ArrayBuffer& aArrayBuff, long aPosition,
|
|
ErrorResult& aError);
|
|
|
|
#ifdef MOZ_UNIFFI_FIXTURES
|
|
Maybe<already_AddRefed<Promise>> UniFFIFixturesCallAsync(
|
|
const GlobalObject& aGlobal, uint64_t aId,
|
|
const Sequence<ScaffoldingType>& aArgs, ErrorResult& aError);
|
|
bool UniFFIFixturesCallSync(
|
|
const GlobalObject& aGlobal, uint64_t aId,
|
|
const Sequence<ScaffoldingType>& aArgs,
|
|
RootedDictionary<UniFFIScaffoldingCallResult>& aReturnValue,
|
|
ErrorResult& aError);
|
|
Maybe<already_AddRefed<UniFFIPointer>> UniFFIFixturesReadPointer(
|
|
const GlobalObject& aGlobal, uint64_t aId, const ArrayBuffer& aArrayBuff,
|
|
long aPosition, ErrorResult& aError);
|
|
bool UniFFIFixturesWritePointer(const GlobalObject& aGlobal, uint64_t aId,
|
|
const UniFFIPointer& aPtr,
|
|
const ArrayBuffer& aArrayBuff, long aPosition,
|
|
ErrorResult& aError);
|
|
#endif
|
|
} // namespace mozilla::uniffi
|
|
|
|
namespace mozilla::dom {
|
|
|
|
// Implement the interface using the generated functions
|
|
|
|
already_AddRefed<Promise> UniFFIScaffolding::CallAsync(
|
|
const GlobalObject& aGlobal, uint64_t aId,
|
|
const Sequence<ScaffoldingType>& aArgs, ErrorResult& aError) {
|
|
Maybe<already_AddRefed<Promise>> firstTry =
|
|
uniffi::UniFFICallAsync(aGlobal, aId, aArgs, aError);
|
|
if (firstTry.isSome()) {
|
|
return firstTry.extract();
|
|
}
|
|
#ifdef MOZ_UNIFFI_FIXTURES
|
|
Maybe<already_AddRefed<Promise>> secondTry =
|
|
uniffi::UniFFIFixturesCallAsync(aGlobal, aId, aArgs, aError);
|
|
if (secondTry.isSome()) {
|
|
return secondTry.extract();
|
|
}
|
|
#endif
|
|
|
|
aError.ThrowUnknownError(
|
|
nsPrintfCString("Unknown function id: %" PRIu64, aId));
|
|
return nullptr;
|
|
}
|
|
|
|
void UniFFIScaffolding::CallSync(
|
|
const GlobalObject& aGlobal, uint64_t aId,
|
|
const Sequence<ScaffoldingType>& aArgs,
|
|
RootedDictionary<UniFFIScaffoldingCallResult>& aReturnValue,
|
|
ErrorResult& aError) {
|
|
if (uniffi::UniFFICallSync(aGlobal, aId, aArgs, aReturnValue, aError)) {
|
|
return;
|
|
}
|
|
#ifdef MOZ_UNIFFI_FIXTURES
|
|
if (uniffi::UniFFIFixturesCallSync(aGlobal, aId, aArgs, aReturnValue,
|
|
aError)) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
aError.ThrowUnknownError(
|
|
nsPrintfCString("Unknown function id: %" PRIu64, aId));
|
|
}
|
|
|
|
already_AddRefed<UniFFIPointer> UniFFIScaffolding::ReadPointer(
|
|
const GlobalObject& aGlobal, uint64_t aId, const ArrayBuffer& aArrayBuff,
|
|
long aPosition, ErrorResult& aError) {
|
|
Maybe<already_AddRefed<UniFFIPointer>> firstTry =
|
|
uniffi::UniFFIReadPointer(aGlobal, aId, aArrayBuff, aPosition, aError);
|
|
if (firstTry.isSome()) {
|
|
return firstTry.extract();
|
|
}
|
|
#ifdef MOZ_UNIFFI_FIXTURES
|
|
Maybe<already_AddRefed<UniFFIPointer>> secondTry =
|
|
uniffi::UniFFIFixturesReadPointer(aGlobal, aId, aArrayBuff, aPosition,
|
|
aError);
|
|
if (secondTry.isSome()) {
|
|
return secondTry.extract();
|
|
}
|
|
#endif
|
|
|
|
aError.ThrowUnknownError(nsPrintfCString("Unknown object id: %" PRIu64, aId));
|
|
return nullptr;
|
|
}
|
|
|
|
void UniFFIScaffolding::WritePointer(const GlobalObject& aGlobal, uint64_t aId,
|
|
const UniFFIPointer& aPtr,
|
|
const ArrayBuffer& aArrayBuff,
|
|
long aPosition, ErrorResult& aError) {
|
|
if (uniffi::UniFFIWritePointer(aGlobal, aId, aPtr, aArrayBuff, aPosition,
|
|
aError)) {
|
|
return;
|
|
}
|
|
#ifdef MOZ_UNIFFI_FIXTURES
|
|
if (uniffi::UniFFIFixturesWritePointer(aGlobal, aId, aPtr, aArrayBuff,
|
|
aPosition, aError)) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
aError.ThrowUnknownError(nsPrintfCString("Unknown object id: %" PRIu64, aId));
|
|
}
|
|
|
|
void UniFFIScaffolding::RegisterCallbackHandler(
|
|
GlobalObject& aGlobal, uint64_t aInterfaceId,
|
|
UniFFICallbackHandler& aCallbackHandler, ErrorResult& aError) {
|
|
uniffi::RegisterCallbackHandler(aInterfaceId, aCallbackHandler, aError);
|
|
}
|
|
|
|
void UniFFIScaffolding::DeregisterCallbackHandler(GlobalObject& aGlobal,
|
|
uint64_t aInterfaceId,
|
|
ErrorResult& aError) {
|
|
uniffi::DeregisterCallbackHandler(aInterfaceId, aError);
|
|
}
|
|
|
|
} // namespace mozilla::dom
|