Skip to content

Commit

Permalink
fix wasm compatibility when no_std is used in Abigen (#825)
Browse files Browse the repository at this point in the history
Abigen with `no_std` should now be wasm friendly and use the fuels crates directly instead of through `fuels` which causes non-wasm compatible dependencies to be included atm.
  • Loading branch information
segfault-magnet authored Feb 9, 2023
1 parent 4cb767e commit 1d142fe
Show file tree
Hide file tree
Showing 17 changed files with 220 additions and 82 deletions.
1 change: 0 additions & 1 deletion ci_checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@ cargo fmt --all --verbose -- --check &&
cargo clippy --all-targets --all-features &&
cargo test --all-targets --all-features &&
cargo test --all-targets &&
cargo test --all-targets &&
# May fail after `cargo doc`
cargo run --bin check-docs
61 changes: 49 additions & 12 deletions packages/fuels-code-gen/src/program_bindings/abigen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use inflector::Inflector;
use itertools::Itertools;
use proc_macro2::TokenStream;
use quote::quote;
use regex::Regex;

use crate::{
error::Result,
Expand Down Expand Up @@ -38,13 +39,39 @@ impl Abigen {
let generated_code = Self::generate_code(no_std, parsed_targets)?;

let use_statements = generated_code.use_statements_for_uniquely_named_types();
let code = generated_code.code;

let code = if no_std {
Self::wasm_paths_hotfix(generated_code.code)
} else {
generated_code.code
};

Ok(quote! {
#code
#use_statements
})
}
fn wasm_paths_hotfix(code: TokenStream) -> TokenStream {
[
(r#"::\s*fuels\s*::\s*core"#, "::fuels_core"),
(r#"::\s*fuels\s*::\s*macros"#, "::fuels_macros"),
(r#"::\s*fuels\s*::\s*programs"#, "::fuels_programs"),
(r#"::\s*fuels\s*::\s*signers"#, "::fuels_signers"),
(r#"::\s*fuels\s*::\s*tx"#, "::fuel_tx"),
(r#"::\s*fuels\s*::\s*types"#, "::fuels_types"),
(r#"::\s*std\s*::\s*string"#, "::alloc::string"),
(r#"::\s*std\s*::\s*format"#, "::alloc::format"),
(r#"::\s*std\s*::\s*vec"#, "::alloc::vec"),
(r#"::\s*std\s*::\s*boxed"#, "::alloc::boxed"),
]
.map(|(reg_expr_str, substitute)| (Regex::new(reg_expr_str).unwrap(), substitute))
.into_iter()
.fold(code.to_string(), |code, (regex, wasm_include)| {
regex.replace_all(&code, wasm_include).to_string()
})
.parse()
.expect("Wasm hotfix failed!")
}

fn generate_code(
no_std: bool,
Expand All @@ -54,7 +81,7 @@ impl Abigen {
let shared_types = Self::filter_shared_types(all_custom_types);

let bindings = Self::generate_all_bindings(parsed_targets, no_std, &shared_types)?;
let shared_types = Self::generate_shared_types(shared_types)?;
let shared_types = Self::generate_shared_types(shared_types, no_std)?;

Ok(shared_types
.append(bindings)
Expand All @@ -81,10 +108,10 @@ impl Abigen {
) -> Result<GeneratedCode> {
let mod_name = ident(&format!("{}_mod", &target.name.to_snake_case()));

let types = generate_types(target.source.types.clone(), shared_types)?;
let types = generate_types(target.source.types.clone(), shared_types, no_std)?;
let bindings = generate_bindings(target, no_std, shared_types)?;

Ok(limited_std_prelude()
Ok(limited_std_prelude(no_std)
.append(types)
.append(bindings)
.wrap_in_mod(&mod_name))
Expand All @@ -97,13 +124,16 @@ impl Abigen {
.collect()
}

fn generate_shared_types(shared_types: HashSet<FullTypeDeclaration>) -> Result<GeneratedCode> {
let types = generate_types(shared_types, &HashSet::default())?;
fn generate_shared_types(
shared_types: HashSet<FullTypeDeclaration>,
no_std: bool,
) -> Result<GeneratedCode> {
let types = generate_types(shared_types, &HashSet::default(), no_std)?;

if types.is_empty() {
Ok(Default::default())
} else {
Ok(limited_std_prelude()
Ok(limited_std_prelude(no_std)
.append(types)
.wrap_in_mod(&ident("shared_types")))
}
Expand Down Expand Up @@ -132,18 +162,25 @@ impl Abigen {
}
}

fn limited_std_prelude() -> GeneratedCode {
fn limited_std_prelude(no_std: bool) -> GeneratedCode {
let lib = if no_std {
quote! {::alloc}
} else {
quote! {::std}
};

let code = quote! {
use ::std::{
use ::core::{
clone::Clone,
convert::{Into, TryFrom, From},
format,
iter::IntoIterator,
iter::Iterator,
marker::Sized,
panic, vec,
string::ToString
panic,
};

use #lib::{string::ToString, format, vec};

};

GeneratedCode {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub(crate) fn contract_bindings(
let provider = self.wallet.get_provider()?;
wallet.set_provider(provider.clone());

::std::result::Result::Ok(Self { contract_id: self.contract_id.clone(), wallet: wallet, log_decoder: self.log_decoder.clone()})
::core::result::Result::Ok(Self { contract_id: self.contract_id.clone(), wallet: wallet, log_decoder: self.log_decoder.clone()})
}

pub async fn get_balances(&self) -> ::fuels::types::errors::Result<::std::collections::HashMap<::std::string::String, u64>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub(crate) fn predicate_bindings(
}

pub fn load_from(file_path: &str) -> ::fuels::types::errors::Result<Self> {
::std::result::Result::Ok(Self::new(::std::fs::read(file_path)?))
::core::result::Result::Ok(Self::new(::std::fs::read(file_path)?))
}

pub fn address(&self) -> &::fuels::types::bech32::Bech32Address {
Expand All @@ -62,7 +62,7 @@ pub(crate) fn predicate_bindings(
pub async fn receive(&self, from: &::fuels::signers::wallet::WalletUnlocked,
amount: u64,
asset_id: ::fuels::types::AssetId,
tx_parameters: ::std::option::Option<::fuels::core::parameters::TxParameters>
tx_parameters: ::core::option::Option<::fuels::core::parameters::TxParameters>
) -> ::fuels::types::errors::Result<(::std::string::String, ::std::vec::Vec<::fuels::tx::Receipt>)> {
let tx_parameters = tx_parameters.unwrap_or_default();
from
Expand All @@ -78,7 +78,7 @@ pub(crate) fn predicate_bindings(
pub async fn spend(&self, to: &::fuels::signers::wallet::WalletUnlocked,
amount: u64,
asset_id: ::fuels::types::AssetId,
tx_parameters: ::std::option::Option<::fuels::core::parameters::TxParameters>
tx_parameters: ::core::option::Option<::fuels::core::parameters::TxParameters>
) -> ::fuels::types::errors::Result<::std::vec::Vec<::fuels::tx::Receipt>> {
let tx_parameters = tx_parameters.unwrap_or_default();
to
Expand Down
4 changes: 2 additions & 2 deletions packages/fuels-code-gen/src/program_bindings/abigen/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ pub(crate) fn logs_lookup_instantiation_code(
let resolved_logs = resolve_logs(logged_types, shared_types);
let log_id_param_type_pairs = generate_log_id_param_type_pairs(&resolved_logs);
let contract_id = contract_id
.map(|id| quote! { ::std::option::Option::Some(#id) })
.unwrap_or_else(|| quote! {::std::option::Option::None});
.map(|id| quote! { ::core::option::Option::Some(#id) })
.unwrap_or_else(|| quote! {::core::option::Option::None});
quote! {::fuels::programs::logs::log_type_lookup(&[#(#log_id_param_type_pairs),*], #contract_id)}
}

Expand Down
36 changes: 33 additions & 3 deletions packages/fuels-code-gen/src/program_bindings/custom_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,16 @@ mod utils;
pub(crate) fn generate_types<T: IntoIterator<Item = FullTypeDeclaration>>(
types: T,
shared_types: &HashSet<FullTypeDeclaration>,
no_std: bool,
) -> Result<GeneratedCode> {
HashSet::from_iter(types)
.difference(shared_types)
.filter(|ttype| !should_skip_codegen(&ttype.type_field))
.filter_map(|ttype| {
if ttype.is_struct_type() {
Some(expand_custom_struct(ttype, shared_types))
Some(expand_custom_struct(ttype, shared_types, no_std))
} else if ttype.is_enum_type() {
Some(expand_custom_enum(ttype, shared_types))
Some(expand_custom_enum(ttype, shared_types, no_std))
} else {
None
}
Expand Down Expand Up @@ -133,6 +134,7 @@ mod tests {
let actual = expand_custom_enum(
&FullTypeDeclaration::from_counterpart(&p, &types),
&HashSet::default(),
false,
)?
.code;

Expand All @@ -147,6 +149,8 @@ mod tests {
::fuels::macros::Tokenizable,
::fuels::macros::TryFrom
)]
#[FuelsTypesPath("::fuels::types")]
#[FuelsCorePath("::fuels::core")]
pub enum MatchaTea<> {
LongIsland(u64),
MoscowMule(bool)
Expand All @@ -170,6 +174,7 @@ mod tests {
expand_custom_enum(
&FullTypeDeclaration::from_counterpart(&p, &types),
&HashSet::default(),
false,
)
.expect_err("Was able to construct an enum without variants");

Expand Down Expand Up @@ -224,6 +229,7 @@ mod tests {
let actual = expand_custom_enum(
&FullTypeDeclaration::from_counterpart(&p, &types),
&HashSet::default(),
false,
)?
.code;

Expand All @@ -238,6 +244,8 @@ mod tests {
::fuels::macros::Tokenizable,
::fuels::macros::TryFrom
)]
#[FuelsTypesPath("::fuels::types")]
#[FuelsCorePath("::fuels::core")]
pub enum Amsterdam<> {
Infrastructure(self::Building),
Service(u32)
Expand Down Expand Up @@ -290,6 +298,7 @@ mod tests {
let actual = expand_custom_enum(
&FullTypeDeclaration::from_counterpart(&p, &types),
&HashSet::default(),
false,
)?
.code;

Expand All @@ -304,6 +313,8 @@ mod tests {
::fuels::macros::Tokenizable,
::fuels::macros::TryFrom
)]
#[FuelsTypesPath("::fuels::types")]
#[FuelsCorePath("::fuels::core")]
pub enum SomeEnum < > {
SomeArr([u64; 7usize])
}
Expand Down Expand Up @@ -368,6 +379,7 @@ mod tests {
let actual = expand_custom_enum(
&FullTypeDeclaration::from_counterpart(&p, &types),
&HashSet::default(),
false,
)?
.code;

Expand All @@ -382,6 +394,8 @@ mod tests {
::fuels::macros::Tokenizable,
::fuels::macros::TryFrom
)]
#[FuelsTypesPath("::fuels::types")]
#[FuelsCorePath("::fuels::core")]
pub enum EnumLevel3<> {
El2(self::EnumLevel2)
}
Expand Down Expand Up @@ -447,6 +461,7 @@ mod tests {
let actual = expand_custom_struct(
&FullTypeDeclaration::from_counterpart(&p, &types),
&HashSet::default(),
false,
)?
.code;

Expand All @@ -460,6 +475,8 @@ mod tests {
::fuels::macros::Tokenizable,
::fuels::macros::TryFrom
)]
#[FuelsTypesPath("::fuels::types")]
#[FuelsCorePath("::fuels::core")]
pub struct Cocktail < > {
pub long_island: bool,
pub cosmopolitan: u64,
Expand All @@ -485,6 +502,7 @@ mod tests {
let actual = expand_custom_struct(
&FullTypeDeclaration::from_counterpart(&p, &types),
&HashSet::default(),
false,
)?
.code;

Expand All @@ -498,6 +516,8 @@ mod tests {
::fuels::macros::Tokenizable,
::fuels::macros::TryFrom
)]
#[FuelsTypesPath("::fuels::types")]
#[FuelsCorePath("::fuels::core")]
pub struct SomeEmptyStruct < > {}
};

Expand Down Expand Up @@ -551,6 +571,7 @@ mod tests {
let actual = expand_custom_struct(
&FullTypeDeclaration::from_counterpart(&p, &types),
&HashSet::default(),
false,
)?
.code;

Expand All @@ -564,6 +585,8 @@ mod tests {
::fuels::macros::Tokenizable,
::fuels::macros::TryFrom
)]
#[FuelsTypesPath("::fuels::types")]
#[FuelsCorePath("::fuels::core")]
pub struct Cocktail < > {
pub long_island: self::Shaker,
pub mojito: u32
Expand Down Expand Up @@ -657,6 +680,7 @@ mod tests {
let actual = expand_custom_struct(
&FullTypeDeclaration::from_counterpart(s1, &types),
&HashSet::default(),
false,
)?
.code;

Expand All @@ -670,6 +694,8 @@ mod tests {
::fuels::macros::Tokenizable,
::fuels::macros::TryFrom
)]
#[FuelsTypesPath("::fuels::types")]
#[FuelsCorePath("::fuels::core")]
pub struct MyStruct1 < > {
pub x: u64,
pub y: ::fuels::types::Bits256
Expand All @@ -683,6 +709,7 @@ mod tests {
let actual = expand_custom_struct(
&FullTypeDeclaration::from_counterpart(s2, &types),
&HashSet::default(),
false,
)?
.code;

Expand All @@ -696,6 +723,8 @@ mod tests {
::fuels::macros::Tokenizable,
::fuels::macros::TryFrom
)]
#[FuelsTypesPath("::fuels::types")]
#[FuelsCorePath("::fuels::core")]
pub struct MyStruct2 < > {
pub x: bool,
pub y: self::MyStruct1
Expand All @@ -714,7 +743,8 @@ mod tests {
let shared_types = HashSet::from_iter(types.iter().take(1).cloned());

// when
let generated_code = generate_types(types, &shared_types).expect("Should have succeeded.");
let generated_code =
generate_types(types, &shared_types, false).expect("Should have succeeded.");

// then
assert_eq!(
Expand Down
Loading

0 comments on commit 1d142fe

Please sign in to comment.