Skip to main content

libgir/analysis/
trampolines.rs

1use log::error;
2
3use super::{
4    bounds::{BoundType, Bounds},
5    conversion_type::ConversionType,
6    ffi_type::used_ffi_type,
7    ref_mode::RefMode,
8    rust_type::RustType,
9    trampoline_parameters::{self, Parameters},
10};
11use crate::{
12    config::{self, gobjects::GObject},
13    env::Env,
14    library,
15    nameutil::signal_to_snake,
16    parser::is_empty_c_type,
17    traits::IntoString,
18    version::Version,
19};
20
21#[derive(Debug, Clone)]
22pub struct Trampoline {
23    pub name: String,
24    pub parameters: Parameters,
25    pub ret: library::Parameter,
26    // This field is used for user callbacks in `codegen::function_body_chunk` when generating
27    // inner C functions. We need to have the bound name in order to create variables and also to
28    // pass to the C function bounds (otherwise it won't compile because it doesn't know how to
29    // infer the bounds).
30    pub bound_name: String,
31    pub bounds: Bounds,
32    pub version: Option<Version>,
33    pub inhibit: bool,
34    pub concurrency: library::Concurrency,
35    pub is_notify: bool,
36    pub scope: Option<gir_parser::FunctionScope>,
37    /// It's used to group callbacks
38    pub user_data_index: usize,
39    pub destroy_index: usize,
40    pub nullable: bool,
41    /// This field is used to give the type name when generating the "IsA<X>"
42    /// part.
43    pub type_name: String,
44}
45
46pub type Trampolines = Vec<Trampoline>;
47
48pub fn analyze(
49    env: &Env,
50    signal: &library::Signal,
51    type_tid: library::TypeId,
52    in_trait: bool,
53    fundamental_type: bool,
54    configured_signals: &[&config::signals::Signal],
55    obj: &GObject,
56    used_types: &mut Vec<String>,
57    version: Option<Version>,
58) -> Result<Trampoline, Vec<String>> {
59    let errors = closure_errors(env, signal);
60    if !errors.is_empty() {
61        warn_main!(
62            type_tid,
63            "Can't generate {} trampoline for signal '{}'",
64            type_tid.full_name(&env.library),
65            signal.name
66        );
67        return Err(errors);
68    }
69
70    let is_notify = signal.name.starts_with("notify::");
71
72    let name = format!("{}_trampoline", signal_to_snake(&signal.name));
73
74    // TODO: move to object.signal.return config
75    let inhibit = configured_signals.iter().any(|f| f.inhibit);
76    if inhibit && signal.ret.typ() != library::TypeId::tid_bool() {
77        error!("Wrong return type for Inhibit for signal '{}'", signal.name);
78    }
79
80    let mut bounds: Bounds = Default::default();
81
82    if in_trait || fundamental_type {
83        let type_name = RustType::builder(env, type_tid)
84            .ref_mode(RefMode::ByRefFake)
85            .try_build();
86        if fundamental_type {
87            bounds.add_parameter(
88                "this",
89                &type_name.into_string(),
90                BoundType::AsRef(None),
91                false,
92            );
93        } else {
94            bounds.add_parameter(
95                "this",
96                &type_name.into_string(),
97                BoundType::IsA(None),
98                false,
99            );
100        }
101    }
102
103    let parameters = if is_notify {
104        let mut parameters = trampoline_parameters::Parameters::new(1);
105
106        let owner = env.type_(type_tid);
107        let c_type = format!("{}*", owner.get_glib_name().unwrap());
108
109        let transform = parameters.prepare_transformation(
110            env,
111            type_tid,
112            "this".to_owned(),
113            &c_type,
114            library::ParameterDirection::In,
115            gir_parser::TransferOwnership::None,
116            false,
117            crate::analysis::ref_mode::RefMode::ByRef,
118            ConversionType::Borrow,
119        );
120        parameters.transformations.push(transform);
121
122        parameters
123    } else {
124        trampoline_parameters::analyze(env, &signal.parameters, type_tid, configured_signals, None)
125    };
126
127    if in_trait || fundamental_type {
128        let type_name = RustType::builder(env, type_tid)
129            .ref_mode(RefMode::ByRefFake)
130            .try_build();
131        if fundamental_type {
132            bounds.add_parameter(
133                "this",
134                &type_name.into_string(),
135                BoundType::AsRef(None),
136                false,
137            );
138        } else {
139            bounds.add_parameter(
140                "this",
141                &type_name.into_string(),
142                BoundType::IsA(None),
143                false,
144            );
145        }
146    }
147
148    for par in &parameters.rust_parameters {
149        if let Ok(rust_type) = RustType::builder(env, par.typ)
150            .direction(par.direction)
151            .try_from_glib(&par.try_from_glib)
152            .try_build()
153        {
154            used_types.extend(rust_type.into_used_types());
155        }
156    }
157    for par in &parameters.c_parameters {
158        if let Some(ffi_type) = used_ffi_type(env, par.typ, &par.c_type) {
159            used_types.push(ffi_type);
160        }
161    }
162
163    let mut ret_nullable = signal.ret.is_nullable();
164
165    if signal.ret.typ() != Default::default() {
166        if let Ok(rust_type) = RustType::builder(env, signal.ret.typ())
167            .direction(library::ParameterDirection::Out)
168            .try_build()
169        {
170            // No GString
171            used_types.extend(rust_type.into_used_types());
172        }
173        if let Some(ffi_type) = used_ffi_type(env, signal.ret.typ(), signal.ret.c_type()) {
174            used_types.push(ffi_type);
175        }
176
177        let nullable_override = configured_signals.iter().find_map(|f| f.ret.nullable);
178        if let Some(nullable) = nullable_override {
179            ret_nullable = nullable;
180        }
181    }
182
183    let concurrency = configured_signals
184        .iter()
185        .map(|f| f.concurrency)
186        .next()
187        .unwrap_or(obj.concurrency);
188
189    let mut ret = signal.ret.clone();
190    ret.set_nullable(ret_nullable);
191
192    let trampoline = Trampoline {
193        name,
194        parameters,
195        ret,
196        bounds,
197        version,
198        inhibit,
199        concurrency,
200        is_notify,
201        bound_name: String::new(),
202        scope: None,
203        user_data_index: 0,
204        destroy_index: 0,
205        nullable: false,
206        type_name: env.library.type_(type_tid).get_name(),
207    };
208    Ok(trampoline)
209}
210
211fn closure_errors(env: &Env, signal: &library::Signal) -> Vec<String> {
212    let mut errors: Vec<String> = Vec::new();
213    for par in &signal.parameters {
214        if let Some(error) = type_error(env, par) {
215            errors.push(format!(
216                "{} {}: {}",
217                error,
218                par.name(),
219                par.typ().full_name(&env.library)
220            ));
221        }
222    }
223    if signal.ret.typ() != Default::default()
224        && let Some(error) = type_error(env, &signal.ret)
225    {
226        errors.push(format!(
227            "{} return value {}",
228            error,
229            signal.ret.typ().full_name(&env.library)
230        ));
231    }
232    errors
233}
234
235pub fn type_error(env: &Env, par: &library::Parameter) -> Option<&'static str> {
236    use super::rust_type::TypeError::*;
237    if par.direction().is_out() {
238        Some("Out")
239    } else if par.direction() == library::ParameterDirection::InOut {
240        Some("InOut")
241    } else if is_empty_c_type(par.c_type()) {
242        Some("Empty ctype")
243    } else if ConversionType::of(env, par.typ()) == ConversionType::Unknown {
244        Some("Unknown conversion")
245    } else {
246        match RustType::try_new(env, par.typ()) {
247            Err(Ignored(_)) => Some("Ignored"),
248            Err(Mismatch(_)) => Some("Mismatch"),
249            Err(Unimplemented(_)) => Some("Unimplemented"),
250            Ok(_) => None,
251        }
252    }
253}