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 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 pub user_data_index: usize,
39 pub destroy_index: usize,
40 pub nullable: library::Nullable,
41 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 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 ¶meters.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 ¶meters.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 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}