Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix custom types in generic positions in export function signatures #130

Merged
merged 1 commit into from
Jul 20, 2022
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## main

- Fix custom types in generic positions in export function signatures.

## 2.0.0

### Breaking changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ pub fn export_fp_untagged(arg: FpUntagged) -> FpUntagged;
#[fp_bindgen_support::fp_export_signature]
pub fn export_generics(arg: StructWithGenerics<u64>) -> StructWithGenerics<u64>;

#[fp_bindgen_support::fp_export_signature]
pub fn export_get_bytes() -> Result<serde_bytes::ByteBuf, String>;

#[fp_bindgen_support::fp_export_signature]
pub fn export_multiple_primitives(arg1: i8, arg2: String) -> i64;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,27 @@ impl Runtime {
Ok(result)
}

pub fn export_get_bytes(
&self,
) -> Result<Result<serde_bytes::ByteBuf, String>, InvocationError> {
let result = self.export_get_bytes_raw();
let result = result.map(|ref data| deserialize_from_slice(data));
result
}
pub fn export_get_bytes_raw(&self) -> Result<Vec<u8>, InvocationError> {
let mut env = RuntimeInstanceData::default();
let import_object = create_import_object(self.module.store(), &env);
let instance = Instance::new(&self.module, &import_object).unwrap();
env.init_with_instance(&instance).unwrap();
let function = instance
.exports
.get_native_function::<(), FatPtr>("__fp_gen_export_get_bytes")
.map_err(|_| InvocationError::FunctionNotExported)?;
let result = function.call()?;
let result = import_from_guest_raw(&env, result);
Ok(result)
}

pub fn export_multiple_primitives(
&self,
arg1: i8,
Expand Down Expand Up @@ -695,7 +716,7 @@ impl Runtime {
Ok(result)
}

#[doc = " Example how plugin could expose async data-fetching capabilities."]
/// Example how plugin could expose async data-fetching capabilities.
pub async fn fetch_data(
&self,
r#type: String,
Expand All @@ -721,7 +742,7 @@ impl Runtime {
Ok(result)
}

#[doc = " Called on the plugin to give it a chance to initialize."]
/// Called on the plugin to give it a chance to initialize.
pub fn init(&self) -> Result<(), InvocationError> {
let result = self.init_raw();
result
Expand All @@ -740,7 +761,7 @@ impl Runtime {
Ok(result)
}

#[doc = " Example how plugin could expose a reducer."]
/// Example how plugin could expose a reducer.
pub fn reducer_bridge(&self, action: ReduxAction) -> Result<StateUpdate, InvocationError> {
let action = serialize_to_vec(&action);
let result = self.reducer_bridge_raw(action);
Expand All @@ -765,42 +786,42 @@ impl Runtime {

fn create_import_object(store: &Store, env: &RuntimeInstanceData) -> ImportObject {
imports! {
"fp" => {
"__fp_host_resolve_async_value" => Function :: new_native_with_env (store , env . clone () , resolve_async_value) ,
"__fp_gen_import_explicit_bound_point" => Function :: new_native_with_env (store , env . clone () , _import_explicit_bound_point) ,
"__fp_gen_import_fp_adjacently_tagged" => Function :: new_native_with_env (store , env . clone () , _import_fp_adjacently_tagged) ,
"__fp_gen_import_fp_enum" => Function :: new_native_with_env (store , env . clone () , _import_fp_enum) ,
"__fp_gen_import_fp_flatten" => Function :: new_native_with_env (store , env . clone () , _import_fp_flatten) ,
"__fp_gen_import_fp_internally_tagged" => Function :: new_native_with_env (store , env . clone () , _import_fp_internally_tagged) ,
"__fp_gen_import_fp_struct" => Function :: new_native_with_env (store , env . clone () , _import_fp_struct) ,
"__fp_gen_import_fp_untagged" => Function :: new_native_with_env (store , env . clone () , _import_fp_untagged) ,
"__fp_gen_import_generics" => Function :: new_native_with_env (store , env . clone () , _import_generics) ,
"__fp_gen_import_get_bytes" => Function :: new_native_with_env (store , env . clone () , _import_get_bytes) ,
"__fp_gen_import_multiple_primitives" => Function :: new_native_with_env (store , env . clone () , _import_multiple_primitives) ,
"__fp_gen_import_primitive_bool" => Function :: new_native_with_env (store , env . clone () , _import_primitive_bool) ,
"__fp_gen_import_primitive_f32" => Function :: new_native_with_env (store , env . clone () , _import_primitive_f32) ,
"__fp_gen_import_primitive_f64" => Function :: new_native_with_env (store , env . clone () , _import_primitive_f64) ,
"__fp_gen_import_primitive_i16" => Function :: new_native_with_env (store , env . clone () , _import_primitive_i16) ,
"__fp_gen_import_primitive_i32" => Function :: new_native_with_env (store , env . clone () , _import_primitive_i32) ,
"__fp_gen_import_primitive_i64" => Function :: new_native_with_env (store , env . clone () , _import_primitive_i64) ,
"__fp_gen_import_primitive_i8" => Function :: new_native_with_env (store , env . clone () , _import_primitive_i8) ,
"__fp_gen_import_primitive_u16" => Function :: new_native_with_env (store , env . clone () , _import_primitive_u16) ,
"__fp_gen_import_primitive_u32" => Function :: new_native_with_env (store , env . clone () , _import_primitive_u32) ,
"__fp_gen_import_primitive_u64" => Function :: new_native_with_env (store , env . clone () , _import_primitive_u64) ,
"__fp_gen_import_primitive_u8" => Function :: new_native_with_env (store , env . clone () , _import_primitive_u8) ,
"__fp_gen_import_serde_adjacently_tagged" => Function :: new_native_with_env (store , env . clone () , _import_serde_adjacently_tagged) ,
"__fp_gen_import_serde_enum" => Function :: new_native_with_env (store , env . clone () , _import_serde_enum) ,
"__fp_gen_import_serde_flatten" => Function :: new_native_with_env (store , env . clone () , _import_serde_flatten) ,
"__fp_gen_import_serde_internally_tagged" => Function :: new_native_with_env (store , env . clone () , _import_serde_internally_tagged) ,
"__fp_gen_import_serde_struct" => Function :: new_native_with_env (store , env . clone () , _import_serde_struct) ,
"__fp_gen_import_serde_untagged" => Function :: new_native_with_env (store , env . clone () , _import_serde_untagged) ,
"__fp_gen_import_string" => Function :: new_native_with_env (store , env . clone () , _import_string) ,
"__fp_gen_import_timestamp" => Function :: new_native_with_env (store , env . clone () , _import_timestamp) ,
"__fp_gen_import_void_function" => Function :: new_native_with_env (store , env . clone () , _import_void_function) ,
"__fp_gen_import_void_function_empty_result" => Function :: new_native_with_env (store , env . clone () , _import_void_function_empty_result) ,
"__fp_gen_import_void_function_empty_return" => Function :: new_native_with_env (store , env . clone () , _import_void_function_empty_return) ,
"__fp_gen_log" => Function :: new_native_with_env (store , env . clone () , _log) ,
"__fp_gen_make_http_request" => Function :: new_native_with_env (store , env . clone () , _make_http_request) ,
"fp" => {
"__fp_host_resolve_async_value" => Function::new_native_with_env(store, env.clone(), resolve_async_value),
"__fp_gen_import_explicit_bound_point" => Function::new_native_with_env(store, env.clone(), _import_explicit_bound_point),
"__fp_gen_import_fp_adjacently_tagged" => Function::new_native_with_env(store, env.clone(), _import_fp_adjacently_tagged),
"__fp_gen_import_fp_enum" => Function::new_native_with_env(store, env.clone(), _import_fp_enum),
"__fp_gen_import_fp_flatten" => Function::new_native_with_env(store, env.clone(), _import_fp_flatten),
"__fp_gen_import_fp_internally_tagged" => Function::new_native_with_env(store, env.clone(), _import_fp_internally_tagged),
"__fp_gen_import_fp_struct" => Function::new_native_with_env(store, env.clone(), _import_fp_struct),
"__fp_gen_import_fp_untagged" => Function::new_native_with_env(store, env.clone(), _import_fp_untagged),
"__fp_gen_import_generics" => Function::new_native_with_env(store, env.clone(), _import_generics),
"__fp_gen_import_get_bytes" => Function::new_native_with_env(store, env.clone(), _import_get_bytes),
"__fp_gen_import_multiple_primitives" => Function::new_native_with_env(store, env.clone(), _import_multiple_primitives),
"__fp_gen_import_primitive_bool" => Function::new_native_with_env(store, env.clone(), _import_primitive_bool),
"__fp_gen_import_primitive_f32" => Function::new_native_with_env(store, env.clone(), _import_primitive_f32),
"__fp_gen_import_primitive_f64" => Function::new_native_with_env(store, env.clone(), _import_primitive_f64),
"__fp_gen_import_primitive_i16" => Function::new_native_with_env(store, env.clone(), _import_primitive_i16),
"__fp_gen_import_primitive_i32" => Function::new_native_with_env(store, env.clone(), _import_primitive_i32),
"__fp_gen_import_primitive_i64" => Function::new_native_with_env(store, env.clone(), _import_primitive_i64),
"__fp_gen_import_primitive_i8" => Function::new_native_with_env(store, env.clone(), _import_primitive_i8),
"__fp_gen_import_primitive_u16" => Function::new_native_with_env(store, env.clone(), _import_primitive_u16),
"__fp_gen_import_primitive_u32" => Function::new_native_with_env(store, env.clone(), _import_primitive_u32),
"__fp_gen_import_primitive_u64" => Function::new_native_with_env(store, env.clone(), _import_primitive_u64),
"__fp_gen_import_primitive_u8" => Function::new_native_with_env(store, env.clone(), _import_primitive_u8),
"__fp_gen_import_serde_adjacently_tagged" => Function::new_native_with_env(store, env.clone(), _import_serde_adjacently_tagged),
"__fp_gen_import_serde_enum" => Function::new_native_with_env(store, env.clone(), _import_serde_enum),
"__fp_gen_import_serde_flatten" => Function::new_native_with_env(store, env.clone(), _import_serde_flatten),
"__fp_gen_import_serde_internally_tagged" => Function::new_native_with_env(store, env.clone(), _import_serde_internally_tagged),
"__fp_gen_import_serde_struct" => Function::new_native_with_env(store, env.clone(), _import_serde_struct),
"__fp_gen_import_serde_untagged" => Function::new_native_with_env(store, env.clone(), _import_serde_untagged),
"__fp_gen_import_string" => Function::new_native_with_env(store, env.clone(), _import_string),
"__fp_gen_import_timestamp" => Function::new_native_with_env(store, env.clone(), _import_timestamp),
"__fp_gen_import_void_function" => Function::new_native_with_env(store, env.clone(), _import_void_function),
"__fp_gen_import_void_function_empty_result" => Function::new_native_with_env(store, env.clone(), _import_void_function_empty_result),
"__fp_gen_import_void_function_empty_return" => Function::new_native_with_env(store, env.clone(), _import_void_function_empty_return),
"__fp_gen_log" => Function::new_native_with_env(store, env.clone(), _log),
"__fp_gen_make_http_request" => Function::new_native_with_env(store, env.clone(), _make_http_request),
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export type Exports = {
exportFpStruct?: (arg: types.FpPropertyRenaming) => types.FpPropertyRenaming;
exportFpUntagged?: (arg: types.FpUntagged) => types.FpUntagged;
exportGenerics?: (arg: types.StructWithGenerics<number>) => types.StructWithGenerics<number>;
exportGetBytes?: () => types.Result<ArrayBuffer, string>;
exportMultiplePrimitives?: (arg1: number, arg2: string) => bigint;
exportPrimitiveBool?: (arg: boolean) => boolean;
exportPrimitiveF32?: (arg: number) => number;
Expand Down Expand Up @@ -89,6 +90,7 @@ export type Exports = {
exportFpStructRaw?: (arg: Uint8Array) => Uint8Array;
exportFpUntaggedRaw?: (arg: Uint8Array) => Uint8Array;
exportGenericsRaw?: (arg: Uint8Array) => Uint8Array;
exportGetBytesRaw?: () => Uint8Array;
exportMultiplePrimitivesRaw?: (arg1: number, arg2: Uint8Array) => bigint;
exportPrimitiveBoolRaw?: (arg: boolean) => boolean;
exportPrimitiveI16Raw?: (arg: number) => number;
Expand Down Expand Up @@ -439,6 +441,12 @@ export async function createRuntime(
return parseObject<types.StructWithGenerics<number>>(export_fn(arg_ptr));
};
})(),
exportGetBytes: (() => {
const export_fn = instance.exports.__fp_gen_export_get_bytes as any;
if (!export_fn) return;

return () => parseObject<types.Result<ArrayBuffer, string>>(export_fn());
})(),
exportMultiplePrimitives: (() => {
const export_fn = instance.exports.__fp_gen_export_multiple_primitives as any;
if (!export_fn) return;
Expand Down Expand Up @@ -648,6 +656,12 @@ export async function createRuntime(
return importFromMemory(export_fn(arg_ptr));
};
})(),
exportGetBytesRaw: (() => {
const export_fn = instance.exports.__fp_gen_export_get_bytes as any;
if (!export_fn) return;

return () => importFromMemory(export_fn());
})(),
exportMultiplePrimitivesRaw: (() => {
const export_fn = instance.exports.__fp_gen_export_multiple_primitives as any;
if (!export_fn) return;
Expand Down
3 changes: 3 additions & 0 deletions examples/example-protocol/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ fp_export! {
// See `types/generics.rs` for more info.
fn export_generics(arg: StructWithGenerics<u64>) -> StructWithGenerics<u64>;

// Custom type in a generic position.
fn export_get_bytes() -> Result<ByteBuf, String>;

// Passing custom types with property/variant renaming.
//
// See `types/renaming.rs` for more info.
Expand Down
74 changes: 2 additions & 72 deletions fp-bindgen/src/functions.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::utils::normalize_return_type;
use crate::{docs::get_doc_lines, types::TypeIdent};
use quote::{format_ident, quote, ToTokens};
use quote::ToTokens;
use std::{collections::BTreeSet, convert::TryFrom};
use syn::{token::Async, FnArg, ForeignItemFn};
use syn::{FnArg, ForeignItemFn};

/// Maps from function name to the stringified function declaration.
#[derive(Debug, Default)]
Expand Down Expand Up @@ -100,78 +100,8 @@ impl PartialOrd for Function {
}
}

impl ToTokens for Function {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let Self {
name,
args,
doc_lines,
is_async,
return_type,
} = self;

let name = format_ident!("{}", name);

let asyncness = is_async.then(|| Async {
..Default::default()
});

(quote! {
#(#[doc = #doc_lines])*
#asyncness fn #name(#(#args),*) -> #return_type
})
.to_tokens(tokens);
}
}

#[derive(Debug, Eq, PartialEq)]
pub struct FunctionArg {
pub name: String,
pub ty: TypeIdent,
}

impl ToTokens for FunctionArg {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let Self { name, ty } = self;
let name = format_ident!("{}", name);

(quote! { #name: #ty }).to_tokens(tokens)
}
}

#[cfg(test)]
mod test {
use super::{Function, FunctionArg};
use crate::types::TypeIdent;
use quote::ToTokens;

#[test]
fn test_function_arg_to_tokens() {
let arg = FunctionArg {
name: "foobar".into(),
ty: TypeIdent::from("i64"),
};

let stringified = arg.into_token_stream().to_string();

pretty_assertions::assert_eq!(&stringified, "foobar : i64");
}

#[test]
fn test_function_to_tokens() {
let func = Function {
name: "foobar".into(),
is_async: false,
doc_lines: vec![],
return_type: Some(TypeIdent::from("String")),
args: vec![FunctionArg {
name: "a1".into(),
ty: TypeIdent::from("u64"),
}],
};

let string = func.into_token_stream().to_string();

pretty_assertions::assert_eq!(&string, "fn foobar (a1 : u64) -> String");
}
}
24 changes: 16 additions & 8 deletions fp-bindgen/src/generators/rust_plugin/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::functions::Function;
use crate::types::is_runtime_bound;
use crate::{
functions::FunctionList,
Expand Down Expand Up @@ -189,18 +190,25 @@ pub fn generate_type_bindings(types: &TypeMap, path: &str, module_key: &str) {
);
}

pub fn format_doc_lines(doc_lines: &[String]) -> String {
doc_lines
.iter()
.map(|line| format!("///{}\n", line))
.collect::<Vec<_>>()
.join("")
}

pub fn format_modifiers(function: &Function) -> String {
if function.is_async { "async " } else { "" }.to_owned()
}

fn format_functions(export_functions: FunctionList, types: &TypeMap, macro_path: &str) -> String {
export_functions
.iter()
.map(|func| {
let name = &func.name;
let doc = func
.doc_lines
.iter()
.map(|line| format!("///{}\n", line))
.collect::<Vec<_>>()
.join("");
let modifiers = if func.is_async { "async " } else { "" };
let doc = format_doc_lines(&func.doc_lines);
let modifiers = format_modifiers(func);
let args_with_types = func
.args
.iter()
Expand All @@ -220,7 +228,7 @@ fn format_functions(export_functions: FunctionList, types: &TypeMap, macro_path:
.join("\n\n")
}

fn format_ident(ident: &TypeIdent, types: &TypeMap) -> String {
pub fn format_ident(ident: &TypeIdent, types: &TypeMap) -> String {
match types.get(ident) {
Some(ty) => format_type_with_ident(ty, ident, types),
None => ident.to_string(), // Must be a generic.
Expand Down
Loading