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 if par.c_type != "GDestroyNotify" {
389 to_glib_extras.insert(pos, to_glib_extra);
390 }
391 }
392 callback_info
393 } else {
394 None
395 };
396
397 if rust_type.is_function() {
398 if par.c_type != "GDestroyNotify" {
399 let callback_parameters_config = configured_functions.iter().find_map(|f| {
400 f.parameters
401 .iter()
402 .find(|p| p.ident.is_match(&par.name))
403 .map(|p| &p.callback_parameters)
404 });
405 if let Some((mut callback, destroy_index)) = analyze_callback(
406 func_name,
407 type_tid,
408 env,
409 par,
410 &callback_info,
411 commented,
412 imports,
413 &c_parameters,
414 rust_type,
415 callback_parameters_config,
416 ) {
417 if let Some(destroy_index) = destroy_index {
418 let user_data = cross_user_data_check
419 .entry(destroy_index)
420 .or_insert_with(|| callback.user_data_index);
421 if *user_data != callback.user_data_index {
422 warn_main!(
423 type_tid,
424 "`{}`: Different destructors cannot share the same user data",
425 func_name
426 );
427 *commented = true;
428 }
429 callback.destroy_index = destroy_index;
430 } else {
431 user_data_indexes.insert(callback.user_data_index);
432 to_remove.push(callback.user_data_index);
433 }
434 callbacks.push(callback);
435 to_replace.push((pos, par.typ));
436 continue;
437 }
438 } else if let Some((mut callback, _)) = analyze_callback(
439 func_name,
440 type_tid,
441 env,
442 par,
443 &callback_info,
444 commented,
445 imports,
446 &c_parameters,
447 rust_type,
448 None,
449 ) {
450 if let Some(user_data_index) = cross_user_data_check.get(&pos) {
453 callback.user_data_index = *user_data_index;
454 callback.destroy_index = pos;
455 } else {
456 warn_main!(
457 type_tid,
458 "`{}`: no user data point to the destroy callback",
459 func_name,
460 );
461 *commented = true;
462 }
463 if !find_callback_bound_to_destructor(callbacks, &mut callback, pos) {
466 destructors_to_update.push((pos, destroys.len()));
468 }
469 destroys.push(callback);
470 to_remove.push(pos);
471 continue;
472 }
473 }
474 if !*commented {
475 *commented |= RustType::builder(env, par.typ)
476 .direction(par.direction)
477 .scope(par.scope)
478 .try_from_glib(&par.try_from_glib)
479 .try_build_param()
480 .is_err();
481 }
482 }
483 for (destroy_index, pos_in_destroys) in destructors_to_update {
484 if !find_callback_bound_to_destructor(
485 callbacks,
486 &mut destroys[pos_in_destroys],
487 destroy_index,
488 ) {
489 warn_main!(
490 type_tid,
491 "`{}`: destructor without linked callback",
492 func_name
493 );
494 }
495 }
496 }
497
498 if cross_user_data_check
500 .values()
501 .collect::<Vec<_>>()
502 .windows(2)
503 .any(|a| a[0] == a[1])
504 {
505 *commented = true;
506 warn_main!(
507 type_tid,
508 "`{}`: Different user data share the same destructors",
509 func.name
510 );
511 }
512
513 if !destroys.is_empty() || !callbacks.is_empty() {
514 for (pos, typ) in to_replace {
515 let ty = env.library.type_(typ);
516 params[pos].typ = typ;
517 params[pos].c_type = ty.get_glib_name().unwrap().to_owned();
518 }
519 let mut s = to_remove
520 .iter()
521 .chain(cross_user_data_check.values())
522 .collect::<HashSet<_>>() .into_iter()
524 .collect::<Vec<_>>();
525 s.sort(); for pos in s.iter().rev() {
528 params.remove(**pos);
529 }
530 *parameters = function_parameters::analyze(
531 env,
532 params,
533 configured_functions,
534 disable_length_detect,
535 false,
536 in_trait,
537 );
538 } else {
539 warn_main!(
540 type_tid,
541 "`{}`: this is supposed to be a callback function but no callback was found...",
542 func.name
543 );
544 *commented = true;
545 }
546}
547
548fn analyze_function(
549 env: &Env,
550 obj: &config::gobjects::GObject,
551 func_name: &str,
552 name: String,
553 status: GStatus,
554 func: &library::Function,
555 type_tid: Option<library::TypeId>,
556 in_trait: bool,
557 is_boxed: bool,
558 configured_functions: &[&config::functions::Function],
559 imports: &mut Imports,
560) -> Info {
561 let ns_id = type_tid.map_or(MAIN_NAMESPACE, |t| t.ns_id);
562 let type_tid = type_tid.unwrap_or_default();
563 let r#async = func.finish_func.is_some()
564 || func.parameters.iter().any(|parameter| {
565 parameter.scope == ParameterScope::Async && parameter.c_type == "GAsyncReadyCallback"
566 });
567 let has_callback_parameter = !r#async
568 && func
569 .parameters
570 .iter()
571 .any(|par| env.library.type_(par.typ).is_function());
572 let concurrency = match env.library.type_(type_tid) {
573 library::Type::Class(_) | library::Type::Interface(_) | library::Type::Record(_) => {
574 obj.concurrency
575 }
576 _ => library::Concurrency::SendSync,
577 };
578
579 let mut commented = false;
580 let mut bounds: Bounds = Default::default();
581 let mut to_glib_extras = HashMap::<usize, String>::new();
582 let mut used_types: Vec<String> = Vec::with_capacity(4);
583 let mut trampoline = None;
584 let mut callbacks = Vec::new();
585 let mut destroys = Vec::new();
586 let mut async_future = None;
587
588 if !r#async
589 && !has_callback_parameter
590 && func
591 .parameters
592 .iter()
593 .any(|par| par.c_type == "GDestroyNotify")
594 {
595 warn_main!(
599 type_tid,
600 "Function \"{}\" with destroy callback without callbacks",
601 func.name
602 );
603 commented = true;
604 }
605
606 let mut new_name = configured_functions.iter().find_map(|f| f.rename.clone());
607 let is_constructor = configured_functions.iter().find_map(|f| f.is_constructor);
608
609 let bypass_auto_rename = configured_functions.iter().any(|f| f.bypass_auto_rename);
610 let is_constructor = is_constructor.unwrap_or(false);
611 if !bypass_auto_rename && new_name.is_none() {
612 if func.kind == library::FunctionKind::Constructor || is_constructor {
613 if func.kind == library::FunctionKind::Constructor && is_constructor {
614 warn_main!(
615 type_tid,
616 "`{}`: config forces 'constructor' on an already gir-annotated 'constructor'",
617 func_name
618 );
619 }
620
621 if name.starts_with("new_from")
622 || name.starts_with("new_with")
623 || name.starts_with("new_for")
624 {
625 new_name = Some(name[4..].to_string());
626 }
627 } else {
628 let nb_in_params = func
629 .parameters
630 .iter()
631 .filter(|param| library::ParameterDirection::In == param.direction)
632 .fold(0, |acc, _| acc + 1);
633 let is_bool_getter = (func.parameters.len() == nb_in_params)
634 && (func.ret.typ == library::TypeId::tid_bool()
635 || func.ret.typ == library::TypeId::tid_c_bool());
636 new_name = getter_rules::try_rename_would_be_getter(&name, is_bool_getter)
637 .ok()
638 .map(getter_rules::NewName::unwrap);
639 }
640 }
641
642 let version = configured_functions
643 .iter()
644 .filter_map(|f| f.version)
645 .min()
646 .or(func.version);
647
648 let version = env.config.filter_version(version);
649 let deprecated_version = func.deprecated_version;
650 let visibility = configured_functions
651 .iter()
652 .find_map(|f| f.visibility)
653 .unwrap_or_default();
654 let cfg_condition = configured_functions
655 .iter()
656 .find_map(|f| f.cfg_condition.clone());
657 let doc_hidden = configured_functions.iter().any(|f| f.doc_hidden);
658 let doc_trait_name = configured_functions
659 .iter()
660 .find_map(|f| f.doc_trait_name.clone());
661 let doc_struct_name = configured_functions
662 .iter()
663 .find_map(|f| f.doc_struct_name.clone());
664 let doc_ignore_parameters = configured_functions
665 .iter()
666 .find(|f| !f.doc_ignore_parameters.is_empty())
667 .map(|f| f.doc_ignore_parameters.clone())
668 .unwrap_or_default();
669 let disable_length_detect = configured_functions.iter().any(|f| f.disable_length_detect);
670 let no_future = configured_functions.iter().any(|f| f.no_future);
671 let unsafe_ = configured_functions.iter().any(|f| f.unsafe_);
672 let assertion = configured_functions.iter().find_map(|f| f.assertion);
673
674 let imports = &mut imports.with_defaults(version, &cfg_condition);
675
676 let ret = return_value::analyze(
677 env,
678 obj,
679 func,
680 type_tid,
681 configured_functions,
682 &mut used_types,
683 imports,
684 );
685 commented |= ret.commented;
686
687 let mut params = func.parameters.clone();
688 let mut parameters = function_parameters::analyze(
689 env,
690 ¶ms,
691 configured_functions,
692 disable_length_detect,
693 r#async,
694 in_trait,
695 );
696 parameters.analyze_return(env, &ret.parameter);
697
698 if let Some(ref f) = ret.parameter {
699 if let Type::Function(_) = env.library.type_(f.lib_par.typ) {
700 if env.config.work_mode.is_normal() {
701 warn!("Function \"{}\" returns callback", func.name);
702 commented = true;
703 }
704 }
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 {
757 if !rust_type.as_str().ends_with("GString") || par.c_type == "gchar***" {
758 used_types.extend(rust_type.into_used_types());
759 }
760 }
761 let (to_glib_extra, callback_info) = bounds.add_for_parameter(
762 env,
763 func,
764 par,
765 r#async,
766 library::Concurrency::None,
767 configured_functions,
768 );
769 if let Some(to_glib_extra) = to_glib_extra {
770 to_glib_extras.insert(pos, to_glib_extra);
771 }
772
773 analyze_async(
774 env,
775 func,
776 type_tid,
777 new_name.as_ref().unwrap_or(&name),
778 callback_info,
779 &mut commented,
780 &mut trampoline,
781 no_future,
782 &mut async_future,
783 configured_functions,
784 ¶meters,
785 );
786 let type_error = !(r#async
787 && *env.library.type_(par.typ) == Type::Basic(library::Basic::Pointer))
788 && RustType::builder(env, par.typ)
789 .direction(par.direction)
790 .scope(par.scope)
791 .try_from_glib(&par.try_from_glib)
792 .try_build_param()
793 .is_err();
794 if type_error {
795 commented = true;
796 }
797 }
798 if r#async && trampoline.is_none() {
799 commented = true;
800 }
801 } else {
802 analyze_callbacks(
803 env,
804 func,
805 &mut cross_user_data_check,
806 &mut user_data_indexes,
807 &mut parameters,
808 &mut used_types,
809 &mut bounds,
810 &mut to_glib_extras,
811 imports,
812 &mut destroys,
813 &mut callbacks,
814 &mut params,
815 configured_functions,
816 disable_length_detect,
817 in_trait,
818 &mut commented,
819 concurrency,
820 type_tid,
821 );
822 }
823 }
824
825 for par in ¶meters.rust_parameters {
826 let is_len_for_par = |t: &Transformation| {
828 if let TransformationType::Length { ref array_name, .. } = t.transformation_type {
829 array_name == &par.name
830 } else {
831 false
832 }
833 };
834 if is_carray_with_direct_elements(env, par.typ)
835 && !parameters.transformations.iter().any(is_len_for_par)
836 {
837 commented = true;
838 }
839 }
840
841 let (outs, unsupported_outs) = out_parameters::analyze(
842 env,
843 func,
844 ¶meters.c_parameters,
845 &ret,
846 configured_functions,
847 );
848 if unsupported_outs {
849 warn_main!(
850 type_tid,
851 "Function {} has unsupported outs",
852 func.c_identifier.as_ref().unwrap_or(&func.name)
853 );
854 commented = true;
855 }
856
857 if r#async && status.need_generate() && !commented {
858 imports.add("std::boxed::Box as Box_");
859 imports.add("std::pin::Pin");
860
861 if let Some(ref trampoline) = trampoline {
862 for out in &trampoline.output_params {
863 if let Ok(rust_type) = RustType::builder(env, out.lib_par.typ)
864 .direction(ParameterDirection::Out)
865 .try_build()
866 {
867 used_types.extend(rust_type.into_used_types());
868 }
869 }
870 if let Some(ref out) = trampoline.ffi_ret {
871 if let Ok(rust_type) = RustType::builder(env, out.lib_par.typ)
872 .direction(ParameterDirection::Return)
873 .try_build()
874 {
875 used_types.extend(rust_type.into_used_types());
876 }
877 }
878 }
879 }
880
881 if status.need_generate() && !commented {
882 if (!destroys.is_empty() || !callbacks.is_empty())
883 && callbacks.iter().any(|c| !c.scope.is_call())
884 {
885 imports.add("std::boxed::Box as Box_");
886 }
887
888 for transformation in &mut parameters.transformations {
889 if let Some(to_glib_extra) = to_glib_extras.get(&transformation.ind_c) {
890 transformation
891 .transformation_type
892 .set_to_glib_extra(to_glib_extra);
893 }
894 }
895
896 imports.add("crate::ffi");
897
898 imports.add_used_types(&used_types);
899 if ret.base_tid.is_some() {
900 imports.add("glib::prelude::*");
901 }
902
903 if func.name.parse::<special_functions::Type>().is_err()
904 || parameters.c_parameters.iter().any(|p| p.move_)
905 {
906 imports.add("glib::translate::*");
907 }
908 bounds.update_imports(imports);
909 }
910
911 let is_method = func.kind == library::FunctionKind::Method;
912 let assertion =
913 assertion.unwrap_or_else(|| SafetyAssertionMode::of(env, is_method, ¶meters));
914
915 let generate_doc = configured_functions.iter().all(|f| f.generate_doc);
916
917 Info {
918 name,
919 func_name: func_name.to_string(),
920 new_name,
921 glib_name: func.c_identifier.as_ref().unwrap().clone(),
922 status,
923 kind: func.kind,
924 visibility,
925 type_name: RustType::try_new(env, type_tid),
926 parameters,
927 ret,
928 bounds,
929 outs,
930 version,
931 deprecated_version,
932 not_version: None,
933 cfg_condition,
934 assertion,
935 doc_hidden,
936 doc_trait_name,
937 doc_struct_name,
938 doc_ignore_parameters,
939 r#async,
940 unsafe_,
941 trampoline,
942 async_future,
943 callbacks,
944 destroys,
945 remove_params: cross_user_data_check.values().copied().collect::<Vec<_>>(),
946 commented,
947 hidden: false,
948 ns_id,
949 generate_doc,
950 get_property: func.get_property.clone(),
951 set_property: func.set_property.clone(),
952 }
953}
954
955pub fn is_carray_with_direct_elements(env: &Env, typ: library::TypeId) -> bool {
956 match *env.library.type_(typ) {
957 Type::CArray(inner_tid) => {
958 use super::conversion_type::ConversionType;
959 matches!(env.library.type_(inner_tid), Type::Basic(..) if ConversionType::of(env, inner_tid) == ConversionType::Direct)
960 }
961 _ => false,
962 }
963}
964
965fn analyze_async(
966 env: &Env,
967 func: &library::Function,
968 type_tid: library::TypeId,
969 codegen_name: &str,
970 callback_info: Option<CallbackInfo>,
971 commented: &mut bool,
972 trampoline: &mut Option<AsyncTrampoline>,
973 no_future: bool,
974 async_future: &mut Option<AsyncFuture>,
975 configured_functions: &[&config::functions::Function],
976 parameters: &function_parameters::Parameters,
977) -> bool {
978 if let Some(CallbackInfo {
979 callback_type,
980 success_parameters,
981 error_parameters,
982 bound_name,
983 }) = callback_info
984 {
985 *commented |= callback_type.contains("/*");
987 let func_name = func.c_identifier.as_ref().unwrap();
988 let finish_func_name = if let Some(finish_func_name) = &func.finish_func {
989 finish_func_name.to_string()
990 } else {
991 finish_function_name(func_name)
992 };
993 let mut output_params = vec![];
994 let mut ffi_ret = None;
995 if let Some(function) = find_function(env, &finish_func_name) {
996 if use_function_return_for_result(
997 env,
998 function.ret.typ,
999 &func.name,
1000 configured_functions,
1001 ) {
1002 ffi_ret = Some(analysis::Parameter::from_return_value(
1003 env,
1004 &function.ret,
1005 configured_functions,
1006 ));
1007 }
1008
1009 for param in &function.parameters {
1010 let mut lib_par = param.clone();
1011 if nameutil::needs_mangling(¶m.name) {
1012 lib_par.name = nameutil::mangle_keywords(&*param.name).into_owned();
1013 }
1014 let configured_parameters = configured_functions.matched_parameters(&lib_par.name);
1015 output_params.push(analysis::Parameter::from_parameter(
1016 env,
1017 &lib_par,
1018 &configured_parameters,
1019 ));
1020 }
1021 }
1022 if trampoline.is_some() || async_future.is_some() {
1023 warn_main!(
1024 type_tid,
1025 "{}: Cannot handle callbacks and async parameters at the same time for the \
1026 moment",
1027 func.name
1028 );
1029 *commented = true;
1030 return false;
1031 }
1032 if !*commented && success_parameters.is_empty() {
1033 if success_parameters.is_empty() {
1034 warn_main!(
1035 type_tid,
1036 "{}: missing success parameters for async future",
1037 func.name
1038 );
1039 }
1040 *commented = true;
1041 return false;
1042 }
1043 let is_method = func.kind == FunctionKind::Method;
1044
1045 *trampoline = Some(AsyncTrampoline {
1046 is_method,
1047 has_error_parameter: error_parameters.is_some(),
1048 name: format!("{codegen_name}_trampoline"),
1049 finish_func_name: format!("{}::{}", env.main_sys_crate_name(), finish_func_name),
1050 callback_type,
1051 bound_name,
1052 output_params,
1053 ffi_ret,
1054 });
1055
1056 if !no_future {
1057 *async_future = Some(AsyncFuture {
1058 is_method,
1059 name: format!("{}_future", codegen_name.trim_end_matches("_async")),
1060 success_parameters,
1061 error_parameters,
1062 assertion: match SafetyAssertionMode::of(env, is_method, parameters) {
1063 SafetyAssertionMode::None => SafetyAssertionMode::None,
1064 _ => SafetyAssertionMode::Skip,
1067 },
1068 });
1069 }
1070 true
1071 } else {
1072 false
1073 }
1074}
1075
1076fn analyze_callback(
1077 func_name: &str,
1078 type_tid: library::TypeId,
1079 env: &Env,
1080 par: &CParameter,
1081 callback_info: &Option<CallbackInfo>,
1082 commented: &mut bool,
1083 imports: &mut Imports,
1084 c_parameters: &[(&CParameter, usize)],
1085 rust_type: &Type,
1086 callback_parameters_config: Option<&config::functions::CallbackParameters>,
1087) -> Option<(Trampoline, Option<usize>)> {
1088 let mut imports_to_add = Vec::new();
1089
1090 if let Type::Function(func) = rust_type {
1091 if par.c_type != "GDestroyNotify" {
1092 if let Some(user_data) = par.user_data_index {
1093 if user_data >= c_parameters.len() {
1094 warn_main!(type_tid,
1095 "function `{}` has an invalid user data index of {} when there are {} parameters",
1096 func_name,
1097 user_data,
1098 c_parameters.len());
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 {
1190 if !rust_type.as_str().ends_with("GString")
1191 && !rust_type.as_str().ends_with("GAsyncResult")
1192 {
1193 imports_to_add.extend(rust_type.into_used_types());
1194 }
1195 }
1196 let user_data_index = par.user_data_index.unwrap_or(0);
1197 if par.c_type != "GDestroyNotify" && c_parameters.len() <= user_data_index {
1198 warn_main!(
1199 type_tid,
1200 "`{}`: Invalid user data index of `{}`",
1201 func.name,
1202 user_data_index
1203 );
1204 *commented = true;
1205 None
1206 } else if match par.destroy_index {
1207 Some(destroy_index) => c_parameters.len() <= destroy_index,
1208 None => false,
1209 } {
1210 warn_main!(
1211 type_tid,
1212 "`{}`: Invalid destroy index of `{}`",
1213 func.name,
1214 par.destroy_index.unwrap()
1215 );
1216 *commented = true;
1217 None
1218 } else {
1219 if !*commented {
1220 for import in imports_to_add {
1221 imports.add_used_type(&import);
1222 }
1223 }
1224 Some((
1225 Trampoline {
1226 name: par.name.to_string(),
1227 parameters,
1228 ret: func.ret.clone(),
1229 bound_name: match callback_info {
1230 Some(x) => x.bound_name.to_string(),
1231 None => match RustType::builder(env, par.typ)
1232 .direction(par.direction)
1233 .nullable(par.nullable)
1234 .scope(par.scope)
1235 .try_build()
1236 {
1237 Ok(rust_type) => rust_type.into_string(),
1238 Err(_) => {
1239 warn_main!(type_tid, "`{}`: unknown type", func.name);
1240 return None;
1241 }
1242 },
1243 },
1244 bounds: Bounds::default(),
1245 version: None,
1246 inhibit: false,
1247 concurrency: library::Concurrency::None,
1248 is_notify: false,
1249 scope: par.scope,
1250 user_data_index: if par.c_type != "GDestroyNotify" {
1252 c_parameters[user_data_index].1
1253 } else {
1254 0
1255 },
1256 destroy_index: 0,
1257 nullable: par.nullable,
1258 type_name: env.library.type_(type_tid).get_name(),
1259 },
1260 par.destroy_index
1261 .map(|destroy_index| c_parameters[destroy_index].1),
1262 ))
1263 }
1264 } else {
1265 None
1266 }
1267}
1268
1269pub fn find_function<'a>(env: &'a Env, c_identifier: &str) -> Option<&'a Function> {
1270 let find = |functions: &'a [Function]| -> Option<&'a Function> {
1271 for function in functions {
1272 if let Some(ref func_c_identifier) = function.c_identifier {
1273 if func_c_identifier == c_identifier {
1274 return Some(function);
1275 }
1276 }
1277 }
1278 None
1279 };
1280
1281 if let Some(index) = env.library.find_namespace(&env.config.library_name) {
1282 let namespace = env.library.namespace(index);
1283 if let Some(f) = find(&namespace.functions) {
1284 return Some(f);
1285 }
1286 for typ in &namespace.types {
1287 if let Some(Type::Class(class)) = typ {
1288 if let Some(f) = find(&class.functions) {
1289 return Some(f);
1290 }
1291 } else if let Some(Type::Interface(interface)) = typ {
1292 if let Some(f) = find(&interface.functions) {
1293 return Some(f);
1294 }
1295 }
1296 }
1297 }
1298 None
1299}
1300
1301pub fn finish_function_name(mut func_name: &str) -> String {
1303 if func_name.ends_with("_async") {
1304 let len = func_name.len() - "_async".len();
1305 func_name = &func_name[0..len];
1306 }
1307 format!("{}_finish", &func_name)
1308}
1309
1310pub fn find_index_to_ignore<'a>(
1311 parameters: impl IntoIterator<Item = &'a library::Parameter>,
1312 ret: Option<&'a library::Parameter>,
1313) -> Option<usize> {
1314 parameters
1315 .into_iter()
1316 .chain(ret)
1317 .find(|param| param.array_length.is_some())
1318 .and_then(|param| param.array_length.map(|length| length as usize))
1319}
1320
1321#[cfg(test)]
1322mod tests {
1323 use super::*;
1324
1325 #[test]
1326 fn test_finish_function_name() {
1327 assert_eq!(
1328 "g_file_copy_finish",
1329 &finish_function_name("g_file_copy_async")
1330 );
1331 assert_eq!("g_bus_get_finish", &finish_function_name("g_bus_get"));
1332 }
1333}