1use std::{
7 borrow::Borrow,
8 collections::{HashMap, HashSet},
9};
10
11use log::warn;
12
13use super::{namespaces::NsId, special_functions};
14use crate::{
15 analysis::{
16 self,
17 bounds::{Bounds, CallbackInfo},
18 function_parameters::{self, CParameter, Parameters, Transformation, TransformationType},
19 imports::Imports,
20 is_gpointer,
21 out_parameters::{self, use_function_return_for_result},
22 ref_mode::RefMode,
23 return_value,
24 rust_type::*,
25 safety_assertion_mode::SafetyAssertionMode,
26 signatures::{Signature, Signatures},
27 trampolines::Trampoline,
28 },
29 codegen::Visibility,
30 config::{self, gobjects::GStatus},
31 env::Env,
32 library::{
33 self, Function, FunctionKind, MAIN_NAMESPACE, ParameterDirection, ParameterScope, Transfer,
34 Type,
35 },
36 nameutil,
37 traits::*,
38 version::Version,
39};
40
41#[derive(Clone, Debug)]
42pub struct AsyncTrampoline {
43 pub is_method: bool,
44 pub has_error_parameter: bool,
45 pub name: String,
46 pub finish_func_name: String,
47 pub callback_type: String,
48 pub bound_name: char,
49 pub output_params: Vec<analysis::Parameter>,
50 pub ffi_ret: Option<analysis::Parameter>,
51}
52
53#[derive(Clone, Debug)]
54pub struct AsyncFuture {
55 pub is_method: bool,
56 pub name: String,
57 pub success_parameters: String,
58 pub error_parameters: Option<String>,
59 pub assertion: SafetyAssertionMode,
60}
61
62#[derive(Debug)]
63pub struct Info {
64 pub name: String,
65 pub func_name: String,
66 pub new_name: Option<String>,
67 pub glib_name: String,
68 pub status: GStatus,
69 pub kind: library::FunctionKind,
70 pub visibility: Visibility,
71 pub type_name: Result,
72 pub parameters: Parameters,
73 pub ret: return_value::Info,
74 pub bounds: Bounds,
75 pub outs: out_parameters::Info,
76 pub version: Option<Version>,
77 pub deprecated_version: Option<Version>,
78 pub not_version: Option<Version>,
79 pub cfg_condition: Option<String>,
80 pub assertion: SafetyAssertionMode,
81 pub doc_hidden: bool,
82 pub doc_trait_name: Option<String>,
83 pub doc_struct_name: Option<String>,
84 pub doc_ignore_parameters: HashSet<String>,
85 pub r#async: bool,
86 pub unsafe_: bool,
87 pub trampoline: Option<AsyncTrampoline>,
88 pub callbacks: Vec<Trampoline>,
89 pub destroys: Vec<Trampoline>,
90 pub remove_params: Vec<usize>,
91 pub async_future: Option<AsyncFuture>,
92 pub hidden: bool,
95 pub commented: bool,
97 pub ns_id: NsId,
100 pub generate_doc: bool,
101 pub get_property: Option<String>,
102 pub set_property: Option<String>,
103}
104
105impl Info {
106 pub fn codegen_name(&self) -> &str {
107 self.new_name.as_ref().unwrap_or(&self.name)
108 }
109
110 pub fn is_special(&self) -> bool {
111 self.codegen_name()
112 .trim_end_matches('_')
113 .rsplit('_')
114 .next()
115 .is_some_and(|i| i.parse::<special_functions::Type>().is_ok())
116 }
117
118 pub fn should_be_doc_linked(&self, env: &Env) -> bool {
120 self.should_docs_be_generated(env)
121 && (self.status.manual() || (!self.commented && !self.hidden))
122 }
123
124 pub fn should_docs_be_generated(&self, env: &Env) -> bool {
125 !self.status.ignored() && !self.is_special() && !self.is_async_finish(env)
126 }
127
128 pub fn doc_link(
129 &self,
130 parent: Option<&str>,
131 visible_parent: Option<&str>,
132 is_self: bool,
133 ) -> String {
134 if let Some(p) = parent {
135 if is_self {
136 format!("[`{f}()`][Self::{f}()]", f = self.codegen_name())
137 } else {
138 format!(
139 "[`{visible_parent}::{f}()`][crate::{p}::{f}()]",
140 visible_parent = visible_parent.unwrap_or(p),
141 p = p,
142 f = self.codegen_name()
143 )
144 }
145 } else {
146 format!(
147 "[`{fn_name}()`][crate::{fn_name}()]",
148 fn_name = self.codegen_name()
149 )
150 }
151 }
152
153 pub fn is_async_finish(&self, env: &Env) -> bool {
154 let has_async_result = self
155 .parameters
156 .rust_parameters
157 .iter()
158 .any(|param| param.typ.full_name(&env.library) == "Gio.AsyncResult");
159 self.name.ends_with("_finish") && has_async_result
160 }
161}
162
163pub fn analyze<F: Borrow<library::Function>>(
164 env: &Env,
165 functions: &[F],
166 type_tid: Option<library::TypeId>,
167 in_trait: bool,
168 is_boxed: bool,
169 obj: &config::gobjects::GObject,
170 imports: &mut Imports,
171 mut signatures: Option<&mut Signatures>,
172 deps: Option<&[library::TypeId]>,
173) -> Vec<Info> {
174 let mut funcs = Vec::new();
175
176 'func: for func in functions {
177 let func = func.borrow();
178 let configured_functions = obj.functions.matched(&func.name);
179 let mut status = obj.status;
180 for f in &configured_functions {
181 match f.status {
182 GStatus::Ignore => continue 'func,
183 GStatus::Manual => {
184 status = GStatus::Manual;
185 break;
186 }
187 GStatus::Generate => (),
188 }
189 }
190
191 if env.is_totally_deprecated(
192 Some(type_tid.unwrap_or_default().ns_id),
193 func.deprecated_version,
194 ) {
195 continue;
196 }
197 let name = nameutil::mangle_keywords(&*func.name).into_owned();
198 let signature_params = Signature::new(func);
199 let mut not_version = None;
200 if func.kind == library::FunctionKind::Method
201 && let Some(deps) = deps
202 {
203 let (has, version) = signature_params.has_in_deps(env, &name, deps);
204 if has
205 && let Some(v) = version
206 && v > env.config.min_cfg_version
207 {
208 not_version = version;
209 }
210 }
211 if let Some(signatures) = signatures.as_mut() {
212 signatures.insert(name.clone(), signature_params);
213 }
214
215 let mut info = analyze_function(
216 env,
217 obj,
218 &func.name,
219 name,
220 status,
221 func,
222 type_tid,
223 in_trait,
224 is_boxed,
225 &configured_functions,
226 imports,
227 );
228 info.not_version = not_version;
229 funcs.push(info);
230 }
231
232 funcs
233}
234
235fn fixup_gpointer_parameter(
236 env: &Env,
237 type_tid: library::TypeId,
238 is_boxed: bool,
239 in_trait: bool,
240 parameters: &mut Parameters,
241 idx: usize,
242) {
243 use crate::analysis::ffi_type;
244
245 let instance_parameter = idx == 0;
246
247 let glib_name = env.library.type_(type_tid).get_glib_name().unwrap();
248 let ffi_name = ffi_type::ffi_type(env, type_tid, glib_name).unwrap();
249 let pointer_type = if is_boxed { "*const" } else { "*mut" };
250 parameters.rust_parameters[idx].typ = type_tid;
251 parameters.c_parameters[idx].typ = type_tid;
252 parameters.c_parameters[idx].instance_parameter = instance_parameter;
253 parameters.c_parameters[idx].ref_mode = RefMode::ByRef;
254 parameters.c_parameters[idx].transfer = Transfer::None;
255 parameters.transformations[idx] = Transformation {
256 ind_c: idx,
257 ind_rust: Some(idx),
258 transformation_type: TransformationType::ToGlibPointer {
259 name: parameters.rust_parameters[idx].name.clone(),
260 instance_parameter,
261 transfer: Transfer::None,
262 ref_mode: RefMode::ByRef,
263 to_glib_extra: Default::default(),
264 explicit_target_type: format!("{} {}", pointer_type, ffi_name.as_str()),
265 pointer_cast: format!(
266 " as {}",
267 nameutil::use_glib_if_needed(env, "ffi::gconstpointer")
268 ),
269 in_trait,
270 nullable: false,
271 move_: false,
272 },
273 };
274}
275
276fn fixup_special_functions(
277 env: &Env,
278 name: &str,
279 type_tid: library::TypeId,
280 is_boxed: bool,
281 in_trait: bool,
282 parameters: &mut Parameters,
283) {
284 if name == "hash"
287 && parameters.c_parameters.len() == 1
288 && parameters.c_parameters[0].c_type == "gconstpointer"
289 {
290 fixup_gpointer_parameter(env, type_tid, is_boxed, in_trait, parameters, 0);
291 }
292
293 if (name == "compare" || name == "equal" || name == "is_equal")
294 && parameters.c_parameters.len() == 2
295 && parameters.c_parameters[0].c_type == "gconstpointer"
296 && parameters.c_parameters[1].c_type == "gconstpointer"
297 {
298 fixup_gpointer_parameter(env, type_tid, is_boxed, in_trait, parameters, 0);
299 fixup_gpointer_parameter(env, type_tid, is_boxed, in_trait, parameters, 1);
300 }
301}
302
303fn find_callback_bound_to_destructor(
304 callbacks: &[Trampoline],
305 destroy: &mut Trampoline,
306 destroy_index: usize,
307) -> bool {
308 for call in callbacks {
309 if call.destroy_index == destroy_index {
310 destroy.nullable = call.nullable;
311 destroy.bound_name = call.bound_name.clone();
312 return true;
313 }
314 }
315 false
316}
317
318fn analyze_callbacks(
319 env: &Env,
320 func: &library::Function,
321 cross_user_data_check: &mut HashMap<usize, usize>,
322 user_data_indexes: &mut HashSet<usize>,
323 parameters: &mut Parameters,
324 used_types: &mut Vec<String>,
325 bounds: &mut Bounds,
326 to_glib_extras: &mut HashMap<usize, String>,
327 imports: &mut Imports,
328 destroys: &mut Vec<Trampoline>,
329 callbacks: &mut Vec<Trampoline>,
330 params: &mut Vec<library::Parameter>,
331 configured_functions: &[&config::functions::Function],
332 disable_length_detect: bool,
333 in_trait: bool,
334 commented: &mut bool,
335 concurrency: library::Concurrency,
336 type_tid: library::TypeId,
337) {
338 let mut to_replace = Vec::new();
339 let mut to_remove = Vec::new();
340
341 {
342 let mut c_parameters = Vec::new();
345 for (pos, par) in parameters.c_parameters.iter().enumerate() {
346 if par.instance_parameter {
347 continue;
348 }
349 c_parameters.push((par, pos));
350 }
351
352 let func_name = match &func.c_identifier {
353 Some(n) => n,
354 None => &func.name,
355 };
356 let mut destructors_to_update = Vec::new();
357 for pos in 0..parameters.c_parameters.len() {
358 if cross_user_data_check.values().any(|p| *p == pos) || user_data_indexes.contains(&pos)
360 {
361 continue;
362 }
363 let par = ¶meters.c_parameters[pos];
364 assert!(
365 !par.instance_parameter || pos == 0,
366 "Wrong instance parameter in {}",
367 func.c_identifier.as_ref().unwrap()
368 );
369 if let Ok(rust_type) = RustType::builder(env, par.typ)
370 .direction(par.direction)
371 .try_from_glib(&par.try_from_glib)
372 .try_build()
373 {
374 used_types.extend(rust_type.into_used_types());
375 }
376 let rust_type = env.library.type_(par.typ);
377 let callback_info = if !*par.nullable || !rust_type.is_function() {
378 let (to_glib_extra, callback_info) = bounds.add_for_parameter(
379 env,
380 func,
381 par,
382 false,
383 concurrency,
384 configured_functions,
385 );
386 if let Some(to_glib_extra) = to_glib_extra {
387 let pos_adjusted_for_removed_params =
388 pos - to_remove.len() - cross_user_data_check.len();
389 if par.c_type != "GDestroyNotify" {
390 to_glib_extras.insert(pos_adjusted_for_removed_params, to_glib_extra);
391 }
392 }
393 callback_info
394 } else {
395 None
396 };
397
398 if rust_type.is_function() {
399 if par.c_type != "GDestroyNotify" {
400 let callback_parameters_config = configured_functions.iter().find_map(|f| {
401 f.parameters
402 .iter()
403 .find(|p| p.ident.is_match(&par.name))
404 .map(|p| &p.callback_parameters)
405 });
406 if let Some((mut callback, destroy_index)) = analyze_callback(
407 func_name,
408 type_tid,
409 env,
410 par,
411 &callback_info,
412 commented,
413 imports,
414 &c_parameters,
415 rust_type,
416 callback_parameters_config,
417 ) {
418 if let Some(destroy_index) = destroy_index {
419 let user_data = cross_user_data_check
420 .entry(destroy_index)
421 .or_insert_with(|| callback.user_data_index);
422 if *user_data != callback.user_data_index {
423 warn_main!(
424 type_tid,
425 "`{}`: Different destructors cannot share the same user data",
426 func_name
427 );
428 *commented = true;
429 }
430 callback.destroy_index = destroy_index;
431 } else {
432 user_data_indexes.insert(callback.user_data_index);
433 to_remove.push(callback.user_data_index);
434 }
435 callbacks.push(callback);
436 to_replace.push((pos, par.typ));
437 continue;
438 }
439 } else if let Some((mut callback, _)) = analyze_callback(
440 func_name,
441 type_tid,
442 env,
443 par,
444 &callback_info,
445 commented,
446 imports,
447 &c_parameters,
448 rust_type,
449 None,
450 ) {
451 if let Some(user_data_index) = cross_user_data_check.get(&pos) {
454 callback.user_data_index = *user_data_index;
455 callback.destroy_index = pos;
456 } else {
457 warn_main!(
458 type_tid,
459 "`{}`: no user data point to the destroy callback",
460 func_name,
461 );
462 *commented = true;
463 }
464 if !find_callback_bound_to_destructor(callbacks, &mut callback, pos) {
467 destructors_to_update.push((pos, destroys.len()));
469 }
470 destroys.push(callback);
471 to_remove.push(pos);
472 continue;
473 }
474 }
475 if !*commented {
476 *commented |= RustType::builder(env, par.typ)
477 .direction(par.direction)
478 .scope(par.scope)
479 .try_from_glib(&par.try_from_glib)
480 .try_build_param()
481 .is_err();
482 }
483 }
484 for (destroy_index, pos_in_destroys) in destructors_to_update {
485 if !find_callback_bound_to_destructor(
486 callbacks,
487 &mut destroys[pos_in_destroys],
488 destroy_index,
489 ) {
490 warn_main!(
491 type_tid,
492 "`{}`: destructor without linked callback",
493 func_name
494 );
495 }
496 }
497 }
498
499 if cross_user_data_check
501 .values()
502 .collect::<Vec<_>>()
503 .windows(2)
504 .any(|a| a[0] == a[1])
505 {
506 *commented = true;
507 warn_main!(
508 type_tid,
509 "`{}`: Different user data share the same destructors",
510 func.name
511 );
512 }
513
514 if !destroys.is_empty() || !callbacks.is_empty() {
515 for (pos, typ) in to_replace {
516 let ty = env.library.type_(typ);
517 params[pos].typ = typ;
518 params[pos].c_type = ty.get_glib_name().unwrap().to_owned();
519 }
520 let mut s = to_remove
521 .iter()
522 .chain(cross_user_data_check.values())
523 .collect::<HashSet<_>>() .into_iter()
525 .collect::<Vec<_>>();
526 s.sort(); for pos in s.iter().rev() {
529 params.remove(**pos);
530 }
531 *parameters = function_parameters::analyze(
532 env,
533 params,
534 configured_functions,
535 disable_length_detect,
536 false,
537 in_trait,
538 );
539 } else {
540 warn_main!(
541 type_tid,
542 "`{}`: this is supposed to be a callback function but no callback was found...",
543 func.name
544 );
545 *commented = true;
546 }
547}
548
549fn analyze_function(
550 env: &Env,
551 obj: &config::gobjects::GObject,
552 func_name: &str,
553 name: String,
554 status: GStatus,
555 func: &library::Function,
556 type_tid: Option<library::TypeId>,
557 in_trait: bool,
558 is_boxed: bool,
559 configured_functions: &[&config::functions::Function],
560 imports: &mut Imports,
561) -> Info {
562 let ns_id = type_tid.map_or(MAIN_NAMESPACE, |t| t.ns_id);
563 let type_tid = type_tid.unwrap_or_default();
564 let r#async = func.finish_func.is_some()
565 || func.parameters.iter().any(|parameter| {
566 parameter.scope == ParameterScope::Async && parameter.c_type == "GAsyncReadyCallback"
567 });
568 let has_callback_parameter = !r#async
569 && func
570 .parameters
571 .iter()
572 .any(|par| env.library.type_(par.typ).is_function());
573 let concurrency = match env.library.type_(type_tid) {
574 library::Type::Class(_) | library::Type::Interface(_) | library::Type::Record(_) => {
575 obj.concurrency
576 }
577 _ => library::Concurrency::SendSync,
578 };
579
580 let mut commented = false;
581 let mut bounds: Bounds = Default::default();
582 let mut to_glib_extras = HashMap::<usize, String>::new();
583 let mut used_types: Vec<String> = Vec::with_capacity(4);
584 let mut trampoline = None;
585 let mut callbacks = Vec::new();
586 let mut destroys = Vec::new();
587 let mut async_future = None;
588
589 if !r#async
590 && !has_callback_parameter
591 && func
592 .parameters
593 .iter()
594 .any(|par| par.c_type == "GDestroyNotify")
595 {
596 warn_main!(
600 type_tid,
601 "Function \"{}\" with destroy callback without callbacks",
602 func.name
603 );
604 commented = true;
605 }
606
607 let mut new_name = configured_functions.iter().find_map(|f| f.rename.clone());
608 let is_constructor = configured_functions.iter().find_map(|f| f.is_constructor);
609
610 let bypass_auto_rename = configured_functions.iter().any(|f| f.bypass_auto_rename);
611 let is_constructor = is_constructor.unwrap_or(false);
612 if !bypass_auto_rename && new_name.is_none() {
613 if func.kind == library::FunctionKind::Constructor || is_constructor {
614 if func.kind == library::FunctionKind::Constructor && is_constructor {
615 warn_main!(
616 type_tid,
617 "`{}`: config forces 'constructor' on an already gir-annotated 'constructor'",
618 func_name
619 );
620 }
621
622 if name.starts_with("new_from")
623 || name.starts_with("new_with")
624 || name.starts_with("new_for")
625 {
626 new_name = Some(name[4..].to_string());
627 }
628 } else {
629 let nb_in_params = func
630 .parameters
631 .iter()
632 .filter(|param| library::ParameterDirection::In == param.direction)
633 .fold(0, |acc, _| acc + 1);
634 let is_bool_getter = (func.parameters.len() == nb_in_params)
635 && (func.ret.typ == library::TypeId::tid_bool()
636 || func.ret.typ == library::TypeId::tid_c_bool());
637 new_name = getter_rules::try_rename_would_be_getter(&name, is_bool_getter)
638 .ok()
639 .map(getter_rules::NewName::unwrap);
640 }
641 }
642
643 let version = configured_functions
644 .iter()
645 .filter_map(|f| f.version)
646 .min()
647 .or(func.version);
648
649 let version = env.config.filter_version(version);
650 let deprecated_version = func.deprecated_version;
651 let visibility = configured_functions
652 .iter()
653 .find_map(|f| f.visibility)
654 .unwrap_or_default();
655 let cfg_condition = configured_functions
656 .iter()
657 .find_map(|f| f.cfg_condition.clone());
658 let doc_hidden = configured_functions.iter().any(|f| f.doc_hidden);
659 let doc_trait_name = configured_functions
660 .iter()
661 .find_map(|f| f.doc_trait_name.clone());
662 let doc_struct_name = configured_functions
663 .iter()
664 .find_map(|f| f.doc_struct_name.clone());
665 let doc_ignore_parameters = configured_functions
666 .iter()
667 .find(|f| !f.doc_ignore_parameters.is_empty())
668 .map(|f| f.doc_ignore_parameters.clone())
669 .unwrap_or_default();
670 let disable_length_detect = configured_functions.iter().any(|f| f.disable_length_detect);
671 let no_future = configured_functions.iter().any(|f| f.no_future);
672 let unsafe_ = configured_functions.iter().any(|f| f.unsafe_);
673 let assertion = configured_functions.iter().find_map(|f| f.assertion);
674
675 let imports = &mut imports.with_defaults(version, &cfg_condition);
676
677 let ret = return_value::analyze(
678 env,
679 obj,
680 func,
681 type_tid,
682 configured_functions,
683 &mut used_types,
684 imports,
685 );
686 commented |= ret.commented;
687
688 let mut params = func.parameters.clone();
689 let mut parameters = function_parameters::analyze(
690 env,
691 ¶ms,
692 configured_functions,
693 disable_length_detect,
694 r#async,
695 in_trait,
696 );
697 parameters.analyze_return(env, &ret.parameter);
698
699 if let Some(ref f) = ret.parameter
700 && let Type::Function(_) = env.library.type_(f.lib_par.typ)
701 && env.config.work_mode.is_normal()
702 {
703 warn!("Function \"{}\" returns callback", func.name);
704 commented = true;
705 }
706
707 fixup_special_functions(
708 env,
709 name.as_str(),
710 type_tid,
711 is_boxed,
712 in_trait,
713 &mut parameters,
714 );
715
716 let mut cross_user_data_check: HashMap<usize, usize> = HashMap::new();
719 let mut user_data_indexes: HashSet<usize> = HashSet::new();
720
721 if status.need_generate() {
722 if !has_callback_parameter {
723 let mut to_remove = Vec::new();
724 let mut correction_instance = 0;
725 for par in parameters.c_parameters.iter() {
726 if par.scope.is_none() {
727 continue;
728 }
729 if let Some(index) = par.user_data_index {
730 to_remove.push(index);
731 }
732 if let Some(index) = par.destroy_index {
733 to_remove.push(index);
734 }
735 }
736 for (pos, par) in parameters.c_parameters.iter().enumerate() {
737 if par.instance_parameter {
738 correction_instance = 1;
739 }
740
741 if r#async
742 && pos >= correction_instance
743 && to_remove.contains(&(pos - correction_instance))
744 {
745 continue;
746 }
747 assert!(
748 !par.instance_parameter || pos == 0,
749 "Wrong instance parameter in {}",
750 func.c_identifier.as_ref().unwrap()
751 );
752 if let Ok(rust_type) = RustType::builder(env, par.typ)
753 .direction(par.direction)
754 .try_from_glib(&par.try_from_glib)
755 .try_build()
756 && (!rust_type.as_str().ends_with("GString") || par.c_type == "gchar***")
757 {
758 used_types.extend(rust_type.into_used_types());
759 }
760 let (to_glib_extra, callback_info) = bounds.add_for_parameter(
761 env,
762 func,
763 par,
764 r#async,
765 library::Concurrency::None,
766 configured_functions,
767 );
768 if let Some(to_glib_extra) = to_glib_extra {
769 to_glib_extras.insert(pos, to_glib_extra);
770 }
771
772 analyze_async(
773 env,
774 func,
775 type_tid,
776 new_name.as_ref().unwrap_or(&name),
777 callback_info,
778 &mut commented,
779 &mut trampoline,
780 no_future,
781 &mut async_future,
782 configured_functions,
783 ¶meters,
784 );
785 let type_error = !(r#async
786 && *env.library.type_(par.typ) == Type::Basic(library::Basic::Pointer))
787 && RustType::builder(env, par.typ)
788 .direction(par.direction)
789 .scope(par.scope)
790 .try_from_glib(&par.try_from_glib)
791 .try_build_param()
792 .is_err();
793 if type_error {
794 commented = true;
795 }
796 }
797 if r#async && trampoline.is_none() {
798 commented = true;
799 }
800 } else {
801 analyze_callbacks(
802 env,
803 func,
804 &mut cross_user_data_check,
805 &mut user_data_indexes,
806 &mut parameters,
807 &mut used_types,
808 &mut bounds,
809 &mut to_glib_extras,
810 imports,
811 &mut destroys,
812 &mut callbacks,
813 &mut params,
814 configured_functions,
815 disable_length_detect,
816 in_trait,
817 &mut commented,
818 concurrency,
819 type_tid,
820 );
821 }
822 }
823
824 for par in ¶meters.rust_parameters {
825 let is_len_for_par = |t: &Transformation| {
827 if let TransformationType::Length { ref array_name, .. } = t.transformation_type {
828 array_name == &par.name
829 } else {
830 false
831 }
832 };
833 if is_carray_with_direct_elements(env, par.typ)
834 && !parameters.transformations.iter().any(is_len_for_par)
835 {
836 commented = true;
837 }
838 }
839
840 let (outs, unsupported_outs) = out_parameters::analyze(
841 env,
842 func,
843 ¶meters.c_parameters,
844 &ret,
845 configured_functions,
846 );
847 if unsupported_outs {
848 warn_main!(
849 type_tid,
850 "Function {} has unsupported outs",
851 func.c_identifier.as_ref().unwrap_or(&func.name)
852 );
853 commented = true;
854 }
855
856 if r#async && status.need_generate() && !commented {
857 imports.add("std::boxed::Box as Box_");
858 imports.add("std::pin::Pin");
859
860 if let Some(ref trampoline) = trampoline {
861 for out in &trampoline.output_params {
862 if let Ok(rust_type) = RustType::builder(env, out.lib_par.typ)
863 .direction(ParameterDirection::Out)
864 .try_build()
865 {
866 used_types.extend(rust_type.into_used_types());
867 }
868 }
869 if let Some(ref out) = trampoline.ffi_ret
870 && let Ok(rust_type) = RustType::builder(env, out.lib_par.typ)
871 .direction(ParameterDirection::Return)
872 .try_build()
873 {
874 used_types.extend(rust_type.into_used_types());
875 }
876 }
877 }
878
879 if status.need_generate() && !commented {
880 if (!destroys.is_empty() || !callbacks.is_empty())
881 && callbacks.iter().any(|c| !c.scope.is_call())
882 {
883 imports.add("std::boxed::Box as Box_");
884 }
885
886 for transformation in &mut parameters.transformations {
887 if let Some(to_glib_extra) = to_glib_extras.get(&transformation.ind_c) {
888 transformation
889 .transformation_type
890 .set_to_glib_extra(to_glib_extra);
891 }
892 }
893
894 imports.add("crate::ffi");
895
896 imports.add_used_types(&used_types);
897 if ret.base_tid.is_some() {
898 imports.add("glib::prelude::*");
899 }
900
901 if func.name.parse::<special_functions::Type>().is_err()
902 || parameters.c_parameters.iter().any(|p| p.move_)
903 {
904 imports.add("glib::translate::*");
905 }
906 bounds.update_imports(imports);
907 }
908
909 let is_method = func.kind == library::FunctionKind::Method;
910 let assertion =
911 assertion.unwrap_or_else(|| SafetyAssertionMode::of(env, is_method, ¶meters));
912
913 let generate_doc = configured_functions.iter().all(|f| f.generate_doc);
914
915 Info {
916 name,
917 func_name: func_name.to_string(),
918 new_name,
919 glib_name: func.c_identifier.as_ref().unwrap().clone(),
920 status,
921 kind: func.kind,
922 visibility,
923 type_name: RustType::try_new(env, type_tid),
924 parameters,
925 ret,
926 bounds,
927 outs,
928 version,
929 deprecated_version,
930 not_version: None,
931 cfg_condition,
932 assertion,
933 doc_hidden,
934 doc_trait_name,
935 doc_struct_name,
936 doc_ignore_parameters,
937 r#async,
938 unsafe_,
939 trampoline,
940 async_future,
941 callbacks,
942 destroys,
943 remove_params: cross_user_data_check.values().copied().collect::<Vec<_>>(),
944 commented,
945 hidden: false,
946 ns_id,
947 generate_doc,
948 get_property: func.get_property.clone(),
949 set_property: func.set_property.clone(),
950 }
951}
952
953pub fn is_carray_with_direct_elements(env: &Env, typ: library::TypeId) -> bool {
954 match *env.library.type_(typ) {
955 Type::CArray(inner_tid) => {
956 use super::conversion_type::ConversionType;
957 matches!(env.library.type_(inner_tid), Type::Basic(..) if ConversionType::of(env, inner_tid) == ConversionType::Direct)
958 }
959 _ => false,
960 }
961}
962
963fn analyze_async(
964 env: &Env,
965 func: &library::Function,
966 type_tid: library::TypeId,
967 codegen_name: &str,
968 callback_info: Option<CallbackInfo>,
969 commented: &mut bool,
970 trampoline: &mut Option<AsyncTrampoline>,
971 no_future: bool,
972 async_future: &mut Option<AsyncFuture>,
973 configured_functions: &[&config::functions::Function],
974 parameters: &function_parameters::Parameters,
975) -> bool {
976 if let Some(CallbackInfo {
977 callback_type,
978 success_parameters,
979 error_parameters,
980 bound_name,
981 }) = callback_info
982 {
983 *commented |= callback_type.contains("/*");
985 let func_name = func.c_identifier.as_ref().unwrap();
986 let finish_func_name = if let Some(finish_func_name) = &func.finish_func {
987 finish_func_name.to_string()
988 } else {
989 finish_function_name(func_name)
990 };
991 let mut output_params = vec![];
992 let mut ffi_ret = None;
993 if let Some(function) = find_function(env, &finish_func_name) {
994 if use_function_return_for_result(
995 env,
996 function.ret.typ,
997 &func.name,
998 configured_functions,
999 ) {
1000 ffi_ret = Some(analysis::Parameter::from_return_value(
1001 env,
1002 &function.ret,
1003 configured_functions,
1004 ));
1005 }
1006
1007 for param in &function.parameters {
1008 let mut lib_par = param.clone();
1009 if nameutil::needs_mangling(¶m.name) {
1010 lib_par.name = nameutil::mangle_keywords(&*param.name).into_owned();
1011 }
1012 let configured_parameters = configured_functions.matched_parameters(&lib_par.name);
1013 output_params.push(analysis::Parameter::from_parameter(
1014 env,
1015 &lib_par,
1016 &configured_parameters,
1017 ));
1018 }
1019 }
1020 if trampoline.is_some() || async_future.is_some() {
1021 warn_main!(
1022 type_tid,
1023 "{}: Cannot handle callbacks and async parameters at the same time for the \
1024 moment",
1025 func.name
1026 );
1027 *commented = true;
1028 return false;
1029 }
1030 if !*commented && success_parameters.is_empty() {
1031 if success_parameters.is_empty() {
1032 warn_main!(
1033 type_tid,
1034 "{}: missing success parameters for async future",
1035 func.name
1036 );
1037 }
1038 *commented = true;
1039 return false;
1040 }
1041 let is_method = func.kind == FunctionKind::Method;
1042
1043 *trampoline = Some(AsyncTrampoline {
1044 is_method,
1045 has_error_parameter: error_parameters.is_some(),
1046 name: format!("{codegen_name}_trampoline"),
1047 finish_func_name: format!("{}::{}", env.main_sys_crate_name(), finish_func_name),
1048 callback_type,
1049 bound_name,
1050 output_params,
1051 ffi_ret,
1052 });
1053
1054 if !no_future {
1055 *async_future = Some(AsyncFuture {
1056 is_method,
1057 name: format!("{}_future", codegen_name.trim_end_matches("_async")),
1058 success_parameters,
1059 error_parameters,
1060 assertion: match SafetyAssertionMode::of(env, is_method, parameters) {
1061 SafetyAssertionMode::None => SafetyAssertionMode::None,
1062 _ => SafetyAssertionMode::Skip,
1065 },
1066 });
1067 }
1068 true
1069 } else {
1070 false
1071 }
1072}
1073
1074fn analyze_callback(
1075 func_name: &str,
1076 type_tid: library::TypeId,
1077 env: &Env,
1078 par: &CParameter,
1079 callback_info: &Option<CallbackInfo>,
1080 commented: &mut bool,
1081 imports: &mut Imports,
1082 c_parameters: &[(&CParameter, usize)],
1083 rust_type: &Type,
1084 callback_parameters_config: Option<&config::functions::CallbackParameters>,
1085) -> Option<(Trampoline, Option<usize>)> {
1086 let mut imports_to_add = Vec::new();
1087
1088 if let Type::Function(func) = rust_type {
1089 if par.c_type != "GDestroyNotify" {
1090 if let Some(user_data) = par.user_data_index {
1091 if user_data >= c_parameters.len() {
1092 warn_main!(
1093 type_tid,
1094 "function `{}` has an invalid user data index of {} when there are {} parameters",
1095 func_name,
1096 user_data,
1097 c_parameters.len()
1098 );
1099 return None;
1100 } else if !is_gpointer(&c_parameters[user_data].0.c_type) {
1101 *commented = true;
1102 warn_main!(
1103 type_tid,
1104 "function `{}`'s callback `{}` has invalid user data",
1105 func_name,
1106 par.name
1107 );
1108 return None;
1109 }
1110 } else {
1111 *commented = true;
1112 warn_main!(
1113 type_tid,
1114 "function `{}`'s callback `{}` without associated user data",
1115 func_name,
1116 par.name
1117 );
1118 return None;
1119 }
1120 if let Some(destroy_index) = par.destroy_index {
1121 if destroy_index >= c_parameters.len() {
1122 warn_main!(
1123 type_tid,
1124 "function `{}` has an invalid destroy index of {} when there are {} \
1125 parameters",
1126 func_name,
1127 destroy_index,
1128 c_parameters.len()
1129 );
1130 return None;
1131 }
1132 if c_parameters[destroy_index].0.c_type != "GDestroyNotify" {
1133 *commented = true;
1134 warn_main!(
1135 type_tid,
1136 "function `{}`'s callback `{}` has invalid destroy callback",
1137 func_name,
1138 par.name
1139 );
1140 return None;
1141 }
1142 }
1143 }
1144
1145 if par.c_type != "GDestroyNotify"
1148 && (func.parameters.is_empty() || !func.parameters.iter().any(|c| c.closure.is_some()))
1149 {
1150 *commented = true;
1151 warn_main!(
1152 type_tid,
1153 "Closure type `{}` doesn't provide user data for function {}",
1154 par.c_type,
1155 func_name,
1156 );
1157 return None;
1158 }
1159
1160 let parameters = crate::analysis::trampoline_parameters::analyze(
1161 env,
1162 &func.parameters,
1163 par.typ,
1164 &[],
1165 callback_parameters_config,
1166 );
1167 if par.c_type != "GDestroyNotify" && !*commented {
1168 *commented |= func.parameters.iter().any(|p| {
1169 if p.closure.is_none() {
1170 crate::analysis::trampolines::type_error(env, p).is_some()
1171 } else {
1172 false
1173 }
1174 });
1175 }
1176 for p in ¶meters.rust_parameters {
1177 if let Ok(rust_type) = RustType::builder(env, p.typ)
1178 .direction(p.direction)
1179 .nullable(p.nullable)
1180 .try_from_glib(&p.try_from_glib)
1181 .try_build()
1182 {
1183 imports_to_add.extend(rust_type.into_used_types());
1184 }
1185 }
1186 if let Ok(rust_type) = RustType::builder(env, func.ret.typ)
1187 .direction(ParameterDirection::Return)
1188 .try_build()
1189 && !rust_type.as_str().ends_with("GString")
1190 && !rust_type.as_str().ends_with("GAsyncResult")
1191 {
1192 imports_to_add.extend(rust_type.into_used_types());
1193 }
1194 let user_data_index = par.user_data_index.unwrap_or(0);
1195 if par.c_type != "GDestroyNotify" && c_parameters.len() <= user_data_index {
1196 warn_main!(
1197 type_tid,
1198 "`{}`: Invalid user data index of `{}`",
1199 func.name,
1200 user_data_index
1201 );
1202 *commented = true;
1203 None
1204 } else if match par.destroy_index {
1205 Some(destroy_index) => c_parameters.len() <= destroy_index,
1206 None => false,
1207 } {
1208 warn_main!(
1209 type_tid,
1210 "`{}`: Invalid destroy index of `{}`",
1211 func.name,
1212 par.destroy_index.unwrap()
1213 );
1214 *commented = true;
1215 None
1216 } else {
1217 if !*commented {
1218 for import in imports_to_add {
1219 imports.add_used_type(&import);
1220 }
1221 }
1222 Some((
1223 Trampoline {
1224 name: par.name.to_string(),
1225 parameters,
1226 ret: func.ret.clone(),
1227 bound_name: match callback_info {
1228 Some(x) => x.bound_name.to_string(),
1229 None => match RustType::builder(env, par.typ)
1230 .direction(par.direction)
1231 .nullable(par.nullable)
1232 .scope(par.scope)
1233 .try_build()
1234 {
1235 Ok(rust_type) => rust_type.into_string(),
1236 Err(_) => {
1237 warn_main!(type_tid, "`{}`: unknown type", func.name);
1238 return None;
1239 }
1240 },
1241 },
1242 bounds: Bounds::default(),
1243 version: None,
1244 inhibit: false,
1245 concurrency: library::Concurrency::None,
1246 is_notify: false,
1247 scope: par.scope,
1248 user_data_index: if par.c_type != "GDestroyNotify" {
1250 c_parameters[user_data_index].1
1251 } else {
1252 0
1253 },
1254 destroy_index: 0,
1255 nullable: par.nullable,
1256 type_name: env.library.type_(type_tid).get_name(),
1257 },
1258 par.destroy_index
1259 .map(|destroy_index| c_parameters[destroy_index].1),
1260 ))
1261 }
1262 } else {
1263 None
1264 }
1265}
1266
1267pub fn find_function<'a>(env: &'a Env, c_identifier: &str) -> Option<&'a Function> {
1268 let find = |functions: &'a [Function]| -> Option<&'a Function> {
1269 for function in functions {
1270 if let Some(ref func_c_identifier) = function.c_identifier
1271 && func_c_identifier == c_identifier
1272 {
1273 return Some(function);
1274 }
1275 }
1276 None
1277 };
1278
1279 if let Some(index) = env.library.find_namespace(&env.config.library_name) {
1280 let namespace = env.library.namespace(index);
1281 if let Some(f) = find(&namespace.functions) {
1282 return Some(f);
1283 }
1284 for typ in &namespace.types {
1285 if let Some(Type::Class(class)) = typ {
1286 if let Some(f) = find(&class.functions) {
1287 return Some(f);
1288 }
1289 } else if let Some(Type::Interface(interface)) = typ
1290 && let Some(f) = find(&interface.functions)
1291 {
1292 return Some(f);
1293 }
1294 }
1295 }
1296 None
1297}
1298
1299pub fn finish_function_name(mut func_name: &str) -> String {
1301 if func_name.ends_with("_async") {
1302 let len = func_name.len() - "_async".len();
1303 func_name = &func_name[0..len];
1304 }
1305 format!("{}_finish", &func_name)
1306}
1307
1308pub fn find_index_to_ignore<'a>(
1309 parameters: impl IntoIterator<Item = &'a library::Parameter>,
1310 ret: Option<&'a library::Parameter>,
1311) -> Option<usize> {
1312 parameters
1313 .into_iter()
1314 .chain(ret)
1315 .find(|param| param.array_length.is_some())
1316 .and_then(|param| param.array_length.map(|length| length as usize))
1317}
1318
1319#[cfg(test)]
1320mod tests {
1321 use super::*;
1322
1323 #[test]
1324 fn test_finish_function_name() {
1325 assert_eq!(
1326 "g_file_copy_finish",
1327 &finish_function_name("g_file_copy_async")
1328 );
1329 assert_eq!("g_bus_get_finish", &finish_function_name("g_bus_get"));
1330 }
1331}