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