Skip to content

Commit

Permalink
Copypaste enum analysis and function generation to bitfield
Browse files Browse the repository at this point in the history
  • Loading branch information
MarijnS95 committed Nov 21, 2020
1 parent c3ea369 commit 82ca289
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 45 deletions.
136 changes: 136 additions & 0 deletions src/analysis/flags.rs
Original file line number Diff line number Diff line change
@@ -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<functions::Info>,
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> {
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)
}
30 changes: 30 additions & 0 deletions src/analysis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -48,6 +49,9 @@ pub struct Analysis {
pub constants: Vec<constants::Info>,
pub enumerations: Vec<enums::Info>,
pub enum_imports: Imports,

pub flags: Vec<flags::Info>,
pub flags_imports: Imports,
}

pub fn run(env: &mut Env) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand Down
125 changes: 80 additions & 45 deletions src/codegen/flags.rs
Original file line number Diff line number Diff line change
@@ -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,
},
Expand All @@ -16,64 +18,69 @@ use std::{
};

pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec<String>) {
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)?;
Expand Down Expand Up @@ -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::<Vec<_>>();

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,
Expand Down

0 comments on commit 82ca289

Please sign in to comment.