From f140f3dd637f4f42bd1d3cdad254c77cb432463b Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:55:34 +0530 Subject: [PATCH 01/20] Adds FFI macro to generate a yaml file for each class --- rust/.cargo/config.toml | 2 + rust/.gitignore | 3 + rust/Cargo.lock | 88 +++++++--- rust/Cargo.toml | 1 + rust/tw_macros/Cargo.toml | 15 ++ rust/tw_macros/src/lib.rs | 12 ++ rust/tw_macros/src/tw_ffi.rs | 151 ++++++++++++++++++ rust/wallet_core_rs/Cargo.toml | 1 + .../src/ffi/ton/address_converter.rs | 4 + 9 files changed, 255 insertions(+), 22 deletions(-) create mode 100644 rust/.cargo/config.toml create mode 100644 rust/tw_macros/Cargo.toml create mode 100644 rust/tw_macros/src/lib.rs create mode 100644 rust/tw_macros/src/tw_ffi.rs diff --git a/rust/.cargo/config.toml b/rust/.cargo/config.toml new file mode 100644 index 00000000000..47684146c24 --- /dev/null +++ b/rust/.cargo/config.toml @@ -0,0 +1,2 @@ +[env] +CARGO_WORKSPACE_DIR = { value = "", relative = true } diff --git a/rust/.gitignore b/rust/.gitignore index bca6f9b85aa..d963b2c89d3 100644 --- a/rust/.gitignore +++ b/rust/.gitignore @@ -5,3 +5,6 @@ target/ # These are backup files generated by rustfmt **/*.rs.bk + +# Generated by tw_macros +bindings/ diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3b6ce25bf99..569a19651bc 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -314,7 +314,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.96", "syn_derive", ] @@ -531,7 +531,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.96", ] [[package]] @@ -567,6 +567,17 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "derive-syn-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "derive_arbitrary" version = "1.3.0" @@ -750,9 +761,9 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -839,9 +850,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", @@ -1199,9 +1210,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -1223,9 +1234,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -1411,9 +1422,9 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.189" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -1429,13 +1440,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.189" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.96", ] [[package]] @@ -1457,7 +1468,20 @@ checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.96", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", ] [[package]] @@ -1540,7 +1564,7 @@ checksum = "e6dc88f1f470d9de1001ffbb90d2344c9dd1a615f5467daf0574e2975dfd9ebd" dependencies = [ "starknet-curve", "starknet-ff", - "syn 2.0.37", + "syn 2.0.96", ] [[package]] @@ -1594,7 +1618,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.37", + "syn 2.0.96", ] [[package]] @@ -1616,9 +1640,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -1634,7 +1658,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.96", ] [[package]] @@ -2029,11 +2053,24 @@ dependencies = [ "tw_encoding", "tw_hash", "tw_keypair", + "tw_macros", "tw_memory", "tw_misc", "zeroize", ] +[[package]] +name = "tw_macros" +version = "0.1.0" +dependencies = [ + "derive-syn-parse", + "proc-macro2", + "quote", + "serde", + "serde_yaml", + "syn 2.0.96", +] + [[package]] name = "tw_memory" version = "0.1.0" @@ -2286,6 +2323,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "uuid" version = "1.7.0" @@ -2319,6 +2362,7 @@ dependencies = [ "tw_ethereum", "tw_hash", "tw_keypair", + "tw_macros", "tw_memory", "tw_misc", "tw_number", @@ -2469,5 +2513,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.96", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 692beec2692..95e876acbed 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -28,6 +28,7 @@ members = [ "tw_evm", "tw_hash", "tw_keypair", + "tw_macros", "tw_memory", "tw_misc", "tw_number", diff --git a/rust/tw_macros/Cargo.toml b/rust/tw_macros/Cargo.toml new file mode 100644 index 00000000000..8a0343c15be --- /dev/null +++ b/rust/tw_macros/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "tw_macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +derive-syn-parse = "0.2.0" +proc-macro2 = "1.0.93" +quote = "1.0.38" +serde = { version = "1.0", features = ["derive"] } +serde_yaml = "0.9.21" +syn = { version = "2.0.96", features = ["full"] } diff --git a/rust/tw_macros/src/lib.rs b/rust/tw_macros/src/lib.rs new file mode 100644 index 00000000000..26e59f3ee90 --- /dev/null +++ b/rust/tw_macros/src/lib.rs @@ -0,0 +1,12 @@ +use proc_macro::TokenStream; + +mod tw_ffi; + +#[proc_macro_attribute] +pub fn tw_ffi(attr: TokenStream, item: TokenStream) -> TokenStream { + match tw_ffi::tw_ffi(attr.into(), item.into()) { + Ok(item) => item.into(), + Err(e) => e.to_compile_error().into(), + } +} + diff --git a/rust/tw_macros/src/tw_ffi.rs b/rust/tw_macros/src/tw_ffi.rs new file mode 100644 index 00000000000..8d3ca7617da --- /dev/null +++ b/rust/tw_macros/src/tw_ffi.rs @@ -0,0 +1,151 @@ +use derive_syn_parse::Parse; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::{parse::{Parse, ParseStream}, parse2, parse_quote, Ident, Result, Token}; +use serde::{Deserialize, Serialize}; + +use std::env; +use std::fs; +use std::path::Path; +use std::path::PathBuf; +pub mod keywords { + use syn::custom_keyword; + + custom_keyword!(ty); + custom_keyword!(class); + custom_keyword!(name); + custom_keyword!(static_function); +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum TWFFIType { + StaticFunction, +} + +impl Parse for TWFFIType { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::static_function) { + input.parse::()?; + Ok(Self::StaticFunction) + } else { + Err(lookahead.error()) + } + } +} + +impl ToString for TWFFIType { + fn to_string(&self) -> String { + match self { + TWFFIType::StaticFunction => "static_function".to_string(), + } + } +} + +#[derive(Parse, Clone)] +pub struct TWFFIAttrArgs { + pub ty_keyword: Option, + #[parse_if(ty_keyword.is_some())] + pub _eq: Option, + #[parse_if(ty_keyword.is_some())] + pub ty: Option, + #[parse_if(ty_keyword.is_some())] + pub _comma: Option, + + pub class_keyword: Option, + #[parse_if(class_keyword.is_some())] + pub _eq2: Option, + #[parse_if(class_keyword.is_some())] + pub class: Option, + #[parse_if(class_keyword.is_some())] + pub _comma2: Option, + + pub name_keyword: Option, + #[parse_if(name_keyword.is_some())] + pub _eq3: Option, + #[parse_if(name_keyword.is_some())] + pub name: Option, +} + + +#[derive(Deserialize, Serialize, Debug)] +pub struct TWConfig { + pub class: String, + pub static_functions: Vec, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct TWStaticFunction { + pub name: String, + pub rust_name: String, + pub args: Vec, + pub return_type: String, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct TWArg { + pub name: String, + pub ty: String, +} + +pub fn tw_ffi(attr: TokenStream2, item: TokenStream2) -> Result { + let args = parse2::(attr)?; + + let func = parse2::(item.clone())?; + let func_name = func.sig.ident.to_string(); + let func_args = func.sig.inputs.iter().map(|arg| match arg { + syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => (quote!( #pat ), quote!( #ty )), + _ => (quote!(), quote!()), + }) + .map(|(name, ty)| TWArg { + name: name.to_string(), + ty: ty.to_string(), + }) + .collect::>(); + + let return_type = func.sig.output; + let return_type = match return_type { + syn::ReturnType::Type(_, ty) => quote!(#ty).to_string(), + _ => "void".to_string(), + }; + + let class = args.class.unwrap().to_string(); + let static_function = TWStaticFunction { + name: args.name.unwrap().to_string(), + rust_name: func_name, + args: func_args, + return_type, + }; + + if let Ok(out_dir) = env::var("CARGO_WORKSPACE_DIR") { + let bindings_dir = Path::new(&out_dir).join("bindings"); + fs::create_dir_all(&bindings_dir).expect("Failed to create bindings directory"); + let yaml_file_path = bindings_dir.join(format!("{}.yaml", class)); + + let mut config = if yaml_file_path.exists() { + serde_yaml::from_str(&fs::read_to_string(&yaml_file_path).expect("Failed to read existing YAML file")).expect("Failed to parse YAML file") + } else { + TWConfig { + class, + static_functions: vec![], + } + }; + config.static_functions.push(static_function); + + let yaml_output: String = serde_yaml::to_string(&config).expect("Failed to serialize to YAML"); + fs::write(&yaml_file_path, yaml_output).expect("Failed to write YAML file"); + } else { + panic!("CARGO_WORKSPACE_DIR is not set"); + } + + Ok(item) +} + +#[test] +fn test_ffi_attr_arg_parsing() { + let args = parse2::(quote!{ + ty = static_function, + class = MyClass, + name = MyName + }).unwrap(); +} diff --git a/rust/wallet_core_rs/Cargo.toml b/rust/wallet_core_rs/Cargo.toml index 1b8f34f853a..c66a335b140 100644 --- a/rust/wallet_core_rs/Cargo.toml +++ b/rust/wallet_core_rs/Cargo.toml @@ -43,6 +43,7 @@ tw_hash = { path = "../tw_hash", optional = true } tw_keypair = { path = "../tw_keypair", optional = true } tw_memory = { path = "../tw_memory", optional = true } tw_number = { path = "../tw_number", optional = true } +tw_macros = { path = "../tw_macros" } tw_misc = { path = "../tw_misc" } tw_proto = { path = "../tw_proto", optional = true } tw_solana = { path = "../chains/tw_solana", optional = true } diff --git a/rust/wallet_core_rs/src/ffi/ton/address_converter.rs b/rust/wallet_core_rs/src/ffi/ton/address_converter.rs index f16ee89ac69..59c1f73e3ab 100644 --- a/rust/wallet_core_rs/src/ffi/ton/address_converter.rs +++ b/rust/wallet_core_rs/src/ffi/ton/address_converter.rs @@ -5,6 +5,7 @@ #![allow(clippy::missing_safety_doc)] use std::str::FromStr; +use tw_macros::tw_ffi; use tw_memory::ffi::tw_string::TWString; use tw_memory::ffi::RawPtrTrait; use tw_misc::try_or_else; @@ -17,6 +18,7 @@ use tw_ton::modules::address_converter::AddressConverter; /// /// \param address Address to be converted into a Bag Of Cells (BoC). /// \return Pointer to a base64 encoded Bag Of Cells (BoC). Null if invalid address provided. +#[tw_ffi(ty = static_function, class = TWTONAddressConverter, name = ToBoc)] #[no_mangle] pub unsafe extern "C" fn tw_ton_address_converter_to_boc( address: *const TWString, @@ -39,6 +41,7 @@ pub unsafe extern "C" fn tw_ton_address_converter_to_boc( /// /// \param boc Base64 encoded Bag Of Cells (BoC). /// \return Pointer to a Jetton address. +#[tw_ffi(ty = static_function, class = TWTONAddressConverter, name = FromBoc)] #[no_mangle] pub unsafe extern "C" fn tw_ton_address_converter_from_boc(boc: *const TWString) -> *mut TWString { let boc = try_or_else!(TWString::from_ptr_as_ref(boc), std::ptr::null_mut); @@ -58,6 +61,7 @@ pub unsafe extern "C" fn tw_ton_address_converter_from_boc(boc: *const TWString) /// \param bounceable whether the result address should be bounceable. /// \param testnet whether the result address should be testnet. /// \return user-friendly address str. +#[tw_ffi(ty = static_function, class = TWTONAddressConverter, name = ToUserFriendly)] #[no_mangle] pub unsafe extern "C" fn tw_ton_address_converter_to_user_friendly( address: *const TWString, From 19ee48607e9af119014e7ac54be4e37698d6fc58 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:35:08 +0530 Subject: [PATCH 02/20] Adds generator --- .gitignore | 2 + codegen-v2/Cargo.lock | 54 ++++++- codegen-v2/Cargo.toml | 2 + codegen-v2/src/codegen/cpp_v2/mod.rs | 137 ++++++++++++++++++ codegen-v2/src/codegen/mod.rs | 1 + codegen-v2/src/main.rs | 2 + .../TrustWalletCore/TWTONAddressConverter.h | 43 ------ rust/Cargo.lock | 4 +- rust/tw_macros/Cargo.toml | 1 + rust/tw_macros/src/tw_ffi.rs | 58 +++----- rust/tw_misc/src/code_gen.rs | 21 +++ rust/tw_misc/src/lib.rs | 2 + src/interface/TWTONAddressConverter.cpp | 46 ------ .../TWTONAddressConverterTests.cpp | 2 +- 14 files changed, 239 insertions(+), 136 deletions(-) create mode 100644 codegen-v2/src/codegen/cpp_v2/mod.rs delete mode 100644 include/TrustWalletCore/TWTONAddressConverter.h create mode 100644 rust/tw_misc/src/code_gen.rs delete mode 100644 src/interface/TWTONAddressConverter.cpp diff --git a/.gitignore b/.gitignore index 7b093f9e1a3..51ed57ead5f 100644 --- a/.gitignore +++ b/.gitignore @@ -36,7 +36,9 @@ swift/wallet-core/ codegen-v2/bindings/ src/Generated/*.cpp +src/interface/*Gen.cpp include/TrustWalletCore/TWHRP.h +include/TrustWalletCore/TW*Gen.h include/TrustWalletCore/TW*Proto.h include/TrustWalletCore/TWEthereumChainID.h diff --git a/codegen-v2/Cargo.lock b/codegen-v2/Cargo.lock index 6ed11b1c48a..607d74e6f2d 100644 --- a/codegen-v2/Cargo.lock +++ b/codegen-v2/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -39,6 +39,8 @@ dependencies = [ "serde_json", "serde_yaml", "toml_edit", + "tw_macros", + "tw_misc", ] [[package]] @@ -69,6 +71,17 @@ dependencies = [ "typenum", ] +[[package]] +name = "derive-syn-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.10.7" @@ -214,18 +227,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -293,9 +306,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -339,6 +352,27 @@ dependencies = [ "winnow", ] +[[package]] +name = "tw_macros" +version = "0.1.0" +dependencies = [ + "derive-syn-parse", + "proc-macro2", + "quote", + "serde", + "serde_yaml", + "syn", + "tw_misc", +] + +[[package]] +name = "tw_misc" +version = "0.1.0" +dependencies = [ + "serde", + "zeroize", +] + [[package]] name = "typenum" version = "1.17.0" @@ -383,3 +417,9 @@ checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/codegen-v2/Cargo.toml b/codegen-v2/Cargo.toml index 43c2afea1c6..48a6fbf6d58 100644 --- a/codegen-v2/Cargo.toml +++ b/codegen-v2/Cargo.toml @@ -21,3 +21,5 @@ serde_yaml = "0.9.21" toml_edit = "0.21.0" handlebars = "4.3.6" heck = "0.4.1" +tw_macros = { path = "../rust/tw_macros" } +tw_misc = { path = "../rust/tw_misc", features = ["serde"] } diff --git a/codegen-v2/src/codegen/cpp_v2/mod.rs b/codegen-v2/src/codegen/cpp_v2/mod.rs new file mode 100644 index 00000000000..8360a860ab2 --- /dev/null +++ b/codegen-v2/src/codegen/cpp_v2/mod.rs @@ -0,0 +1,137 @@ +use std::fs; +use std::io::Write; +use tw_misc::code_gen::TWConfig; + +use crate::Result; +fn map_type(ty: &str) -> String { + match ty.trim() { + s if s.starts_with("* const") => format!("{} *_Nonnull", &s[8..]), + s if s.starts_with("* mut") => format!("{} *_Nullable", &s[6..]), + "bool" => "bool".to_string(), + _ => ty.to_string(), + } +} + +fn generate_conversion_code(ty: &str, name: &str) -> (String, String) { + match ty { + "TWString *_Nonnull" => { + let code = format!(" auto& {name}String = *reinterpret_cast({name});\n", name=name) + + format!(" const Rust::TWStringWrapper {name}RustStr = {name}String;\n", name=name).as_str(); + (code, format!("{}RustStr.get()", name)) + }, + _ => ("".to_string(), name.to_string()), + } +} + +pub fn generate_cpp_bindings() -> Result<()> { + const IN_DIR: &str = "../rust/bindings/"; + const HEADER_OUT_DIR: &str = "../include/TrustWalletCore/"; + const SOURCE_OUT_DIR: &str = "../src/interface/"; + + std::fs::create_dir_all(HEADER_OUT_DIR)?; + std::fs::create_dir_all(SOURCE_OUT_DIR)?; + + let entries = fs::read_dir(IN_DIR)?; + + for entry in entries { + let entry = entry?; + let file_path = entry.path(); + + if file_path.is_dir() { + println!("Found unexpected directory: {}", file_path.display()); + continue; + } + + let file_contents = fs::read_to_string(&file_path)?; + + let info: TWConfig = serde_yaml::from_str(&file_contents).expect("Failed to parse YAML file"); + + let file_path = format!("{HEADER_OUT_DIR}/{}Gen.h", info.class); + + let mut file = std::fs::File::create(&file_path)?; + writeln!(file, "// Copyright © 2017 Trust Wallet.\n")?; + writeln!(file, "#pragma once\n")?; + writeln!(file, "#include \"TWBase.h\"")?; + + // Include headers based on argument types + let mut included_headers = std::collections::HashSet::new(); + for func in &info.static_functions { + for arg in &func.args { + if arg.ty.contains("TWString") && included_headers.insert("TWString.h") { + writeln!(file, "#include \"TWString.h\"")?; + } + // Additional type checks can be added here in the future + } + } + + writeln!(file, "\nTW_EXTERN_C_BEGIN\n")?; + + let class_name = format!("{}", info.class); + let class_dec = format!("TW_EXPORT_CLASS\nstruct {};\n", class_name.clone()); + writeln!(file, "{}", class_dec)?; + + for func in &info.static_functions { + let return_type = map_type(&func.return_type); + let mut func_dec = format!("TW_EXPORT_STATIC_METHOD\n{} {}(", return_type, class_name.clone() + &func.name); + for (i, arg) in func.args.iter().enumerate() { + let func_type = map_type(&arg.ty); + func_dec += format!("{} {}", func_type, arg.name).as_str(); + if i < func.args.len() - 1 { + func_dec += ", "; + } + } + func_dec += ");\n"; + writeln!(file, "{}", func_dec)?; + } + + writeln!(file, "TW_EXTERN_C_END")?; + + file.flush()?; + + let file_path = format!("{SOURCE_OUT_DIR}/{}Gen.cpp", info.class); + + let mut file = std::fs::File::create(&file_path)?; + writeln!(file, "// Copyright © 2017 Trust Wallet.\n")?; + writeln!(file, "{}", format!("#include ", info.class))?; + writeln!(file, "#include \"rust/Wrapper.h\"")?; + + writeln!(file, "\nusing namespace TW;\n")?; + + for func in &info.static_functions { + let return_type = map_type(&func.return_type); + let mut func_dec = format!("{} {}(", return_type, class_name.clone() + &func.name); + let mut conversion_code = vec![]; + for (i, arg) in func.args.iter().enumerate() { + let func_type = map_type(&arg.ty); + let code_with_name = generate_conversion_code(&func_type, &arg.name); + conversion_code.push(code_with_name); + func_dec += format!("{} {}", func_type, arg.name).as_str(); + if i < func.args.len() - 1 { + func_dec += ", "; + } + } + func_dec += ") {\n"; + for code in &conversion_code { + func_dec += code.0.as_str(); + } + if return_type == "TWString *_Nullable" { + func_dec += format!(" const Rust::TWStringWrapper result = Rust::{}(", func.rust_name).as_str(); + for (i, arg) in conversion_code.iter().enumerate() { + func_dec += format!("{}", arg.1).as_str(); + if i < conversion_code.len() - 1 { + func_dec += ", "; + } + } + func_dec += ");\n"; + func_dec += format!(" if (!result) return nullptr;\n").as_str(); + func_dec += format!(" return TWStringCreateWithUTF8Bytes(result.c_str());\n").as_str(); + } + func_dec += "}\n"; + writeln!(file, "{}", func_dec)?; + } + + file.flush()?; + } + + Ok(()) +} diff --git a/codegen-v2/src/codegen/mod.rs b/codegen-v2/src/codegen/mod.rs index 1d8522e6b9f..3453d2bbb20 100644 --- a/codegen-v2/src/codegen/mod.rs +++ b/codegen-v2/src/codegen/mod.rs @@ -3,6 +3,7 @@ // Copyright © 2017 Trust Wallet. pub mod cpp; +pub mod cpp_v2; pub mod proto; pub mod rust; pub mod swift; diff --git a/codegen-v2/src/main.rs b/codegen-v2/src/main.rs index 53fd140cc65..b726773a60b 100644 --- a/codegen-v2/src/main.rs +++ b/codegen-v2/src/main.rs @@ -2,6 +2,7 @@ // // Copyright © 2017 Trust Wallet. +use libparser::codegen::cpp_v2::generate_cpp_bindings; use libparser::codegen::swift::RenderIntput; use libparser::codegen::{cpp, proto, rust}; use libparser::coin_id::CoinId; @@ -23,6 +24,7 @@ fn main() -> Result<()> { "new-evmchain" => new_evmchain(&args[2..]), "new-cosmos-chain" => new_cosmos_chain(&args[2..]), "swift" => generate_swift_bindings(), + "cpp" => generate_cpp_bindings(), _ => Err(Error::InvalidCommand), } } diff --git a/include/TrustWalletCore/TWTONAddressConverter.h b/include/TrustWalletCore/TWTONAddressConverter.h deleted file mode 100644 index 39bb4dfed7e..00000000000 --- a/include/TrustWalletCore/TWTONAddressConverter.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "TWBase.h" -#include "TWString.h" - -TW_EXTERN_C_BEGIN - -/// TON address operations. -TW_EXPORT_CLASS -struct TWTONAddressConverter; - -/// Converts a TON user address into a Bag of Cells (BoC) with a single root Cell. -/// The function is mostly used to request a Jetton user address via `get_wallet_address` RPC. -/// https://docs.ton.org/develop/dapps/asset-processing/jettons#retrieving-jetton-wallet-addresses-for-a-given-user -/// -/// \param address Address to be converted into a Bag Of Cells (BoC). -/// \return Pointer to a base64 encoded Bag Of Cells (BoC). Null if invalid address provided. -TW_EXPORT_STATIC_METHOD -TWString *_Nullable TWTONAddressConverterToBoc(TWString *_Nonnull address); - -/// Parses a TON address from a Bag of Cells (BoC) with a single root Cell. -/// The function is mostly used to parse a Jetton user address received on `get_wallet_address` RPC. -/// https://docs.ton.org/develop/dapps/asset-processing/jettons#retrieving-jetton-wallet-addresses-for-a-given-user -/// -/// \param boc Base64 encoded Bag Of Cells (BoC). -/// \return Pointer to a Jetton address. -TW_EXPORT_STATIC_METHOD -TWString *_Nullable TWTONAddressConverterFromBoc(TWString *_Nonnull boc); - -/// Converts any TON address format to user friendly with the given parameters. -/// -/// \param address raw or user-friendly address to be converted. -/// \param bounceable whether the result address should be bounceable. -/// \param testnet whether the result address should be testnet. -/// \return user-friendly address str. -TW_EXPORT_STATIC_METHOD -TWString *_Nullable TWTONAddressConverterToUserFriendly(TWString *_Nonnull address, bool bounceable, bool testnet); - -TW_EXTERN_C_END diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 569a19651bc..a0eadf18006 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aead" @@ -2053,7 +2053,6 @@ dependencies = [ "tw_encoding", "tw_hash", "tw_keypair", - "tw_macros", "tw_memory", "tw_misc", "zeroize", @@ -2069,6 +2068,7 @@ dependencies = [ "serde", "serde_yaml", "syn 2.0.96", + "tw_misc", ] [[package]] diff --git a/rust/tw_macros/Cargo.toml b/rust/tw_macros/Cargo.toml index 8a0343c15be..9bfe4c0ea71 100644 --- a/rust/tw_macros/Cargo.toml +++ b/rust/tw_macros/Cargo.toml @@ -13,3 +13,4 @@ quote = "1.0.38" serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9.21" syn = { version = "2.0.96", features = ["full"] } +tw_misc = { path = "../tw_misc", features = ["serde"] } diff --git a/rust/tw_macros/src/tw_ffi.rs b/rust/tw_macros/src/tw_ffi.rs index 8d3ca7617da..d3efffc903f 100644 --- a/rust/tw_macros/src/tw_ffi.rs +++ b/rust/tw_macros/src/tw_ffi.rs @@ -1,13 +1,14 @@ use derive_syn_parse::Parse; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use syn::{parse::{Parse, ParseStream}, parse2, parse_quote, Ident, Result, Token}; -use serde::{Deserialize, Serialize}; +use syn::{parse::{Parse, ParseStream}, parse2, Ident, Result, Token}; use std::env; use std::fs; use std::path::Path; -use std::path::PathBuf; + +use tw_misc::code_gen::{TWConfig, TWStaticFunction, TWArg}; + pub mod keywords { use syn::custom_keyword; @@ -44,50 +45,29 @@ impl ToString for TWFFIType { #[derive(Parse, Clone)] pub struct TWFFIAttrArgs { - pub ty_keyword: Option, - #[parse_if(ty_keyword.is_some())] + pub _ty_keyword: Option, + #[parse_if(_ty_keyword.is_some())] pub _eq: Option, - #[parse_if(ty_keyword.is_some())] - pub ty: Option, - #[parse_if(ty_keyword.is_some())] + #[parse_if(_ty_keyword.is_some())] + pub _ty: Option, + #[parse_if(_ty_keyword.is_some())] pub _comma: Option, - pub class_keyword: Option, - #[parse_if(class_keyword.is_some())] + pub _class_keyword: Option, + #[parse_if(_class_keyword.is_some())] pub _eq2: Option, - #[parse_if(class_keyword.is_some())] + #[parse_if(_class_keyword.is_some())] pub class: Option, - #[parse_if(class_keyword.is_some())] + #[parse_if(_class_keyword.is_some())] pub _comma2: Option, - pub name_keyword: Option, - #[parse_if(name_keyword.is_some())] + pub _name_keyword: Option, + #[parse_if(_name_keyword.is_some())] pub _eq3: Option, - #[parse_if(name_keyword.is_some())] + #[parse_if(_name_keyword.is_some())] pub name: Option, } - -#[derive(Deserialize, Serialize, Debug)] -pub struct TWConfig { - pub class: String, - pub static_functions: Vec, -} - -#[derive(Deserialize, Serialize, Debug)] -pub struct TWStaticFunction { - pub name: String, - pub rust_name: String, - pub args: Vec, - pub return_type: String, -} - -#[derive(Deserialize, Serialize, Debug)] -pub struct TWArg { - pub name: String, - pub ty: String, -} - pub fn tw_ffi(attr: TokenStream2, item: TokenStream2) -> Result { let args = parse2::(attr)?; @@ -130,7 +110,11 @@ pub fn tw_ffi(attr: TokenStream2, item: TokenStream2) -> Result { static_functions: vec![], } }; - config.static_functions.push(static_function); + if let Some(idx) = config.static_functions.iter().position(|f| f.name == static_function.name) { + config.static_functions[idx] = static_function; + } else { + config.static_functions.push(static_function); + } let yaml_output: String = serde_yaml::to_string(&config).expect("Failed to serialize to YAML"); fs::write(&yaml_file_path, yaml_output).expect("Failed to write YAML file"); diff --git a/rust/tw_misc/src/code_gen.rs b/rust/tw_misc/src/code_gen.rs new file mode 100644 index 00000000000..467a19ee147 --- /dev/null +++ b/rust/tw_misc/src/code_gen.rs @@ -0,0 +1,21 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Debug)] +pub struct TWConfig { + pub class: String, + pub static_functions: Vec, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct TWStaticFunction { + pub name: String, + pub rust_name: String, + pub args: Vec, + pub return_type: String, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct TWArg { + pub name: String, + pub ty: String, +} diff --git a/rust/tw_misc/src/lib.rs b/rust/tw_misc/src/lib.rs index eeccb7e62b0..75aa740c826 100644 --- a/rust/tw_misc/src/lib.rs +++ b/rust/tw_misc/src/lib.rs @@ -2,6 +2,8 @@ // // Copyright © 2017 Trust Wallet. +#[cfg(feature = "serde")] +pub mod code_gen; pub mod macros; #[cfg(feature = "serde")] pub mod serde; diff --git a/src/interface/TWTONAddressConverter.cpp b/src/interface/TWTONAddressConverter.cpp deleted file mode 100644 index 4113448ad24..00000000000 --- a/src/interface/TWTONAddressConverter.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include - -#include "Base64.h" -#include "rust/Wrapper.h" - -using namespace TW; - -TWString *_Nullable TWTONAddressConverterToBoc(TWString *_Nonnull address) { - auto& addressString = *reinterpret_cast(address); - - const Rust::TWStringWrapper addressRustStr = addressString; - const Rust::TWStringWrapper bocRustStr = Rust::tw_ton_address_converter_to_boc(addressRustStr.get()); - if (!bocRustStr) { - return nullptr; - } - - return TWStringCreateWithUTF8Bytes(bocRustStr.c_str()); -} - -TWString *_Nullable TWTONAddressConverterFromBoc(TWString *_Nonnull boc) { - auto& bocEncoded = *reinterpret_cast(boc); - - const Rust::TWStringWrapper bocRustStr = bocEncoded; - const Rust::TWStringWrapper addressRustStr = Rust::tw_ton_address_converter_from_boc(bocRustStr.get()); - if (!addressRustStr) { - return nullptr; - } - - return TWStringCreateWithUTF8Bytes(addressRustStr.c_str()); -} - -TWString *_Nullable TWTONAddressConverterToUserFriendly(TWString *_Nonnull address, bool bounceable, bool testnet) { - auto& addressString = *reinterpret_cast(address); - - const Rust::TWStringWrapper addressRustStr = addressString; - const Rust::TWStringWrapper userFriendlyRustStr = Rust::tw_ton_address_converter_to_user_friendly(addressRustStr.get(), bounceable, testnet); - if (!userFriendlyRustStr) { - return nullptr; - } - - return TWStringCreateWithUTF8Bytes(userFriendlyRustStr.c_str()); -} diff --git a/tests/chains/TheOpenNetwork/TWTONAddressConverterTests.cpp b/tests/chains/TheOpenNetwork/TWTONAddressConverterTests.cpp index 01113313ce8..734ae470a03 100644 --- a/tests/chains/TheOpenNetwork/TWTONAddressConverterTests.cpp +++ b/tests/chains/TheOpenNetwork/TWTONAddressConverterTests.cpp @@ -4,7 +4,7 @@ #include "TestUtilities.h" -#include "TrustWalletCore/TWTONAddressConverter.h" +#include "TrustWalletCore/TWTONAddressConverterGen.h" namespace TW::TheOpenNetwork::tests { From 431877c8155a327bd8d73d164b64287beb9e86b6 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Tue, 21 Jan 2025 12:57:35 +0530 Subject: [PATCH 03/20] Integrates with rust bindgen --- .gitignore | 3 +-- CMakeLists.txt | 1 + codegen-v2/src/codegen/cpp_v2/mod.rs | 23 +++++++++++++------ codegen/bin/codegen | 18 ++++++++++----- .../TWTONAddressConverterTests.cpp | 2 +- tools/rust-bindgen | 4 ++++ 6 files changed, 35 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 51ed57ead5f..77521831c5e 100644 --- a/.gitignore +++ b/.gitignore @@ -36,9 +36,8 @@ swift/wallet-core/ codegen-v2/bindings/ src/Generated/*.cpp -src/interface/*Gen.cpp +include/Generated/TrustWalletCore/*.h include/TrustWalletCore/TWHRP.h -include/TrustWalletCore/TW*Gen.h include/TrustWalletCore/TW*Proto.h include/TrustWalletCore/TWEthereumChainID.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6fec5513326..63c1444f57b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,7 @@ endif () target_include_directories(TrustWalletCore PUBLIC $ + $ $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src diff --git a/codegen-v2/src/codegen/cpp_v2/mod.rs b/codegen-v2/src/codegen/cpp_v2/mod.rs index 8360a860ab2..2707695ab95 100644 --- a/codegen-v2/src/codegen/cpp_v2/mod.rs +++ b/codegen-v2/src/codegen/cpp_v2/mod.rs @@ -25,8 +25,8 @@ fn generate_conversion_code(ty: &str, name: &str) -> (String, String) { pub fn generate_cpp_bindings() -> Result<()> { const IN_DIR: &str = "../rust/bindings/"; - const HEADER_OUT_DIR: &str = "../include/TrustWalletCore/"; - const SOURCE_OUT_DIR: &str = "../src/interface/"; + const HEADER_OUT_DIR: &str = "../include/Generated/TrustWalletCore/"; + const SOURCE_OUT_DIR: &str = "../src/Generated/"; std::fs::create_dir_all(HEADER_OUT_DIR)?; std::fs::create_dir_all(SOURCE_OUT_DIR)?; @@ -46,19 +46,19 @@ pub fn generate_cpp_bindings() -> Result<()> { let info: TWConfig = serde_yaml::from_str(&file_contents).expect("Failed to parse YAML file"); - let file_path = format!("{HEADER_OUT_DIR}/{}Gen.h", info.class); + let file_path = format!("{HEADER_OUT_DIR}/{}.h", info.class); let mut file = std::fs::File::create(&file_path)?; writeln!(file, "// Copyright © 2017 Trust Wallet.\n")?; writeln!(file, "#pragma once\n")?; - writeln!(file, "#include \"TWBase.h\"")?; + writeln!(file, "#include ")?; // Include headers based on argument types let mut included_headers = std::collections::HashSet::new(); for func in &info.static_functions { for arg in &func.args { if arg.ty.contains("TWString") && included_headers.insert("TWString.h") { - writeln!(file, "#include \"TWString.h\"")?; + writeln!(file, "#include ")?; } // Additional type checks can be added here in the future } @@ -88,11 +88,11 @@ pub fn generate_cpp_bindings() -> Result<()> { file.flush()?; - let file_path = format!("{SOURCE_OUT_DIR}/{}Gen.cpp", info.class); + let file_path = format!("{SOURCE_OUT_DIR}/{}.cpp", info.class); let mut file = std::fs::File::create(&file_path)?; writeln!(file, "// Copyright © 2017 Trust Wallet.\n")?; - writeln!(file, "{}", format!("#include ", info.class))?; + writeln!(file, "{}", format!("#include ", info.class))?; writeln!(file, "#include \"rust/Wrapper.h\"")?; writeln!(file, "\nusing namespace TW;\n")?; @@ -125,6 +125,15 @@ pub fn generate_cpp_bindings() -> Result<()> { func_dec += ");\n"; func_dec += format!(" if (!result) return nullptr;\n").as_str(); func_dec += format!(" return TWStringCreateWithUTF8Bytes(result.c_str());\n").as_str(); + } else { + func_dec += format!(" return Rust::{}(", func.rust_name).as_str(); + for (i, arg) in conversion_code.iter().enumerate() { + func_dec += format!("{}", arg.1).as_str(); + if i < conversion_code.len() - 1 { + func_dec += ", "; + } + } + func_dec += ");\n"; } func_dec += "}\n"; writeln!(file, "{}", func_dec)?; diff --git a/codegen/bin/codegen b/codegen/bin/codegen index 5cd99179bb8..84e2230087b 100755 --- a/codegen/bin/codegen +++ b/codegen/bin/codegen @@ -15,6 +15,7 @@ Encoding.default_internal = Encoding::UTF_8 options = OpenStruct.new # default input / output path options.input = "#{CurrentDir}/../../include/TrustWalletCore" +options.input_generated = "#{CurrentDir}/../../include/Generated/TrustWalletCore" options.output = "#{CurrentDir}/../../" options.swift = true options.java = true @@ -62,14 +63,19 @@ end.parse! entities = [] files = [] -Dir.foreach(options.input) do |item| - next if ['.', '..', '.DS_Store'].include?(item) +file_paths = [] +[options.input, options.input_generated].each do |input_dir| + Dir.foreach(input_dir) do |item| + next if ['.', '..', '.DS_Store'].include?(item) - entity = Parser.new(path: File.expand_path(File.join(options.input, item))).parse - next if entity.nil? + file_path = File.expand_path(File.join(input_dir, item)) + file_paths << file_path + entity = Parser.new(path: file_path).parse + next if entity.nil? - entities << entity - files << File.basename(item, '.h').sub(/^TW/, '') + entities << entity + files << File.basename(item, '.h').sub(/^TW/, '') + end end generator = CodeGenerator.new(entities: entities, files: files, output_folder: options.output) diff --git a/tests/chains/TheOpenNetwork/TWTONAddressConverterTests.cpp b/tests/chains/TheOpenNetwork/TWTONAddressConverterTests.cpp index 734ae470a03..01113313ce8 100644 --- a/tests/chains/TheOpenNetwork/TWTONAddressConverterTests.cpp +++ b/tests/chains/TheOpenNetwork/TWTONAddressConverterTests.cpp @@ -4,7 +4,7 @@ #include "TestUtilities.h" -#include "TrustWalletCore/TWTONAddressConverterGen.h" +#include "TrustWalletCore/TWTONAddressConverter.h" namespace TW::TheOpenNetwork::tests { diff --git a/tools/rust-bindgen b/tools/rust-bindgen index 0edaf9c23a5..2ed44523398 100755 --- a/tools/rust-bindgen +++ b/tools/rust-bindgen @@ -72,6 +72,9 @@ cbindgen --crate $CRATE --output ../src/rust/bindgen/$HEADER_NAME cd - [[ -e rust/target/release/${TARGET_NAME} ]] && cp rust/target/release/${TARGET_NAME} build/local/lib/ +echo "Generating C++ files..." +pushd codegen-v2 && cargo run -- cpp && popd + if isTargetSpecified "ios" && [[ $(uname) == "Darwin" ]]; then cd rust cat > $TARGET_XCFRAMEWORK_NAME/Info.plist << EOF @@ -146,3 +149,4 @@ cat > $TARGET_XCFRAMEWORK_NAME/Info.plist << EOF EOF cd - fi + From 0e0791c1c1596f63b2d06c803649fe82b45be4ee Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Tue, 21 Jan 2025 13:09:27 +0530 Subject: [PATCH 04/20] Moves rust-bindgen before codegen --- tools/generate-files | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/generate-files b/tools/generate-files index de3abc7f906..a987d9c19f6 100755 --- a/tools/generate-files +++ b/tools/generate-files @@ -56,15 +56,15 @@ mkdir -p swift/Sources/Generated/Protobuf swift/Sources/Generated/Enums # Generate coins info from registry.json codegen/bin/coins +# Generate rust bindgen +tools/rust-bindgen "$@" + # Generate interface code, Swift bindings excluded. codegen/bin/codegen # Convert doxygen comments to appropriate format tools/doxygen_convert_comments -# Generate rust bindgen -tools/rust-bindgen "$@" - # Generate Java, C++ and Swift Protobuf files if [ -x "$(command -v protoc-gen-swift)" ] && isTargetSpecified "ios"; then From c6afd757f0e0b937bde20a311676d8b4ead50315 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Tue, 21 Jan 2025 13:09:46 +0530 Subject: [PATCH 05/20] Minor --- rust/Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 956fb44fe15..9367be57850 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2241,6 +2241,7 @@ dependencies = [ "tw_hash", "tw_keypair", "tw_memory", + "tw_misc", "tw_proto", ] From 434e2bf2f453906cb3bd7e1fa34aa55880512161 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Tue, 21 Jan 2025 13:18:33 +0530 Subject: [PATCH 06/20] FMT --- codegen-v2/src/codegen/cpp_v2/mod.rs | 44 +++++++++++----- codegen-v2/src/codegen/template_generator.rs | 9 +++- rust/tw_macros/src/lib.rs | 1 - rust/tw_macros/src/tw_ffi.rs | 54 +++++++++++++------- 4 files changed, 75 insertions(+), 33 deletions(-) diff --git a/codegen-v2/src/codegen/cpp_v2/mod.rs b/codegen-v2/src/codegen/cpp_v2/mod.rs index 2707695ab95..8e2c3f2fbb9 100644 --- a/codegen-v2/src/codegen/cpp_v2/mod.rs +++ b/codegen-v2/src/codegen/cpp_v2/mod.rs @@ -15,10 +15,16 @@ fn map_type(ty: &str) -> String { fn generate_conversion_code(ty: &str, name: &str) -> (String, String) { match ty { "TWString *_Nonnull" => { - let code = format!(" auto& {name}String = *reinterpret_cast({name});\n", name=name) + - format!(" const Rust::TWStringWrapper {name}RustStr = {name}String;\n", name=name).as_str(); + let code = format!( + " auto& {name}String = *reinterpret_cast({name});\n", + name = name + ) + format!( + " const Rust::TWStringWrapper {name}RustStr = {name}String;\n", + name = name + ) + .as_str(); (code, format!("{}RustStr.get()", name)) - }, + } _ => ("".to_string(), name.to_string()), } } @@ -27,7 +33,7 @@ pub fn generate_cpp_bindings() -> Result<()> { const IN_DIR: &str = "../rust/bindings/"; const HEADER_OUT_DIR: &str = "../include/Generated/TrustWalletCore/"; const SOURCE_OUT_DIR: &str = "../src/Generated/"; - + std::fs::create_dir_all(HEADER_OUT_DIR)?; std::fs::create_dir_all(SOURCE_OUT_DIR)?; @@ -44,7 +50,8 @@ pub fn generate_cpp_bindings() -> Result<()> { let file_contents = fs::read_to_string(&file_path)?; - let info: TWConfig = serde_yaml::from_str(&file_contents).expect("Failed to parse YAML file"); + let info: TWConfig = + serde_yaml::from_str(&file_contents).expect("Failed to parse YAML file"); let file_path = format!("{HEADER_OUT_DIR}/{}.h", info.class); @@ -52,7 +59,7 @@ pub fn generate_cpp_bindings() -> Result<()> { writeln!(file, "// Copyright © 2017 Trust Wallet.\n")?; writeln!(file, "#pragma once\n")?; writeln!(file, "#include ")?; - + // Include headers based on argument types let mut included_headers = std::collections::HashSet::new(); for func in &info.static_functions { @@ -72,7 +79,11 @@ pub fn generate_cpp_bindings() -> Result<()> { for func in &info.static_functions { let return_type = map_type(&func.return_type); - let mut func_dec = format!("TW_EXPORT_STATIC_METHOD\n{} {}(", return_type, class_name.clone() + &func.name); + let mut func_dec = format!( + "TW_EXPORT_STATIC_METHOD\n{} {}(", + return_type, + class_name.clone() + &func.name + ); for (i, arg) in func.args.iter().enumerate() { let func_type = map_type(&arg.ty); func_dec += format!("{} {}", func_type, arg.name).as_str(); @@ -87,12 +98,16 @@ pub fn generate_cpp_bindings() -> Result<()> { writeln!(file, "TW_EXTERN_C_END")?; file.flush()?; - + let file_path = format!("{SOURCE_OUT_DIR}/{}.cpp", info.class); let mut file = std::fs::File::create(&file_path)?; writeln!(file, "// Copyright © 2017 Trust Wallet.\n")?; - writeln!(file, "{}", format!("#include ", info.class))?; + writeln!( + file, + "{}", + format!("#include ", info.class) + )?; writeln!(file, "#include \"rust/Wrapper.h\"")?; writeln!(file, "\nusing namespace TW;\n")?; @@ -115,7 +130,11 @@ pub fn generate_cpp_bindings() -> Result<()> { func_dec += code.0.as_str(); } if return_type == "TWString *_Nullable" { - func_dec += format!(" const Rust::TWStringWrapper result = Rust::{}(", func.rust_name).as_str(); + func_dec += format!( + " const Rust::TWStringWrapper result = Rust::{}(", + func.rust_name + ) + .as_str(); for (i, arg) in conversion_code.iter().enumerate() { func_dec += format!("{}", arg.1).as_str(); if i < conversion_code.len() - 1 { @@ -124,7 +143,8 @@ pub fn generate_cpp_bindings() -> Result<()> { } func_dec += ");\n"; func_dec += format!(" if (!result) return nullptr;\n").as_str(); - func_dec += format!(" return TWStringCreateWithUTF8Bytes(result.c_str());\n").as_str(); + func_dec += + format!(" return TWStringCreateWithUTF8Bytes(result.c_str());\n").as_str(); } else { func_dec += format!(" return Rust::{}(", func.rust_name).as_str(); for (i, arg) in conversion_code.iter().enumerate() { @@ -141,6 +161,6 @@ pub fn generate_cpp_bindings() -> Result<()> { file.flush()?; } - + Ok(()) } diff --git a/codegen-v2/src/codegen/template_generator.rs b/codegen-v2/src/codegen/template_generator.rs index 7b725ba74e7..6fae696716d 100644 --- a/codegen-v2/src/codegen/template_generator.rs +++ b/codegen-v2/src/codegen/template_generator.rs @@ -38,7 +38,14 @@ impl TemplateGenerator { .add_pattern("{TW_CRATE_NAME}", coin.id.to_tw_crate_name()) .add_pattern("{COIN_ID}", coin.id.as_str()) .add_pattern("{COIN_TYPE}", coin.coin_type()) - .add_pattern("{COIN_NAME}", if coin.display_name.len() > 0 { &coin.display_name } else { &coin.name }) + .add_pattern( + "{COIN_NAME}", + if coin.display_name.len() > 0 { + &coin.display_name + } else { + &coin.name + }, + ) .add_pattern("{SYMBOL}", &coin.symbol) .add_pattern("{DECIMALS}", coin.decimals) .add_pattern("{P2PKH_PREFIX}", coin.p2pkh_prefix) diff --git a/rust/tw_macros/src/lib.rs b/rust/tw_macros/src/lib.rs index 26e59f3ee90..3a903eadb1d 100644 --- a/rust/tw_macros/src/lib.rs +++ b/rust/tw_macros/src/lib.rs @@ -9,4 +9,3 @@ pub fn tw_ffi(attr: TokenStream, item: TokenStream) -> TokenStream { Err(e) => e.to_compile_error().into(), } } - diff --git a/rust/tw_macros/src/tw_ffi.rs b/rust/tw_macros/src/tw_ffi.rs index d3efffc903f..787716beca7 100644 --- a/rust/tw_macros/src/tw_ffi.rs +++ b/rust/tw_macros/src/tw_ffi.rs @@ -1,13 +1,16 @@ use derive_syn_parse::Parse; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use syn::{parse::{Parse, ParseStream}, parse2, Ident, Result, Token}; +use syn::{ + parse::{Parse, ParseStream}, + parse2, Ident, Result, Token, +}; use std::env; use std::fs; use std::path::Path; -use tw_misc::code_gen::{TWConfig, TWStaticFunction, TWArg}; +use tw_misc::code_gen::{TWArg, TWConfig, TWStaticFunction}; pub mod keywords { use syn::custom_keyword; @@ -73,15 +76,19 @@ pub fn tw_ffi(attr: TokenStream2, item: TokenStream2) -> Result { let func = parse2::(item.clone())?; let func_name = func.sig.ident.to_string(); - let func_args = func.sig.inputs.iter().map(|arg| match arg { - syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => (quote!( #pat ), quote!( #ty )), - _ => (quote!(), quote!()), - }) - .map(|(name, ty)| TWArg { - name: name.to_string(), - ty: ty.to_string(), - }) - .collect::>(); + let func_args = func + .sig + .inputs + .iter() + .map(|arg| match arg { + syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => (quote!( #pat ), quote!( #ty )), + _ => (quote!(), quote!()), + }) + .map(|(name, ty)| TWArg { + name: name.to_string(), + ty: ty.to_string(), + }) + .collect::>(); let return_type = func.sig.output; let return_type = match return_type { @@ -101,22 +108,30 @@ pub fn tw_ffi(attr: TokenStream2, item: TokenStream2) -> Result { let bindings_dir = Path::new(&out_dir).join("bindings"); fs::create_dir_all(&bindings_dir).expect("Failed to create bindings directory"); let yaml_file_path = bindings_dir.join(format!("{}.yaml", class)); - + let mut config = if yaml_file_path.exists() { - serde_yaml::from_str(&fs::read_to_string(&yaml_file_path).expect("Failed to read existing YAML file")).expect("Failed to parse YAML file") + serde_yaml::from_str( + &fs::read_to_string(&yaml_file_path).expect("Failed to read existing YAML file"), + ) + .expect("Failed to parse YAML file") } else { TWConfig { class, static_functions: vec![], } - }; - if let Some(idx) = config.static_functions.iter().position(|f| f.name == static_function.name) { + }; + if let Some(idx) = config + .static_functions + .iter() + .position(|f| f.name == static_function.name) + { config.static_functions[idx] = static_function; } else { config.static_functions.push(static_function); } - - let yaml_output: String = serde_yaml::to_string(&config).expect("Failed to serialize to YAML"); + + let yaml_output: String = + serde_yaml::to_string(&config).expect("Failed to serialize to YAML"); fs::write(&yaml_file_path, yaml_output).expect("Failed to write YAML file"); } else { panic!("CARGO_WORKSPACE_DIR is not set"); @@ -127,9 +142,10 @@ pub fn tw_ffi(attr: TokenStream2, item: TokenStream2) -> Result { #[test] fn test_ffi_attr_arg_parsing() { - let args = parse2::(quote!{ + let args = parse2::(quote! { ty = static_function, class = MyClass, name = MyName - }).unwrap(); + }) + .unwrap(); } From 184a408ac780a0a543f568b7f7137a7edbb3e61f Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Tue, 21 Jan 2025 13:27:46 +0530 Subject: [PATCH 07/20] Makes clippy happy --- rust/tw_macros/src/tw_ffi.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rust/tw_macros/src/tw_ffi.rs b/rust/tw_macros/src/tw_ffi.rs index 787716beca7..461c49d0b6e 100644 --- a/rust/tw_macros/src/tw_ffi.rs +++ b/rust/tw_macros/src/tw_ffi.rs @@ -7,6 +7,7 @@ use syn::{ }; use std::env; +use std::fmt; use std::fs; use std::path::Path; @@ -38,10 +39,10 @@ impl Parse for TWFFIType { } } -impl ToString for TWFFIType { - fn to_string(&self) -> String { +impl fmt::Display for TWFFIType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - TWFFIType::StaticFunction => "static_function".to_string(), + TWFFIType::StaticFunction => write!(f, "static_function"), } } } From e839079c5ded52e9bcb76c8a648e8d7c7cb21953 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:32:33 +0530 Subject: [PATCH 08/20] Includes generated files in kotlin build --- codegen/bin/codegen | 2 -- kotlin/wallet-core-kotlin/build.gradle.kts | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/codegen/bin/codegen b/codegen/bin/codegen index 84e2230087b..1438a5e73a2 100755 --- a/codegen/bin/codegen +++ b/codegen/bin/codegen @@ -63,13 +63,11 @@ end.parse! entities = [] files = [] -file_paths = [] [options.input, options.input_generated].each do |input_dir| Dir.foreach(input_dir) do |item| next if ['.', '..', '.DS_Store'].include?(item) file_path = File.expand_path(File.join(input_dir, item)) - file_paths << file_path entity = Parser.new(path: file_path).parse next if entity.nil? diff --git a/kotlin/wallet-core-kotlin/build.gradle.kts b/kotlin/wallet-core-kotlin/build.gradle.kts index 8fc8be0a32f..68345c5508f 100644 --- a/kotlin/wallet-core-kotlin/build.gradle.kts +++ b/kotlin/wallet-core-kotlin/build.gradle.kts @@ -100,7 +100,8 @@ kotlin { val main by compilations.getting main.cinterops.create("WalletCore") { packageName = "com.trustwallet.core" - headers(rootDir.parentFile.resolve("include/TrustWalletCore").listFiles()!!) + headers(rootDir.parentFile.resolve("include/TrustWalletCore").listFiles()!! + + rootDir.parentFile.resolve("include/Generated/TrustWalletCore").listFiles()!!) } } } From d9e127b885bf9b235b0f35aaeb2a5247670c6971 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:57:33 +0530 Subject: [PATCH 09/20] Adds include dir as well --- kotlin/wallet-core-kotlin/build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kotlin/wallet-core-kotlin/build.gradle.kts b/kotlin/wallet-core-kotlin/build.gradle.kts index 68345c5508f..9c165707044 100644 --- a/kotlin/wallet-core-kotlin/build.gradle.kts +++ b/kotlin/wallet-core-kotlin/build.gradle.kts @@ -101,7 +101,8 @@ kotlin { main.cinterops.create("WalletCore") { packageName = "com.trustwallet.core" headers(rootDir.parentFile.resolve("include/TrustWalletCore").listFiles()!! + - rootDir.parentFile.resolve("include/Generated/TrustWalletCore").listFiles()!!) + rootDir.parentFile.resolve("include/Generated/TrustWalletCore").listFiles()!! + + rootDir.parentFile.resolve("include").listFiles()!!) } } } From 70b333b0dbcf452c846cc007ad6f91d35b474069 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Tue, 21 Jan 2025 16:35:05 +0530 Subject: [PATCH 10/20] Try to use includeDirs --- kotlin/wallet-core-kotlin/build.gradle.kts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/kotlin/wallet-core-kotlin/build.gradle.kts b/kotlin/wallet-core-kotlin/build.gradle.kts index 9c165707044..b04858f3aa1 100644 --- a/kotlin/wallet-core-kotlin/build.gradle.kts +++ b/kotlin/wallet-core-kotlin/build.gradle.kts @@ -100,9 +100,13 @@ kotlin { val main by compilations.getting main.cinterops.create("WalletCore") { packageName = "com.trustwallet.core" - headers(rootDir.parentFile.resolve("include/TrustWalletCore").listFiles()!! + - rootDir.parentFile.resolve("include/Generated/TrustWalletCore").listFiles()!! + - rootDir.parentFile.resolve("include").listFiles()!!) + includeDirs( + rootDir.parentFile.resolve("include"), + rootDir.parentFile.resolve("include/TrustWalletCore"), + rootDir.parentFile.resolve("include/Generated/TrustWalletCore") + ) + headers(rootDir.parentFile.resolve("include/Generated/TrustWalletCore").listFiles()!! + + rootDir.parentFile.resolve("include/TrustWalletCore").listFiles()!!) } } } From 42e8b22da69b6fcde38530b836576d1568703b28 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:12:33 +0530 Subject: [PATCH 11/20] Addresses review comments --- codegen-v2/Cargo.lock | 40 --- codegen-v2/Cargo.toml | 2 - codegen-v2/src/codegen/cpp/code_gen.rs | 256 ++++++++++++++++++++ codegen-v2/src/codegen/cpp/mod.rs | 1 + codegen-v2/src/codegen/cpp_v2/mod.rs | 166 ------------- codegen-v2/src/codegen/mod.rs | 1 - codegen-v2/src/main.rs | 2 +- docs/registry.md | 2 +- rust/Cargo.lock | 1 - rust/tw_macros/Cargo.toml | 1 - rust/{tw_misc => tw_macros}/src/code_gen.rs | 0 rust/tw_macros/src/lib.rs | 1 + rust/tw_macros/src/tw_ffi.rs | 26 +- rust/tw_misc/src/lib.rs | 2 - 14 files changed, 277 insertions(+), 224 deletions(-) create mode 100644 codegen-v2/src/codegen/cpp/code_gen.rs delete mode 100644 codegen-v2/src/codegen/cpp_v2/mod.rs rename rust/{tw_misc => tw_macros}/src/code_gen.rs (100%) diff --git a/codegen-v2/Cargo.lock b/codegen-v2/Cargo.lock index 607d74e6f2d..04e9c8c4583 100644 --- a/codegen-v2/Cargo.lock +++ b/codegen-v2/Cargo.lock @@ -39,8 +39,6 @@ dependencies = [ "serde_json", "serde_yaml", "toml_edit", - "tw_macros", - "tw_misc", ] [[package]] @@ -71,17 +69,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "derive-syn-parse" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "digest" version = "0.10.7" @@ -352,27 +339,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "tw_macros" -version = "0.1.0" -dependencies = [ - "derive-syn-parse", - "proc-macro2", - "quote", - "serde", - "serde_yaml", - "syn", - "tw_misc", -] - -[[package]] -name = "tw_misc" -version = "0.1.0" -dependencies = [ - "serde", - "zeroize", -] - [[package]] name = "typenum" version = "1.17.0" @@ -417,9 +383,3 @@ checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/codegen-v2/Cargo.toml b/codegen-v2/Cargo.toml index 48a6fbf6d58..43c2afea1c6 100644 --- a/codegen-v2/Cargo.toml +++ b/codegen-v2/Cargo.toml @@ -21,5 +21,3 @@ serde_yaml = "0.9.21" toml_edit = "0.21.0" handlebars = "4.3.6" heck = "0.4.1" -tw_macros = { path = "../rust/tw_macros" } -tw_misc = { path = "../rust/tw_misc", features = ["serde"] } diff --git a/codegen-v2/src/codegen/cpp/code_gen.rs b/codegen-v2/src/codegen/cpp/code_gen.rs new file mode 100644 index 00000000000..1e90718d11b --- /dev/null +++ b/codegen-v2/src/codegen/cpp/code_gen.rs @@ -0,0 +1,256 @@ +use serde::{Deserialize, Serialize}; +use std::fs; +use std::io::Write; + +use crate::Result; + +static IN_DIR: &str = "../rust/bindings/"; +static HEADER_OUT_DIR: &str = "../include/Generated/TrustWalletCore/"; +static SOURCE_OUT_DIR: &str = "../src/Generated/"; + +#[derive(Deserialize, Serialize, Debug)] +pub struct TWConfig { + pub class: String, + pub static_functions: Vec, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct TWStaticFunction { + pub name: String, + pub rust_name: String, + pub args: Vec, + pub return_type: String, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct TWArg { + pub name: String, + pub ty: String, +} + +fn convert_rust_type_to_cpp(ty: &str) -> String { + match ty.trim() { + s if s.starts_with("* const") => format!("{} *_Nonnull", &s[8..]), + s if s.starts_with("* mut") => format!("{} *_Nullable", &s[6..]), + "bool" => "bool".to_string(), + _ => ty.to_string(), + } +} + +fn generate_license(file: &mut std::fs::File) -> Result<()> { + writeln!(file, "// Copyright © 2017 Trust Wallet.\n")?; + Ok(()) +} + +fn generate_header_guard(file: &mut std::fs::File) -> Result<()> { + writeln!(file, "#pragma once\n")?; + Ok(()) +} + +fn generate_header_includes(file: &mut std::fs::File, info: &TWConfig) -> Result<()> { + writeln!(file, "#include ")?; + + // Include headers based on argument types + let mut included_headers = std::collections::HashSet::new(); + for func in &info.static_functions { + for arg in &func.args { + if arg.ty.contains("TWString") && included_headers.insert("TWString.h") { + writeln!(file, "#include ")?; + } + // Additional type checks can be added here in the future + } + } + + Ok(()) +} + +fn generate_class_declaration(file: &mut std::fs::File, info: &TWConfig) -> Result<()> { + let class_name = format!("{}", info.class); + let class_dec = format!("TW_EXPORT_CLASS\nstruct {};\n", class_name.clone()); + writeln!(file, "{}", class_dec)?; + Ok(()) +} + +fn generate_function_signature( + class_name: &str, + func: &TWStaticFunction, + is_declaration: bool, +) -> String { + let return_type = convert_rust_type_to_cpp(&func.return_type); + let mut signature = format!( + "{}{} {}{}", + if is_declaration { + "TW_EXPORT_STATIC_METHOD " + } else { + "" + }, + return_type, + class_name, + &func.name + ); + signature += "("; + for (i, arg) in func.args.iter().enumerate() { + signature += format!("{} {}", convert_rust_type_to_cpp(&arg.ty), arg.name).as_str(); + if i < func.args.len() - 1 { + signature += ", "; + } + } + signature += ")"; + signature +} + +fn generate_function_declaration( + file: &mut std::fs::File, + class_name: &str, + func: &TWStaticFunction, +) -> Result<()> { + let mut func_dec = generate_function_signature(class_name, func, true); + func_dec += ";\n"; + writeln!(file, "{}", func_dec)?; + Ok(()) +} + +pub fn generate_header(info: &TWConfig) -> Result<()> { + let file_path = format!("{HEADER_OUT_DIR}/{}.h", info.class); + let mut file = std::fs::File::create(&file_path)?; + + generate_license(&mut file)?; + generate_header_guard(&mut file)?; + generate_header_includes(&mut file, info)?; + + writeln!(file, "\nTW_EXTERN_C_BEGIN\n")?; + + generate_class_declaration(&mut file, info)?; + for func in &info.static_functions { + generate_function_declaration(&mut file, &info.class, func)?; + } + + writeln!(file, "TW_EXTERN_C_END")?; + + file.flush()?; + + Ok(()) +} + +fn generate_source_includes(file: &mut std::fs::File, info: &TWConfig) -> Result<()> { + writeln!( + file, + "{}", + format!("#include ", info.class) + )?; + writeln!(file, "#include \"rust/Wrapper.h\"")?; + Ok(()) +} + +fn generate_function_call(args: &Vec) -> String { + let mut func_call = "(".to_string(); + for (i, arg) in args.iter().enumerate() { + func_call += format!("{}", arg).as_str(); + if i < args.len() - 1 { + func_call += ", "; + } + } + func_call += ");\n"; + func_call +} + +fn generate_return_string(func: &TWStaticFunction, converted_args: &Vec) -> String { + let mut return_string = String::new(); + match func.return_type.as_str() { + "* mut TWString" => { + return_string += format!( + " const Rust::TWStringWrapper result = Rust::{}", + func.rust_name + ) + .as_str(); + return_string += generate_function_call(&converted_args).as_str(); + return_string += format!(" if (!result) {{ return nullptr; }}\n").as_str(); + return_string += + format!(" return TWStringCreateWithUTF8Bytes(result.c_str());\n").as_str(); + } + _ => { + return_string += format!(" return Rust::{}(", func.rust_name).as_str(); + return_string += generate_function_call(&converted_args).as_str(); + } + } + return_string +} + +fn generate_conversion_code_with_var_name(ty: &str, name: &str) -> (String, String) { + match ty { + "TWString *_Nonnull" => { + let code = format!( + " auto& {name}String = *reinterpret_cast({name});\n", + name = name + ) + format!( + " const Rust::TWStringWrapper {name}RustStr = {name}String;\n", + name = name + ) + .as_str(); + (code, format!("{}RustStr.get()", name)) + } + _ => ("".to_string(), name.to_string()), + } +} + +fn generate_function_definition( + file: &mut std::fs::File, + info: &TWConfig, + func: &TWStaticFunction, +) -> Result<()> { + let mut func_def = generate_function_signature(&info.class, func, false); + func_def += " {\n"; + let mut converted_args = vec![]; + for arg in func.args.iter() { + let func_type = convert_rust_type_to_cpp(&arg.ty); + let (conversion_code, converted_arg) = + generate_conversion_code_with_var_name(&func_type, &arg.name); + func_def += conversion_code.as_str(); + converted_args.push(converted_arg); + } + let return_string = generate_return_string(func, &converted_args); + func_def += return_string.as_str(); + func_def += "}\n"; + writeln!(file, "{}", func_def)?; + Ok(()) +} + +fn generate_source(info: &TWConfig) -> Result<()> { + let file_path = format!("{SOURCE_OUT_DIR}/{}.cpp", info.class); + let mut file = std::fs::File::create(&file_path)?; + + generate_license(&mut file)?; + generate_source_includes(&mut file, info)?; + + writeln!(file, "\nusing namespace TW;\n")?; + + for func in &info.static_functions { + generate_function_definition(&mut file, info, func)?; + } + + file.flush()?; + + Ok(()) +} + +pub fn generate_cpp_bindings() -> Result<()> { + std::fs::create_dir_all(HEADER_OUT_DIR)?; + std::fs::create_dir_all(SOURCE_OUT_DIR)?; + + let entries = fs::read_dir(IN_DIR)?; + for entry in entries { + let file_path = entry?.path(); + if file_path.is_dir() { + println!("Found unexpected directory: {}", file_path.display()); + continue; + } + + let file_contents = fs::read_to_string(&file_path)?; + let info: TWConfig = + serde_yaml::from_str(&file_contents).expect("Failed to parse YAML file"); + + generate_header(&info)?; + generate_source(&info)?; + } + Ok(()) +} diff --git a/codegen-v2/src/codegen/cpp/mod.rs b/codegen-v2/src/codegen/cpp/mod.rs index 1ef7188d86f..147960dd30c 100644 --- a/codegen-v2/src/codegen/cpp/mod.rs +++ b/codegen-v2/src/codegen/cpp/mod.rs @@ -7,6 +7,7 @@ use std::env; use std::path::PathBuf; pub mod blockchain_dispatcher_generator; +pub mod code_gen; pub mod entry_generator; pub mod new_blockchain; pub mod new_cosmos_chain; diff --git a/codegen-v2/src/codegen/cpp_v2/mod.rs b/codegen-v2/src/codegen/cpp_v2/mod.rs deleted file mode 100644 index 8e2c3f2fbb9..00000000000 --- a/codegen-v2/src/codegen/cpp_v2/mod.rs +++ /dev/null @@ -1,166 +0,0 @@ -use std::fs; -use std::io::Write; -use tw_misc::code_gen::TWConfig; - -use crate::Result; -fn map_type(ty: &str) -> String { - match ty.trim() { - s if s.starts_with("* const") => format!("{} *_Nonnull", &s[8..]), - s if s.starts_with("* mut") => format!("{} *_Nullable", &s[6..]), - "bool" => "bool".to_string(), - _ => ty.to_string(), - } -} - -fn generate_conversion_code(ty: &str, name: &str) -> (String, String) { - match ty { - "TWString *_Nonnull" => { - let code = format!( - " auto& {name}String = *reinterpret_cast({name});\n", - name = name - ) + format!( - " const Rust::TWStringWrapper {name}RustStr = {name}String;\n", - name = name - ) - .as_str(); - (code, format!("{}RustStr.get()", name)) - } - _ => ("".to_string(), name.to_string()), - } -} - -pub fn generate_cpp_bindings() -> Result<()> { - const IN_DIR: &str = "../rust/bindings/"; - const HEADER_OUT_DIR: &str = "../include/Generated/TrustWalletCore/"; - const SOURCE_OUT_DIR: &str = "../src/Generated/"; - - std::fs::create_dir_all(HEADER_OUT_DIR)?; - std::fs::create_dir_all(SOURCE_OUT_DIR)?; - - let entries = fs::read_dir(IN_DIR)?; - - for entry in entries { - let entry = entry?; - let file_path = entry.path(); - - if file_path.is_dir() { - println!("Found unexpected directory: {}", file_path.display()); - continue; - } - - let file_contents = fs::read_to_string(&file_path)?; - - let info: TWConfig = - serde_yaml::from_str(&file_contents).expect("Failed to parse YAML file"); - - let file_path = format!("{HEADER_OUT_DIR}/{}.h", info.class); - - let mut file = std::fs::File::create(&file_path)?; - writeln!(file, "// Copyright © 2017 Trust Wallet.\n")?; - writeln!(file, "#pragma once\n")?; - writeln!(file, "#include ")?; - - // Include headers based on argument types - let mut included_headers = std::collections::HashSet::new(); - for func in &info.static_functions { - for arg in &func.args { - if arg.ty.contains("TWString") && included_headers.insert("TWString.h") { - writeln!(file, "#include ")?; - } - // Additional type checks can be added here in the future - } - } - - writeln!(file, "\nTW_EXTERN_C_BEGIN\n")?; - - let class_name = format!("{}", info.class); - let class_dec = format!("TW_EXPORT_CLASS\nstruct {};\n", class_name.clone()); - writeln!(file, "{}", class_dec)?; - - for func in &info.static_functions { - let return_type = map_type(&func.return_type); - let mut func_dec = format!( - "TW_EXPORT_STATIC_METHOD\n{} {}(", - return_type, - class_name.clone() + &func.name - ); - for (i, arg) in func.args.iter().enumerate() { - let func_type = map_type(&arg.ty); - func_dec += format!("{} {}", func_type, arg.name).as_str(); - if i < func.args.len() - 1 { - func_dec += ", "; - } - } - func_dec += ");\n"; - writeln!(file, "{}", func_dec)?; - } - - writeln!(file, "TW_EXTERN_C_END")?; - - file.flush()?; - - let file_path = format!("{SOURCE_OUT_DIR}/{}.cpp", info.class); - - let mut file = std::fs::File::create(&file_path)?; - writeln!(file, "// Copyright © 2017 Trust Wallet.\n")?; - writeln!( - file, - "{}", - format!("#include ", info.class) - )?; - writeln!(file, "#include \"rust/Wrapper.h\"")?; - - writeln!(file, "\nusing namespace TW;\n")?; - - for func in &info.static_functions { - let return_type = map_type(&func.return_type); - let mut func_dec = format!("{} {}(", return_type, class_name.clone() + &func.name); - let mut conversion_code = vec![]; - for (i, arg) in func.args.iter().enumerate() { - let func_type = map_type(&arg.ty); - let code_with_name = generate_conversion_code(&func_type, &arg.name); - conversion_code.push(code_with_name); - func_dec += format!("{} {}", func_type, arg.name).as_str(); - if i < func.args.len() - 1 { - func_dec += ", "; - } - } - func_dec += ") {\n"; - for code in &conversion_code { - func_dec += code.0.as_str(); - } - if return_type == "TWString *_Nullable" { - func_dec += format!( - " const Rust::TWStringWrapper result = Rust::{}(", - func.rust_name - ) - .as_str(); - for (i, arg) in conversion_code.iter().enumerate() { - func_dec += format!("{}", arg.1).as_str(); - if i < conversion_code.len() - 1 { - func_dec += ", "; - } - } - func_dec += ");\n"; - func_dec += format!(" if (!result) return nullptr;\n").as_str(); - func_dec += - format!(" return TWStringCreateWithUTF8Bytes(result.c_str());\n").as_str(); - } else { - func_dec += format!(" return Rust::{}(", func.rust_name).as_str(); - for (i, arg) in conversion_code.iter().enumerate() { - func_dec += format!("{}", arg.1).as_str(); - if i < conversion_code.len() - 1 { - func_dec += ", "; - } - } - func_dec += ");\n"; - } - func_dec += "}\n"; - writeln!(file, "{}", func_dec)?; - } - - file.flush()?; - } - - Ok(()) -} diff --git a/codegen-v2/src/codegen/mod.rs b/codegen-v2/src/codegen/mod.rs index 3453d2bbb20..1d8522e6b9f 100644 --- a/codegen-v2/src/codegen/mod.rs +++ b/codegen-v2/src/codegen/mod.rs @@ -3,7 +3,6 @@ // Copyright © 2017 Trust Wallet. pub mod cpp; -pub mod cpp_v2; pub mod proto; pub mod rust; pub mod swift; diff --git a/codegen-v2/src/main.rs b/codegen-v2/src/main.rs index b726773a60b..f420701bd6c 100644 --- a/codegen-v2/src/main.rs +++ b/codegen-v2/src/main.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use libparser::codegen::cpp_v2::generate_cpp_bindings; +use libparser::codegen::cpp::code_gen::generate_cpp_bindings; use libparser::codegen::swift::RenderIntput; use libparser::codegen::{cpp, proto, rust}; use libparser::coin_id::CoinId; diff --git a/docs/registry.md b/docs/registry.md index 1473aa7b5ca..8a109c526d2 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -131,7 +131,7 @@ This list is generated from [./registry.json](../registry.json) | 10004689 | IoTeX EVM | IOTX | | | | 10007000 | NativeZetaChain | ZETA | | | | 10007700 | NativeCanto | CANTO | | | -| 10008217 | Kaia | KAIA | | | +| 10008217 | Kaia | KAIA | | | | 10009000 | Avalanche C-Chain | AVAX | | | | 10009001 | Evmos | EVMOS | | | | 10042170 | Arbitrum Nova | ETH | | | diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 9367be57850..6867b6325c0 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2070,7 +2070,6 @@ dependencies = [ "serde", "serde_yaml", "syn 2.0.96", - "tw_misc", ] [[package]] diff --git a/rust/tw_macros/Cargo.toml b/rust/tw_macros/Cargo.toml index 9bfe4c0ea71..8a0343c15be 100644 --- a/rust/tw_macros/Cargo.toml +++ b/rust/tw_macros/Cargo.toml @@ -13,4 +13,3 @@ quote = "1.0.38" serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9.21" syn = { version = "2.0.96", features = ["full"] } -tw_misc = { path = "../tw_misc", features = ["serde"] } diff --git a/rust/tw_misc/src/code_gen.rs b/rust/tw_macros/src/code_gen.rs similarity index 100% rename from rust/tw_misc/src/code_gen.rs rename to rust/tw_macros/src/code_gen.rs diff --git a/rust/tw_macros/src/lib.rs b/rust/tw_macros/src/lib.rs index 3a903eadb1d..8c0cd9533ed 100644 --- a/rust/tw_macros/src/lib.rs +++ b/rust/tw_macros/src/lib.rs @@ -1,5 +1,6 @@ use proc_macro::TokenStream; +mod code_gen; mod tw_ffi; #[proc_macro_attribute] diff --git a/rust/tw_macros/src/tw_ffi.rs b/rust/tw_macros/src/tw_ffi.rs index 461c49d0b6e..61f291132ae 100644 --- a/rust/tw_macros/src/tw_ffi.rs +++ b/rust/tw_macros/src/tw_ffi.rs @@ -11,7 +11,7 @@ use std::fmt; use std::fs; use std::path::Path; -use tw_misc::code_gen::{TWArg, TWConfig, TWStaticFunction}; +use crate::code_gen::{TWArg, TWConfig, TWStaticFunction}; pub mod keywords { use syn::custom_keyword; @@ -141,12 +141,20 @@ pub fn tw_ffi(attr: TokenStream2, item: TokenStream2) -> Result { Ok(item) } -#[test] -fn test_ffi_attr_arg_parsing() { - let args = parse2::(quote! { - ty = static_function, - class = MyClass, - name = MyName - }) - .unwrap(); +#[cfg(test)] +mod tests { + use super::*; + use proc_macro2::Span; + + #[test] + fn test_ffi_attr_arg_parsing() { + let args = parse2::(quote! { + ty = static_function, + class = MyClass, + name = MyName + }) + .unwrap(); + assert_eq!(args.class, Some(Ident::new("MyClass", Span::call_site()))); + assert_eq!(args.name, Some(Ident::new("MyName", Span::call_site()))); + } } diff --git a/rust/tw_misc/src/lib.rs b/rust/tw_misc/src/lib.rs index 75aa740c826..eeccb7e62b0 100644 --- a/rust/tw_misc/src/lib.rs +++ b/rust/tw_misc/src/lib.rs @@ -2,8 +2,6 @@ // // Copyright © 2017 Trust Wallet. -#[cfg(feature = "serde")] -pub mod code_gen; pub mod macros; #[cfg(feature = "serde")] pub mod serde; From 6ac6e6fcc89a007007ca00d5f25c8c25d3c1e380 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:13:13 +0530 Subject: [PATCH 12/20] Minor --- rust/Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 6867b6325c0..cdecec6380b 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2218,6 +2218,7 @@ name = "tw_substrate" version = "0.1.0" dependencies = [ "tw_coin_entry", + "tw_encoding", "tw_hash", "tw_keypair", "tw_memory", From 72f3cbb023df2f92c7afd97153273f7efdb31e62 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Thu, 23 Jan 2025 11:51:51 +0530 Subject: [PATCH 13/20] Addresses review comments --- codegen-v2/src/codegen/cpp/code_gen.rs | 105 ++++++++++++++----------- docs/registry.md | 2 +- 2 files changed, 60 insertions(+), 47 deletions(-) diff --git a/codegen-v2/src/codegen/cpp/code_gen.rs b/codegen-v2/src/codegen/cpp/code_gen.rs index 1e90718d11b..4a6dacd840f 100644 --- a/codegen-v2/src/codegen/cpp/code_gen.rs +++ b/codegen-v2/src/codegen/cpp/code_gen.rs @@ -1,7 +1,9 @@ use serde::{Deserialize, Serialize}; +use std::fmt::Write as _; use std::fs; use std::io::Write; +use crate::Error::BadFormat; use crate::Result; static IN_DIR: &str = "../rust/bindings/"; @@ -38,6 +40,8 @@ fn convert_rust_type_to_cpp(ty: &str) -> String { } fn generate_license(file: &mut std::fs::File) -> Result<()> { + writeln!(file, "// SPDX-License-Identifier: Apache-2.0")?; + writeln!(file, "//")?; writeln!(file, "// Copyright © 2017 Trust Wallet.\n")?; Ok(()) } @@ -65,9 +69,7 @@ fn generate_header_includes(file: &mut std::fs::File, info: &TWConfig) -> Result } fn generate_class_declaration(file: &mut std::fs::File, info: &TWConfig) -> Result<()> { - let class_name = format!("{}", info.class); - let class_dec = format!("TW_EXPORT_CLASS\nstruct {};\n", class_name.clone()); - writeln!(file, "{}", class_dec)?; + writeln!(file, "TW_EXPORT_CLASS\nstruct {};\n", info.class)?; Ok(()) } @@ -75,28 +77,29 @@ fn generate_function_signature( class_name: &str, func: &TWStaticFunction, is_declaration: bool, -) -> String { +) -> Result { let return_type = convert_rust_type_to_cpp(&func.return_type); - let mut signature = format!( - "{}{} {}{}", - if is_declaration { - "TW_EXPORT_STATIC_METHOD " - } else { - "" - }, - return_type, - class_name, - &func.name - ); + let whether_export = if is_declaration { + "TW_EXPORT_STATIC_METHOD " + } else { + "" + }; + let mut signature = format!("{whether_export}{return_type} {class_name}{}", func.name); signature += "("; for (i, arg) in func.args.iter().enumerate() { - signature += format!("{} {}", convert_rust_type_to_cpp(&arg.ty), arg.name).as_str(); + write!( + &mut signature, + "{} {}", + convert_rust_type_to_cpp(&arg.ty), + arg.name + ) + .map_err(|e| BadFormat(e.to_string()))?; if i < func.args.len() - 1 { signature += ", "; } } signature += ")"; - signature + Ok(signature) } fn generate_function_declaration( @@ -104,9 +107,8 @@ fn generate_function_declaration( class_name: &str, func: &TWStaticFunction, ) -> Result<()> { - let mut func_dec = generate_function_signature(class_name, func, true); - func_dec += ";\n"; - writeln!(file, "{}", func_dec)?; + let func_dec = generate_function_signature(class_name, func, true)?; + writeln!(file, "{func_dec};\n")?; Ok(()) } @@ -135,61 +137,72 @@ pub fn generate_header(info: &TWConfig) -> Result<()> { fn generate_source_includes(file: &mut std::fs::File, info: &TWConfig) -> Result<()> { writeln!( file, - "{}", - format!("#include ", info.class) + "#include ", + info.class )?; writeln!(file, "#include \"rust/Wrapper.h\"")?; Ok(()) } -fn generate_function_call(args: &Vec) -> String { +fn generate_function_call(args: &Vec) -> Result { let mut func_call = "(".to_string(); for (i, arg) in args.iter().enumerate() { - func_call += format!("{}", arg).as_str(); + write!(&mut func_call, "{arg}").map_err(|e| BadFormat(e.to_string()))?; if i < args.len() - 1 { func_call += ", "; } } func_call += ");\n"; - func_call + Ok(func_call) } -fn generate_return_string(func: &TWStaticFunction, converted_args: &Vec) -> String { +fn generate_return_type(func: &TWStaticFunction, converted_args: &Vec) -> Result { let mut return_string = String::new(); match func.return_type.as_str() { "* mut TWString" => { - return_string += format!( + write!( + &mut return_string, " const Rust::TWStringWrapper result = Rust::{}", func.rust_name ) - .as_str(); - return_string += generate_function_call(&converted_args).as_str(); - return_string += format!(" if (!result) {{ return nullptr; }}\n").as_str(); - return_string += - format!(" return TWStringCreateWithUTF8Bytes(result.c_str());\n").as_str(); + .map_err(|e| BadFormat(e.to_string()))?; + return_string += generate_function_call(&converted_args)?.as_str(); + writeln!(&mut return_string, " if (!result) {{ return nullptr; }}") + .map_err(|e| BadFormat(e.to_string()))?; + writeln!( + &mut return_string, + " return TWStringCreateWithUTF8Bytes(result.c_str());" + ) + .map_err(|e| BadFormat(e.to_string()))?; } _ => { - return_string += format!(" return Rust::{}(", func.rust_name).as_str(); - return_string += generate_function_call(&converted_args).as_str(); + writeln!(&mut return_string, " return Rust::{}", func.rust_name) + .map_err(|e| BadFormat(e.to_string()))?; + return_string += generate_function_call(&converted_args)?.as_str(); } } - return_string + Ok(return_string) } -fn generate_conversion_code_with_var_name(ty: &str, name: &str) -> (String, String) { +fn generate_conversion_code_with_var_name(ty: &str, name: &str) -> Result<(String, String)> { match ty { "TWString *_Nonnull" => { - let code = format!( - " auto& {name}String = *reinterpret_cast({name});\n", + let mut conversion_code = String::new(); + writeln!( + &mut conversion_code, + " auto& {name}String = *reinterpret_cast({name});", name = name - ) + format!( - " const Rust::TWStringWrapper {name}RustStr = {name}String;\n", + ) + .map_err(|e| BadFormat(e.to_string()))?; + writeln!( + &mut conversion_code, + " const Rust::TWStringWrapper {name}RustStr = {name}String;", name = name ) - .as_str(); - (code, format!("{}RustStr.get()", name)) + .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustStr.get()", name))) } - _ => ("".to_string(), name.to_string()), + _ => Ok(("".to_string(), name.to_string())), } } @@ -198,17 +211,17 @@ fn generate_function_definition( info: &TWConfig, func: &TWStaticFunction, ) -> Result<()> { - let mut func_def = generate_function_signature(&info.class, func, false); + let mut func_def = generate_function_signature(&info.class, func, false)?; func_def += " {\n"; let mut converted_args = vec![]; for arg in func.args.iter() { let func_type = convert_rust_type_to_cpp(&arg.ty); let (conversion_code, converted_arg) = - generate_conversion_code_with_var_name(&func_type, &arg.name); + generate_conversion_code_with_var_name(&func_type, &arg.name)?; func_def += conversion_code.as_str(); converted_args.push(converted_arg); } - let return_string = generate_return_string(func, &converted_args); + let return_string = generate_return_type(func, &converted_args)?; func_def += return_string.as_str(); func_def += "}\n"; writeln!(file, "{}", func_def)?; diff --git a/docs/registry.md b/docs/registry.md index 8a109c526d2..1473aa7b5ca 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -131,7 +131,7 @@ This list is generated from [./registry.json](../registry.json) | 10004689 | IoTeX EVM | IOTX | | | | 10007000 | NativeZetaChain | ZETA | | | | 10007700 | NativeCanto | CANTO | | | -| 10008217 | Kaia | KAIA | | | +| 10008217 | Kaia | KAIA | | | | 10009000 | Avalanche C-Chain | AVAX | | | | 10009001 | Evmos | EVMOS | | | | 10042170 | Arbitrum Nova | ETH | | | From 163f578a5c3d64d491e364b8f84b216badda8947 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Thu, 23 Jan 2025 12:00:24 +0530 Subject: [PATCH 14/20] Further changes --- codegen-v2/src/codegen/cpp/code_gen.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/codegen-v2/src/codegen/cpp/code_gen.rs b/codegen-v2/src/codegen/cpp/code_gen.rs index 4a6dacd840f..2764f259f31 100644 --- a/codegen-v2/src/codegen/cpp/code_gen.rs +++ b/codegen-v2/src/codegen/cpp/code_gen.rs @@ -32,8 +32,12 @@ pub struct TWArg { fn convert_rust_type_to_cpp(ty: &str) -> String { match ty.trim() { - s if s.starts_with("* const") => format!("{} *_Nonnull", &s[8..]), - s if s.starts_with("* mut") => format!("{} *_Nullable", &s[6..]), + s if s.starts_with("* const ") => { + format!("{} *_Nonnull", s.strip_prefix("* const ").expect("Checked")) + } + s if s.starts_with("* mut ") => { + format!("{} *_Nullable", s.strip_prefix("* mut ").expect("Checked")) + } "bool" => "bool".to_string(), _ => ty.to_string(), } From 093fe139e1adbc6ab5802f0d0fa6d3e8815976d7 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Thu, 23 Jan 2025 14:31:15 +0530 Subject: [PATCH 15/20] Adds type aliases --- codegen-v2/Cargo.lock | 30 +++++++++++++++ codegen-v2/Cargo.toml | 1 + codegen-v2/src/codegen/cpp/code_gen.rs | 37 +++++++++++++------ rust/tw_memory/src/ffi/mod.rs | 5 +++ .../src/ffi/ton/address_converter.rs | 12 +++--- 5 files changed, 68 insertions(+), 17 deletions(-) diff --git a/codegen-v2/Cargo.lock b/codegen-v2/Cargo.lock index 04e9c8c4583..098922255d8 100644 --- a/codegen-v2/Cargo.lock +++ b/codegen-v2/Cargo.lock @@ -35,6 +35,7 @@ dependencies = [ "handlebars", "heck", "pathdiff", + "regex", "serde", "serde_json", "serde_yaml", @@ -230,6 +231,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "ryu" version = "1.0.17" diff --git a/codegen-v2/Cargo.toml b/codegen-v2/Cargo.toml index 43c2afea1c6..b1692860f20 100644 --- a/codegen-v2/Cargo.toml +++ b/codegen-v2/Cargo.toml @@ -21,3 +21,4 @@ serde_yaml = "0.9.21" toml_edit = "0.21.0" handlebars = "4.3.6" heck = "0.4.1" +regex = "1.11.1" diff --git a/codegen-v2/src/codegen/cpp/code_gen.rs b/codegen-v2/src/codegen/cpp/code_gen.rs index 2764f259f31..9a79f3a26c2 100644 --- a/codegen-v2/src/codegen/cpp/code_gen.rs +++ b/codegen-v2/src/codegen/cpp/code_gen.rs @@ -1,3 +1,4 @@ +use regex::Regex; use serde::{Deserialize, Serialize}; use std::fmt::Write as _; use std::fs; @@ -31,15 +32,29 @@ pub struct TWArg { } fn convert_rust_type_to_cpp(ty: &str) -> String { - match ty.trim() { - s if s.starts_with("* const ") => { - format!("{} *_Nonnull", s.strip_prefix("* const ").expect("Checked")) - } - s if s.starts_with("* mut ") => { - format!("{} *_Nullable", s.strip_prefix("* mut ").expect("Checked")) - } - "bool" => "bool".to_string(), - _ => ty.to_string(), + let trimmed = ty.replace(" ", ""); + if let Some(captures) = Regex::new(r"^Nonnull<(.+)>$") + .expect("Failed to create regex") + .captures(&trimmed) + { + format!("{} *_Nonnull", &captures[1]) + } else if let Some(captures) = Regex::new(r"^NonnullMut<(.+)>$") + .expect("Failed to create regex") + .captures(&trimmed) + { + format!("{} *_Nonnull", &captures[1]) + } else if let Some(captures) = Regex::new(r"^Nullable<(.+)>$") + .expect("Failed to create regex") + .captures(&trimmed) + { + format!("{} *_Nullable", &captures[1]) + } else if let Some(captures) = Regex::new(r"^NullableMut<(.+)>$") + .expect("Failed to create regex") + .captures(&trimmed) + { + format!("{} *_Nullable", &captures[1]) + } else { + ty.to_string() } } @@ -162,8 +177,8 @@ fn generate_function_call(args: &Vec) -> Result { fn generate_return_type(func: &TWStaticFunction, converted_args: &Vec) -> Result { let mut return_string = String::new(); - match func.return_type.as_str() { - "* mut TWString" => { + match func.return_type.replace(" ", "").as_str() { + "NullableMut" | "Nullable" => { write!( &mut return_string, " const Rust::TWStringWrapper result = Rust::{}", diff --git a/rust/tw_memory/src/ffi/mod.rs b/rust/tw_memory/src/ffi/mod.rs index c69f1550a6a..0ee0b6becba 100644 --- a/rust/tw_memory/src/ffi/mod.rs +++ b/rust/tw_memory/src/ffi/mod.rs @@ -54,3 +54,8 @@ pub trait RawPtrTrait: Sized { Some(Box::from_raw(raw)) } } + +pub type Nullable = *const T; +pub type Nonnull = *const T; +pub type NullableMut = *mut T; +pub type NonnullMut = *mut T; diff --git a/rust/wallet_core_rs/src/ffi/ton/address_converter.rs b/rust/wallet_core_rs/src/ffi/ton/address_converter.rs index 59c1f73e3ab..ec6058ec13a 100644 --- a/rust/wallet_core_rs/src/ffi/ton/address_converter.rs +++ b/rust/wallet_core_rs/src/ffi/ton/address_converter.rs @@ -7,7 +7,7 @@ use std::str::FromStr; use tw_macros::tw_ffi; use tw_memory::ffi::tw_string::TWString; -use tw_memory::ffi::RawPtrTrait; +use tw_memory::ffi::{Nonnull, NullableMut, RawPtrTrait}; use tw_misc::try_or_else; use tw_ton::address::TonAddress; use tw_ton::modules::address_converter::AddressConverter; @@ -21,8 +21,8 @@ use tw_ton::modules::address_converter::AddressConverter; #[tw_ffi(ty = static_function, class = TWTONAddressConverter, name = ToBoc)] #[no_mangle] pub unsafe extern "C" fn tw_ton_address_converter_to_boc( - address: *const TWString, -) -> *mut TWString { + address: Nonnull, +) -> NullableMut { let address = try_or_else!(TWString::from_ptr_as_ref(address), std::ptr::null_mut); let address_str = try_or_else!(address.as_str(), std::ptr::null_mut); let address_ton = try_or_else!(TonAddress::from_str(address_str), std::ptr::null_mut); @@ -43,7 +43,7 @@ pub unsafe extern "C" fn tw_ton_address_converter_to_boc( /// \return Pointer to a Jetton address. #[tw_ffi(ty = static_function, class = TWTONAddressConverter, name = FromBoc)] #[no_mangle] -pub unsafe extern "C" fn tw_ton_address_converter_from_boc(boc: *const TWString) -> *mut TWString { +pub unsafe extern "C" fn tw_ton_address_converter_from_boc(boc: Nonnull) -> NullableMut { let boc = try_or_else!(TWString::from_ptr_as_ref(boc), std::ptr::null_mut); let boc_str = try_or_else!(boc.as_str(), std::ptr::null_mut); @@ -64,10 +64,10 @@ pub unsafe extern "C" fn tw_ton_address_converter_from_boc(boc: *const TWString) #[tw_ffi(ty = static_function, class = TWTONAddressConverter, name = ToUserFriendly)] #[no_mangle] pub unsafe extern "C" fn tw_ton_address_converter_to_user_friendly( - address: *const TWString, + address: Nonnull, bounceable: bool, testnet: bool, -) -> *mut TWString { +) -> NullableMut { let address = try_or_else!(TWString::from_ptr_as_ref(address), std::ptr::null_mut); let address_str = try_or_else!(address.as_str(), std::ptr::null_mut); let address_ton = try_or_else!(TonAddress::from_str(address_str), std::ptr::null_mut) From c4184be33bfd53766a8198bb3a200cf965d58778 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Thu, 23 Jan 2025 14:35:13 +0530 Subject: [PATCH 16/20] FMT --- rust/wallet_core_rs/src/ffi/ton/address_converter.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/wallet_core_rs/src/ffi/ton/address_converter.rs b/rust/wallet_core_rs/src/ffi/ton/address_converter.rs index ec6058ec13a..e8e1fe57699 100644 --- a/rust/wallet_core_rs/src/ffi/ton/address_converter.rs +++ b/rust/wallet_core_rs/src/ffi/ton/address_converter.rs @@ -43,7 +43,9 @@ pub unsafe extern "C" fn tw_ton_address_converter_to_boc( /// \return Pointer to a Jetton address. #[tw_ffi(ty = static_function, class = TWTONAddressConverter, name = FromBoc)] #[no_mangle] -pub unsafe extern "C" fn tw_ton_address_converter_from_boc(boc: Nonnull) -> NullableMut { +pub unsafe extern "C" fn tw_ton_address_converter_from_boc( + boc: Nonnull, +) -> NullableMut { let boc = try_or_else!(TWString::from_ptr_as_ref(boc), std::ptr::null_mut); let boc_str = try_or_else!(boc.as_str(), std::ptr::null_mut); From 7f7b4f7ccbafc771e92bab23b884a2e0ef360435 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Thu, 23 Jan 2025 16:30:23 +0530 Subject: [PATCH 17/20] Fixes nullable as well --- codegen-v2/src/codegen/cpp/code_gen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-v2/src/codegen/cpp/code_gen.rs b/codegen-v2/src/codegen/cpp/code_gen.rs index 9a79f3a26c2..e341e9a2851 100644 --- a/codegen-v2/src/codegen/cpp/code_gen.rs +++ b/codegen-v2/src/codegen/cpp/code_gen.rs @@ -205,7 +205,7 @@ fn generate_return_type(func: &TWStaticFunction, converted_args: &Vec) - fn generate_conversion_code_with_var_name(ty: &str, name: &str) -> Result<(String, String)> { match ty { - "TWString *_Nonnull" => { + "TWString *_Nonnull" | "TWString *_Nullable" => { let mut conversion_code = String::new(); writeln!( &mut conversion_code, From 651e7caca896eb4ce565ae64bf71cedd9a4e72e2 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:36:51 +0530 Subject: [PATCH 18/20] Adds docs --- codegen-v2/src/codegen/cpp/code_gen.rs | 4 ++++ rust/tw_macros/src/code_gen.rs | 1 + rust/tw_macros/src/tw_ffi.rs | 11 +++++++++++ 3 files changed, 16 insertions(+) diff --git a/codegen-v2/src/codegen/cpp/code_gen.rs b/codegen-v2/src/codegen/cpp/code_gen.rs index e341e9a2851..ac87276c94a 100644 --- a/codegen-v2/src/codegen/cpp/code_gen.rs +++ b/codegen-v2/src/codegen/cpp/code_gen.rs @@ -23,6 +23,7 @@ pub struct TWStaticFunction { pub rust_name: String, pub args: Vec, pub return_type: String, + pub docs: Vec, } #[derive(Deserialize, Serialize, Debug)] @@ -127,6 +128,9 @@ fn generate_function_declaration( func: &TWStaticFunction, ) -> Result<()> { let func_dec = generate_function_signature(class_name, func, true)?; + for doc in &func.docs { + writeln!(file, "/// {}", doc)?; + } writeln!(file, "{func_dec};\n")?; Ok(()) } diff --git a/rust/tw_macros/src/code_gen.rs b/rust/tw_macros/src/code_gen.rs index 467a19ee147..3b311486b9b 100644 --- a/rust/tw_macros/src/code_gen.rs +++ b/rust/tw_macros/src/code_gen.rs @@ -12,6 +12,7 @@ pub struct TWStaticFunction { pub rust_name: String, pub args: Vec, pub return_type: String, + pub docs: Vec, } #[derive(Deserialize, Serialize, Debug)] diff --git a/rust/tw_macros/src/tw_ffi.rs b/rust/tw_macros/src/tw_ffi.rs index 61f291132ae..f9a897efcf5 100644 --- a/rust/tw_macros/src/tw_ffi.rs +++ b/rust/tw_macros/src/tw_ffi.rs @@ -98,11 +98,22 @@ pub fn tw_ffi(attr: TokenStream2, item: TokenStream2) -> Result { }; let class = args.class.unwrap().to_string(); + let docs = func.attrs.iter().flat_map(|attr| { + if let syn::Meta::NameValue(meta) = &attr.meta { + if meta.path.is_ident("doc") { + if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit), .. }) = &meta.value { + return Some(lit.value().trim().to_string()); + } + } + } + None + }).collect::>(); let static_function = TWStaticFunction { name: args.name.unwrap().to_string(), rust_name: func_name, args: func_args, return_type, + docs, }; if let Ok(out_dir) = env::var("CARGO_WORKSPACE_DIR") { From 4b7d462c979b687f86dcc24fa214ae3e5a709482 Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:43:12 +0530 Subject: [PATCH 19/20] FMT --- rust/tw_macros/src/tw_ffi.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/rust/tw_macros/src/tw_ffi.rs b/rust/tw_macros/src/tw_ffi.rs index f9a897efcf5..fcf1c804f91 100644 --- a/rust/tw_macros/src/tw_ffi.rs +++ b/rust/tw_macros/src/tw_ffi.rs @@ -98,16 +98,24 @@ pub fn tw_ffi(attr: TokenStream2, item: TokenStream2) -> Result { }; let class = args.class.unwrap().to_string(); - let docs = func.attrs.iter().flat_map(|attr| { - if let syn::Meta::NameValue(meta) = &attr.meta { - if meta.path.is_ident("doc") { - if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit), .. }) = &meta.value { - return Some(lit.value().trim().to_string()); + let docs = func + .attrs + .iter() + .flat_map(|attr| { + if let syn::Meta::NameValue(meta) = &attr.meta { + if meta.path.is_ident("doc") { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit), + .. + }) = &meta.value + { + return Some(lit.value().trim().to_string()); + } } } - } - None - }).collect::>(); + None + }) + .collect::>(); let static_function = TWStaticFunction { name: args.name.unwrap().to_string(), rust_name: func_name, From cc3bd2c828d594bc6202a42436e353b06314309f Mon Sep 17 00:00:00 2001 From: gupnik <17176722+gupnik@users.noreply.github.com> Date: Mon, 27 Jan 2025 12:42:33 +0530 Subject: [PATCH 20/20] Handles nullable input --- codegen-v2/src/codegen/cpp/code_gen.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/codegen-v2/src/codegen/cpp/code_gen.rs b/codegen-v2/src/codegen/cpp/code_gen.rs index ac87276c94a..a73f1d89a9e 100644 --- a/codegen-v2/src/codegen/cpp/code_gen.rs +++ b/codegen-v2/src/codegen/cpp/code_gen.rs @@ -209,21 +209,31 @@ fn generate_return_type(func: &TWStaticFunction, converted_args: &Vec) - fn generate_conversion_code_with_var_name(ty: &str, name: &str) -> Result<(String, String)> { match ty { - "TWString *_Nonnull" | "TWString *_Nullable" => { + "TWString *_Nonnull" => { let mut conversion_code = String::new(); writeln!( &mut conversion_code, - " auto& {name}String = *reinterpret_cast({name});", - name = name + "\tauto& {name}String = *reinterpret_cast({name});\n\ + \tconst Rust::TWStringWrapper {name}RustStr = {name}String;" ) .map_err(|e| BadFormat(e.to_string()))?; + Ok((conversion_code, format!("{}RustStr.get()", name))) + } + "TWString *_Nullable" => { + let mut conversion_code = String::new(); writeln!( &mut conversion_code, - " const Rust::TWStringWrapper {name}RustStr = {name}String;", - name = name + "\tconst TW::Rust::TWString* {name}Ptr;\n\ + \tif ({name} != nullptr) {{\n\ + \t\tauto& {name}String = *reinterpret_cast({name});\n\ + \t\tconst Rust::TWStringWrapper {name}RustStr = {name}String;\n\ + \t\t{name}Ptr = {name}RustStr.get();\n\ + \t}} else {{\n\ + \t\t{name}Ptr = nullptr;\n\ + \t}}" ) .map_err(|e| BadFormat(e.to_string()))?; - Ok((conversion_code, format!("{}RustStr.get()", name))) + Ok((conversion_code, format!("{}Ptr", name))) } _ => Ok(("".to_string(), name.to_string())), }