Skip to content

Commit

Permalink
Fix custom types in generic positions in export function signatures (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
arendjr authored Jul 20, 2022
1 parent a3330e3 commit 8f4f1db
Show file tree
Hide file tree
Showing 10 changed files with 360 additions and 578 deletions.
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

0 comments on commit 8f4f1db

Please sign in to comment.