Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .fvmrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"flutter": "3.27.4",
"flavors": {}
}
}
1 change: 1 addition & 0 deletions frb_codegen/src/binary/commands_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ fn compute_codegen_config_from_naive_command_args(args: GenerateCommandArgsPrima
dump: args.dump,
dump_all: positive_bool_arg(args.dump_all),
rust_features: args.rust_features,
use_oxidized: None,
}
}

Expand Down
2 changes: 2 additions & 0 deletions frb_codegen/src/library/codegen/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub struct Config {
pub dump: Option<Vec<ConfigDumpContent>>,
pub dump_all: Option<bool>,
pub rust_features: Option<Vec<String>>,
pub use_oxidized: Option<bool>,
}

#[derive(Debug, Serialize, Deserialize, Default)]
Expand Down Expand Up @@ -109,4 +110,5 @@ generate_merge!(
dump,
dump_all,
rust_features,
use_oxidized,
);
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use crate::codegen::generator::wire::rust::internal_config::GeneratorWireRustInt
use crate::codegen::ir::mir::ty::rust_opaque::RustOpaqueCodecMode;
use crate::codegen::Config;
use crate::library::commands::cargo_metadata::execute_cargo_metadata;
use crate::utils::dart_repository::dart_repo::DartRepository;
use crate::utils::dart_repository::get_dart_package_name;
use log::debug;
use crate::utils::path_utils::path_to_string;
use crate::utils::syn_utils::canonicalize_rust_type;
use anyhow::Context;
Expand Down Expand Up @@ -56,6 +58,7 @@ pub(super) fn parse(args: Args) -> anyhow::Result<GeneratorInternalConfig> {
let default_external_library_loader =
compute_default_external_library_loader(rust_crate_dir, dart_root, config);
let c_symbol_prefix = compute_c_symbol_prefix(dart_root)?;
let use_oxidized = config.use_oxidized.unwrap_or_else(|| detect_oxidized_dependency(dart_root));

Ok(GeneratorInternalConfig {
api_dart: GeneratorApiDartInternalConfig {
Expand All @@ -66,6 +69,7 @@ pub(super) fn parse(args: Args) -> anyhow::Result<GeneratorInternalConfig> {
dart_entrypoint_class_name: dart_output_class_name_pack.entrypoint_class_name.clone(),
dart_preamble: config.dart_preamble.clone().unwrap_or_default(),
dart_type_rename: compute_dart_type_rename(config)?,
use_oxidized,
},
wire: GeneratorWireInternalConfig {
dart: GeneratorWireDartInternalConfig {
Expand Down Expand Up @@ -204,3 +208,18 @@ fn compute_dart_type_rename(config: &Config) -> anyhow::Result<HashMap<String, S
.flatten()
.collect())
}

/// Detect if the oxidized package is in the project's dependencies.
/// When detected, fallible functions will return Result<T, E> instead of throwing.
fn detect_oxidized_dependency(dart_root: &Path) -> bool {
match DartRepository::from_path(dart_root) {
Ok(repo) => {
let has_oxidized = repo.has_dependency("oxidized");
if has_oxidized {
debug!("Detected oxidized package in dependencies - will generate Result return types");
}
has_oxidized
}
Err(_) => false,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ pub(crate) struct GeneratorApiDartInternalConfig {
pub dart_entrypoint_class_name: String,
pub dart_preamble: String,
pub dart_type_rename: HashMap<String, String>,
/// Whether oxidized package is available (auto-detected from pubspec.yaml).
/// When true, fallible functions return Result<T, E> instead of throwing.
pub use_oxidized: bool,
}
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,18 @@ fn generate_function_dart_return_type(
);
}

// If oxidized is enabled and the function is fallible, wrap in Result<T, E>
let use_oxidized = func.oxidized.unwrap_or(context.config.use_oxidized);
if use_oxidized && func.fallible() && return_stream.is_none() {
let error_type = func
.output
.error
.as_ref()
.map(|e| ApiDartGenerator::new(e.clone(), context).dart_api_type())
.unwrap_or_else(|| "Object".to_owned());
inner = format!("Result<{inner}, {error_type}>");
}

let return_future = if return_stream.is_some() {
func.stream_dart_await
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub(crate) fn generate_api_impl_normal_function(

let wire_func_name = wire_func_name(func);
let inner_func_stmt = dart2rust_codec.generate_dart2rust_inner_func_stmt(func, &wire_func_name);
let execute_func_name = generate_execute_func_name(func);
let execute_func_name = generate_execute_func_name(func, context);

let codec = generate_rust2dart_codec_object(func);
let call_ffi_args = generate_call_ffi_args(func);
Expand Down Expand Up @@ -108,10 +108,13 @@ pub(crate) fn generate_api_impl_normal_function(
})
}

fn generate_execute_func_name(func: &MirFunc) -> &str {
match func.mode {
MirFuncMode::Normal => "executeNormal",
MirFuncMode::Sync => "executeSync",
fn generate_execute_func_name(func: &MirFunc, context: WireDartGeneratorContext) -> &'static str {
let use_oxidized = func.oxidized.unwrap_or(context.api_dart_config.use_oxidized) && func.fallible();
match (func.mode, use_oxidized) {
(MirFuncMode::Normal, true) => "executeNormalAsResult",
(MirFuncMode::Normal, false) => "executeNormal",
(MirFuncMode::Sync, true) => "executeSyncAsResult",
(MirFuncMode::Sync, false) => "executeSync",
}
}

Expand Down
6 changes: 4 additions & 2 deletions frb_codegen/src/library/codegen/ir/hir/flat/type_alias.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use crate::codegen::ir::hir::flat::component::HirFlatComponent;
use crate::codegen::ir::hir::misc::serializers::serialize_syn;
use crate::codegen::ir::hir::misc::serializers::{serialize_option_syn, serialize_syn};
use serde::Serialize;
use syn::Type;
use syn::{Generics, Type};

#[derive(Clone, Debug, Serialize)]
pub struct HirFlatTypeAlias {
pub(crate) ident: String,
#[serde(serialize_with = "serialize_syn")]
pub(crate) target: Type,
#[serde(serialize_with = "serialize_option_syn")]
pub(crate) generics: Option<Generics>,
}

impl HirFlatComponent<String> for HirFlatTypeAlias {
Expand Down
10 changes: 10 additions & 0 deletions frb_codegen/src/library/codegen/ir/hir/misc/serializers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ pub(crate) fn serialize_vec_syn<T: ToTokens, S: Serializer>(
values.serialize(s)
}

pub(crate) fn serialize_option_syn<T: ToTokens, S: Serializer>(
value: &Option<T>,
s: S,
) -> Result<S::Ok, S::Error> {
match value {
Some(v) => quote::quote!(#v).to_string().serialize(s),
None => s.serialize_none(),
}
}

// pub(crate) fn serialize_item_trait<S: Serializer>(x: &ItemTrait, s: S) -> Result<S::Ok, S::Error> {
// s.serialize_str(&format!("ItemTrait(ident={})", x.ident))
// }
Expand Down
1 change: 1 addition & 0 deletions frb_codegen/src/library/codegen/ir/mir/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub struct MirFunc {
pub rust_call_code: Option<String>,
pub rust_aop_after: Option<String>,
pub impl_mode: MirFuncImplMode,
pub oxidized: Option<bool>,
// Currently, we use serde only for tests. Since lineno can be unstable, we skip this field for comparison
#[serde(skip_serializing)]
pub src_lineno_pseudo: usize,
Expand Down
16 changes: 15 additions & 1 deletion frb_codegen/src/library/codegen/parser/hir/flat/exporter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::codegen::ir::hir::flat::pack::HirFlatPack;
use crate::codegen::ir::hir::flat::struct_or_enum::{HirFlatEnum, HirFlatStruct};
use crate::codegen::ir::hir::flat::traits::HirFlatTrait;
use crate::codegen::ir::hir::flat::type_alias::HirFlatTypeAlias;
use log::debug;
use std::collections::HashMap;
use std::fmt::{Debug, Display};
Expand All @@ -21,7 +22,20 @@ impl HirFlatPack {
}

pub(crate) fn types_map(&self) -> HashMap<String, Type> {
vec_to_map_with_warn(&self.types, |x| (x.ident.clone(), x.target.clone()))
// Only include non-generic type aliases here.
// Generic type aliases are handled separately via generic_types_map()
// to support proper generic parameter substitution.
vec_to_map_with_warn(
&self.types.iter().filter(|x| x.generics.is_none()).collect::<Vec<_>>(),
|x| (x.ident.clone(), x.target.clone()),
)
}

pub(crate) fn generic_types_map(&self) -> HashMap<String, &HirFlatTypeAlias> {
vec_to_map_with_warn(
&self.types.iter().filter(|x| x.generics.is_some()).collect::<Vec<_>>(),
|x| (x.ident.clone(), *x),
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ use syn::ItemType;

pub(crate) fn parse_syn_item_type(item_type: ItemType) -> Option<HirFlatTypeAlias> {
// debug!("parse_syn_item_type item_type={item_type:?}");
if item_type.generics.where_clause.is_none() && item_type.generics.lt_token.is_none() {
Some(HirFlatTypeAlias {
ident: item_type.ident.to_string(),
target: *item_type.ty,
})
} else {
None
}
let has_generics =
item_type.generics.where_clause.is_some() || item_type.generics.lt_token.is_some();
Some(HirFlatTypeAlias {
ident: item_type.ident.to_string(),
target: *item_type.ty,
generics: if has_generics {
Some(item_type.generics)
} else {
None
},
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,28 @@ use syn::Type;
use topological_sort::TopologicalSort;

pub(crate) fn transform(mut pack: HirFlatPack) -> anyhow::Result<HirFlatPack> {
let map_raw = (pack.types.iter())
// Separate generic and non-generic type aliases
// Generic type aliases (e.g., `type Result<T> = std::result::Result<T, MyError>`)
// are kept as-is since resolve_type_aliases doesn't handle generic substitution yet.
// TODO: Enhance resolve_type_aliases to handle generic type alias substitution
let (generic_aliases, non_generic_aliases): (Vec<_>, Vec<_>) =
pack.types.into_iter().partition(|x| x.generics.is_some());

let map_raw = non_generic_aliases
.iter()
.map(|x| (x.ident.clone(), x.target.clone()))
.collect();
let map_transformed = resolve_type_aliases(map_raw);
let vec_transformed = (map_transformed.into_iter())
.map(|(ident, target)| HirFlatTypeAlias { ident, target })
.map(|(ident, target)| HirFlatTypeAlias {
ident,
target,
generics: None,
})
.collect_vec();

pack.types = vec_transformed;
// Combine resolved non-generic aliases with the original generic aliases
pack.types = vec_transformed.into_iter().chain(generic_aliases).collect();

Ok(pack)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ impl FrbAttributes {
self.any_eq(&FrbAttribute::Init)
}

pub(crate) fn oxidized(&self) -> bool {
self.any_eq(&FrbAttribute::Oxidized)
}

pub(crate) fn ignore(&self) -> bool {
self.any_eq(&FrbAttribute::Ignore)
}
Expand Down Expand Up @@ -307,6 +311,7 @@ mod frb_keyword {
syn::custom_keyword!(dart_type);
syn::custom_keyword!(ui_state);
syn::custom_keyword!(ui_mutation);
syn::custom_keyword!(oxidized);
}

struct FrbAttributesInner(Vec<FrbAttribute>);
Expand Down Expand Up @@ -363,6 +368,7 @@ enum FrbAttribute {
SemiSerialize,
UiState,
UiMutation,
Oxidized,
}

impl Parse for FrbAttribute {
Expand Down Expand Up @@ -430,7 +436,8 @@ impl Parse for FrbAttribute {
.or_else(|| parse_keyword::<ui_state, _>(input, &lookahead, ui_state, UiState))
.or_else(|| {
parse_keyword::<ui_mutation, _>(input, &lookahead, ui_mutation, UiMutation)
});
})
.or_else(|| parse_keyword::<oxidized, _>(input, &lookahead, oxidized, Oxidized));
if let Some(keyword_output) = keyword_output {
return keyword_output;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ pub(super) fn parse_auto_accessor_of_field(
rust_aop_after: (ty_struct.ui_state && accessor_mode == MirFuncAccessorMode::Setter)
.then(|| UI_MUTATION_FUNCTION_RUST_AOP_AFTER.to_owned()),
impl_mode: MirFuncImplMode::Normal,
oxidized: None,
src_lineno_pseudo: compute_src_lineno_pseudo(struct_name, field),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ fn parse_constant(
rust_call_code: Some(rust_call_code),
rust_aop_after: None,
impl_mode: MirFuncImplMode::Normal,
oxidized: None,
src_lineno_pseudo: constant.item_const.span().start().line,
})))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ impl<'a, 'b> FunctionParser<'a, 'b> {
rust_aop_after: (attributes.ui_mutation())
.then(|| UI_MUTATION_FUNCTION_RUST_AOP_AFTER.to_owned()),
impl_mode,
oxidized: if attributes.oxidized() { Some(true) } else { None },
src_lineno_pseudo: src_lineno,
}))
}
Expand Down
5 changes: 5 additions & 0 deletions frb_codegen/src/library/codegen/parser/mir/parser/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::codegen::ir::early_generator::trait_def_info::IrEarlyGeneratorTraitDe
use crate::codegen::ir::hir::flat::struct_or_enum::HirFlatEnum;
use crate::codegen::ir::hir::flat::struct_or_enum::HirFlatStruct;
use crate::codegen::ir::hir::flat::traits::HirFlatTrait;
use crate::codegen::ir::hir::flat::type_alias::HirFlatTypeAlias;
use crate::codegen::ir::mir::custom_ser_des::MirCustomSerDes;
use crate::codegen::ir::mir::func::MirFuncOwnerInfo;
use crate::codegen::ir::mir::pack::{MirEnumPool, MirStructPool};
Expand Down Expand Up @@ -58,6 +59,7 @@ pub(crate) struct TypeParser<'a> {
src_enums: HashMap<String, &'a HirFlatEnum>,
pub(super) src_traits: HashMap<String, &'a HirFlatTrait>,
src_types: HashMap<String, Type>,
pub(crate) src_generic_types: HashMap<String, &'a HirFlatTypeAlias>,
pub(super) proxied_types: Vec<IrEarlyGeneratorProxiedType>,
pub(super) trait_def_infos: Vec<IrEarlyGeneratorTraitDefInfo>,
pub(super) custom_ser_des_infos: Vec<MirCustomSerDes>,
Expand All @@ -77,6 +79,7 @@ impl<'a> TypeParser<'a> {
ir_pack.hir_flat_pack.enums_map(),
ir_pack.hir_flat_pack.traits_map(),
ir_pack.hir_flat_pack.types_map(),
ir_pack.hir_flat_pack.generic_types_map(),
ir_pack.proxied_types.clone(),
ir_pack.trait_def_infos.clone(),
)
Expand All @@ -87,6 +90,7 @@ impl<'a> TypeParser<'a> {
src_enums: HashMap<String, &'a HirFlatEnum>,
src_traits: HashMap<String, &'a HirFlatTrait>,
src_types: HashMap<String, Type>,
src_generic_types: HashMap<String, &'a HirFlatTypeAlias>,
proxied_types: Vec<IrEarlyGeneratorProxiedType>,
trait_def_infos: Vec<IrEarlyGeneratorTraitDefInfo>,
) -> Self {
Expand All @@ -95,6 +99,7 @@ impl<'a> TypeParser<'a> {
src_enums,
src_traits,
src_types,
src_generic_types,
proxied_types,
trait_def_infos,
custom_ser_des_infos: Default::default(),
Expand Down
Loading
Loading