diff --git a/core/src/ast/methods.rs b/core/src/ast/methods.rs index 10b0a8a3b..29caac317 100644 --- a/core/src/ast/methods.rs +++ b/core/src/ast/methods.rs @@ -3,8 +3,8 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use syn::*; -use super::utils::get_doc_lines; -use super::{CustomType, TypeName}; +use super::{utils::get_doc_lines, ModSymbol}; +use super::{Path, TypeName}; /// A method declared in the `impl` associated with an FFI struct. /// Includes both static and non-static methods, which can be distinguished @@ -32,9 +32,8 @@ pub struct Method { impl Method { /// Extracts a [`Method`] from an AST node inside an `impl`. - pub fn from_syn(m: &ImplItemMethod, self_type: &TypePath) -> Method { - assert!(self_type.path.segments.len() == 1); - let self_ident = self_type.path.segments[0].ident.clone(); + pub fn from_syn(m: &ImplItemMethod, self_path: &Path) -> Method { + let self_ident = self_path.elements.last().unwrap(); let method_ident = &m.sig.ident; let extern_ident = Ident::new( format!("{}_{}", &self_ident.to_string(), method_ident.to_string()).as_str(), @@ -56,11 +55,11 @@ impl Method { name: "self".to_string(), ty: if rec.reference.is_some() { TypeName::Reference( - Box::new(TypeName::Named(self_ident.to_string())), + Box::new(TypeName::Named(self_path.clone())), rec.mutability.is_some(), ) } else { - TypeName::Named(self_ident.to_string()) + TypeName::Named(self_path.clone()) }, }, _ => panic!("Unexpected self param type"), @@ -87,18 +86,19 @@ impl Method { /// Any references to opaque structs that are invalid are pushed into the `errors` vector. pub fn check_opaque<'a>( &'a self, - env: &HashMap, + in_path: &Path, + env: &HashMap>, errors: &mut Vec<&'a TypeName>, ) { self.self_param .iter() - .for_each(|m| m.ty.check_opaque(env, errors)); + .for_each(|m| m.ty.check_opaque(in_path, env, errors)); self.params .iter() - .for_each(|m| m.ty.check_opaque(env, errors)); + .for_each(|m| m.ty.check_opaque(in_path, env, errors)); self.return_type .iter() - .for_each(|t| t.check_opaque(env, errors)); + .for_each(|t| t.check_opaque(in_path, env, errors)); } /// Checks whether the method qualifies for special writeable handling. @@ -151,7 +151,7 @@ mod tests { use quote::quote; use syn; - use super::Method; + use super::{Method, Path}; #[test] fn static_methods() { @@ -163,10 +163,7 @@ mod tests { } }) .unwrap(), - &syn::parse2(quote! { - MyStructContainingMethod - }) - .unwrap() + &Path::empty().sub_path("MyStructContainingMethod".to_string()) )); insta::assert_yaml_snapshot!(Method::from_syn( @@ -180,10 +177,7 @@ mod tests { } }) .unwrap(), - &syn::parse2(quote! { - MyStructContainingMethod - }) - .unwrap() + &Path::empty().sub_path("MyStructContainingMethod".to_string()) )); } @@ -196,10 +190,7 @@ mod tests { } }) .unwrap(), - &syn::parse2(quote! { - MyStructContainingMethod - }) - .unwrap() + &Path::empty().sub_path("MyStructContainingMethod".to_string()) )); insta::assert_yaml_snapshot!(Method::from_syn( @@ -209,10 +200,7 @@ mod tests { } }) .unwrap(), - &syn::parse2(quote! { - MyStructContainingMethod - }) - .unwrap() + &Path::empty().sub_path("MyStructContainingMethod".to_string()) )); } } diff --git a/core/src/ast/mod.rs b/core/src/ast/mod.rs index 9b9d89a46..c1bb57b8f 100644 --- a/core/src/ast/mod.rs +++ b/core/src/ast/mod.rs @@ -12,6 +12,9 @@ mod structs; pub use structs::{OpaqueStruct, Struct}; mod types; -pub use types::{CustomType, PrimitiveType, TypeName}; +pub use types::{CustomType, ModSymbol, PrimitiveType, TypeName}; + +mod paths; +pub use paths::Path; mod utils; diff --git a/core/src/ast/modules.rs b/core/src/ast/modules.rs index 71e55b367..599a6ecd7 100644 --- a/core/src/ast/modules.rs +++ b/core/src/ast/modules.rs @@ -2,13 +2,16 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use quote::ToTokens; -use syn::{ImplItem, Item, ItemMod}; +use syn::{ImplItem, Item, ItemMod, UseTree}; -use super::{CustomType, Method, OpaqueStruct, Struct, TypeName}; +use super::{CustomType, Method, ModSymbol, OpaqueStruct, Path, Struct, TypeName}; #[derive(Clone, Serialize, Deserialize, Debug)] pub struct Module { + pub name: String, + pub imports: Vec<(Path, String)>, pub declared_types: HashMap, + pub sub_modules: Vec, } impl Module { @@ -18,20 +21,55 @@ impl Module { /// Any references to opaque structs that are invalid are pushed into the `errors` vector. pub fn check_opaque<'a>( &'a self, - env: &HashMap, + in_path: &Path, + env: &HashMap>, errors: &mut Vec<&'a TypeName>, ) { self.declared_types .values() - .for_each(|t| t.check_opaque(env, errors)); + .for_each(|t| t.check_opaque(&in_path.sub_path(self.name.clone()), env, errors)); + + self.sub_modules + .iter() + .for_each(|m| m.check_opaque(&in_path.sub_path(self.name.clone()), env, errors)); } -} -impl From<&ItemMod> for Module { - /// Get all custom types defined in a module as a mapping from their name to - /// the extracted metadata. - fn from(input: &ItemMod) -> Module { + fn insert_all_types(&self, in_path: Path, out: &mut HashMap>) { + let mut mod_symbols = HashMap::new(); + + self.imports.iter().for_each(|(path, name)| { + mod_symbols.insert(name.clone(), ModSymbol::Alias(path.clone())); + }); + + self.declared_types.iter().for_each(|(k, v)| { + if mod_symbols + .insert(k.clone(), ModSymbol::CustomType(v.clone())) + .is_some() + { + panic!("Two types were declared with the same name, this needs to be implemented"); + } + }); + + let path_to_self = in_path.sub_path(self.name.clone()); + self.sub_modules.iter().for_each(|m| { + m.insert_all_types(path_to_self.clone(), out); + mod_symbols.insert(m.name.clone(), ModSymbol::SubModule(m.name.clone())); + }); + + out.insert(path_to_self, mod_symbols); + } + + pub fn from_syn(input: &ItemMod, force_analyze: bool) -> Module { let mut custom_types_by_name = HashMap::new(); + let mut sub_modules = Vec::new(); + let mut imports = Vec::new(); + + let analyze_types = force_analyze + || input + .attrs + .iter() + .any(|a| a.path.to_token_stream().to_string() == "diplomat :: bridge"); + input .content .as_ref() @@ -39,61 +77,93 @@ impl From<&ItemMod> for Module { .1 .iter() .for_each(|a| match a { + Item::Use(u) => { + extract_imports(&Path::empty(), &u.tree, &mut imports); + } Item::Struct(strct) => { - if strct - .attrs - .iter() - .any(|a| a.path.to_token_stream().to_string() == "diplomat :: opaque") - { - custom_types_by_name.insert( - strct.ident.to_string(), - CustomType::Opaque(OpaqueStruct::from(strct)), - ); - } else { - custom_types_by_name.insert( - strct.ident.to_string(), - CustomType::Struct(Struct::from(strct)), - ); + if analyze_types { + if strct + .attrs + .iter() + .any(|a| a.path.to_token_stream().to_string() == "diplomat :: opaque") + { + custom_types_by_name.insert( + strct.ident.to_string(), + CustomType::Opaque(OpaqueStruct::from(strct)), + ); + } else { + custom_types_by_name.insert( + strct.ident.to_string(), + CustomType::Struct(Struct::from(strct)), + ); + } } } Item::Impl(ipl) => { - assert!(ipl.trait_.is_none()); - - let self_typ = match ipl.self_ty.as_ref() { - syn::Type::Path(s) => s, - _ => panic!("Self type not found"), - }; - - let mut new_methods = ipl - .items - .iter() - .filter_map(|i| match i { - ImplItem::Method(m) => Some(Method::from_syn(m, self_typ)), - _ => None, - }) - .collect(); - - assert!(self_typ.path.segments.len() == 1); - let self_ident = self_typ.path.segments[0].ident.clone(); - - match custom_types_by_name - .get_mut(&self_ident.to_string()) - .unwrap() - { - CustomType::Struct(strct) => { - strct.methods.append(&mut new_methods); - } - CustomType::Opaque(strct) => { - strct.methods.append(&mut new_methods); + if analyze_types { + assert!(ipl.trait_.is_none()); + + let self_typ = match ipl.self_ty.as_ref() { + syn::Type::Path(s) => Path::from_syn(&s.path), + _ => panic!("Self type not found"), + }; + + let mut new_methods = ipl + .items + .iter() + .filter_map(|i| match i { + ImplItem::Method(m) => Some(Method::from_syn(m, &self_typ)), + _ => None, + }) + .collect(); + + let self_ident = self_typ.elements.last().unwrap(); + + match custom_types_by_name.get_mut(self_ident).unwrap() { + CustomType::Struct(strct) => { + strct.methods.append(&mut new_methods); + } + CustomType::Opaque(strct) => { + strct.methods.append(&mut new_methods); + } } } } + Item::Mod(item_mod) => { + sub_modules.push(Module::from_syn(item_mod, false)); + } _ => {} }); Module { + name: input.ident.to_string(), + imports, declared_types: custom_types_by_name, + sub_modules, + } + } +} + +fn extract_imports(base_path: &Path, use_tree: &UseTree, out: &mut Vec<(Path, String)>) { + match use_tree { + UseTree::Name(name) => out.push(( + base_path.sub_path(name.ident.to_string()), + name.ident.to_string(), + )), + UseTree::Path(path) => { + extract_imports(&base_path.sub_path(path.ident.to_string()), &path.tree, out) } + UseTree::Glob(_) => todo!("Glob imports are not yet supported"), + UseTree::Group(group) => { + group + .items + .iter() + .for_each(|i| extract_imports(base_path, &i, out)); + } + UseTree::Rename(rename) => out.push(( + base_path.sub_path(rename.ident.to_string()), + rename.rename.to_string(), + )), } } @@ -109,26 +179,26 @@ impl File { /// Any references to opaque structs that are invalid are pushed into the `errors` vector. pub fn check_opaque<'a>( &'a self, - env: &HashMap, + env: &HashMap>, errors: &mut Vec<&'a TypeName>, ) { self.modules .values() - .for_each(|t| t.check_opaque(env, errors)); + .for_each(|t| t.check_opaque(&Path::empty(), env, errors)); } /// Fuses all declared types into a single environment `HashMap`. - pub fn all_types(&self) -> HashMap { + pub fn all_types(&self) -> HashMap> { let mut out = HashMap::new(); + let mut top_symbols = HashMap::new(); + self.modules.values().for_each(|m| { - m.declared_types.iter().for_each(|(k, v)| { - if out.insert(k.clone(), v.clone()).is_some() { - panic!( - "Two types were declared with the same name, this needs to be implemented" - ); - } - }) + m.insert_all_types(Path::empty(), &mut out); + top_symbols.insert(m.name.clone(), ModSymbol::SubModule(m.name.clone())); }); + + out.insert(Path::empty(), top_symbols); + out } } @@ -139,13 +209,10 @@ impl From<&syn::File> for File { let mut out = HashMap::new(); file.items.iter().for_each(|i| { if let Item::Mod(item_mod) = i { - if item_mod - .attrs - .iter() - .any(|a| a.path.to_token_stream().to_string() == "diplomat :: bridge") - { - out.insert(item_mod.ident.to_string(), Module::from(item_mod)); - } + out.insert( + item_mod.ident.to_string(), + Module::from_syn(item_mod, false), + ); } }); @@ -160,6 +227,8 @@ mod tests { use quote::quote; use syn; + use crate::ast::Path; + use super::{File, Module, TypeName}; #[test] @@ -168,7 +237,7 @@ mod tests { settings.set_sort_maps(true); settings.bind(|| { - insta::assert_yaml_snapshot!(Module::from( + insta::assert_yaml_snapshot!(Module::from_syn( &syn::parse2(quote! { mod ffi { struct NonOpaqueStruct { @@ -202,7 +271,8 @@ mod tests { } } }) - .unwrap() + .unwrap(), + true )); }); } @@ -271,8 +341,8 @@ mod tests { assert_eq!( errors, vec![ - &TypeName::Named("OpaqueStruct".to_string()), - &TypeName::Named("OpaqueStruct".to_string()) + &TypeName::Named(Path::empty().sub_path("OpaqueStruct".to_string())), + &TypeName::Named(Path::empty().sub_path("OpaqueStruct".to_string())) ] ); } diff --git a/core/src/ast/paths.rs b/core/src/ast/paths.rs new file mode 100644 index 000000000..bec379beb --- /dev/null +++ b/core/src/ast/paths.rs @@ -0,0 +1,52 @@ +use proc_macro2::Span; +use serde::{Deserialize, Serialize}; + +#[derive(Hash, Eq, PartialEq, Deserialize, Serialize, Clone, Debug)] +pub struct Path { + pub elements: Vec, +} + +impl Path { + pub fn get_super(&self) -> Path { + let mut new_elements = self.elements.clone(); + new_elements.remove(new_elements.len() - 1); + Path { + elements: new_elements, + } + } + + pub fn sub_path(&self, ident: String) -> Path { + let mut new_elements = self.elements.clone(); + new_elements.push(ident); + Path { + elements: new_elements, + } + } + + pub fn to_syn(&self) -> syn::Path { + syn::Path { + leading_colon: None, + segments: self + .elements + .iter() + .map(|s| syn::PathSegment { + ident: syn::Ident::new(s, Span::call_site()), + arguments: syn::PathArguments::None, + }) + .collect(), + } + } + + pub fn from_syn(path: &syn::Path) -> Path { + let mut out = vec![]; + for elem in path.segments.iter() { + out.push(elem.ident.to_string()) + } + + Path { elements: out } + } + + pub fn empty() -> Path { + Path { elements: vec![] } + } +} diff --git a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods-2.snap b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods-2.snap index facd16bda..eb7c3d878 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods-2.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods-2.snap @@ -1,6 +1,6 @@ --- source: core/src/ast/methods.rs -expression: "Method::from_syn(&syn::parse2(quote! {\n fn\n foo(& mut self, x : u64, y : MyCustomStruct)\n -> u64 { x }\n }).unwrap(),\n &syn::parse2(quote! { MyStructContainingMethod }).unwrap())" +expression: "Method::from_syn(&syn::parse2(quote! {\n fn\n foo(& mut self, x : u64, y : MyCustomStruct)\n -> u64 { x }\n }).unwrap(),\n &Path::empty().sub_path(\"MyStructContainingMethod\".to_string()))" --- name: foo @@ -10,7 +10,9 @@ self_param: name: self ty: Reference: - - Named: MyStructContainingMethod + - Named: + elements: + - MyStructContainingMethod - true params: - name: x @@ -18,7 +20,9 @@ params: Primitive: u64 - name: y ty: - Named: MyCustomStruct + Named: + elements: + - MyCustomStruct return_type: Primitive: u64 diff --git a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods.snap b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods.snap index 573614f99..6996782ec 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__nonstatic_methods.snap @@ -1,6 +1,6 @@ --- source: core/src/ast/methods.rs -expression: "Method::from_syn(&syn::parse2(quote! {\n fn foo(& self, x : u64, y : MyCustomStruct)\n { }\n }).unwrap(),\n &syn::parse2(quote! { MyStructContainingMethod }).unwrap())" +expression: "Method::from_syn(&syn::parse2(quote! {\n fn foo(& self, x : u64, y : MyCustomStruct)\n { }\n }).unwrap(),\n &Path::empty().sub_path(\"MyStructContainingMethod\".to_string()))" --- name: foo @@ -10,7 +10,9 @@ self_param: name: self ty: Reference: - - Named: MyStructContainingMethod + - Named: + elements: + - MyStructContainingMethod - false params: - name: x @@ -18,6 +20,8 @@ params: Primitive: u64 - name: y ty: - Named: MyCustomStruct + Named: + elements: + - MyCustomStruct return_type: ~ diff --git a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods-2.snap b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods-2.snap index cef69b973..6ad0e6594 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods-2.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods-2.snap @@ -1,6 +1,6 @@ --- source: core/src/ast/methods.rs -expression: "Method::from_syn(&syn::parse2(quote! {\n /// Some docs.\n /// Some more docs.\n ///\n /// Even more docs.\n fn foo(x : u64, y : MyCustomStruct) -> u64\n { x }\n }).unwrap(),\n &syn::parse2(quote! { MyStructContainingMethod }).unwrap())" +expression: "Method::from_syn(&syn::parse2(quote! {\n /// Some docs.\n /// Some more docs.\n ///\n /// Even more docs.\n fn foo(x : u64, y : MyCustomStruct) -> u64\n { x }\n }).unwrap(),\n &Path::empty().sub_path(\"MyStructContainingMethod\".to_string()))" --- name: foo @@ -13,7 +13,9 @@ params: Primitive: u64 - name: y ty: - Named: MyCustomStruct + Named: + elements: + - MyCustomStruct return_type: Primitive: u64 diff --git a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods.snap b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods.snap index d8d3cca18..b95f8de3c 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__methods__tests__static_methods.snap @@ -1,6 +1,6 @@ --- source: core/src/ast/methods.rs -expression: "Method::from_syn(&syn::parse2(quote! {\n /// Some docs.\n fn foo(x : u64, y : MyCustomStruct) { }\n }).unwrap(),\n &syn::parse2(quote! { MyStructContainingMethod }).unwrap())" +expression: "Method::from_syn(&syn::parse2(quote! {\n /// Some docs.\n fn foo(x : u64, y : MyCustomStruct) { }\n }).unwrap(),\n &Path::empty().sub_path(\"MyStructContainingMethod\".to_string()))" --- name: foo @@ -13,6 +13,8 @@ params: Primitive: u64 - name: y ty: - Named: MyCustomStruct + Named: + elements: + - MyCustomStruct return_type: ~ diff --git a/core/src/ast/snapshots/diplomat_core__ast__modules__tests__simple_mod.snap b/core/src/ast/snapshots/diplomat_core__ast__modules__tests__simple_mod.snap index c1a848b42..456ab8c17 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__modules__tests__simple_mod.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__modules__tests__simple_mod.snap @@ -1,8 +1,10 @@ --- source: core/src/ast/modules.rs -expression: "Module::from(&syn::parse2(quote! {\n mod ffi\n {\n struct NonOpaqueStruct\n { a : i32, b : Box < NonOpaqueStruct > }\n impl NonOpaqueStruct\n {\n fn new(x : i32) -> NonOpaqueStruct\n { unimplemented ! () ; } fn\n set_a(& mut self, new_a : i32)\n { self . a = new_a ; }\n } #[diplomat :: opaque] struct OpaqueStruct\n { a : SomeExternalType } impl OpaqueStruct\n {\n fn new() -> Box < OpaqueStruct >\n { unimplemented ! () ; } fn\n get_string(& self) -> String\n { unimplemented ! () }\n }\n }\n }).unwrap())" +expression: "Module::from_syn(&syn::parse2(quote! {\n mod ffi\n {\n struct NonOpaqueStruct\n { a : i32, b : Box < NonOpaqueStruct > }\n impl NonOpaqueStruct\n {\n fn new(x : i32) -> NonOpaqueStruct\n { unimplemented ! () ; } fn\n set_a(& mut self, new_a : i32)\n { self . a = new_a ; }\n } #[diplomat :: opaque] struct\n OpaqueStruct { a : SomeExternalType }\n impl OpaqueStruct\n {\n fn new() -> Box < OpaqueStruct >\n { unimplemented ! () ; } fn\n get_string(& self) -> String\n { unimplemented ! () }\n }\n }\n }).unwrap(), true)" --- +name: ffi +imports: [] declared_types: NonOpaqueStruct: Struct: @@ -14,7 +16,9 @@ declared_types: - "" - - b - Box: - Named: NonOpaqueStruct + Named: + elements: + - NonOpaqueStruct - "" methods: - name: new @@ -26,7 +30,9 @@ declared_types: ty: Primitive: i32 return_type: - Named: NonOpaqueStruct + Named: + elements: + - NonOpaqueStruct - name: set_a doc_lines: "" full_path_name: NonOpaqueStruct_set_a @@ -34,7 +40,9 @@ declared_types: name: self ty: Reference: - - Named: NonOpaqueStruct + - Named: + elements: + - NonOpaqueStruct - true params: - name: new_a @@ -53,7 +61,9 @@ declared_types: params: [] return_type: Box: - Named: OpaqueStruct + Named: + elements: + - OpaqueStruct - name: get_string doc_lines: "" full_path_name: OpaqueStruct_get_string @@ -61,9 +71,14 @@ declared_types: name: self ty: Reference: - - Named: OpaqueStruct + - Named: + elements: + - OpaqueStruct - false params: [] return_type: - Named: String + Named: + elements: + - String +sub_modules: [] diff --git a/core/src/ast/snapshots/diplomat_core__ast__structs__tests__simple_struct.snap b/core/src/ast/snapshots/diplomat_core__ast__structs__tests__simple_struct.snap index eede9b6c4..ce0b774ef 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__structs__tests__simple_struct.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__structs__tests__simple_struct.snap @@ -11,7 +11,9 @@ fields: - "" - - b - Box: - Named: MyLocalStruct + Named: + elements: + - MyLocalStruct - "" methods: [] diff --git a/core/src/ast/snapshots/diplomat_core__ast__types__tests__typename_boxes-2.snap b/core/src/ast/snapshots/diplomat_core__ast__types__tests__typename_boxes-2.snap index 70555aad3..53cb06465 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__types__tests__typename_boxes-2.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__types__tests__typename_boxes-2.snap @@ -4,5 +4,7 @@ expression: "TypeName::from(&syn::parse2(quote! { Box < MyLocalStruct > }).unwra --- Box: - Named: MyLocalStruct + Named: + elements: + - MyLocalStruct diff --git a/core/src/ast/snapshots/diplomat_core__ast__types__tests__typename_named.snap b/core/src/ast/snapshots/diplomat_core__ast__types__tests__typename_named.snap index fa0833daf..aa4fb81d9 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__types__tests__typename_named.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__types__tests__typename_named.snap @@ -3,5 +3,7 @@ source: core/src/ast/types.rs expression: "TypeName::from(&syn::parse2(quote! { MyLocalStruct }).unwrap())" --- -Named: MyLocalStruct +Named: + elements: + - MyLocalStruct diff --git a/core/src/ast/snapshots/diplomat_core__ast__types__tests__typename_references-2.snap b/core/src/ast/snapshots/diplomat_core__ast__types__tests__typename_references-2.snap index 7d3c89b9f..839c3bfc7 100644 --- a/core/src/ast/snapshots/diplomat_core__ast__types__tests__typename_references-2.snap +++ b/core/src/ast/snapshots/diplomat_core__ast__types__tests__typename_references-2.snap @@ -4,6 +4,8 @@ expression: "TypeName::from(&syn::parse2(quote! { & mut MyLocalStruct }).unwrap( --- Reference: - - Named: MyLocalStruct + - Named: + elements: + - MyLocalStruct - true diff --git a/core/src/ast/types.rs b/core/src/ast/types.rs index b3985893f..861664281 100644 --- a/core/src/ast/types.rs +++ b/core/src/ast/types.rs @@ -7,7 +7,7 @@ use lazy_static::lazy_static; use std::collections::HashMap; use std::iter::FromIterator; -use super::{Method, OpaqueStruct, Struct}; +use super::{Method, OpaqueStruct, Path, Struct}; /// A type declared inside a Diplomat-annotated module. #[derive(Clone, Serialize, Deserialize, Debug)] @@ -50,24 +50,37 @@ impl CustomType { /// Any references to opaque structs that are invalid are pushed into the `errors` vector. pub fn check_opaque<'a>( &'a self, - env: &HashMap, + in_path: &Path, + env: &HashMap>, errors: &mut Vec<&'a TypeName>, ) { match self { CustomType::Struct(strct) => { for (_, field, _) in strct.fields.iter() { - field.check_opaque(env, errors); + field.check_opaque(in_path, env, errors); } } CustomType::Opaque(_) => {} } for method in self.methods().iter() { - method.check_opaque(env, errors); + method.check_opaque(in_path, env, errors); } } } +/// A symbol declared in a module, which can either be a pointer to another path, +/// or a custom type defined directly inside that module +#[derive(Clone, Serialize, Deserialize, Debug)] +pub enum ModSymbol { + /// A symbol that is a pointer to another path. + Alias(Path), + /// A symbol that is a submodule. + SubModule(String), + /// A symbol that is a custom type. + CustomType(CustomType), +} + /// A local type reference, such as the type of a field, parameter, or return value. /// Unlike [`CustomType`], which represents a type declaration, [`TypeName`]s can compose /// types through references and boxing, and can also capture unresolved paths. @@ -77,7 +90,7 @@ pub enum TypeName { Primitive(PrimitiveType), /// An unresolved path to a custom type, which can be resolved after all types /// are collected with [`TypeName::resolve()`]. - Named(String), + Named(Path), /// An optionally mutable reference to another type. Reference(Box, /* mutable */ bool), /// A `Box` type. @@ -97,7 +110,10 @@ impl TypeName { TypeName::Primitive(name) => { syn::Type::Path(syn::parse_str(PRIMITIVE_TO_STRING.get(name).unwrap()).unwrap()) } - TypeName::Named(name) => syn::Type::Path(syn::parse_str(name.as_str()).unwrap()), + TypeName::Named(name) => syn::Type::Path(syn::TypePath { + qself: None, + path: name.to_syn(), + }), TypeName::Reference(underlying, mutable) => syn::Type::Reference(TypeReference { and_token: syn::token::And(Span::call_site()), lifetime: None, @@ -110,7 +126,7 @@ impl TypeName { }), TypeName::Box(underlying) => syn::Type::Path(TypePath { qself: None, - path: Path { + path: syn::Path { leading_colon: None, segments: Punctuated::from_iter(vec![PathSegment { ident: Ident::new("Box", Span::call_site()), @@ -127,7 +143,7 @@ impl TypeName { }), TypeName::Option(underlying) => syn::Type::Path(TypePath { qself: None, - path: Path { + path: syn::Path { leading_colon: None, segments: Punctuated::from_iter(vec![PathSegment { ident: Ident::new("Option", Span::call_site()), @@ -155,28 +171,92 @@ impl TypeName { /// If this is a [`TypeName::Named`], grab the [`CustomType`] it points to from /// the `env`, which contains all [`CustomType`]s across all FFI modules. - pub fn resolve<'a>(&self, env: &'a HashMap) -> &'a CustomType { + pub fn resolve_with_path<'a>( + &self, + in_path: &Path, + env: &'a HashMap>, + ) -> (Path, &'a CustomType) { match self { - TypeName::Named(name) => env.get(name).unwrap(), + TypeName::Named(local_path) => { + let mut cur_path = in_path.clone(); + for (i, elem) in local_path.elements.iter().enumerate() { + match elem.as_ref() { + "crate" => { + // TODO(shadaj): get the enclosing trate from env when we support multiple crates + cur_path = Path::empty() + } + + "super" => cur_path = cur_path.get_super(), + + o => match env.get(&cur_path).and_then(|env| env.get(o)) { + Some(ModSymbol::Alias(p)) => { + let mut remaining_elements: Vec = + local_path.elements.iter().skip(i + 1).cloned().collect(); + let mut new_path = p.elements.clone(); + new_path.append(&mut remaining_elements); + return TypeName::Named(Path { elements: new_path }) + .resolve_with_path(&cur_path.clone(), env); + } + Some(ModSymbol::SubModule(name)) => { + cur_path.elements.push(name.clone()); + } + Some(ModSymbol::CustomType(t)) => { + if i == local_path.elements.len() - 1 { + return (cur_path, t); + } else { + panic!( + "Unexpected custom type when resolving symbol {} in {}", + o, + cur_path.elements.join("::") + ) + } + } + None => panic!( + "Could not resolve symbol {} in {}", + o, + cur_path.elements.join("::") + ), + }, + } + } + + panic!( + "Path {} does not point to a custom type", + in_path.elements.join("::") + ) + } _ => panic!(), } } + pub fn resolve<'a>( + &self, + in_path: &Path, + env: &'a HashMap>, + ) -> &'a CustomType { + self.resolve_with_path(in_path, env).1 + } + fn check_opaque_internal<'a>( &'a self, - env: &HashMap, + in_path: &Path, + env: &HashMap>, behind_reference: bool, errors: &mut Vec<&'a TypeName>, ) { match self { TypeName::Reference(underlying, _) => { - underlying.check_opaque_internal(env, true, errors) + underlying.check_opaque_internal(in_path, env, true, errors) + } + TypeName::Box(underlying) => { + underlying.check_opaque_internal(in_path, env, true, errors) + } + TypeName::Option(underlying) => { + underlying.check_opaque_internal(in_path, env, false, errors) } - TypeName::Box(underlying) => underlying.check_opaque_internal(env, true, errors), - TypeName::Option(underlying) => underlying.check_opaque_internal(env, false, errors), TypeName::Primitive(_) => {} TypeName::Named(_) => { - if let CustomType::Opaque(_) = self.resolve(env) { + if let CustomType::Opaque(_) = self.resolve(in_path, env) { if !behind_reference { errors.push(self) } @@ -193,10 +273,11 @@ impl TypeName { /// Any references to opaque structs that are invalid are pushed into the `errors` vector. pub fn check_opaque<'a>( &'a self, - env: &HashMap, + in_path: &Path, + env: &HashMap>, errors: &mut Vec<&'a TypeName>, ) { - self.check_opaque_internal(env, false, errors); + self.check_opaque_internal(in_path, env, false, errors); } } @@ -252,7 +333,7 @@ impl From<&syn::Type> for TypeName { { TypeName::Writeable } else { - TypeName::Named(p.path.to_token_stream().to_string()) + TypeName::Named(Path::from_syn(&p.path)) } } _ => panic!(), diff --git a/example/src/data_provider.rs b/example/src/data_provider.rs new file mode 100644 index 000000000..528154f76 --- /dev/null +++ b/example/src/data_provider.rs @@ -0,0 +1,17 @@ +#[diplomat::bridge] +pub mod ffi { + use icu_provider::serde::SerdeDeDataProvider; + + #[diplomat::opaque] + /// An ICU4X data provider, capable of loading ICU4X data keys from some source. + /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu_provider/prelude/trait.DataProvider.html) for more information. + pub struct ICU4XDataProvider(pub Box); + + impl ICU4XDataProvider { + /// Construct a [StaticDataProvider](https://unicode-org.github.io/icu4x-docs/doc/icu_testdata/fn.get_static_provider.html). + fn new_static() -> Box { + let provider = icu_testdata::get_static_provider(); + Box::new(ICU4XDataProvider(Box::new(provider))) + } + } +} diff --git a/example/src/decimal.rs b/example/src/decimal.rs new file mode 100644 index 000000000..d07a16383 --- /dev/null +++ b/example/src/decimal.rs @@ -0,0 +1,93 @@ +#[diplomat::bridge] +pub mod ffi { + use icu::decimal::{ + options::{FixedDecimalFormatOptions, GroupingStrategy, SignDisplay}, + FixedDecimalFormat, + }; + use writeable::Writeable; + + use crate::{ + data_provider::ffi::ICU4XDataProvider, fixed_decimal::ffi::ICU4XFixedDecimal, + locale::ffi::ICU4XLocale, + }; + + #[diplomat::opaque] + /// An ICU4X Fixed Decimal Format object, capable of formatting a [`ICU4XFixedDecimal`] as a string. + /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu/decimal/struct.FixedDecimalFormat.html) for more information. + pub struct ICU4XFixedDecimalFormat(pub FixedDecimalFormat<'static, 'static>); + + pub struct ICU4XFixedDecimalFormatResult { + /// The [`ICU4XFixedDecimalFormat`], exists if creation was successful. + pub fdf: Option>, + /// Whether creating the [`ICU4XFixedDecimalFormat`] was successful. + pub success: bool, + } + + pub struct ICU4XFixedDecimalFormatOptions { + pub grouping_strategy: u8, + pub sign_display: u8, + } + + impl ICU4XFixedDecimalFormatOptions { + pub fn default() -> ICU4XFixedDecimalFormatOptions { + ICU4XFixedDecimalFormatOptions { + grouping_strategy: 0, + sign_display: 0, + } + } + } + + impl ICU4XFixedDecimalFormat { + /// Creates a new [`ICU4XFixedDecimalFormat`] from locale data. See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu/decimal/struct.FixedDecimalFormat.html#method.try_new) for more information. + fn try_new( + locale: &ICU4XLocale, + provider: &ICU4XDataProvider, + options: ICU4XFixedDecimalFormatOptions, + ) -> ICU4XFixedDecimalFormatResult { + let langid = locale.0.as_ref().clone(); + let provider = provider.0.as_ref(); + + if let Result::Ok(fdf) = FixedDecimalFormat::try_new( + langid, + provider, + FixedDecimalFormatOptions { + grouping_strategy: match options.grouping_strategy { + 0 => GroupingStrategy::Auto, + 1 => GroupingStrategy::Never, + 2 => GroupingStrategy::Always, + 3 => GroupingStrategy::Min2, + _ => panic!(), + }, + sign_display: match options.sign_display { + 0 => SignDisplay::Auto, + 1 => SignDisplay::Never, + 2 => SignDisplay::Always, + 3 => SignDisplay::ExceptZero, + 4 => SignDisplay::Negative, + _ => panic!(), + }, + }, + ) { + ICU4XFixedDecimalFormatResult { + fdf: Some(Box::new(ICU4XFixedDecimalFormat(fdf))), + success: true, + } + } else { + ICU4XFixedDecimalFormatResult { + fdf: None, + success: false, + } + } + } + + /// Formats a [`ICU4XFixedDecimal`] to a string. See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu/decimal/struct.FixedDecimalFormat.html#method.format) for more information. + fn format_write( + &self, + value: &ICU4XFixedDecimal, + write: &mut diplomat_runtime::DiplomatWriteable, + ) { + self.0.format(&value.0).write_to(write).unwrap(); + write.flush(); + } + } +} diff --git a/example/src/fixed_decimal.rs b/example/src/fixed_decimal.rs new file mode 100644 index 000000000..daa45903c --- /dev/null +++ b/example/src/fixed_decimal.rs @@ -0,0 +1,34 @@ +#[diplomat::bridge] +pub mod ffi { + use fixed_decimal::FixedDecimal; + use writeable::Writeable; + + #[diplomat::opaque] + /// A decimal number. See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/fixed_decimal/decimal/struct.FixedDecimal.html) for more information. + pub struct ICU4XFixedDecimal(pub FixedDecimal); + + impl ICU4XFixedDecimal { + /// Construct an [`ICU4XFixedDecimal`] from an integer. + fn new(v: i32) -> Box { + Box::new(ICU4XFixedDecimal(FixedDecimal::from(v))) + } + + /// Multiply the [`ICU4XFixedDecimal`] by a given power of ten. + /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/fixed_decimal/decimal/struct.FixedDecimal.html#method.multiply_pow10) for more information. + fn multiply_pow10(&mut self, power: i16) { + self.0.multiply_pow10(power).unwrap(); + } + + /// Invert the sign of the [`ICU4XFixedDecimal`]. + /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/fixed_decimal/decimal/struct.FixedDecimal.html#method.negate) for more information. + fn negate(&mut self) { + self.0.negate() + } + + /// Format the [`ICU4XFixedDecimal`] as a string. + /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/fixed_decimal/decimal/struct.FixedDecimal.html#method.write_to) for more information. + fn to_string(&self, to: &mut diplomat_runtime::DiplomatWriteable) { + self.0.write_to(to).unwrap(); + } + } +} diff --git a/example/src/lib.rs b/example/src/lib.rs index f58ea7236..50afd3991 100644 --- a/example/src/lib.rs +++ b/example/src/lib.rs @@ -1,149 +1,4 @@ -#[diplomat::bridge] -mod ffi { - use std::str::FromStr; - - use fixed_decimal::FixedDecimal; - use icu::{ - decimal::{ - options::{FixedDecimalFormatOptions, GroupingStrategy, SignDisplay}, - FixedDecimalFormat, - }, - locid::Locale, - }; - use icu_provider::serde::SerdeDeDataProvider; - use writeable::Writeable; - - #[diplomat::opaque] - /// A decimal number. See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/fixed_decimal/decimal/struct.FixedDecimal.html) for more information. - pub struct ICU4XFixedDecimal(pub FixedDecimal); - - impl ICU4XFixedDecimal { - /// Construct an [`ICU4XFixedDecimal`] from an integer. - fn new(v: i32) -> Box { - Box::new(ICU4XFixedDecimal(FixedDecimal::from(v))) - } - - /// Multiply the [`ICU4XFixedDecimal`] by a given power of ten. - /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/fixed_decimal/decimal/struct.FixedDecimal.html#method.multiply_pow10) for more information. - fn multiply_pow10(&mut self, power: i16) { - self.0.multiply_pow10(power).unwrap(); - } - - /// Invert the sign of the [`ICU4XFixedDecimal`]. - /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/fixed_decimal/decimal/struct.FixedDecimal.html#method.negate) for more information. - fn negate(&mut self) { - self.0.negate() - } - - /// Format the [`ICU4XFixedDecimal`] as a string. - /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/fixed_decimal/decimal/struct.FixedDecimal.html#method.write_to) for more information. - fn to_string(&self, to: &mut diplomat_runtime::DiplomatWriteable) { - self.0.write_to(to).unwrap(); - } - } - - #[diplomat::opaque] - /// An ICU4X Locale, capable of representing strings like `"en-US"`. - /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu/locid/struct.Locale.html) for more information. - pub struct ICU4XLocale(pub Locale); - - impl ICU4XLocale { - /// Construct an [`ICU4XLocale`] from an locale identifier. - fn new(name: &str) -> Box { - Box::new(ICU4XLocale(Locale::from_str(name).unwrap())) - } - } - - #[diplomat::opaque] - /// An ICU4X data provider, capable of loading ICU4X data keys from some source. - /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu_provider/prelude/trait.DataProvider.html) for more information. - pub struct ICU4XDataProvider(Box); - - impl ICU4XDataProvider { - /// Construct a [StaticDataProvider](https://unicode-org.github.io/icu4x-docs/doc/icu_testdata/fn.get_static_provider.html). - fn new_static() -> Box { - let provider = icu_testdata::get_static_provider(); - Box::new(ICU4XDataProvider(Box::new(provider))) - } - } - - #[diplomat::opaque] - /// An ICU4X Fixed Decimal Format object, capable of formatting a [`ICU4XFixedDecimal`] as a string. - /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu/decimal/struct.FixedDecimalFormat.html) for more information. - pub struct ICU4XFixedDecimalFormat(pub FixedDecimalFormat<'static, 'static>); - - pub struct ICU4XFixedDecimalFormatResult { - /// The [`ICU4XFixedDecimalFormat`], exists if creation was successful. - pub fdf: Option>, - /// Whether creating the [`ICU4XFixedDecimalFormat`] was successful. - pub success: bool, - } - - pub struct ICU4XFixedDecimalFormatOptions { - pub grouping_strategy: u8, - pub sign_display: u8, - } - - impl ICU4XFixedDecimalFormatOptions { - pub fn default() -> ICU4XFixedDecimalFormatOptions { - ICU4XFixedDecimalFormatOptions { - grouping_strategy: 0, - sign_display: 0, - } - } - } - - impl ICU4XFixedDecimalFormat { - /// Creates a new [`ICU4XFixedDecimalFormat`] from locale data. See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu/decimal/struct.FixedDecimalFormat.html#method.try_new) for more information. - fn try_new( - locale: &ICU4XLocale, - provider: &ICU4XDataProvider, - options: ICU4XFixedDecimalFormatOptions, - ) -> ICU4XFixedDecimalFormatResult { - let langid = locale.0.as_ref().clone(); - let provider = provider.0.as_ref(); - - if let Result::Ok(fdf) = FixedDecimalFormat::try_new( - langid, - provider, - FixedDecimalFormatOptions { - grouping_strategy: match options.grouping_strategy { - 0 => GroupingStrategy::Auto, - 1 => GroupingStrategy::Never, - 2 => GroupingStrategy::Always, - 3 => GroupingStrategy::Min2, - _ => panic!(), - }, - sign_display: match options.sign_display { - 0 => SignDisplay::Auto, - 1 => SignDisplay::Never, - 2 => SignDisplay::Always, - 3 => SignDisplay::ExceptZero, - 4 => SignDisplay::Negative, - _ => panic!(), - }, - }, - ) { - ICU4XFixedDecimalFormatResult { - fdf: Some(Box::new(ICU4XFixedDecimalFormat(fdf))), - success: true, - } - } else { - ICU4XFixedDecimalFormatResult { - fdf: None, - success: false, - } - } - } - - /// Formats a [`ICU4XFixedDecimal`] to a string. See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu/decimal/struct.FixedDecimalFormat.html#method.format) for more information. - fn format_write( - &self, - value: &ICU4XFixedDecimal, - write: &mut diplomat_runtime::DiplomatWriteable, - ) { - self.0.format(&value.0).write_to(write).unwrap(); - write.flush(); - } - } -} +pub mod data_provider; +pub mod decimal; +pub mod fixed_decimal; +pub mod locale; diff --git a/example/src/locale.rs b/example/src/locale.rs new file mode 100644 index 000000000..0561a99b0 --- /dev/null +++ b/example/src/locale.rs @@ -0,0 +1,17 @@ +#[diplomat::bridge] +pub mod ffi { + use icu::locid::Locale; + use std::str::FromStr; + + #[diplomat::opaque] + /// An ICU4X Locale, capable of representing strings like `"en-US"`. + /// See [the Rust docs](https://unicode-org.github.io/icu4x-docs/doc/icu/locid/struct.Locale.html) for more information. + pub struct ICU4XLocale(pub Locale); + + impl ICU4XLocale { + /// Construct an [`ICU4XLocale`] from an locale identifier. + fn new(name: &str) -> Box { + Box::new(ICU4XLocale(Locale::from_str(name).unwrap())) + } + } +} diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 4e26336ed..3228ee660 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -146,7 +146,7 @@ fn gen_custom_type_method(strct: &ast::CustomType, m: &ast::Method) -> Item { None => Item::Fn( syn::parse2(quote! { #[no_mangle] - pub extern "C" fn #extern_ident(#(#all_params),*) { + extern "C" fn #extern_ident(#(#all_params),*) { #method_invocation(#(#all_params_invocation),*); } }) @@ -158,7 +158,7 @@ fn gen_custom_type_method(strct: &ast::CustomType, m: &ast::Method) -> Item { Item::Fn( syn::parse2(quote! { #[no_mangle] - pub extern "C" fn #extern_ident(#(#all_params),*) -> #return_typ_syn { + extern "C" fn #extern_ident(#(#all_params),*) -> #return_typ_syn { #method_invocation(#(#all_params_invocation),*) } }) @@ -169,7 +169,7 @@ fn gen_custom_type_method(strct: &ast::CustomType, m: &ast::Method) -> Item { } fn gen_bridge(input: ItemMod) -> ItemMod { - let module = ast::Module::from(&input); + let module = ast::Module::from_syn(&input, true); let (brace, mut new_contents) = input.content.unwrap(); new_contents.iter_mut().for_each(|c| { @@ -204,7 +204,7 @@ fn gen_bridge(input: ItemMod) -> ItemMod { new_contents.push(Item::Fn( syn::parse2(quote! { #[no_mangle] - pub extern "C" fn #destroy_ident(this: Box<#type_ident>) {} + extern "C" fn #destroy_ident(this: Box<#type_ident>) {} }) .unwrap(), )); diff --git a/macro/src/snapshots/diplomat__tests__method_taking_str.snap b/macro/src/snapshots/diplomat__tests__method_taking_str.snap index a3bceddbb..cdf7a42d8 100644 --- a/macro/src/snapshots/diplomat__tests__method_taking_str.snap +++ b/macro/src/snapshots/diplomat__tests__method_taking_str.snap @@ -12,13 +12,13 @@ mod ffi { } } #[no_mangle] - pub extern "C" fn Foo_from_str(s_diplomat_data: *const u8, s_diplomat_len: usize) { + extern "C" fn Foo_from_str(s_diplomat_data: *const u8, s_diplomat_len: usize) { Foo::from_str(unsafe { std::str::from_utf8(std::slice::from_raw_parts(s_diplomat_data, s_diplomat_len)) .unwrap() }); } #[no_mangle] - pub extern "C" fn Foo_destroy(this: Box) {} + extern "C" fn Foo_destroy(this: Box) {} } diff --git a/tool/src/c/mod.rs b/tool/src/c/mod.rs index bc34df9fa..133694f5e 100644 --- a/tool/src/c/mod.rs +++ b/tool/src/c/mod.rs @@ -6,10 +6,12 @@ use std::fmt::Write; use diplomat_core::ast::{self, PrimitiveType}; use indenter::indented; +use crate::util; + static RUNTIME_H: &str = include_str!("runtime.h"); pub fn gen_bindings( - env: &HashMap, + env: &HashMap>, outs: &mut HashMap<&str, String>, ) -> fmt::Result { let diplomat_runtime_out = outs.entry("diplomat_runtime.h").or_insert_with(String::new); @@ -25,35 +27,41 @@ pub fn gen_bindings( writeln!(out, "extern \"C\" {{")?; writeln!(out, "#endif")?; - let mut all_types: Vec<&ast::CustomType> = env.values().collect(); - all_types.sort_by_key(|t| t.name()); + let mut all_types = util::get_all_custom_types(env); + all_types.sort_by_key(|t| t.1.name()); - for custom_type in &all_types { + for (in_path, custom_type) in &all_types { if let ast::CustomType::Opaque(_) = custom_type { writeln!(out)?; - gen_struct(custom_type, env, out)?; + gen_struct(custom_type, in_path, env, out)?; } } let mut structs_seen = HashSet::new(); let mut structs_order = Vec::new(); - for custom_type in &all_types { + for (in_path, custom_type) in &all_types { if let ast::CustomType::Struct(strct) = custom_type { if !structs_seen.contains(&strct.name) { - topological_sort_structs(strct, &mut structs_seen, &mut structs_order, env); + topological_sort_structs( + strct, + (*in_path).clone(), + &mut structs_seen, + &mut structs_order, + env, + ); } } } - for strct in structs_order { + for (in_path, strct) in structs_order { writeln!(out)?; - gen_struct(&ast::CustomType::Struct(strct), env, out)?; + gen_struct(&ast::CustomType::Struct(strct.clone()), &in_path, env, out)?; } - for custom_type in all_types { + for (in_path, custom_type) in all_types { for method in custom_type.methods() { writeln!(out)?; - gen_method(method, env, out)?; + gen_method(method, in_path, env, out)?; } writeln!( @@ -73,7 +81,8 @@ pub fn gen_bindings( fn gen_struct( custom_type: &ast::CustomType, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, out: &mut W, ) -> fmt::Result { match custom_type { @@ -86,7 +95,7 @@ fn gen_struct( let mut class_body_out = indented(out).with_str(" "); for (name, typ, _) in strct.fields.iter() { writeln!(&mut class_body_out)?; - gen_field(name, typ, env, &mut class_body_out)?; + gen_field(name, typ, in_path, env, &mut class_body_out)?; } writeln!(out)?; writeln!(out, "}} {};", strct.name)?; @@ -99,10 +108,11 @@ fn gen_struct( fn gen_field( name: &str, typ: &ast::TypeName, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, out: &mut W, ) -> fmt::Result { - gen_type(typ, env, out)?; + gen_type(typ, in_path, env, out)?; write!(out, " {};", name)?; Ok(()) @@ -110,12 +120,13 @@ fn gen_field( fn gen_method( method: &ast::Method, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, out: &mut W, ) -> fmt::Result { match &method.return_type { Some(ret_type) => { - gen_type(ret_type, env, out)?; + gen_type(ret_type, in_path, env, out)?; } None => { @@ -141,7 +152,7 @@ fn gen_method( param.name, param.name )?; } else { - gen_type(¶m.ty, env, out)?; + gen_type(¶m.ty, in_path, env, out)?; write!(out, " {}", param.name)?; } } @@ -153,16 +164,17 @@ fn gen_method( fn gen_type( typ: &ast::TypeName, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, out: &mut W, ) -> fmt::Result { match typ { ast::TypeName::Named(_) => { - write!(out, "{}", typ.resolve(env).name())?; + write!(out, "{}", typ.resolve(in_path, env).name())?; } ast::TypeName::Box(underlying) => { - gen_type(underlying.as_ref(), env, out)?; + gen_type(underlying.as_ref(), in_path, env, out)?; write!(out, "*")?; } @@ -170,7 +182,7 @@ fn gen_type( if !mutable { write!(out, "const ")?; } - gen_type(underlying.as_ref(), env, out)?; + gen_type(underlying.as_ref(), in_path, env, out)?; write!(out, "*")?; } @@ -180,7 +192,7 @@ fn gen_type( ast::TypeName::Option(underlying) => match underlying.as_ref() { ast::TypeName::Box(_) => { - gen_type(underlying.as_ref(), env, out)?; + gen_type(underlying.as_ref(), in_path, env, out)?; } _ => todo!(), @@ -214,25 +226,26 @@ pub fn c_type_for_prim(prim: &PrimitiveType) -> &str { } } -pub fn topological_sort_structs( - root: &ast::Struct, +pub fn topological_sort_structs<'a>( + root: &'a ast::Struct, + in_path: ast::Path, seen: &mut HashSet, - order: &mut Vec, - env: &HashMap, + order: &mut Vec<(ast::Path, &'a ast::Struct)>, + env: &'a HashMap>, ) { seen.insert(root.name.clone()); for (_, typ, _) in &root.fields { if let ast::TypeName::Named(_) = typ { - match typ.resolve(env) { - ast::CustomType::Struct(strct) => { + match typ.resolve_with_path(&in_path, env) { + (path, ast::CustomType::Struct(strct)) => { if !seen.contains(&strct.name) { - topological_sort_structs(strct, seen, order, env); + topological_sort_structs(strct, path, seen, order, env); } } - ast::CustomType::Opaque(_) => {} + (_, ast::CustomType::Opaque(_)) => {} } } } - order.push(root.clone()); + order.push((in_path, root)); } diff --git a/tool/src/cpp/mod.rs b/tool/src/cpp/mod.rs index 10a909c4f..9cca8e61a 100644 --- a/tool/src/cpp/mod.rs +++ b/tool/src/cpp/mod.rs @@ -6,10 +6,12 @@ use std::fmt::Write; use diplomat_core::ast; use indenter::indented; +use crate::util; + static RUNTIME_HPP: &str = include_str!("runtime.hpp"); pub fn gen_bindings( - env: &HashMap, + env: &HashMap>, outs: &mut HashMap<&str, String>, ) -> fmt::Result { super::c::gen_bindings(env, outs)?; @@ -29,10 +31,10 @@ pub fn gen_bindings( writeln!(out, "#include \"diplomat_runtime.hpp\"")?; writeln!(out)?; - let mut all_types: Vec<&ast::CustomType> = env.values().collect(); - all_types.sort_by_key(|t| t.name()); + let mut all_types = util::get_all_custom_types(env); + all_types.sort_by_key(|t| t.1.name()); - for custom_type in &all_types { + for (_, custom_type) in &all_types { writeln!(out)?; match custom_type { ast::CustomType::Opaque(_) => { @@ -45,20 +47,21 @@ pub fn gen_bindings( } } - for custom_type in &all_types { + for (in_path, custom_type) in &all_types { if let ast::CustomType::Opaque(_) = custom_type { writeln!(out)?; - gen_struct(custom_type, true, env, out)?; + gen_struct(custom_type, in_path, true, env, out)?; } } let mut structs_seen = HashSet::new(); let mut structs_order = Vec::new(); for custom_type in &all_types { - if let ast::CustomType::Struct(strct) = custom_type { + if let (in_path, ast::CustomType::Struct(strct)) = custom_type { if !structs_seen.contains(&strct.name) { super::c::topological_sort_structs( strct, + (*in_path).clone(), &mut structs_seen, &mut structs_order, env, @@ -67,14 +70,20 @@ pub fn gen_bindings( } } - for strct in structs_order { + for (in_path, strct) in structs_order { writeln!(out)?; - gen_struct(&ast::CustomType::Struct(strct), true, env, out)?; + gen_struct( + &ast::CustomType::Struct(strct.clone()), + &in_path, + true, + env, + out, + )?; } - for custom_type in &all_types { + for (in_path, custom_type) in all_types { writeln!(out)?; - gen_struct(custom_type, false, env, out)?; + gen_struct(custom_type, in_path, false, env, out)?; } Ok(()) @@ -82,8 +91,9 @@ pub fn gen_bindings( fn gen_struct( custom_type: &ast::CustomType, + in_path: &ast::Path, is_header: bool, - env: &HashMap, + env: &HashMap>, out: &mut W, ) -> fmt::Result { if is_header { @@ -118,7 +128,14 @@ fn gen_struct( }; for method in &opaque.methods { - gen_method(custom_type, method, is_header, env, &mut public_body)?; + gen_method( + custom_type, + method, + in_path, + is_header, + env, + &mut public_body, + )?; } if is_header { @@ -159,13 +176,20 @@ fn gen_struct( if is_header { for (name, typ, _) in &strct.fields { - gen_type(typ, false, env, &mut public_body)?; + gen_type(typ, in_path, false, env, &mut public_body)?; writeln!(&mut public_body, " {};", name)?; } } for method in &strct.methods { - gen_method(custom_type, method, is_header, env, &mut public_body)?; + gen_method( + custom_type, + method, + in_path, + is_header, + env, + &mut public_body, + )?; } if is_header { @@ -180,8 +204,9 @@ fn gen_struct( fn gen_method( enclosing_type: &ast::CustomType, method: &ast::Method, + in_path: &ast::Path, is_header: bool, - env: &HashMap, + env: &HashMap>, out: &mut W, ) -> fmt::Result { if method.self_param.is_none() && is_header { @@ -195,7 +220,7 @@ fn gen_method( } else { match &method.return_type { Some(ret_type) => { - gen_type(ret_type, false, env, out)?; + gen_type(ret_type, in_path, false, env, out)?; } None => { @@ -228,7 +253,7 @@ fn gen_method( write!(out, ", ")?; } - gen_type(¶m.ty, false, env, out)?; + gen_type(¶m.ty, in_path, false, env, out)?; write!(out, " {}", param.name)?; } @@ -247,6 +272,7 @@ fn gen_method( "this", false, ¶m.ty, + in_path, env, true, &mut method_body, @@ -264,6 +290,7 @@ fn gen_method( ¶m.name, false, ¶m.ty, + in_path, env, param.name == "self", &mut method_body, @@ -295,6 +322,7 @@ fn gen_method( ), "out_value", ret_typ, + in_path, env, &mut method_body, ); @@ -317,12 +345,13 @@ fn gen_method( fn gen_type( typ: &ast::TypeName, + in_path: &ast::Path, behind_ref: bool, - env: &HashMap, + env: &HashMap>, out: &mut W, ) -> fmt::Result { match typ { - ast::TypeName::Named(_) => match typ.resolve(env) { + ast::TypeName::Named(_) => match typ.resolve(in_path, env) { ast::CustomType::Opaque(opaque) => { if behind_ref { write!(out, "{}", opaque.name)?; @@ -340,7 +369,7 @@ fn gen_type( }, ast::TypeName::Box(underlying) => { - gen_type(underlying.as_ref(), true, env, out)?; + gen_type(underlying.as_ref(), in_path, true, env, out)?; if behind_ref { write!(out, "*")?; } @@ -350,7 +379,7 @@ fn gen_type( if !mutable { write!(out, "const ")?; } - gen_type(underlying.as_ref(), true, env, out)?; + gen_type(underlying.as_ref(), in_path, true, env, out)?; write!(out, "&")?; if behind_ref { write!(out, "*")?; @@ -360,7 +389,7 @@ fn gen_type( ast::TypeName::Option(underlying) => match underlying.as_ref() { ast::TypeName::Box(_) => { write!(out, "std::optional<")?; - gen_type(underlying.as_ref(), behind_ref, env, out)?; + gen_type(underlying.as_ref(), in_path, behind_ref, env, out)?; write!(out, ">")?; } @@ -399,12 +428,13 @@ fn gen_rust_to_cpp( cpp: &str, path: &str, typ: &ast::TypeName, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, out: &mut W, ) -> String { match typ { ast::TypeName::Box(underlying) => match underlying.as_ref() { - ast::TypeName::Named(_name) => match underlying.resolve(env) { + ast::TypeName::Named(_name) => match underlying.resolve(in_path, env) { ast::CustomType::Opaque(opaque) => { format!("{}({})", opaque.name, cpp) } @@ -416,12 +446,12 @@ fn gen_rust_to_cpp( }, _o => todo!(), }, - ast::TypeName::Named(_) => match typ.resolve(env) { - ast::CustomType::Opaque(_) => { + ast::TypeName::Named(_) => match typ.resolve_with_path(in_path, env) { + (_, ast::CustomType::Opaque(_)) => { panic!("Cannot handle opaque structs by value"); } - ast::CustomType::Struct(strct) => { + (in_path, ast::CustomType::Struct(strct)) => { let raw_struct_id = format!("diplomat_raw_struct_{}", path); writeln!(out, "capi::{} {} = {};", strct.name, raw_struct_id, cpp).unwrap(); let mut all_fields_wrapped = vec![]; @@ -433,6 +463,7 @@ fn gen_rust_to_cpp( &format!("{}.{}", raw_struct_id, name), &format!("{}_{}", path, name), typ, + &in_path, env, out ) @@ -449,12 +480,13 @@ fn gen_rust_to_cpp( writeln!(out, "auto {} = {};", raw_value_id, cpp).unwrap(); let wrapped_value_id = format!("diplomat_optional_{}", path); - gen_type(typ, false, env, out).unwrap(); + gen_type(typ, in_path, false, env, out).unwrap(); writeln!(out, " {};", wrapped_value_id).unwrap(); writeln!(out, "if ({} != nullptr) {{", raw_value_id).unwrap(); - let some_expr = gen_rust_to_cpp(&raw_value_id, path, underlying.as_ref(), env, out); + let some_expr = + gen_rust_to_cpp(&raw_value_id, path, underlying.as_ref(), in_path, env, out); writeln!(out, " {} = {};", wrapped_value_id, some_expr).unwrap(); writeln!(out, "}} else {{").unwrap(); @@ -472,20 +504,29 @@ fn gen_rust_to_cpp( } } +#[allow(clippy::too_many_arguments)] fn gen_cpp_to_rust( cpp: &str, path: &str, behind_ref: bool, typ: &ast::TypeName, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, is_self: bool, out: &mut W, ) -> String { match typ { - ast::TypeName::Reference(underlying, _) => { - gen_cpp_to_rust(cpp, path, true, underlying.as_ref(), env, is_self, out) - } - ast::TypeName::Named(_) => match typ.resolve(env) { + ast::TypeName::Reference(underlying, _) => gen_cpp_to_rust( + cpp, + path, + true, + underlying.as_ref(), + in_path, + env, + is_self, + out, + ), + ast::TypeName::Named(_) => match typ.resolve(in_path, env) { ast::CustomType::Opaque(_opaque) => { if behind_ref { if is_self { @@ -511,6 +552,7 @@ fn gen_cpp_to_rust( &format!("{}_{}", path, name), false, typ, + in_path, env, false, out diff --git a/tool/src/js/docs.rs b/tool/src/js/docs.rs index 996b8d30f..11115b671 100644 --- a/tool/src/js/docs.rs +++ b/tool/src/js/docs.rs @@ -5,13 +5,18 @@ use diplomat_core::ast; use indenter::indented; use pulldown_cmark::{BrokenLink, CowStr, Event, LinkType, Options, Parser, Tag}; +use crate::util; + /// Generate RST-formatted Sphinx docs for all FFI types. Currently assumes a JS target. -pub fn gen_docs(env: &HashMap, out: &mut W) -> fmt::Result { - let mut all_types: Vec<&ast::CustomType> = env.values().collect(); - all_types.sort_by_key(|t| t.name()); - for custom_type in all_types { +pub fn gen_docs( + env: &HashMap>, + out: &mut W, +) -> fmt::Result { + let mut all_types: Vec<(&ast::Path, &ast::CustomType)> = util::get_all_custom_types(env); + all_types.sort_by_key(|t| t.1.name()); + for (in_path, custom_type) in all_types { writeln!(out)?; - gen_custom_type_docs(out, custom_type, env)?; + gen_custom_type_docs(out, custom_type, in_path, env)?; } Ok(()) } @@ -19,24 +24,25 @@ pub fn gen_docs(env: &HashMap, out: &mut pub fn gen_custom_type_docs( out: &mut W, typ: &ast::CustomType, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, ) -> fmt::Result { writeln!(out, ".. js:class:: {}", typ.name())?; writeln!(out)?; let mut class_indented = indented(out).with_str(" "); - markdown_to_rst(&mut class_indented, typ.doc_lines(), env)?; + markdown_to_rst(&mut class_indented, typ.doc_lines(), in_path, env)?; writeln!(class_indented)?; if let ast::CustomType::Struct(strct) = typ { for field in strct.fields.iter() { writeln!(&mut class_indented)?; - gen_field_docs(&mut class_indented, field, env)?; + gen_field_docs(&mut class_indented, field, in_path, env)?; } } for method in typ.methods() { writeln!(&mut class_indented)?; - gen_method_docs(&mut class_indented, method, env)?; + gen_method_docs(&mut class_indented, method, in_path, env)?; } Ok(()) } @@ -44,7 +50,8 @@ pub fn gen_custom_type_docs( pub fn gen_method_docs( out: &mut W, method: &ast::Method, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, ) -> fmt::Result { let mut param_names: Vec = method.params.iter().map(|p| p.name.clone()).collect(); if method.is_writeable_out() { @@ -68,7 +75,7 @@ pub fn gen_method_docs( } let mut method_indented = indented(out).with_str(" "); - markdown_to_rst(&mut method_indented, &method.doc_lines, env)?; + markdown_to_rst(&mut method_indented, &method.doc_lines, in_path, env)?; writeln!(method_indented)?; Ok(()) @@ -77,13 +84,14 @@ pub fn gen_method_docs( pub fn gen_field_docs( out: &mut W, field: &(String, ast::TypeName, String), - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, ) -> fmt::Result { writeln!(out, ".. js:attribute:: {}", field.0)?; writeln!(out)?; let mut method_indented = indented(out).with_str(" "); - markdown_to_rst(&mut method_indented, &field.2, env)?; + markdown_to_rst(&mut method_indented, &field.2, in_path, env)?; writeln!(method_indented)?; Ok(()) @@ -92,7 +100,8 @@ pub fn gen_field_docs( fn markdown_to_rst( out: &mut W, markdown: &str, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, ) -> fmt::Result { let mut broken_link_callback = |broken: BrokenLink| { Some(( @@ -142,12 +151,11 @@ fn markdown_to_rst( } Event::Code(text) => { if in_shortcut { - let resolved = env.get(text.as_ref()); - if let Some(custom_type) = resolved { - write!(out, ":js:class:`{}`", custom_type.name())?; - } else { - panic!("Failed to resolve doc reference to {}", text); - } + let shortcut_path = ast::Path { + elements: text.split("::").map(|s| s.to_string()).collect(), + }; + let resolved = ast::TypeName::Named(shortcut_path).resolve(in_path, env); + write!(out, ":js:class:`{}`", resolved.name())?; } else { write!(out, "``{}``", text)?; } diff --git a/tool/src/js/mod.rs b/tool/src/js/mod.rs index b25f0a859..4f25806c2 100644 --- a/tool/src/js/mod.rs +++ b/tool/src/js/mod.rs @@ -5,14 +5,14 @@ use std::{fmt::Write, usize}; use diplomat_core::ast::{self, PrimitiveType}; use indenter::indented; -use crate::layout; +use crate::{layout, util}; pub mod docs; static RUNTIME_MJS: &str = include_str!("runtime.mjs"); pub fn gen_bindings( - env: &HashMap, + env: &HashMap>, outs: &mut HashMap<&str, String>, ) -> fmt::Result { let diplomat_runtime_out = outs @@ -38,11 +38,11 @@ pub fn gen_bindings( )?; writeln!(out, "}});")?; - let mut all_types: Vec<&ast::CustomType> = env.values().collect(); - all_types.sort_by_key(|t| t.name()); - for custom_type in all_types { + let mut all_types = util::get_all_custom_types(env); + all_types.sort_by_key(|t| t.1.name()); + for (in_path, custom_type) in all_types { writeln!(out)?; - gen_struct(out, custom_type, env)?; + gen_struct(out, custom_type, in_path, env)?; } Ok(()) @@ -51,7 +51,8 @@ pub fn gen_bindings( fn gen_struct( out: &mut W, custom_type: &ast::CustomType, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, ) -> fmt::Result { writeln!( out, @@ -79,14 +80,14 @@ fn gen_struct( for method in custom_type.methods().iter() { writeln!(&mut class_body_out)?; - gen_method(method, env, &mut class_body_out)?; + gen_method(method, in_path, env, &mut class_body_out)?; } if let ast::CustomType::Struct(strct) = custom_type { - let (_, offsets, _) = layout::struct_size_offsets_max_align(strct, env); + let (_, offsets, _) = layout::struct_size_offsets_max_align(strct, in_path, env); for ((name, typ, _), offset) in strct.fields.iter().zip(offsets.iter()) { writeln!(&mut class_body_out)?; - gen_field(name, typ, *offset, env, &mut class_body_out)?; + gen_field(name, typ, in_path, *offset, env, &mut class_body_out)?; } } @@ -97,8 +98,9 @@ fn gen_struct( fn gen_field( name: &str, typ: &ast::TypeName, + in_path: &ast::Path, offset: usize, - env: &HashMap, + env: &HashMap>, out: &mut W, ) -> fmt::Result { writeln!(out, "get {}() {{", name)?; @@ -107,6 +109,7 @@ fn gen_field( gen_value_rust_to_js( &|out| write!(out, "this.underlying + {}", offset), &ast::TypeName::Reference(Box::new(typ.clone()), true), + in_path, env, &mut method_body_out, )?; @@ -117,7 +120,8 @@ fn gen_field( fn gen_method( method: &ast::Method, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, out: &mut W, ) -> fmt::Result { let is_writeable = method.is_writeable_out(); @@ -130,6 +134,7 @@ fn gen_method( gen_value_js_to_rust( p.name.clone(), &p.ty, + in_path, env, &mut pre_stmts, &mut all_param_exprs, @@ -152,7 +157,9 @@ fn gen_method( let all_params_invocation = { if let Some(ast::TypeName::Named(_)) = &method.return_type { - if let ast::CustomType::Struct(_) = method.return_type.as_ref().unwrap().resolve(env) { + if let ast::CustomType::Struct(_) = + method.return_type.as_ref().unwrap().resolve(in_path, env) + { all_param_exprs.insert(0, "diplomat_receive_buffer".to_string()); } } @@ -184,6 +191,7 @@ fn gen_method( gen_value_rust_to_js( &|out| write!(out, "{}", invocation_expr), ret_type, + in_path, env, &mut method_body_out, )?; @@ -219,7 +227,8 @@ fn gen_method( fn gen_value_js_to_rust( param_name: String, typ: &ast::TypeName, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, pre_logic: &mut Vec, invocation_params: &mut Vec, post_logic: &mut Vec, @@ -256,7 +265,7 @@ fn gen_value_js_to_rust( invocation_params.push(format!("{}.underlying", param_name)); } ast::TypeName::Named(_) => { - match typ.resolve(env) { + match typ.resolve(in_path, env) { ast::CustomType::Struct(struct_type) => { // TODO(shadaj): consider if we want to support copying data from a class instance for (field_name, field_type, _) in struct_type.fields.iter() { @@ -270,6 +279,7 @@ fn gen_value_js_to_rust( gen_value_js_to_rust( field_extracted_name, field_type, + in_path, env, pre_logic, invocation_params, @@ -290,15 +300,17 @@ fn gen_value_js_to_rust( fn gen_value_rust_to_js( value_expr: &dyn Fn(&mut dyn fmt::Write) -> fmt::Result, typ: &ast::TypeName, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, out: &mut W, ) -> fmt::Result { match typ { ast::TypeName::Named(_) => { - let custom_type = typ.resolve(env); + let custom_type = typ.resolve(in_path, env); match custom_type { ast::CustomType::Struct(strct) => { - let (strct_size, _, _) = layout::struct_size_offsets_max_align(strct, env); + let (strct_size, _, _) = + layout::struct_size_offsets_max_align(strct, in_path, env); writeln!(out, "(() => {{")?; let mut iife_indent = indented(out).with_str(" "); writeln!( @@ -315,7 +327,7 @@ fn gen_value_rust_to_js( )?; for (name, typ, _) in strct.fields.iter() { - gen_box_destructor(name, typ, env, &mut iife_indent)?; + gen_box_destructor(name, typ, in_path, env, &mut iife_indent)?; } writeln!( @@ -340,14 +352,20 @@ fn gen_value_rust_to_js( writeln!(out, "(() => {{")?; let mut iife_indent = indented(out).with_str(" "); write!(&mut iife_indent, "const out = ")?; - gen_rust_reference_to_js(underlying.as_ref(), value_expr, env, &mut iife_indent)?; + gen_rust_reference_to_js( + underlying.as_ref(), + in_path, + value_expr, + env, + &mut iife_indent, + )?; writeln!(&mut iife_indent, ";")?; if let ast::TypeName::Named(_) = underlying.as_ref() { writeln!( &mut iife_indent, "{}_box_destroy_registry.register(out, out.underlying)", - underlying.resolve(env).name() + underlying.resolve(in_path, env).name() )?; } @@ -363,7 +381,7 @@ fn gen_value_rust_to_js( } ast::TypeName::Reference(underlying, _mutability) => { - gen_rust_reference_to_js(underlying.as_ref(), value_expr, env, out)?; + gen_rust_reference_to_js(underlying.as_ref(), in_path, value_expr, env, out)?; } ast::TypeName::Writeable => todo!(), ast::TypeName::StrReference => todo!(), @@ -375,7 +393,8 @@ fn gen_value_rust_to_js( fn gen_box_destructor( name: &str, typ: &ast::TypeName, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, out: &mut W, ) -> Result<(), fmt::Error> { match typ { @@ -387,7 +406,7 @@ fn gen_box_destructor( writeln!( out, "{}_box_destroy_registry.register(out_{}_value, out_{}_value.underlying);", - underlying.resolve(env).name(), + underlying.resolve(in_path, env).name(), name, name )?; @@ -401,7 +420,7 @@ fn gen_box_destructor( ast::TypeName::Option(underlying) => { // TODO(shadaj): don't generate destructor if null - gen_box_destructor(name, underlying.as_ref(), env, out)?; + gen_box_destructor(name, underlying.as_ref(), in_path, env, out)?; } _ => {} @@ -412,14 +431,16 @@ fn gen_box_destructor( fn gen_rust_reference_to_js( underlying: &ast::TypeName, + in_path: &ast::Path, value_expr: &dyn Fn(&mut dyn fmt::Write) -> fmt::Result, - env: &HashMap, + env: &HashMap>, out: &mut W, ) -> fmt::Result { match underlying { ast::TypeName::Box(typ) | ast::TypeName::Reference(typ, _) => { gen_rust_reference_to_js( typ.as_ref(), + in_path, &|out| { write!(out, "(new Uint32Array(wasm.memory.buffer, ")?; value_expr(out)?; @@ -434,13 +455,13 @@ fn gen_rust_reference_to_js( ast::TypeName::Option(underlying) => match underlying.as_ref() { ast::TypeName::Box(_) => { // TODO(shadaj): return null if pointer is 0 - gen_rust_reference_to_js(underlying.as_ref(), value_expr, env, out)?; + gen_rust_reference_to_js(underlying.as_ref(), in_path, value_expr, env, out)?; } _ => todo!(), }, ast::TypeName::Named(_) => { - let custom_type = underlying.resolve(env); + let custom_type = underlying.resolve(in_path, env); writeln!(out, "(() => {{")?; let mut iife_indent = indented(out).with_str(" "); write!(&mut iife_indent, "const out = new {}(", custom_type.name())?; diff --git a/tool/src/layout.rs b/tool/src/layout.rs index 470c50865..bf802ebad 100644 --- a/tool/src/layout.rs +++ b/tool/src/layout.rs @@ -8,14 +8,15 @@ use diplomat_core::ast; pub fn struct_size_offsets_max_align( strct: &ast::Struct, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, ) -> (usize, Vec, usize) { let mut max_align = 0; let mut next_offset = 0; let mut offsets = vec![]; for (_, typ, _) in &strct.fields { - let (size, align) = type_size_alignment(typ, env); + let (size, align) = type_size_alignment(typ, in_path, env); max_align = max(max_align, align); let padding = (align - (next_offset % align)) % align; next_offset += padding; @@ -28,18 +29,19 @@ pub fn struct_size_offsets_max_align( pub fn type_size_alignment( typ: &ast::TypeName, - env: &HashMap, + in_path: &ast::Path, + env: &HashMap>, ) -> (usize, usize) { match typ { ast::TypeName::Box(_) => (4, 4), ast::TypeName::Reference(_, _) => (4, 4), ast::TypeName::Option(underlying) => match underlying.as_ref() { - ast::TypeName::Box(_) => type_size_alignment(underlying.as_ref(), env), + ast::TypeName::Box(_) => type_size_alignment(underlying.as_ref(), in_path, env), _ => todo!(), }, - ast::TypeName::Named(_) => match typ.resolve(env) { + ast::TypeName::Named(_) => match typ.resolve(in_path, env) { ast::CustomType::Struct(strct) => { - let (size, _, max_align) = struct_size_offsets_max_align(strct, env); + let (size, _, max_align) = struct_size_offsets_max_align(strct, in_path, env); (size, max_align) } diff --git a/tool/src/main.rs b/tool/src/main.rs index e71b3c3f4..3e99edc0c 100644 --- a/tool/src/main.rs +++ b/tool/src/main.rs @@ -7,11 +7,13 @@ mod c; mod cpp; mod js; mod layout; +mod util; fn main() -> std::io::Result<()> { let lib_file = syn_inline_mod::parse_and_inline_modules(Path::new("./src/lib.rs")); let custom_types = ast::File::from(&lib_file); let env = custom_types.all_types(); + let mut opaque_errors = vec![]; custom_types.check_opaque(&env, &mut opaque_errors); if !opaque_errors.is_empty() { diff --git a/tool/src/util.rs b/tool/src/util.rs new file mode 100644 index 000000000..34c454903 --- /dev/null +++ b/tool/src/util.rs @@ -0,0 +1,19 @@ +use std::collections::HashMap; + +use diplomat_core::ast; + +pub fn get_all_custom_types( + env: &HashMap>, +) -> Vec<(&ast::Path, &ast::CustomType)> { + let mut all_types: Vec<(&ast::Path, &ast::CustomType)> = vec![]; + + for (path, mod_symbols) in env { + for symbol in mod_symbols.values() { + if let ast::ModSymbol::CustomType(c) = symbol { + all_types.push((&path, &c)); + } + } + } + + all_types +}