Backed out 2 changesets (bug 1951243, bug 1951241) for causing bustages on GeneratedScaffolding.cpp . CLOSED TREE

Backed out changeset 944bac64c3db (bug 1951243)
Backed out changeset c79b66ed7283 (bug 1951241)
This commit is contained in:
Narcis Beleuzu
2025-04-11 17:13:02 +03:00
parent 2a6c88f7ba
commit 2dbc8ca1dc
27 changed files with 3074 additions and 2967 deletions

View File

@@ -69,7 +69,8 @@ xpcom/reflect/xptcall/md/unix/.*
# Askama template code, which isn't valid C++ in its original form
toolkit/components/uniffi-bindgen-gecko-js/src/templates/.*
# Generated from that template code
toolkit/components/uniffi-js/GeneratedScaffolding.cpp
toolkit/components/uniffi-js/UniFFIGeneratedScaffolding.cpp
toolkit/components/uniffi-js/UniFFIFixtureScaffolding.cpp
# Generated from ./tools/rewriting/ThirdPartyPaths.txt
# awk '{print ""$1".*"}' ./tools/rewriting/ThirdPartyPaths.txt

View File

@@ -1563,7 +1563,8 @@ toolkit/components/nimbus/schemas/ExperimentFeatureManifest.schema.json
toolkit/components/nimbus/schemas/NimbusExperiment.schema.json
toolkit/components/pdfjs/PdfJsDefaultPrefs.js
toolkit/components/pdfjs/PdfJsOverridePrefs.js
toolkit/components/uniffi-js/GeneratedScaffolding.cpp
toolkit/components/uniffi-js/UniFFIGeneratedScaffolding.cpp
toolkit/components/uniffi-js/UniFFIFixtureScaffolding.cpp
toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated
tools/@types/tspaths.json
tools/browsertime/package.json

1
Cargo.lock generated
View File

@@ -2524,7 +2524,6 @@ dependencies = [
name = "gkrust-uniffi-components"
version = "0.1.0"
dependencies = [
"hashbrown 0.15.2",
"relevancy",
"search",
"suggest",

View File

@@ -22,8 +22,6 @@ search = "0.1"
suggest = "0.1"
relevancy = "0.1"
webext-storage = "0.1"
# Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1959469
hashbrown = { version = "0.15.2", features = [ "default-hasher" ] }
[features]
# Should we depend on xpcom crates?

View File

@@ -7,7 +7,7 @@ import subprocess
from mach.decorators import Command, SubCommand
CPP_PATH = "toolkit/components/uniffi-js/GeneratedScaffolding.cpp"
CPP_PATH = "toolkit/components/uniffi-js/UniFFIGeneratedScaffolding.cpp"
JS_DIR = "toolkit/components/uniffi-bindgen-gecko-js/components/generated"
FIXTURE_JS_DIR = "toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated"
DOCS_PATH = "docs/rust-components/api/js/"

View File

@@ -93,7 +93,7 @@ impl CPPScaffoldingTemplate {
arg_types: ffi_func
.arguments()
.iter()
.map(|a| ffi_type_name(&a.type_()))
.map(|a| cpp_type(&a.type_()))
.chain(
ffi_func
.has_rust_call_status_arg()
@@ -108,7 +108,7 @@ impl CPPScaffoldingTemplate {
arg_types: ffi_callback
.arguments()
.into_iter()
.map(|a| ffi_type_name(&a.type_()))
.map(|a| cpp_type(&a.type_()))
.chain(
ffi_callback
.has_rust_call_status_arg()
@@ -125,7 +125,7 @@ impl CPPScaffoldingTemplate {
.into_iter()
.map(|f| FfiFieldCpp {
name: f.name().to_snake_case(),
type_: ffi_type_name(&f.type_()),
type_: cpp_type(&f.type_()),
})
.collect(),
}),
@@ -182,7 +182,7 @@ impl CPPScaffoldingTemplate {
let cbi_name_snake = cbi.name().to_snake_case();
CallbackInterfaceVTable {
type_: ffi_type_name(&cbi.vtable()),
type_: cpp_type(&cbi.vtable()),
var_name: format!("kCallbackInterfaceVtable{cbi_name}"),
method_handlers: cbi
.vtable_methods()
@@ -196,13 +196,13 @@ impl CPPScaffoldingTemplate {
arguments: method
.arguments()
.iter()
.map(|arg| {
let ffi_type = arg.as_type().into();
CallbackMethodArgument {
.map(|arg| CallbackMethodArgument {
name: arg.name().to_snake_case(),
ffi_type: ffi_type_name(&ffi_type),
ffi_value_class: ffi_value_class(ci, &ffi_type),
}
type_: cpp_type(&arg.as_type().into()),
scaffolding_converter: scaffolding_converter(
ci,
&arg.as_type().into(),
),
})
.collect(),
}
@@ -337,8 +337,8 @@ struct CallbackMethodHandler {
struct CallbackMethodArgument {
name: String,
ffi_type: String,
ffi_value_class: String,
type_: String,
scaffolding_converter: String,
}
struct ScaffoldingCall {
@@ -366,7 +366,7 @@ impl ScaffoldingCall {
.into_iter()
.map(|a| ScaffoldingCallArgument {
var_name: format!("m{}", a.name().to_upper_camel_case()),
ffi_value_class: ffi_value_class(ci, &a.type_()),
scaffolding_converter: scaffolding_converter(ci, &a.type_()),
})
.collect::<Vec<_>>();
@@ -384,9 +384,8 @@ impl ScaffoldingCall {
// function always returns a handle.
return_type: callable
.return_type()
.map(|return_type| FfiType::from(return_type))
.map(|return_type| ScaffoldingCallReturnType {
ffi_value_class: ffi_value_class(ci, &return_type),
scaffolding_converter: scaffolding_converter(ci, &return_type.into()),
}),
arguments,
async_info,
@@ -399,12 +398,12 @@ impl ScaffoldingCall {
}
struct ScaffoldingCallReturnType {
ffi_value_class: String,
scaffolding_converter: String,
}
struct ScaffoldingCallArgument {
var_name: String,
ffi_value_class: String,
scaffolding_converter: String,
}
struct ScaffoldingCallAsyncInfo {
@@ -413,7 +412,7 @@ struct ScaffoldingCallAsyncInfo {
free_fn: String,
}
fn ffi_value_class(ci: &ComponentInterface, ffi_type: &FfiType) -> String {
fn scaffolding_converter(ci: &ComponentInterface, ffi_type: &FfiType) -> String {
match ffi_type {
FfiType::RustArcPtr(name) => {
// Check if this is an external type
@@ -422,29 +421,17 @@ fn ffi_value_class(ci: &ComponentInterface, ffi_type: &FfiType) -> String {
let crate_name = ty.module_path().expect("External type without module path");
if external_ty_name == name {
return format!(
"FfiValueObjectHandle<&{}>",
"ScaffoldingObjectConverter<&{}>",
pointer_type(crate_name_to_namespace(&crate_name), name),
);
}
}
format!(
"FfiValueObjectHandle<&{}>",
"ScaffoldingObjectConverter<&{}>",
pointer_type(ci.namespace(), name),
)
}
FfiType::UInt8
| FfiType::Int8
| FfiType::UInt16
| FfiType::Int16
| FfiType::UInt32
| FfiType::Int32
| FfiType::UInt64
| FfiType::Int64 => format!("FfiValueInt<{}>", ffi_type_name(ffi_type)),
FfiType::Float32 | FfiType::Float64 => {
format!("FfiValueFloat<{}>", ffi_type_name(ffi_type))
}
FfiType::RustBuffer(_) => "FfiValueRustBuffer".to_owned(),
_ => format!("FfiConverter<{}>", ffi_type_name(ffi_type)),
_ => format!("ScaffoldingConverter<{}>", cpp_type(ffi_type)),
}
}
@@ -456,8 +443,8 @@ fn pointer_type(namespace: &str, name: &str) -> String {
)
}
// C++ type for an FFI value
fn ffi_type_name(ffi_type: &FfiType) -> String {
// Type for the Rust scaffolding code
fn cpp_type(ffi_type: &FfiType) -> String {
match ffi_type {
FfiType::UInt8 => "uint8_t".to_owned(),
FfiType::Int8 => "int8_t".to_owned(),
@@ -477,14 +464,14 @@ fn ffi_type_name(ffi_type: &FfiType) -> String {
FfiType::Callback(name) | FfiType::Struct(name) => name.to_owned(),
FfiType::VoidPointer => "void*".to_owned(),
FfiType::MutReference(inner) | FfiType::Reference(inner) => {
format!("{}*", ffi_type_name(inner.as_ref()))
format!("{}*", cpp_type(inner.as_ref()))
}
}
}
fn return_type(ffi_type: Option<&FfiType>) -> String {
match ffi_type {
Some(t) => ffi_type_name(t),
Some(t) => cpp_type(t),
None => "void".to_owned(),
}
}

View File

@@ -10,12 +10,12 @@
#include "mozilla/StaticPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ScaffoldingConverter.h"
#include "mozilla/dom/UniFFICall.h"
#include "mozilla/dom/UniFFICallbacks.h"
#include "mozilla/dom/UniFFIPointerType.h"
#include "mozilla/dom/UniFFIScaffolding.h"
#include "mozilla/uniffi/Call.h"
#include "mozilla/uniffi/Callbacks.h"
#include "mozilla/uniffi/FfiValue.h"
#include "mozilla/uniffi/PointerType.h"
#include "mozilla/uniffi/Rust.h"
#include "mozilla/dom/UniFFIRust.h"
namespace mozilla::uniffi {
@@ -78,15 +78,15 @@ static StaticRefPtr<dom::UniFFICallbackHandler> {{ cbi.js_handler_var }};
class {{ handler.class_name }} : public UniffiCallbackMethodHandlerBase {
private:
// Rust arguments
// Rust arguments, converted using ScaffoldingConverter::FromRust.
{%- for a in handler.arguments %}
{{ a.ffi_value_class }} {{ a.name }}{};
typename {{ a.scaffolding_converter }}::IntermediateType {{ a.name }};
{%- endfor %}
public:
{{ handler.class_name }}(size_t aObjectHandle{%- for a in handler.arguments %}, {{ a.ffi_type }} {{ a.name }}{%- endfor %})
{{ handler.class_name }}(size_t aObjectHandle{%- for a in handler.arguments %}, {{ a.type_ }} {{ a.name }}{%- endfor %})
: UniffiCallbackMethodHandlerBase("{{ cbi.name }}", aObjectHandle)
{%- for a in handler.arguments %}, {{ a.name }}({{ a.ffi_value_class }}::FromRust({{ a.name }})){% endfor %} {
{%- for a in handler.arguments %}, {{ a.name }}({{ a.scaffolding_converter }}::FromRust({{ a.name }})){% endfor %} {
}
MOZ_CAN_RUN_SCRIPT
@@ -102,8 +102,9 @@ public:
// Convert each argument
{%- for a in handler.arguments %}
{{ a.name }}.Lift(
{{ a.scaffolding_converter }}::IntoJs(
aCx,
std::move(this->{{ a.name }}),
&uniffiArgs[{{ loop.index0 }}],
aError);
if (aError.Failed()) {
@@ -122,7 +123,7 @@ public:
extern "C" void {{ handler.fn_name }}(
uint64_t uniffiHandle,
{% for a in handler.arguments %}{{ a.ffi_type }} {{ a.name }}, {% endfor %}
{% for a in handler.arguments %}{{ a.type_ }} {{ a.name }}, {% endfor %}
void* uniffiOutReturn,
RustCallStatus* uniffiCallStatus
) {
@@ -220,22 +221,22 @@ void DeregisterCallbackHandler(uint64_t aInterfaceId, ErrorResult& aError) {
{%- when None %}
class {{ scaffolding_call.handler_class_name }} : public UniffiSyncCallHandler {
private:
// LowerRustArgs stores the resulting arguments in these fields
// PrepareRustArgs stores the resulting arguments in these fields
{%- for arg in scaffolding_call.arguments %}
{{ arg.ffi_value_class }} {{ arg.var_name }}{};
typename {{ arg.scaffolding_converter }}::IntermediateType {{ arg.var_name }};
{%- endfor %}
// MakeRustCall stores the result of the call in these fields
{%- match scaffolding_call.return_type %}
{%- when Some(return_type) %}
{{ return_type.ffi_value_class }} mUniffiReturnValue{};
typename {{ return_type.scaffolding_converter }}::IntermediateType mUniffiReturnValue;
{%- else %}
{%- endmatch %}
public:
void LowerRustArgs(const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs, ErrorResult& aError) override {
void PrepareRustArgs(const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs, ErrorResult& aError) override {
{%- for arg in scaffolding_call.arguments %}
{{ arg.var_name }}.Lower(aArgs[{{ loop.index0 }}], aError);
{{ arg.scaffolding_converter }}::FromJs(aArgs[{{ loop.index0 }}], &{{ arg.var_name }}, aError);
if (aError.Failed()) {
return;
}
@@ -245,10 +246,10 @@ public:
void MakeRustCall(RustCallStatus* aOutStatus) override {
{%- match scaffolding_call.return_type %}
{%- when Some(return_type) %}
mUniffiReturnValue = {{ return_type.ffi_value_class }}::FromRust(
mUniffiReturnValue = {{ return_type.scaffolding_converter }}::FromRust(
{{ scaffolding_call.ffi_func_name }}(
{%- for arg in scaffolding_call.arguments %}
{{ arg.var_name }}.IntoRust(),
{{ arg.scaffolding_converter }}::IntoRust(std::move({{ arg.var_name }})),
{%- endfor %}
aOutStatus
)
@@ -256,18 +257,19 @@ public:
{%- else %}
{{ scaffolding_call.ffi_func_name }}(
{%- for arg in scaffolding_call.arguments %}
{{ arg.var_name }}.IntoRust(),
{{ arg.scaffolding_converter }}::IntoRust(std::move({{ arg.var_name }})),
{%- endfor %}
aOutStatus
);
{%- endmatch %}
}
virtual void LiftSuccessfulCallResult(JSContext* aCx, dom::Optional<dom::OwningUniFFIScaffoldingValue>& aDest, ErrorResult& aError) override {
virtual void ExtractSuccessfulCallResult(JSContext* aCx, dom::Optional<dom::OwningUniFFIScaffoldingValue>& aDest, ErrorResult& aError) override {
{%- match scaffolding_call.return_type %}
{%- when Some(return_type) %}
mUniffiReturnValue.Lift(
{{ return_type.scaffolding_converter }}::IntoJs(
aCx,
std::move(mUniffiReturnValue),
&aDest.Construct(),
aError
);
@@ -284,7 +286,7 @@ private:
// Complete stores the result of the call in mUniffiReturnValue
{%- match scaffolding_call.return_type %}
{%- when Some(return_type) %}
{{ return_type.ffi_value_class }} mUniffiReturnValue{};
typename {{ return_type.scaffolding_converter }}::IntermediateType mUniffiReturnValue;
{%- else %}
{%- endmatch %}
@@ -292,10 +294,10 @@ protected:
// Convert a sequence of JS arguments and call the scaffolding function.
// Always called on the main thread since async Rust calls don't block, they
// return a future.
void LowerArgsAndMakeRustCall(const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs, ErrorResult& aError) override {
void PrepareArgsAndMakeRustCall(const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs, ErrorResult& aError) override {
{%- for arg in scaffolding_call.arguments %}
{{ arg.ffi_value_class }} {{ arg.var_name }}{};
{{ arg.var_name }}.Lower(aArgs[{{ loop.index0 }}], aError);
typename {{ arg.scaffolding_converter }}::IntermediateType {{ arg.var_name }};
{{ arg.scaffolding_converter }}::FromJs(aArgs[{{ loop.index0 }}], &{{ arg.var_name }}, aError);
if (aError.Failed()) {
return;
}
@@ -303,7 +305,7 @@ protected:
mFutureHandle = {{ scaffolding_call.ffi_func_name }}(
{%- for arg in scaffolding_call.arguments %}
{{ arg.var_name }}.IntoRust(){% if !loop.last %},{% endif %}
{{ arg.scaffolding_converter }}::IntoRust(std::move({{ arg.var_name }})){% if !loop.last %},{% endif %}
{%- endfor %}
);
}
@@ -311,7 +313,7 @@ protected:
void CallCompleteFn(RustCallStatus* aOutStatus) override {
{%- match scaffolding_call.return_type %}
{%- when Some(return_type) %}
mUniffiReturnValue = {{ return_type.ffi_value_class }}::FromRust(
mUniffiReturnValue = {{ return_type.scaffolding_converter }}::FromRust(
{{ async_info.complete_fn }}(mFutureHandle, aOutStatus));
{%- else %}
{{ async_info.complete_fn }}(mFutureHandle, aOutStatus);
@@ -319,11 +321,12 @@ protected:
}
public:
void LiftSuccessfulCallResult(JSContext* aCx, dom::Optional<dom::OwningUniFFIScaffoldingValue>& aDest, ErrorResult& aError) override {
void ExtractSuccessfulCallResult(JSContext* aCx, dom::Optional<dom::OwningUniFFIScaffoldingValue>& aDest, ErrorResult& aError) override {
{%- match scaffolding_call.return_type %}
{%- when Some(return_type) %}
mUniffiReturnValue.Lift(
{{ return_type.scaffolding_converter }}::IntoJs(
aCx,
std::move(mUniffiReturnValue),
&aDest.Construct(),
aError
);

View File

@@ -1,226 +0,0 @@
/* -*- 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/uniffi/Call.h"
#include "mozilla/uniffi/ResultPromise.h"
namespace mozilla::uniffi {
extern mozilla::LazyLogModule gUniffiLogger;
using dom::GlobalObject;
using dom::OwningUniFFIScaffoldingValue;
using dom::RootedDictionary;
using dom::Sequence;
using dom::UniFFIScaffoldingCallResult;
void UniffiSyncCallHandler::CallSync(
UniquePtr<UniffiSyncCallHandler> aHandler, const GlobalObject& aGlobal,
const Sequence<OwningUniFFIScaffoldingValue>& aArgs,
RootedDictionary<UniFFIScaffoldingCallResult>& aReturnValue,
ErrorResult& aError) {
MOZ_ASSERT(NS_IsMainThread());
aHandler->LowerRustArgs(aArgs, aError);
if (aError.Failed()) {
return;
}
RustCallStatus callStatus{};
aHandler->MakeRustCall(&callStatus);
aHandler->mUniffiCallStatusCode = callStatus.code;
if (callStatus.error_buf.data) {
aHandler->mUniffiCallStatusErrorBuf =
FfiValueRustBuffer(callStatus.error_buf);
}
aHandler->LiftCallResult(aGlobal.Context(), aReturnValue, aError);
}
already_AddRefed<dom::Promise> UniffiSyncCallHandler::CallAsyncWrapper(
UniquePtr<UniffiSyncCallHandler> aHandler, const dom::GlobalObject& aGlobal,
const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs,
ErrorResult& aError) {
MOZ_ASSERT(NS_IsMainThread());
aHandler->LowerRustArgs(aArgs, aError);
if (aError.Failed()) {
return nullptr;
}
uniffi::ResultPromise resultPromise;
resultPromise.Init(aGlobal, aError);
if (aError.Failed()) {
return nullptr;
}
nsresult dispatchResult = NS_DispatchBackgroundTask(
NS_NewRunnableFunction("UniffiSyncCallHandler::CallAsyncWrapper",
[handler = std::move(aHandler),
resultPromise = resultPromise]() mutable {
RustCallStatus callStatus{};
handler->MakeRustCall(&callStatus);
handler->mUniffiCallStatusCode = callStatus.code;
if (callStatus.error_buf.data) {
handler->mUniffiCallStatusErrorBuf =
FfiValueRustBuffer(callStatus.error_buf);
}
resultPromise.Complete(std::move(handler));
}),
NS_DISPATCH_EVENT_MAY_BLOCK);
if (NS_FAILED(dispatchResult)) {
MOZ_LOG(gUniffiLogger, LogLevel::Error,
("[UniFFI] NS_DispatchBackgroundTask failed"));
resultPromise.RejectWithUnexpectedError();
}
return do_AddRef(resultPromise.GetPromise());
}
void UniffiCallHandlerBase::LiftCallResult(
JSContext* aCx,
dom::RootedDictionary<dom::UniFFIScaffoldingCallResult>& aDest,
ErrorResult& aError) {
switch (mUniffiCallStatusCode) {
case RUST_CALL_SUCCESS: {
aDest.mCode = dom::UniFFIScaffoldingCallCode::Success;
LiftSuccessfulCallResult(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;
mUniffiCallStatusErrorBuf.Lift(aCx, &aDest.mData.Construct(), aError);
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.IsSet()) {
mUniffiCallStatusErrorBuf.Lift(aCx, &aDest.mData.Construct(), aError);
}
break;
}
}
}
already_AddRefed<dom::Promise> UniffiAsyncCallHandler::CallAsync(
UniquePtr<UniffiAsyncCallHandler> aHandler,
const dom::GlobalObject& aGlobal,
const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs,
ErrorResult& aError) {
MOZ_ASSERT(NS_IsMainThread());
// Async calls return a Future rather than doing any work. This means we can
// make the call right now on the JS main thread without fear of blocking it.
aHandler->LowerArgsAndMakeRustCall(aArgs, aError);
if (aError.Failed()) {
return nullptr;
}
aHandler->mPromise.Init(aGlobal, aError);
if (aError.Failed()) {
return nullptr;
}
ResultPromise returnPromise(aHandler->mPromise);
// Schedule a poll for the future in a background thread.
nsresult dispatchResult = NS_DispatchBackgroundTask(
NS_NewRunnableFunction("UniffiAsyncCallHandler::CallAsync",
[handler = std::move(aHandler)]() mutable {
UniffiAsyncCallHandler::Poll(std::move(handler));
}));
if (NS_FAILED(dispatchResult)) {
MOZ_LOG(gUniffiLogger, LogLevel::Error,
("[UniFFI] NS_DispatchBackgroundTask failed"));
aHandler->mPromise.RejectWithUnexpectedError();
}
return do_AddRef(returnPromise.GetPromise());
}
// Callback function for async calls
//
// This is passed to Rust when we poll the future alongside a 64-bit handle that
// represents the callback data. For uniffi-bindgen-gecko-js, the handle is a
// `UniffiAsyncCallHandler*` casted to an int.
//
// Rust calls this when either the future is ready or when it's time to poll it
// again.
void UniffiAsyncCallHandler::FutureCallback(uint64_t aCallHandlerHandle,
int8_t aCode) {
// Recreate the UniquePtr we previously released
UniquePtr<UniffiAsyncCallHandler> handler(
reinterpret_cast<UniffiAsyncCallHandler*>(
static_cast<uintptr_t>(aCallHandlerHandle)));
switch (aCode) {
case UNIFFI_FUTURE_READY: {
// `Future::poll` returned a `Ready` value on the Rust side.
// Complete the future
RustCallStatus callStatus{};
handler->CallCompleteFn(&callStatus);
handler->mUniffiCallStatusCode = callStatus.code;
if (callStatus.error_buf.data) {
handler->mUniffiCallStatusErrorBuf =
FfiValueRustBuffer(callStatus.error_buf);
}
// Copy of the ResultPromise before moving the handler that contains it.
ResultPromise promise(handler->mPromise);
promise.Complete(std::move(handler));
break;
}
case UNIFFI_FUTURE_MAYBE_READY: {
// The Rust waker was invoked after `poll` returned a `Pending` value.
// Poll the future again soon in a background task.
nsresult dispatchResult =
NS_DispatchBackgroundTask(NS_NewRunnableFunction(
"UniffiAsyncCallHandler::FutureCallback",
[handler = std::move(handler)]() mutable {
UniffiAsyncCallHandler::Poll(std::move(handler));
}));
if (NS_FAILED(dispatchResult)) {
MOZ_LOG(gUniffiLogger, LogLevel::Error,
("[UniFFI] NS_DispatchBackgroundTask failed"));
handler->mPromise.RejectWithUnexpectedError();
}
break;
}
default: {
// Invalid poll code, this should never happen, but if it does log an
// error and reject the promise.
MOZ_LOG(gUniffiLogger, LogLevel::Error,
("[UniFFI] Invalid poll code in "
"UniffiAsyncCallHandler::FutureCallback %d",
aCode));
handler->mPromise.RejectWithUnexpectedError();
break;
}
};
}
void UniffiAsyncCallHandler::Poll(UniquePtr<UniffiAsyncCallHandler> aHandler) {
auto futureHandle = aHandler->mFutureHandle;
auto pollFn = aHandler->mPollFn;
// Release the UniquePtr into a raw pointer and convert it to a handle
// so that we can pass it as a handle to the UniFFI code. It gets converted
// back in `UniffiAsyncCallHandler::FutureCallback()`, which the Rust code
// guarentees will be called if the future makes progress.
uint64_t selfHandle =
static_cast<uint64_t>(reinterpret_cast<uintptr_t>(aHandler.release()));
pollFn(futureHandle, UniffiAsyncCallHandler::FutureCallback, selfHandle);
}
UniffiAsyncCallHandler::~UniffiAsyncCallHandler() { mFreeFn(mFutureHandle); }
} // namespace mozilla::uniffi

View File

@@ -1,231 +0,0 @@
/* -*- 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 <limits>
#include <type_traits>
#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 <typename T>
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<T>::min() ||
floatValue > dom::PrimitiveConversionTraits_Limits<T>::max()) {
aError.ThrowRangeError("Integer value is out of range"_ns);
return;
}
T intValue = static_cast<T>(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<T>::min() ||
mValue > dom::PrimitiveConversionTraits_Limits<T>::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 <typename T>
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<JSObject*> 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(); }
};
template <const UniFFIPointerType* PointerType>
class FfiValueObjectHandle {
private:
void* mValue = nullptr;
public:
FfiValueObjectHandle() = default;
explicit FfiValueObjectHandle(void* aValue) : mValue(aValue) {}
// Delete copy constructor and assignment as this type is non-copyable.
FfiValueObjectHandle(const FfiValueObjectHandle&) = delete;
FfiValueObjectHandle& operator=(const FfiValueObjectHandle&) = delete;
FfiValueObjectHandle& operator=(FfiValueObjectHandle&& aOther) {
if (mValue && mValue != aOther.mValue) {
FreeHandle();
}
mValue = aOther.mValue;
aOther.mValue = nullptr;
return *this;
}
void Lower(const dom::OwningUniFFIScaffoldingValue& aValue,
ErrorResult& aError) {
if (!aValue.IsUniFFIPointer()) {
aError.ThrowTypeError("Expected UniFFI pointer argument"_ns);
return;
}
dom::UniFFIPointer& value = aValue.GetAsUniFFIPointer();
if (!value.IsSamePtrType(PointerType)) {
aError.ThrowTypeError("Incorrect UniFFI pointer type"_ns);
return;
}
FreeHandle();
mValue = value.ClonePtr();
}
void Lift(JSContext* aContext, dom::OwningUniFFIScaffoldingValue* aDest,
ErrorResult& aError) {
aDest->SetAsUniFFIPointer() =
dom::UniFFIPointer::Create(mValue, PointerType);
mValue = nullptr;
}
void* IntoRust() {
auto temp = mValue;
mValue = nullptr;
return temp;
}
static FfiValueObjectHandle FromRust(void* aValue) {
return FfiValueObjectHandle(aValue);
}
void FreeHandle() {
if (mValue) {
RustCallStatus callStatus{};
(PointerType->destructor)(mValue, &callStatus);
// No need to check `RustCallStatus`, it's only part of the API to match
// other FFI calls. The free function can never fail.
}
}
~FfiValueObjectHandle() {
// If the pointer is non-null, this means Lift/IntoRust was never called
// because there was some failure along the way. Free the pointer to avoid a
// leak
FreeHandle();
}
};
} // namespace mozilla::uniffi
#endif // mozilla_UniFFIFfiConverter_h

View File

@@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsString.h"
#include "mozilla/uniffi/OwnedRustBuffer.h"
#include "mozilla/dom/OwnedRustBuffer.h"
namespace mozilla::uniffi {

View File

@@ -9,7 +9,7 @@
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/uniffi/Rust.h"
#include "mozilla/dom/UniFFIRust.h"
namespace mozilla::uniffi {

View File

@@ -2,3 +2,17 @@
This directory contains C++ helper code for the UniFFI Rust library
(https://github.com/mozilla/uniffi-rs/).
- `UniFFIPointer.*` and `UniFFIPointerType.*` implement the `UniFFIPointer` WebIDL class
- `UniFFI*Scaffolding.cpp` implements the `UniFFIScaffolding` WebIDL class.
- UniFFIGeneratedScaffolding.cpp contains the generated C++ code
- UniFFIScaffolding.cpp is a facade that wraps UniFFIFixtureScaffolding, and
UniFFIGeneratedScaffolding if enabled, to implement the interface.
- `ScaffoldingConverter.h` contain helper code to convert values between JS and Rust. This is used
by the generated code to make scaffolding calls.
- `OwnedRustBuffer.*` implements a C++ class to help manager ownership of a RustBuffer.
- `UniFFIRust.h` contains definitions for the C functions that UniFFI exports.

View File

@@ -1,54 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "mozilla/uniffi/Call.h"
#include "mozilla/uniffi/ResultPromise.h"
namespace mozilla::uniffi {
void ResultPromise::Init(const dom::GlobalObject& aGlobal,
ErrorResult& aError) {
nsCOMPtr<nsIGlobalObject> xpcomGlobal =
do_QueryInterface(aGlobal.GetAsSupports());
RefPtr<dom::Promise> promise = dom::Promise::Create(xpcomGlobal, aError);
if (aError.Failed()) {
return;
}
mPromise =
new nsMainThreadPtrHolder<dom::Promise>("uniffi::ResultPromise", promise);
}
void ResultPromise::Complete(UniquePtr<UniffiCallHandlerBase> aHandler) {
MOZ_ASSERT(mPromise);
NS_DispatchToMainThread(NS_NewRunnableFunction(
"uniffi::ResultPromise::Complete",
[promise = mPromise, handler = std::move(aHandler)]() {
dom::AutoEntryScript aes(promise->GetGlobalObject(),
"uniffi::ResultPromise::Complete");
dom::RootedDictionary<dom::UniFFIScaffoldingCallResult> returnValue(
aes.cx());
ErrorResult error;
handler->LiftCallResult(aes.cx(), returnValue, error);
error.WouldReportJSException();
if (error.Failed()) {
promise->MaybeReject(std::move(error));
} else {
promise->MaybeResolve(returnValue);
}
}));
}
void ResultPromise::RejectWithUnexpectedError() {
MOZ_ASSERT(mPromise);
NS_DispatchToMainThread(NS_NewRunnableFunction(
"uniffi::ResultPromise::RejectWithUnexpectedError", [promise = mPromise] {
promise->MaybeRejectWithUnknownError(
"UniFFI Unexpected Internal Error");
}));
}
} // namespace mozilla::uniffi

View File

@@ -1,68 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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_dom_UniFFIResultPromise_h
#define mozilla_dom_UniFFIResultPromise_h
#include "nsThreadUtils.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/UniFFIBinding.h"
namespace mozilla::uniffi {
extern mozilla::LazyLogModule gUniffiLogger;
class UniffiAsyncCallHandler;
class UniffiCallHandlerBase;
/**
* JS "bridge" for UniFFI
*
* This module intefaces with the SpiderMonkey JS API so that other code can
* focus on the core UniFFI logic.
*/
class ResultPromise {
public:
ResultPromise() = default;
// Initialize a ResultPromise, this must be done before calling any other
// methods.
//
// This must be called on the main thread.
void Init(const dom::GlobalObject& aGlobal, ErrorResult& aError);
// Get a raw `dom::Promise` pointer
//
// Use this to return the promise from a webidl-generated function.
// May only be called on the main thread.
dom::Promise* GetPromise() { return mPromise; }
// Complete the promise using a call handler, this can be called from any
// thread.
//
// After a promise is completed, it must not be used anymore.
void Complete(UniquePtr<UniffiCallHandlerBase> aHandler);
// Reject the promise with an unexpected error.
//
// Use this as a last resort, when something has gone very wrong in the FFI.
//
// After a promise is rejected, it must not be used anymore.
void RejectWithUnexpectedError();
private:
// The `nsMainThreadPtrHandle` ensures that if this type is destroyed
// from off-main-thread, it'll actually be released on the main thread.
// This is important because the promise is a main-thread-only object.
nsMainThreadPtrHandle<dom::Promise> mPromise;
};
} // namespace mozilla::uniffi
#endif // mozilla_dom_UniFFIResultPromise_h

View File

@@ -0,0 +1,211 @@
/* -*- 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 {
// 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>
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 void FromJs(const dom::OwningUniFFIScaffoldingValue& aValue,
IntermediateType* aResult, ErrorResult& aError) {
if (!aValue.IsDouble()) {
aError.ThrowTypeError("Bad argument type"_ns);
return;
}
double value = aValue.GetAsDouble();
if (std::isnan(value)) {
aError.ThrowUnknownError("NaN not allowed"_ns);
return;
}
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()) {
aError.ThrowRangeError(
"UniFFI return value cannot be precisely represented in JS"_ns);
return;
}
}
// 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.
IntermediateType rv = static_cast<IntermediateType>(value);
if constexpr (std::is_integral<IntermediateType>::value) {
if (rv != value) {
aError.ThrowTypeError("Not an integer"_ns);
return;
}
}
*aResult = 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 Rust types are POD types
static IntermediateType FromRust(RustType aValue) { 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::OwningUniFFIScaffoldingValue* aDest,
ErrorResult& aError) {
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()) {
aError.ThrowRangeError(
"UniFFI return value cannot be precisely represented in JS"_ns);
return;
}
}
if constexpr (std::is_floating_point<RustType>::value) {
if (std::isnan(aValue)) {
aError.ThrowUnknownError("NaN not allowed"_ns);
return;
}
}
aDest->SetAsDouble() = aValue;
}
};
template <>
class ScaffoldingConverter<RustBuffer> {
public:
using RustType = RustBuffer;
using IntermediateType = OwnedRustBuffer;
static void FromJs(const dom::OwningUniFFIScaffoldingValue& aValue,
OwnedRustBuffer* aResult, ErrorResult& aError) {
if (!aValue.IsArrayBuffer()) {
aError.ThrowTypeError("Expected ArrayBuffer argument"_ns);
return;
}
*aResult = OwnedRustBuffer::FromArrayBuffer(aValue.GetAsArrayBuffer());
}
static RustBuffer IntoRust(OwnedRustBuffer&& aValue) {
return aValue.IntoRustBuffer();
}
static OwnedRustBuffer FromRust(RustBuffer aValue) {
return OwnedRustBuffer(aValue);
}
static void IntoJs(JSContext* aContext, OwnedRustBuffer&& aValue,
dom::OwningUniFFIScaffoldingValue* aDest,
ErrorResult& aError) {
JS::Rooted<JSObject*> obj(aContext);
aValue.IntoArrayBuffer(aContext, &obj, aError);
if (aError.Failed()) {
return;
}
aDest->SetAsArrayBuffer().Init(obj);
}
};
// ScaffoldingConverter for object pointers
template <const UniFFIPointerType* PointerType>
class ScaffoldingObjectConverter {
public:
using RustType = void*;
using IntermediateType = void*;
static void FromJs(const dom::OwningUniFFIScaffoldingValue& aValue,
void** aResult, ErrorResult& aError) {
if (!aValue.IsUniFFIPointer()) {
aError.ThrowTypeError("Expected UniFFI pointer argument"_ns);
return;
}
dom::UniFFIPointer& value = aValue.GetAsUniFFIPointer();
if (!value.IsSamePtrType(PointerType)) {
aError.ThrowTypeError("Incorrect UniFFI pointer type"_ns);
return;
}
*aResult = value.ClonePtr();
}
static void* IntoRust(void* aValue) { return aValue; }
static void* FromRust(void* aValue) { return aValue; }
static void IntoJs(JSContext* aContext, void* aValue,
dom::OwningUniFFIScaffoldingValue* aDest,
ErrorResult& aError) {
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

View File

@@ -0,0 +1,299 @@
/* -*- 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 {
extern mozilla::LazyLogModule gUniffiLogger;
using dom::GlobalObject;
using dom::OwningUniFFIScaffoldingValue;
using dom::RootedDictionary;
using dom::Sequence;
using dom::UniFFIScaffoldingCallResult;
void UniffiSyncCallHandler::CallSync(
UniquePtr<UniffiSyncCallHandler> aHandler, const GlobalObject& aGlobal,
const Sequence<OwningUniFFIScaffoldingValue>& aArgs,
RootedDictionary<UniFFIScaffoldingCallResult>& aReturnValue,
ErrorResult& aError) {
MOZ_ASSERT(NS_IsMainThread());
aHandler->PrepareRustArgs(aArgs, aError);
if (aError.Failed()) {
return;
}
RustCallStatus callStatus{};
aHandler->MakeRustCall(&callStatus);
aHandler->mUniffiCallStatusCode = callStatus.code;
if (callStatus.error_buf.data) {
aHandler->mUniffiCallStatusErrorBuf = OwnedRustBuffer(callStatus.error_buf);
}
aHandler->ExtractCallResult(aGlobal.Context(), aReturnValue, aError);
}
already_AddRefed<dom::Promise> UniffiSyncCallHandler::CallAsyncWrapper(
UniquePtr<UniffiSyncCallHandler> aHandler, const dom::GlobalObject& aGlobal,
const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs,
ErrorResult& aError) {
MOZ_ASSERT(NS_IsMainThread());
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<UniffiSyncCallHandler>, nsresult, true>::Private(
__func__);
nsresult dispatchResult = NS_DispatchBackgroundTask(
NS_NewRunnableFunction(
__func__,
[handler = std::move(aHandler), taskPromise]() mutable {
RustCallStatus callStatus{};
handler->MakeRustCall(&callStatus);
handler->mUniffiCallStatusCode = callStatus.code;
if (callStatus.error_buf.data) {
handler->mUniffiCallStatusErrorBuf =
OwnedRustBuffer(callStatus.error_buf);
}
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<UniffiSyncCallHandler>, nsresult,
true>::ResolveOrRejectValue&& aResult) {
if (!aResult.IsResolve()) {
returnPromise->MaybeRejectWithUnknownError(__func__);
return;
}
auto handler = std::move(aResult.ResolveValue());
dom::AutoEntryScript aes(xpcomGlobal,
"UniffiSyncCallHandler::CallAsyncWrapper");
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 UniffiCallHandlerBase::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;
}
}
}
already_AddRefed<dom::Promise> UniffiAsyncCallHandler::CallAsync(
UniquePtr<UniffiAsyncCallHandler> aHandler,
const dom::GlobalObject& aGlobal,
const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs,
ErrorResult& aError) {
MOZ_ASSERT(NS_IsMainThread());
// Async calls return a Future rather than doing any work. This means we can
// make the call right now on the JS main thread without fear of blocking it.
aHandler->PrepareArgsAndMakeRustCall(aArgs, aError);
if (aError.Failed()) {
return nullptr;
}
// Create the promise that the handler will resolve
nsCOMPtr<nsIGlobalObject> global(do_QueryInterface(aGlobal.GetAsSupports()));
aHandler->mPromise = dom::Promise::Create(global, aError);
// Also get a copy to return to JS
RefPtr<dom::Promise> returnPromise(aHandler->mPromise);
if (aError.Failed()) {
aError.ThrowUnknownError("[UniFFI] dom::Promise::Create failed"_ns);
return nullptr;
}
// Schedule a poll for the future in a background thread.
nsresult dispatchResult = NS_DispatchBackgroundTask(NS_NewRunnableFunction(
__func__, [handler = std::move(aHandler)]() mutable {
UniffiAsyncCallHandler::Poll(std::move(handler));
}));
if (NS_FAILED(dispatchResult)) {
aError.ThrowUnknownError(
"[UniFFI] UniffiAsyncCallHandler::CallAsync - Error scheduling background task"_ns);
return nullptr;
}
// Return a copy of the JS promise, using forget() to convert it to
// already_AddRefed
return returnPromise.forget();
}
// Callback function for async calls
//
// This is passed to Rust when we poll the future alongside a 64-bit handle that
// represents the callback data. For uniffi-bindgen-gecko-js, the handle is a
// `UniffiAsyncCallHandler*` casted to an int.
//
// Rust calls this when either the future is ready or when it's time to poll it
// again.
void UniffiAsyncCallHandler::FutureCallback(uint64_t aCallHandlerHandle,
int8_t aCode) {
// Recreate the UniquePtr we previously released
UniquePtr<UniffiAsyncCallHandler> handler(
reinterpret_cast<UniffiAsyncCallHandler*>(
static_cast<uintptr_t>(aCallHandlerHandle)));
switch (aCode) {
case UNIFFI_FUTURE_READY: {
// `Future::poll` returned a `Ready` value on the Rust side.
nsresult dispatchResult = NS_DispatchToMainThread(NS_NewRunnableFunction(
__func__, [handler = std::move(handler)]() mutable {
UniffiAsyncCallHandler::Finish(std::move(handler));
}));
if (NS_FAILED(dispatchResult)) {
MOZ_LOG(gUniffiLogger, LogLevel::Error,
("[UniFFI] NS_DispatchToMainThread failed in "
"UniffiAsyncCallHandler::FutureCallback"));
}
break;
}
case UNIFFI_FUTURE_MAYBE_READY: {
// The Rust waker was invoked after `poll` returned a `Pending` value.
// Poll the future again soon in a background task.
nsresult dispatchResult = NS_DispatchBackgroundTask(
NS_NewRunnableFunction(__func__,
[handler = std::move(handler)]() mutable {
UniffiAsyncCallHandler::Poll(
std::move(handler));
}),
NS_DISPATCH_NORMAL);
if (NS_FAILED(dispatchResult)) {
MOZ_LOG(gUniffiLogger, LogLevel::Error,
("[UniFFI] NS_DispatchBackgroundTask failed in "
"UniffiAsyncCallHandler::FutureCallback"));
}
break;
}
default: {
// Invalid poll code, this should never happen, but if it does log an
// error and reject the promise.
MOZ_LOG(gUniffiLogger, LogLevel::Error,
("[UniFFI] Invalid poll code in "
"UniffiAsyncCallHandler::FutureCallback %d",
aCode));
handler->mPromise->MaybeRejectWithUndefined();
break;
}
};
}
void UniffiAsyncCallHandler::Poll(UniquePtr<UniffiAsyncCallHandler> aHandler) {
auto futureHandle = aHandler->mFutureHandle;
auto pollFn = aHandler->mPollFn;
// Release the UniquePtr into a raw pointer and convert it to a handle
// so that we can pass it as a handle to the UniFFI code. It gets converted
// back in `UniffiAsyncCallHandler::FutureCallback()`, which the Rust code
// guarentees will be called if the future makes progress.
uint64_t selfHandle =
static_cast<uint64_t>(reinterpret_cast<uintptr_t>(aHandler.release()));
pollFn(futureHandle, UniffiAsyncCallHandler::FutureCallback, selfHandle);
}
// Complete the Rust future, extract the call result and resolve/reject the JS
// promise
void UniffiAsyncCallHandler::Finish(
UniquePtr<UniffiAsyncCallHandler> aHandler) {
RefPtr<dom::Promise> promise = aHandler->mPromise;
if (!promise) {
return;
}
dom::AutoEntryScript aes(promise->GetGlobalObject(),
"UniffiAsyncCallHandler::Finish");
dom::RootedDictionary<dom::UniFFIScaffoldingCallResult> returnValue(aes.cx());
ErrorResult error;
RustCallStatus callStatus{};
aHandler->CallCompleteFn(&callStatus);
aHandler->mUniffiCallStatusCode = callStatus.code;
if (callStatus.error_buf.data) {
aHandler->mUniffiCallStatusErrorBuf = OwnedRustBuffer(callStatus.error_buf);
}
aHandler->ExtractCallResult(aes.cx(), returnValue, error);
error.WouldReportJSException();
if (error.Failed()) {
aHandler->mPromise->MaybeReject(std::move(error));
} else {
aHandler->mPromise->MaybeResolve(returnValue);
}
}
UniffiAsyncCallHandler::~UniffiAsyncCallHandler() { mFreeFn(mFutureHandle); }
} // namespace mozilla::uniffi

View File

@@ -9,11 +9,9 @@
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/OwnedRustBuffer.h"
#include "mozilla/dom/UniFFIRust.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 {
@@ -30,7 +28,7 @@ namespace mozilla::uniffi {
// In all cases, a new instance is created each time the scaffolding function
// is called.
class UniffiCallHandlerBase {
public:
protected:
// 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
@@ -38,7 +36,7 @@ class UniffiCallHandlerBase {
// value doesn't fit into a JS number.
//
// Called on the main thread.
virtual void LiftSuccessfulCallResult(
virtual void ExtractSuccessfulCallResult(
JSContext* aCx, dom::Optional<dom::OwningUniFFIScaffoldingValue>& aDest,
ErrorResult& aError) = 0;
@@ -54,17 +52,14 @@ class UniffiCallHandlerBase {
// panic=abort.
// - If some other error happens in the C++ layer, then `aError` will be set
// to the error.
void LiftCallResult(
void ExtractCallResult(
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;
OwnedRustBuffer mUniffiCallStatusErrorBuf;
};
// Call scaffolding functions for synchronous Rust calls
@@ -74,7 +69,7 @@ class UniffiSyncCallHandler : public UniffiCallHandlerBase {
// Convert a sequence of JS arguments and store them in this
// UniffiSyncCallHandler. Called on the main thread.
virtual void LowerRustArgs(
virtual void PrepareRustArgs(
const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs,
ErrorResult& aError) = 0;
@@ -121,9 +116,9 @@ class UniffiAsyncCallHandler : public UniffiCallHandlerBase {
//
// 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
// `PrepareRustArgs` and `PrepareArgs` and `MakeRustCall` like in the sync
// case.
virtual void LowerArgsAndMakeRustCall(
virtual void PrepareArgsAndMakeRustCall(
const dom::Sequence<dom::OwningUniFFIScaffoldingValue>& aArgs,
ErrorResult& aError) = 0;
@@ -145,6 +140,11 @@ class UniffiAsyncCallHandler : public UniffiCallHandlerBase {
// Call mPollFn to poll the future
static void Poll(UniquePtr<UniffiAsyncCallHandler> aHandler);
// Complete the async call and resolve the promise returned by CallAsync
//
// Called in the main thread.
static void Finish(UniquePtr<UniffiAsyncCallHandler> aHandler);
public:
virtual ~UniffiAsyncCallHandler();
@@ -159,7 +159,7 @@ class UniffiAsyncCallHandler : public UniffiCallHandlerBase {
private:
// Promise created by CallAsync
ResultPromise mPromise;
RefPtr<dom::Promise> mPromise;
// Callback function for Rust async calls
//

View File

@@ -7,10 +7,10 @@
#include "nsPrintfCString.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "mozilla/uniffi/OwnedRustBuffer.h"
#include "mozilla/dom/OwnedRustBuffer.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/UniFFIBinding.h"
#include "mozilla/uniffi/Callbacks.h"
#include "mozilla/dom/UniFFICallbacks.h"
#include "mozilla/Maybe.h"
#include "mozilla/Logging.h"
#include "mozilla/RefPtr.h"

View File

@@ -9,8 +9,8 @@
#include "mozilla/StaticPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/UniFFIRust.h"
#include "mozilla/dom/UniFFIScaffolding.h"
#include "mozilla/uniffi/Rust.h"
namespace mozilla::uniffi {

View File

@@ -7,10 +7,10 @@
#include "nsPrintfCString.h"
#include "js/GCAPI.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/Logging.h"
#include "mozilla/dom/UniFFIBinding.h"
#include "mozilla/dom/UniFFIPointer.h"
#include "mozilla/uniffi/Rust.h"
#include "mozilla/dom/UniFFIBinding.h"
#include "mozilla/Logging.h"
#include "UniFFIRust.h"
namespace mozilla::uniffi {
extern mozilla::LazyLogModule gUniffiLogger;

View File

@@ -12,7 +12,7 @@
#include "nsString.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/uniffi/PointerType.h"
#include "mozilla/dom/UniFFIPointerType.h"
namespace mozilla::dom {

View File

@@ -10,7 +10,7 @@
#include "nsISupports.h"
#include "nsWrapperCache.h"
#include "nsLiteralString.h"
#include "mozilla/uniffi/Rust.h"
#include "UniFFIRust.h"
namespace mozilla::uniffi {

View File

@@ -12,14 +12,14 @@
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/UniFFICall.h"
#include "mozilla/dom/UniFFICallbacks.h"
#include "mozilla/dom/UniFFIScaffolding.h"
#include "mozilla/uniffi/Call.h"
#include "mozilla/uniffi/Callbacks.h"
// This file implements the UniFFI WebIDL interface by leveraging the generate
// code in UniFFIScaffolding.cpp. It's main purpose is to check if
// MOZ_UNIFFI_FIXTURES is set and only try calling the scaffolding code if it
// is.
// 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;
@@ -35,7 +35,7 @@ using mozilla::uniffi::UniffiSyncCallHandler;
namespace mozilla::uniffi {
mozilla::LazyLogModule gUniffiLogger("uniffi");
// Implemented in GeneratedScaffolding.cpp
// Implemented in UniFFIGeneratedScaffolding.cpp
UniquePtr<UniffiSyncCallHandler> GetSyncCallHandler(uint64_t aId);
UniquePtr<UniffiAsyncCallHandler> GetAsyncCallHandler(uint64_t aId);
Maybe<already_AddRefed<UniFFIPointer>> ReadPointer(

View File

@@ -7,11 +7,10 @@
FINAL_LIBRARY = "xul"
UNIFIED_SOURCES += [
"Call.cpp",
"Callbacks.cpp",
"GeneratedScaffolding.cpp",
"OwnedRustBuffer.cpp",
"ResultPromise.cpp",
"UniFFICall.cpp",
"UniFFICallbacks.cpp",
"UniFFIGeneratedScaffolding.cpp",
"UniFFIPointer.cpp",
"UniFFIScaffolding.cpp",
]
@@ -20,18 +19,14 @@ if CONFIG["MOZ_UNIFFI_FIXTURES"]:
DEFINES["MOZ_UNIFFI_FIXTURES"] = True
EXPORTS.mozilla.dom += [
"UniFFIPointer.h",
"UniFFIScaffolding.h",
]
EXPORTS.mozilla.uniffi += [
"Call.h",
"Callbacks.h",
"FfiValue.h",
"OwnedRustBuffer.h",
"PointerType.h",
"ResultPromise.h",
"Rust.h",
"ScaffoldingConverter.h",
"UniFFICall.h",
"UniFFICallbacks.h",
"UniFFIPointer.h",
"UniFFIPointerType.h",
"UniFFIRust.h",
"UniFFIScaffolding.h",
]
EXTRA_JS_MODULES += [

View File

@@ -215,7 +215,8 @@ toolkit/components/nimbus/schemas/ExperimentFeatureManifest.schema.json
toolkit/components/nimbus/schemas/NimbusExperiment.schema.json
toolkit/components/pdfjs/PdfJsDefaultPrefs.js
toolkit/components/pdfjs/PdfJsOverridePrefs.js
toolkit/components/uniffi-js/GeneratedScaffolding.cpp
toolkit/components/uniffi-js/UniFFIGeneratedScaffolding.cpp
toolkit/components/uniffi-js/UniFFIFixtureScaffolding.cpp
toolkit/components/uniffi-bindgen-gecko-js/fixtures/generated
tools/@types/tspaths.json
tools/browsertime/package.json