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: library::ParameterScope,
37    /// It's used to group callbacks
38    pub user_data_index: usize,
39    pub destroy_index: usize,
40    pub nullable: library::Nullable,
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            library::Transfer::None,
116            library::Nullable(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.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 ret = library::Parameter {
190        nullable: ret_nullable,
191        ..signal.ret.clone()
192    };
193
194    let trampoline = Trampoline {
195        name,
196        parameters,
197        ret,
198        bounds,
199        version,
200        inhibit,
201        concurrency,
202        is_notify,
203        bound_name: String::new(),
204        scope: library::ParameterScope::None,
205        user_data_index: 0,
206        destroy_index: 0,
207        nullable: library::Nullable(false),
208        type_name: env.library.type_(type_tid).get_name(),
209    };
210    Ok(trampoline)
211}
212
213fn closure_errors(env: &Env, signal: &library::Signal) -> Vec<String> {
214    let mut errors: Vec<String> = Vec::new();
215    for par in &signal.parameters {
216        if let Some(error) = type_error(env, par) {
217            errors.push(format!(
218                "{} {}: {}",
219                error,
220                par.name,
221                par.typ.full_name(&env.library)
222            ));
223        }
224    }
225    if signal.ret.typ != Default::default() {
226        if let Some(error) = type_error(env, &signal.ret) {
227            errors.push(format!(
228                "{} return value {}",
229                error,
230                signal.ret.typ.full_name(&env.library)
231            ));
232        }
233    }
234    errors
235}
236
237pub fn type_error(env: &Env, par: &library::Parameter) -> Option<&'static str> {
238    use super::rust_type::TypeError::*;
239    if par.direction == library::ParameterDirection::Out {
240        Some("Out")
241    } else if par.direction == library::ParameterDirection::InOut {
242        Some("InOut")
243    } else if is_empty_c_type(&par.c_type) {
244        Some("Empty ctype")
245    } else if ConversionType::of(env, par.typ) == ConversionType::Unknown {
246        Some("Unknown conversion")
247    } else {
248        match RustType::try_new(env, par.typ) {
249            Err(Ignored(_)) => Some("Ignored"),
250            Err(Mismatch(_)) => Some("Mismatch"),
251            Err(Unimplemented(_)) => Some("Unimplemented"),
252            Ok(_) => None,
253        }
254    }
255}