From 82ca2890a4666ff1b3635e8b33eedf6e0435bd01 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sat, 21 Nov 2020 22:47:04 +0100 Subject: [PATCH] Copypaste enum analysis and function generation to bitfield --- src/analysis/flags.rs | 136 ++++++++++++++++++++++++++++++++++++++++++ src/analysis/mod.rs | 30 ++++++++++ src/codegen/flags.rs | 125 ++++++++++++++++++++++++-------------- 3 files changed, 246 insertions(+), 45 deletions(-) create mode 100644 src/analysis/flags.rs diff --git a/src/analysis/flags.rs b/src/analysis/flags.rs new file mode 100644 index 000000000..07197b798 --- /dev/null +++ b/src/analysis/flags.rs @@ -0,0 +1,136 @@ +use super::{function_parameters::TransformationType, imports::Imports, *}; +use crate::{ + config::gobjects::GObject, + env::Env, + library::{self, Transfer}, + nameutil::*, + traits::*, +}; + +use log::info; + +#[derive(Debug, Default)] +pub struct Info { + pub full_name: String, + pub type_id: library::TypeId, + pub name: String, + pub functions: Vec, + pub specials: special_functions::Infos, +} + +impl Info { + //TODO: add test in tests/ for panic + pub fn type_<'a>(&self, library: &'a library::Library) -> &'a library::Bitfield { + let type_ = library + .type_(self.type_id) + .maybe_ref() + .unwrap_or_else(|| panic!("{} is not an flags.", self.full_name)); + type_ + } +} + +pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option { + info!("Analyzing flags {}", obj.name); + + if !obj.status.need_generate() { + return None; + } + + if !obj + .type_id + .map_or(false, |tid| tid.ns_id == namespaces::MAIN) + { + return None; + } + + let flags_tid = env.library.find_type(0, &obj.name)?; + let type_ = env.type_(flags_tid); + let flags: &library::Bitfield = type_.maybe_ref()?; + + let name = split_namespace_name(&obj.name).1; + + // Mark the type as available within the bitfield namespace: + imports.add_defined(name); + + let has_get_type = flags.glib_get_type.is_some(); + if has_get_type { + imports.add("glib::Type"); + imports.add("glib::StaticType"); + imports.add("glib::value::Value"); + imports.add("glib::value::SetValue"); + imports.add("glib::value::FromValue"); + imports.add("glib::value::FromValueOptional"); + imports.add("gobject_sys"); + } + + if obj.generate_display_trait { + imports.add("std::fmt"); + } + + let mut functions = functions::analyze( + env, + &flags.functions, + flags_tid, + false, + false, + obj, + imports, + None, + None, + ); + + // Gir does not currently mark the first parameter of associated bitfield functions - + // that are identical to its bitfield type - as instance parameter since most languages + // do not support this. + for f in &mut functions { + if f.parameters.c_parameters.is_empty() { + continue; + } + + let first_param = &mut f.parameters.c_parameters[0]; + + if first_param.typ == flags_tid { + first_param.instance_parameter = true; + + let t = f + .parameters + .transformations + .iter_mut() + .find(|t| t.ind_c == 0) + .unwrap(); + + if let TransformationType::ToGlibScalar { name, .. } = &mut t.transformation_type { + *name = "self".to_owned(); + } else { + panic!( + "Enum function instance param must be passed as scalar, not {:?}", + t.transformation_type + ); + } + } + } + + // Flags to_string functions provide static strings and need special error handling. + if let Some(to_string) = functions.iter_mut().find(|f| f.name == "to_string") { + if let Some(ret_param) = &to_string.ret.parameter { + if ret_param.transfer == Transfer::None && to_string.status.need_generate() { + to_string.name = "to_str".to_owned(); + imports.add("std::ffi::CStr"); + } + } + } + + let specials = special_functions::extract(&mut functions); + + special_functions::analyze_imports(&specials, imports); + + let info = Info { + full_name: obj.name.clone(), + type_id: flags_tid, + name: name.to_owned(), + functions, + specials, + }; + + Some(info) +} diff --git a/src/analysis/mod.rs b/src/analysis/mod.rs index ccc06ee0d..1b3fc12e7 100644 --- a/src/analysis/mod.rs +++ b/src/analysis/mod.rs @@ -15,6 +15,7 @@ pub mod constants; pub mod conversion_type; pub mod enums; pub mod ffi_type; +pub mod flags; pub mod function_parameters; pub mod functions; pub mod general; @@ -48,6 +49,9 @@ pub struct Analysis { pub constants: Vec, pub enumerations: Vec, pub enum_imports: Imports, + + pub flags: Vec, + pub flags_imports: Imports, } pub fn run(env: &mut Env) { @@ -90,6 +94,8 @@ pub fn run(env: &mut Env) { analyze_enums(env); + analyze_flags(env); + analyze_constants(env); // Analyze free functions as the last step once all types are analyzed @@ -120,6 +126,30 @@ fn analyze_enums(env: &mut Env) { env.analysis.enum_imports = imports; } +fn analyze_flags(env: &mut Env) { + let mut imports = Imports::new(&env.library); + imports.add("glib::translate::*"); + imports.add(env.main_sys_crate_name()); + + for obj in env.config.objects.values() { + if obj.status.ignored() { + continue; + } + let tid = match env.library.find_type(0, &obj.name) { + Some(x) => x, + None => continue, + }; + + if let Type::Bitfield(_) = env.library.type_(tid) { + if let Some(info) = flags::new(env, obj, &mut imports) { + env.analysis.flags.push(info); + } + } + } + + env.analysis.flags_imports = imports; +} + fn analyze_global_functions(env: &mut Env) { let ns = env.library.namespace(library::MAIN_NAMESPACE); diff --git a/src/codegen/flags.rs b/src/codegen/flags.rs index 59447ccfb..4da0e7b0f 100644 --- a/src/codegen/flags.rs +++ b/src/codegen/flags.rs @@ -1,5 +1,7 @@ +use super::{function, trait_impls}; use crate::{ - analysis::{imports::Imports, namespaces}, + analysis::flags::Info, + analysis::functions::{Info as FuncInfo, Visibility}, codegen::general::{ self, cfg_deprecated, derives, version_condition, version_condition_string, }, @@ -16,64 +18,69 @@ use std::{ }; pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec) { - let configs: Vec<&GObject> = env - .config - .objects - .values() - .filter(|c| { - c.status.need_generate() && c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN) - }) - .collect(); - let has_any = configs - .iter() - .any(|c| matches!(*env.library.type_(c.type_id.unwrap()), Type::Bitfield(_))); - - if !has_any { + if env.analysis.flags.is_empty() { return; } let path = root_path.join("flags.rs"); file_saver::save_to_file(path, env.config.make_backup, |w| { - let mut imports = Imports::new(&env.library); - imports.add(env.main_sys_crate_name()); - imports.add("glib::translate::*"); - - for config in &configs { - if let Type::Bitfield(ref flags) = *env.library.type_(config.type_id.unwrap()) { - if flags.glib_get_type.is_some() { - imports.add("glib::Type"); - imports.add("glib::StaticType"); - imports.add("glib::value::Value"); - imports.add("glib::value::SetValue"); - imports.add("glib::value::FromValue"); - imports.add("glib::value::FromValueOptional"); - imports.add("gobject_sys"); - break; - } - } - } - general::start_comments(w, &env.config)?; - general::uses(w, env, &imports)?; + general::uses(w, env, &env.analysis.flags_imports)?; writeln!(w)?; mod_rs.push("\nmod flags;".into()); - for config in &configs { - if let Type::Bitfield(ref flags) = *env.library.type_(config.type_id.unwrap()) { - if let Some(cfg) = version_condition_string(env, flags.version, false, 0) { - mod_rs.push(cfg); - } - mod_rs.push(format!("pub use self::flags::{};", flags.name)); - generate_flags(env, w, flags, config)?; + for flags_analysis in &env.analysis.flags { + let config = &env.config.objects[&flags_analysis.full_name]; + let flags = flags_analysis.type_(&env.library); + + if let Some(cfg) = version_condition_string(env, flags.version, false, 0) { + mod_rs.push(cfg); } + mod_rs.push(format!("pub use self::flags::{};", flags.name)); + generate_flags(env, w, flags, config, flags_analysis)?; } Ok(()) }); } +fn generate_static_to_str(env: &Env, w: &mut dyn Write, function: &FuncInfo) -> Result<()> { + writeln!(w)?; + version_condition(w, env, function.version, false, 1)?; + + let vis = match function.visibility { + Visibility::Public => "pub ", + _ => "", + }; + + writeln!(w, "\t{}fn {}(self) -> &'static str {{", vis, function.name)?; + + // TODO: Can we somehow reuse function_body_chunk::Builder here? + writeln!( + w, + "\ +\t\tunsafe {{ +\t\t\t{}::{}(self.to_glib()) +\t\t\t\t.as_ref() +\t\t\t\t.map(|ptr| CStr::from_ptr(ptr).to_str().unwrap()) +\t\t\t\t.unwrap_or(\"NONE\") +\t\t}} +\t}}", + env.main_sys_crate_name(), + function.glib_name + )?; + + Ok(()) +} + #[allow(clippy::write_literal)] -fn generate_flags(env: &Env, w: &mut dyn Write, flags: &Bitfield, config: &GObject) -> Result<()> { +fn generate_flags( + env: &Env, + w: &mut dyn Write, + flags: &Bitfield, + config: &GObject, + analysis: &Info, +) -> Result<()> { let sys_crate_name = env.main_sys_crate_name(); cfg_deprecated(w, env, flags.deprecated_version, false, 0)?; version_condition(w, env, flags.version, false, 0)?; @@ -108,12 +115,40 @@ fn generate_flags(env: &Env, w: &mut dyn Write, flags: &Bitfield, config: &GObje writeln!( w, - "{}", - " } -} + " }} +}} " )?; + let functions = analysis + .functions + .iter() + .filter(|f| f.status.need_generate()) + .collect::>(); + + if !functions.is_empty() { + version_condition(w, env, flags.version, false, 0)?; + write!(w, "impl {} {{", analysis.name)?; + for func_analysis in functions { + if func_analysis.name == "to_str" { + generate_static_to_str(env, w, func_analysis)?; + } else { + function::generate(w, env, func_analysis, false, false, 1)?; + } + } + writeln!(w, "}}")?; + writeln!(w)?; + } + + trait_impls::generate( + w, + &analysis.name, + &analysis.functions, + &analysis.specials, + None, + version_condition_string(env, flags.version, false, 0).as_deref(), + )?; + version_condition(w, env, flags.version, false, 0)?; writeln!( w,