Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 145 additions & 8 deletions rs_bindings_from_cc/generate_bindings/generate_dyn_callable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

use arc_anyhow::{bail, Result};
use crubit_abi_type::{CrubitAbiType, CrubitAbiTypeToCppExprTokens, CrubitAbiTypeToCppTokens};
use arc_anyhow::Result;
use crubit_abi_type::{
CrubitAbiType, CrubitAbiTypeToCppExprTokens, CrubitAbiTypeToCppTokens,
CrubitAbiTypeToRustExprTokens, CrubitAbiTypeToRustTokens,
};
use database::db::BindingsGenerator;
use database::rs_snippet::{BackingType, Callable, FnTrait, PassingConvention, RsTypeKind};
use proc_macro2::TokenStream;
Expand Down Expand Up @@ -44,7 +47,7 @@ pub fn dyn_callable_crubit_abi_type(
)
},
BackingType::AnyInvocable => {
let make_cpp_invoker_tokens = generate_make_cpp_invoker_tokens()?;
let make_cpp_invoker_tokens = generate_make_cpp_invoker_tokens(db, callable)?;
quote! {
::any_invocable::AnyInvocableAbi::<#dyn_fn_spelling>::new(
#on_empty_tokens,
Expand Down Expand Up @@ -221,9 +224,9 @@ fn generate_invoker_function_pointer(
let crubit_abi_type_tokens = CrubitAbiTypeToCppTokens(&crubit_abi_type);
let crubit_abi_type_expr_tokens = CrubitAbiTypeToCppExprTokens(&crubit_abi_type);
invoke_ffi_and_transform_to_cpp.extend(quote! {
// Because our bridge buffer is named `out`
return ::crubit::internal::Decode<#crubit_abi_type_tokens>(#crubit_abi_type_expr_tokens, out);
});
// Because our bridge buffer is named `out`
return ::crubit::internal::Decode<#crubit_abi_type_tokens>(#crubit_abi_type_expr_tokens, out);
});
}
PassingConvention::Ctor => {
panic!("Ctor not supported");
Expand Down Expand Up @@ -251,6 +254,140 @@ fn generate_invoker_function_pointer(
}

/// Generates the `make_cpp_invoker` function for AnyInvocable.
fn generate_make_cpp_invoker_tokens() -> Result<TokenStream> {
bail!("AnyInvocable is not yet supported")
///
/// It's a closure that takes a manager and an invoker, and produces a boxed dyn fn that uses the
/// manager and invoker to do the actual work.
///
/// The produced function needs to know how to convert values to and from C++.
fn generate_make_cpp_invoker_tokens(
db: &dyn BindingsGenerator,
callable: &Callable,
) -> Result<TokenStream> {
let param_idents =
(0..callable.param_types.len()).map(|i| format_ident!("param_{i}")).collect::<Vec<_>>();
let rust_param_types = callable.param_types.iter().map(|param_ty| param_ty.to_token_stream(db));
let rust_return_type_fragment = callable.rust_return_type_fragment(db);

let mut c_param_types = Vec::with_capacity(callable.param_types.len());
let mut arg_exprs = Vec::with_capacity(callable.param_types.len());
// We are the caller
for (i, param_ty) in callable.param_types.iter().enumerate() {
let param_ident = &param_idents[i];

match param_ty.passing_convention() {
PassingConvention::AbiCompatible => {
c_param_types.push(param_ty.to_token_stream(db));
arg_exprs.push(quote! { #param_ident });
}
PassingConvention::LayoutCompatible => {
let param_ty_tokens = param_ty.to_token_stream(db);
c_param_types.push(quote! { &mut #param_ty_tokens });
arg_exprs.push(quote! { &mut #param_ident });
}
PassingConvention::ComposablyBridged => {
let crubit_abi_type = db.crubit_abi_type(param_ty.clone())?;
let crubit_abi_type_tokens = CrubitAbiTypeToRustTokens(&crubit_abi_type);
let crubit_abi_type_expr_tokens = CrubitAbiTypeToRustExprTokens(&crubit_abi_type);
// For arguments that are bridge types, we encode the
// Rust value into a buffer and then the argument is a pointer to that buffer.
c_param_types.push(quote! { *const u8 });
arg_exprs.push(quote! {
::bridge_rust::unstable_encode!(@ #crubit_abi_type_expr_tokens, #crubit_abi_type_tokens, #param_ident)
.as_ptr() as *const u8
});
}
PassingConvention::Ctor => {
panic!("Ctor not supported");
}
PassingConvention::OwnedPtr => {
panic!("OwnedPtr not supported");
}
PassingConvention::Void => unreachable!("parameter types cannot be void"),
}
}

// What the extern "C" function should return.
let mut c_return_type_fragment = None;
// Set c_return_type_fragment, or push an out param, or nothing if void.
match callable.return_type.passing_convention() {
PassingConvention::AbiCompatible => {
let c_return_type = callable.return_type.to_token_stream(db);
c_return_type_fragment = Some(quote! { -> #c_return_type });
}
PassingConvention::Void => {}
PassingConvention::LayoutCompatible => {
let return_type_tokens = callable.return_type.to_token_stream(db);
c_param_types.push(quote! { *mut #return_type_tokens });
arg_exprs.push(quote! { &raw mut out });
}
PassingConvention::ComposablyBridged => {
c_param_types.push(quote! { *mut u8 });
arg_exprs.push(quote! { &raw mut out });
}
PassingConvention::Ctor => {
panic!("Ctor not supported");
}
PassingConvention::OwnedPtr => {
panic!("OwnedPtr not supported");
}
};

let mut invoke_ffi_and_transform_to_rust = quote! {
unsafe { c_invoker(managed.state.get() #(, #arg_exprs)*) }
};

match callable.return_type.passing_convention() {
PassingConvention::AbiCompatible => {
// invoke_ffi_and_transform_to_rust is already a trailing expr.
}
PassingConvention::LayoutCompatible => {
invoke_ffi_and_transform_to_rust = quote! {
let out = ::core::mem::MaybeUninit::uninit();
#invoke_ffi_and_transform_to_rust;
unsafe { out.assume_init() }
}
}
PassingConvention::ComposablyBridged => {
let crubit_abi_type = db.crubit_abi_type(callable.return_type.as_ref().clone())?;
let crubit_abi_type_tokens = CrubitAbiTypeToRustTokens(&crubit_abi_type);
let crubit_abi_type_expr_tokens = CrubitAbiTypeToRustExprTokens(&crubit_abi_type);
invoke_ffi_and_transform_to_rust.extend(quote! {
::bridge_rust::unstable_return!(@ #crubit_abi_type_expr_tokens, #crubit_abi_type_tokens, |out| {
#invoke_ffi_and_transform_to_rust
})
});
}
PassingConvention::Ctor => {
panic!("Ctor not supported");
}
PassingConvention::OwnedPtr => {
panic!("OwnedPtr not supported");
}
PassingConvention::Void => {
// Append semicolon to the statement.
invoke_ffi_and_transform_to_rust = quote! {
#invoke_ffi_and_transform_to_rust;
}
}
}

let dyn_fn_spelling = callable.dyn_fn_spelling(db);

Ok(quote! {
|managed: ::any_invocable::ManagedState,
invoker: unsafe extern "C" fn()| -> ::alloc::boxed::Box<#dyn_fn_spelling> {
let c_invoker = unsafe {
::core::mem::transmute::<
unsafe extern "C" fn(),
unsafe extern "C" fn(
*mut ::any_invocable::TypeErasedState
#( , #c_param_types )*
) #c_return_type_fragment
>(invoker)
};
::alloc::boxed::Box::new(move |#( #param_idents: #rust_param_types ),*| #rust_return_type_fragment {
#invoke_ffi_and_transform_to_rust
})
}
})
}
22 changes: 22 additions & 0 deletions rs_bindings_from_cc/test/consume_absl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,25 @@ golden_test(
golden_cc = "absl_functional.golden.cc",
golden_rs = "absl_functional.golden.rs",
)

crubit_test_cc_library(
name = "uses_anyinvocable",
srcs = ["uses_anyinvocable.cc"],
hdrs = ["uses_anyinvocable.h"],
aspect_hints = ["//features:supported"],
deps = [
"@abseil-cpp//absl/functional:any_invocable",
],
)

crubit_rust_test(
name = "uses_anyinvocable_test",
srcs = ["uses_anyinvocable_test.rs"],
cc_deps = [
":uses_anyinvocable",
"@abseil-cpp//absl/functional:any_invocable",
],
deps = [
"@crate_index//:googletest",
],
)
58 changes: 58 additions & 0 deletions rs_bindings_from_cc/test/consume_absl/absl_functional.golden.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "support/internal/offsetof.h"
#include "support/internal/sizeof.h"
#include "support/internal/slot.h"
#include "support/rs_std/dyn_callable.h"

#include <cstddef>
#include <memory>
Expand All @@ -21,6 +22,63 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wthread-safety-analysis"

extern "C" void __CcTemplateInstN4absl12AnyInvocableIFvvOEEE_invoker(
::rs_std::internal_dyn_callable::TypeErasedState* state);
extern "C" void __CcTemplateInstN4absl12AnyInvocableIFvvOEEE_manager(
::absl::internal_any_invocable::FunctionToCall operation,
::absl::internal_any_invocable::TypeErasedState* from,
::absl::internal_any_invocable::TypeErasedState* to);

extern "C" int __CcTemplateInstN4absl12AnyInvocableIKFiiEEE_invoker(
::rs_std::internal_dyn_callable::TypeErasedState* state, int param_0);
extern "C" void __CcTemplateInstN4absl12AnyInvocableIKFiiEEE_manager(
::absl::internal_any_invocable::FunctionToCall operation,
::absl::internal_any_invocable::TypeErasedState* from,
::absl::internal_any_invocable::TypeErasedState* to);

extern "C" void
__rust_thunk___ZN24absl_functional_internal12CallVoidVoidEN4absl12AnyInvocableIFvvOEEE(
const unsigned char* f) {
::crubit::Decoder __f_decoder(::crubit::AnyInvocableAbi<void() &&>::kSize, f);
absl_functional_internal::CallVoidVoid(
::crubit::AnyInvocableAbi<void() &&>(
[](absl::internal_any_invocable::FunctionToCall operation,
absl::internal_any_invocable::TypeErasedState* from,
absl::internal_any_invocable::TypeErasedState* to) noexcept {
__CcTemplateInstN4absl12AnyInvocableIFvvOEEE_manager(operation,
from, to);
},
[](::rs_std::internal_dyn_callable::TypeErasedState* state) -> void {
__CcTemplateInstN4absl12AnyInvocableIFvvOEEE_invoker(state);
})
.Decode(__f_decoder));
}

static_assert((void (*)(class absl::AnyInvocable<void() &&>)) &
::absl_functional_internal::CallVoidVoid);

extern "C" void __rust_thunk___ZN24absl_functional_internal13ReturnIntVoidEv(
unsigned char* __return_abi_buffer) {
::crubit::Encoder __return_encoder(
::crubit::AnyInvocableAbi<int(int) const>::kSize, __return_abi_buffer);
::crubit::AnyInvocableAbi<int(int) const>(
[](absl::internal_any_invocable::FunctionToCall operation,
absl::internal_any_invocable::TypeErasedState* from,
absl::internal_any_invocable::TypeErasedState* to) noexcept {
__CcTemplateInstN4absl12AnyInvocableIKFiiEEE_manager(operation, from,
to);
},
[](::rs_std::internal_dyn_callable::TypeErasedState* state,
int param_0) -> int {
return __CcTemplateInstN4absl12AnyInvocableIKFiiEEE_invoker(state,
param_0);
})
.Encode(absl_functional_internal::ReturnIntVoid(), __return_encoder);
}

static_assert((class absl::AnyInvocable<int(int) const> (*)()) &
::absl_functional_internal::ReturnIntVoid);

static_assert(
CRUBIT_SIZEOF(
class std::basic_string_view<wchar_t, std::char_traits<wchar_t>>) ==
Expand Down
Loading
Loading