1use std::collections::{hash_map::Entry, BTreeMap, HashMap};
2
3use crate::{
4 analysis::{
5 self,
6 conversion_type::ConversionType,
7 function_parameters::{
8 CParameter as AnalysisCParameter, Transformation, TransformationType,
9 },
10 functions::{find_index_to_ignore, AsyncTrampoline},
11 out_parameters::{Mode, ThrowFunctionReturnStrategy},
12 return_value,
13 rust_type::RustType,
14 safety_assertion_mode::SafetyAssertionMode,
15 trampoline_parameters,
16 trampolines::Trampoline,
17 },
18 chunk::{parameter_ffi_call_out, Chunk, Param, TupleMode},
19 env::Env,
20 library::{self, ParameterDirection, TypeId},
21 nameutil::{is_gstring, use_gio_type, use_glib_if_needed, use_glib_type},
22 traits::*,
23};
24
25#[derive(Clone, Debug)]
26enum Parameter {
27 In,
30 Out {
31 parameter: parameter_ffi_call_out::Parameter,
32 mem_mode: OutMemMode,
33 },
34}
35use self::Parameter::*;
36
37#[derive(Clone, Debug, Eq, PartialEq)]
38enum OutMemMode {
39 Uninitialized,
40 UninitializedNamed(String),
41 NullPtr,
42 NullMutPtr,
43}
44
45impl OutMemMode {
46 fn is_uninitialized(&self) -> bool {
47 matches!(*self, Self::Uninitialized)
48 }
49}
50
51#[derive(Clone, Default)]
52struct ReturnValue {
53 pub ret: return_value::Info,
54}
55
56#[derive(Default)]
57pub struct Builder {
58 async_trampoline: Option<AsyncTrampoline>,
59 callbacks: Vec<Trampoline>,
60 destroys: Vec<Trampoline>,
61 glib_name: String,
62 parameters: Vec<Parameter>,
63 transformations: Vec<Transformation>,
64 ret: ReturnValue,
65 outs_as_return: bool,
66 in_unsafe: bool,
67 outs_mode: Mode,
68 assertion: SafetyAssertionMode,
69}
70
71type FuncParameters<'a> = BTreeMap<usize, FuncParameter<'a>>;
74
75struct FuncParameter<'a> {
76 pos: usize,
77 full_type: Option<(String, String)>,
78 callbacks: Vec<&'a Trampoline>,
79}
80
81impl Builder {
82 pub fn new() -> Self {
83 Default::default()
84 }
85 pub fn async_trampoline(&mut self, trampoline: &AsyncTrampoline) -> &mut Self {
86 self.async_trampoline = Some(trampoline.clone());
87 self
88 }
89 pub fn callback(&mut self, trampoline: &Trampoline) -> &mut Self {
90 self.callbacks.push(trampoline.clone());
91 self
92 }
93 pub fn destroy(&mut self, trampoline: &Trampoline) -> &mut Self {
94 self.destroys.push(trampoline.clone());
95 self
96 }
97 pub fn glib_name(&mut self, name: &str) -> &mut Self {
98 self.glib_name = name.into();
99 self
100 }
101 pub fn assertion(&mut self, assertion: SafetyAssertionMode) -> &mut Self {
102 self.assertion = assertion;
103 self
104 }
105 pub fn ret(&mut self, ret: &return_value::Info) -> &mut Self {
106 self.ret = ReturnValue { ret: ret.clone() };
107 self
108 }
109 pub fn parameter(&mut self) -> &mut Self {
110 self.parameters.push(Parameter::In);
111 self
112 }
113 pub fn out_parameter(&mut self, env: &Env, parameter: &AnalysisCParameter) -> &mut Self {
114 let mem_mode = c_type_mem_mode(env, parameter);
115 self.parameters.push(Parameter::Out {
116 parameter: parameter_ffi_call_out::Parameter::new(
117 parameter,
118 mem_mode.is_uninitialized(),
119 ),
120 mem_mode,
121 });
122 self.outs_as_return = true;
123 self
124 }
125
126 pub fn transformations(&mut self, transformations: &[Transformation]) -> &mut Self {
127 self.transformations = transformations.to_owned();
128 self
129 }
130
131 pub fn outs_mode(&mut self, mode: Mode) -> &mut Self {
132 self.outs_mode = mode;
133 self
134 }
135 pub fn in_unsafe(&mut self, in_unsafe: bool) -> &mut Self {
136 self.in_unsafe = in_unsafe;
137 self
138 }
139 pub fn generate(&self, env: &Env, bounds: &str, bounds_names: &str) -> Chunk {
140 let mut body = Vec::new();
141
142 let mut uninitialized_vars = if self.outs_as_return {
143 self.write_out_variables(&mut body, env)
144 } else {
145 Vec::new()
146 };
147
148 let mut group_by_user_data = FuncParameters::new();
149
150 if !self.callbacks.is_empty() || !self.destroys.is_empty() {
152 for (pos, callback) in self.callbacks.iter().enumerate() {
153 let user_data_index = callback.user_data_index;
154 if group_by_user_data.contains_key(&user_data_index) {
155 continue;
156 }
157 let calls = self
158 .callbacks
159 .iter()
160 .filter(|c| c.user_data_index == user_data_index)
161 .collect::<Vec<_>>();
162 group_by_user_data.insert(
163 user_data_index,
164 FuncParameter {
165 pos,
166 full_type: if calls.len() > 1 {
167 if calls.iter().all(|c| c.scope.is_call()) {
168 Some((
169 format!(
170 "&({})",
171 calls
172 .iter()
173 .map(|c| format!("&{}", c.bound_name))
174 .collect::<Vec<_>>()
175 .join(", ")
176 ),
177 format!(
178 "&mut ({})",
179 calls
180 .iter()
181 .map(|c| format!("&mut {}", c.bound_name))
182 .collect::<Vec<_>>()
183 .join(", ")
184 ),
185 ))
186 } else {
187 let s = format!(
188 "Box_<({})>",
189 calls
190 .iter()
191 .map(|c| c.bound_name.to_string())
192 .collect::<Vec<_>>()
193 .join(", ")
194 );
195 Some((s.clone(), s))
196 }
197 } else {
198 None
199 },
200 callbacks: calls,
201 },
202 );
203 }
204 }
205
206 let call = self.generate_call(&group_by_user_data);
207 let call = self.generate_call_conversion(call, &mut uninitialized_vars);
208 let ret = self.generate_out_return(&mut uninitialized_vars);
209 let (call, ret) = self.apply_outs_mode(call, ret, &mut uninitialized_vars);
210
211 body.push(call);
212 if let Some(chunk) = ret {
213 body.push(chunk);
214 }
215
216 let mut chunks = Vec::new();
217
218 self.add_in_array_lengths(&mut chunks);
219 self.add_assertion(&mut chunks);
220
221 if !self.callbacks.is_empty() || !self.destroys.is_empty() {
222 let mut poses = HashMap::with_capacity(group_by_user_data.len());
225 for trampoline in &self.callbacks {
226 *poses
227 .entry(&trampoline.user_data_index)
228 .or_insert_with(|| 0) += 1;
229 }
230 let mut poses = poses
231 .into_iter()
232 .filter(|(_, x)| *x > 1)
233 .map(|(x, _)| (x, 0))
234 .collect::<HashMap<_, _>>();
235 for trampoline in &self.callbacks {
236 let user_data_index = trampoline.user_data_index;
237 let pos = poses.entry(&trampoline.user_data_index);
238 self.add_trampoline(
239 env,
240 &mut chunks,
241 trampoline,
242 &group_by_user_data[&user_data_index].full_type,
243 match pos {
244 Entry::Occupied(ref x) => Some(*x.get()),
245 _ => None,
246 },
247 bounds,
248 bounds_names,
249 false,
250 );
251 pos.and_modify(|x| {
252 *x += 1;
253 });
254 }
255 for destroy in &self.destroys {
256 self.add_trampoline(
257 env,
258 &mut chunks,
259 destroy,
260 &group_by_user_data[&destroy.user_data_index].full_type,
261 None, bounds,
263 bounds_names,
264 true,
265 );
266 }
267 for FuncParameter {
268 pos,
269 full_type,
270 callbacks: calls,
271 } in group_by_user_data.values()
272 {
273 if calls.len() > 1 {
274 chunks.push(Chunk::Let {
275 name: format!("super_callback{pos}"),
276 is_mut: false,
277 value: Box::new(Chunk::Custom(if poses.is_empty() {
278 format!(
279 "Box_::new(Box_::new(({})))",
280 calls
281 .iter()
282 .map(|c| format!("{}_data", c.name))
283 .collect::<Vec<_>>()
284 .join(", ")
285 )
286 } else if calls.iter().all(|c| c.scope.is_call()) {
287 format!(
288 "&mut ({})",
289 calls
290 .iter()
291 .map(|c| format!("{}_data", c.name))
292 .collect::<Vec<_>>()
293 .join(", ")
294 )
295 } else {
296 format!(
297 "Box_::new(({}))",
298 calls
299 .iter()
300 .map(|c| format!("{}_data", c.name))
301 .collect::<Vec<_>>()
302 .join(", ")
303 )
304 })),
305 type_: Some(Box::new(Chunk::Custom(
306 full_type.clone().map(|x| x.0).unwrap(),
307 ))),
308 });
309 } else if !calls.is_empty() {
310 chunks.push(Chunk::Let {
311 name: format!("super_callback{pos}"),
312 is_mut: false,
313 value: Box::new(Chunk::Custom(format!(
314 "{}{}_data",
315 if calls[0].scope.is_call() {
316 "&mut "
317 } else {
318 ""
319 },
320 calls[0].name
321 ))),
322 type_: Some(Box::new(Chunk::Custom(if calls[0].scope.is_call() {
323 format!("&mut {}", calls[0].bound_name)
324 } else {
325 format!("Box_<{}>", calls[0].bound_name)
326 }))),
327 });
328 }
329 }
330 } else if let Some(ref trampoline) = self.async_trampoline {
331 self.add_async_trampoline(env, &mut chunks, trampoline);
332 }
333
334 chunks.push(if self.in_unsafe {
335 Chunk::Chunks(body)
336 } else {
337 Chunk::Unsafe(body)
338 });
339 Chunk::BlockHalf(chunks)
340 }
341
342 fn remove_extra_assume_init(
343 &self,
344 array_length_name: &Option<String>,
345 uninitialized_vars: &mut Vec<(String, bool)>,
346 ) {
347 if let Some(array_length_name) = array_length_name {
350 uninitialized_vars.retain(|(x, _)| x != array_length_name);
351 }
352 }
353
354 fn generate_initialized_value(
355 &self,
356 name: &str,
357 uninitialized_vars: &[(String, bool)],
358 ) -> Chunk {
359 if let Some(need_from_glib) = self.is_uninitialized_var(name, uninitialized_vars) {
360 Chunk::Custom(format!(
361 "{}{}.assume_init(){}",
362 if need_from_glib { "from_glib(" } else { "" },
363 name,
364 if need_from_glib { ")" } else { "" },
365 ))
366 } else {
367 Chunk::Custom(name.to_string())
368 }
369 }
370
371 fn is_uninitialized_var(
372 &self,
373 name: &str,
374 uninitialized_vars: &[(String, bool)],
375 ) -> Option<bool> {
376 uninitialized_vars
377 .iter()
378 .find(|(n, _)| n.eq(name))
379 .map(|(_, need_from_glib)| *need_from_glib)
380 }
381
382 fn add_trampoline(
383 &self,
384 env: &Env,
385 chunks: &mut Vec<Chunk>,
386 trampoline: &Trampoline,
387 full_type: &Option<(String, String)>,
388 pos: Option<usize>,
389 bounds: &str,
390 bounds_names: &str,
391 is_destroy: bool,
392 ) {
393 if !is_destroy {
394 if full_type.is_none() {
395 if trampoline.scope.is_call() {
396 chunks.push(Chunk::Custom(format!(
397 "let mut {0}_data: {1} = {0};",
398 trampoline.name, trampoline.bound_name
399 )));
400 } else {
401 chunks.push(Chunk::Custom(format!(
402 "let {0}_data: Box_<{1}> = Box_::new({0});",
403 trampoline.name, trampoline.bound_name
404 )));
405 }
406 } else if trampoline.scope.is_call() {
407 chunks.push(Chunk::Custom(format!(
408 "let mut {0}_data: &mut {1} = &mut {0};",
409 trampoline.name, trampoline.bound_name
410 )));
411 } else {
412 chunks.push(Chunk::Custom(format!(
413 "let {0}_data: {1} = {0};",
414 trampoline.name, trampoline.bound_name
415 )));
416 }
417 }
418
419 let mut body = Vec::new();
420 let mut arguments = Vec::new();
421
422 for par in &trampoline.parameters.transformations {
423 if par.name == "this"
424 || trampoline.parameters.c_parameters[par.ind_c].is_real_gpointer(env)
425 {
426 continue;
427 }
428 let ty_name = match RustType::try_new(env, par.typ) {
429 Ok(x) => x.into_string(),
430 _ => String::new(),
431 };
432 let nullable = trampoline.parameters.rust_parameters[par.ind_rust].nullable;
433 let is_basic = add_chunk_for_type(env, par.typ, par, &mut body, &ty_name, nullable);
434 if is_gstring(&ty_name) {
435 if *nullable {
436 arguments.push(Chunk::Name(format!(
437 "(*{}).as_ref().map(|s| s.as_str())",
438 par.name
439 )));
440 } else {
441 arguments.push(Chunk::Name(format!("{}.as_str()", par.name)));
442 }
443 continue;
444 }
445 if *nullable && !is_basic {
446 arguments.push(Chunk::Name(format!("{}.as_ref().as_ref()", par.name)));
447 continue;
448 }
449 arguments.push(Chunk::Name(format!(
450 "{}{}",
451 if is_basic { "" } else { "&" },
452 par.name
453 )));
454 }
455
456 let func = trampoline
457 .parameters
458 .c_parameters
459 .last()
460 .map_or_else(|| "Unknown".to_owned(), |p| p.name.clone());
461
462 if let Some(full_type) = full_type {
463 if is_destroy || trampoline.scope.is_async() {
464 body.push(Chunk::Let {
465 name: format!("{}callback", if is_destroy { "_" } else { "" }),
466 is_mut: false,
467 value: Box::new(Chunk::Custom(format!("Box_::from_raw({func} as *mut _)"))),
468 type_: Some(Box::new(Chunk::Custom(full_type.1.clone()))),
469 });
470 } else {
471 body.push(Chunk::Let {
472 name: "callback".to_owned(),
473 is_mut: false,
474 value: Box::new(Chunk::Custom(format!(
475 "{}*({} as *mut _)",
476 if !trampoline.scope.is_call() {
477 "&"
478 } else if pos.is_some() {
479 "&mut "
480 } else {
481 ""
482 },
483 func
484 ))),
485 type_: Some(Box::new(Chunk::Custom(
486 if !trampoline.scope.is_async() && !trampoline.scope.is_call() {
487 format!(
488 "&{}",
489 full_type
490 .1
491 .strip_prefix("Box_<")
492 .unwrap()
493 .strip_suffix(">")
494 .unwrap()
495 )
496 } else {
497 full_type.1.clone()
498 },
499 ))),
500 });
501 if trampoline.scope.is_async() {
502 body.push(Chunk::Custom(format!(
503 "let callback = callback{}{};",
504 if let Some(pos) = pos {
505 format!(".{pos}")
506 } else {
507 String::new()
508 },
509 if *trampoline.nullable {
510 ".expect(\"cannot get closure...\")"
511 } else {
512 ""
513 }
514 )));
515 } else if !trampoline.scope.is_call() {
516 if *trampoline.nullable {
517 body.push(Chunk::Custom(format!(
518 "if let Some(ref callback) = callback{} {{",
519 if let Some(pos) = pos {
520 format!(".{pos}")
521 } else {
522 String::new()
523 }
524 )));
525 } else {
526 body.push(Chunk::Custom(format!(
527 "let callback = &callback{};",
528 if let Some(pos) = pos {
529 format!(".{pos}")
530 } else {
531 String::new()
532 }
533 )));
534 }
535 } else if !trampoline.scope.is_async() && *trampoline.nullable {
536 body.push(Chunk::Custom(format!(
537 "if let Some(ref {}callback) = {} {{",
538 if trampoline.scope.is_call() {
539 "mut "
540 } else {
541 ""
542 },
543 if let Some(pos) = pos {
544 format!("(*callback).{pos}")
545 } else {
546 "*callback".to_owned()
547 }
548 )));
549 }
550 }
551 } else {
552 body.push(Chunk::Let {
553 name: format!("{}callback", if is_destroy { "_" } else { "" }),
554 is_mut: false,
555 value: Box::new(Chunk::Custom(
556 if is_destroy || trampoline.scope.is_async() {
557 format!("Box_::from_raw({} as *mut {})", func, trampoline.bound_name)
558 } else if trampoline.scope.is_call() {
559 format!("{} as *mut {}", func, trampoline.bound_name)
560 } else {
561 format!("&*({} as *mut {})", func, trampoline.bound_name)
562 },
563 )),
564 type_: None,
565 });
566 if !is_destroy && *trampoline.nullable {
567 if trampoline.scope.is_async() {
568 body.push(Chunk::Custom(
569 "let callback = (*callback).expect(\"cannot get closure...\");".to_owned(),
570 ));
571 } else {
572 body.push(Chunk::Custom(format!(
573 "if let Some(ref {}callback) = {} {{",
574 if trampoline.scope.is_call() {
575 "mut "
576 } else {
577 ""
578 },
579 if let Some(pos) = pos {
580 format!("(*callback).{pos}")
581 } else {
582 "*callback".to_owned()
583 }
584 )));
585 }
586 }
587 }
588 if !is_destroy {
589 use crate::writer::to_code::ToCode;
590 body.push(Chunk::Custom(format!(
591 "{}({})",
592 if !*trampoline.nullable {
593 "(*callback)"
594 } else if trampoline.scope.is_async() {
595 "callback"
596 } else {
597 "\tcallback"
598 },
599 arguments
600 .iter()
601 .flat_map(|arg| arg.to_code(env))
602 .collect::<Vec<_>>()
603 .join(", "),
604 )));
605 if !trampoline.scope.is_async() && *trampoline.nullable {
606 body.push(Chunk::Custom("} else {".to_owned()));
607 body.push(Chunk::Custom(
608 "\tpanic!(\"cannot get closure...\")".to_owned(),
609 ));
610 body.push(Chunk::Custom("}".to_owned()));
611 }
612 if trampoline.ret.c_type != "void" {
613 use crate::codegen::trampoline_to_glib::TrampolineToGlib;
614
615 body.push(Chunk::Custom(trampoline.ret.trampoline_to_glib(env)));
616 }
617 }
618
619 let extern_func = Chunk::ExternCFunc {
620 name: format!("{}_func", trampoline.name),
621 parameters: trampoline
622 .parameters
623 .c_parameters
624 .iter()
625 .skip(1) .map(|p| {
627 if p.is_real_gpointer(env) {
628 Param {
629 name: p.name.clone(),
630 typ: use_glib_if_needed(env, "ffi::gpointer"),
631 }
632 } else {
633 Param {
634 name: p.name.clone(),
635 typ: crate::analysis::ffi_type::ffi_type(env, p.typ, &p.c_type)
636 .expect("failed to write c_type")
637 .into_string(),
638 }
639 }
640 })
641 .collect::<Vec<_>>(),
642 body: Box::new(Chunk::Chunks(body)),
643 return_value: if trampoline.ret.c_type != "void" {
644 let p = &trampoline.ret;
645 Some(
646 crate::analysis::ffi_type::ffi_type(env, p.typ, &p.c_type)
647 .expect("failed to write c_type")
648 .into_string(),
649 )
650 } else {
651 None
652 },
653 bounds: bounds.to_owned(),
654 };
655
656 chunks.push(extern_func);
657 let bounds_str = if bounds_names.is_empty() {
658 String::new()
659 } else {
660 format!("::<{bounds_names}>")
661 };
662 if !is_destroy {
663 if *trampoline.nullable {
664 chunks.push(Chunk::Custom(format!(
665 "let {0} = if {0}_data.is_some() {{ Some({0}_func{1} as _) }} else {{ None }};",
666 trampoline.name, bounds_str
667 )));
668 } else {
669 chunks.push(Chunk::Custom(format!(
670 "let {0} = Some({0}_func{1} as _);",
671 trampoline.name, bounds_str
672 )));
673 }
674 } else {
675 chunks.push(Chunk::Custom(format!(
676 "let destroy_call{} = Some({}_func{} as _);",
677 trampoline.destroy_index, trampoline.name, bounds_str
678 )));
679 }
680 }
681
682 fn add_async_trampoline(
683 &self,
684 env: &Env,
685 chunks: &mut Vec<Chunk>,
686 trampoline: &AsyncTrampoline,
687 ) {
688 chunks.push(Chunk::Custom(String::from(
689 r#"
690 let main_context = glib::MainContext::ref_thread_default();
691 let is_main_context_owner = main_context.is_owner();
692 let has_acquired_main_context = (!is_main_context_owner)
693 .then(|| main_context.acquire().ok())
694 .flatten();
695 assert!(
696 is_main_context_owner || has_acquired_main_context.is_some(),
697 "Async operations only allowed if the thread is owning the MainContext"
698 );
699 "#,
700 )));
701
702 chunks.push(Chunk::Let {
703 name: "user_data".to_string(),
704 is_mut: false,
705 value: Box::new(Chunk::Custom(format!(
706 "Box_::new({}::new(callback))",
707 use_glib_type(env, "thread_guard::ThreadGuard")
708 ))),
709 type_: Some(Box::new(Chunk::Custom(format!(
710 "Box_<{}<{}>>",
711 use_glib_type(env, "thread_guard::ThreadGuard"),
712 trampoline.bound_name
713 )))),
714 });
715
716 let mut finish_args = vec![];
717 let mut uninitialized_vars = Vec::new();
718 if trampoline.is_method {
719 finish_args.push(Chunk::Cast {
720 name: "_source_object".to_string(),
721 type_: "*mut _".to_string(),
722 });
723 }
724 let mut found_async_result = false;
725 finish_args.extend(
726 trampoline
727 .output_params
728 .iter()
729 .filter(|out| {
730 out.lib_par.direction == ParameterDirection::Out
731 || out.lib_par.typ.full_name(&env.library) == "Gio.AsyncResult"
732 })
733 .map(|out| {
734 if out.lib_par.typ.full_name(&env.library) == "Gio.AsyncResult" {
735 found_async_result = true;
736 return Chunk::Name("res".to_string());
737 }
738 let kind = type_mem_mode(env, &out.lib_par);
739 let mut par: parameter_ffi_call_out::Parameter = out.into();
740 if kind.is_uninitialized() {
741 par.is_uninitialized = true;
742 uninitialized_vars.push((
743 out.lib_par.name.clone(),
744 self.check_if_need_glib_conversion(env, out.lib_par.typ),
745 ));
746 }
747 Chunk::FfiCallOutParameter { par }
748 }),
749 );
750 assert!(
751 found_async_result,
752 "The check *wasn't* performed in analysis part: Guillaume was wrong!"
753 );
754 let index_to_ignore = find_index_to_ignore(
755 trampoline.output_params.iter().map(|par| &par.lib_par),
756 trampoline.ffi_ret.as_ref().map(|ret| &ret.lib_par),
757 );
758 let mut result: Vec<_> = trampoline
759 .output_params
760 .iter()
761 .enumerate()
762 .filter(|&(index, out)| {
763 out.lib_par.direction == ParameterDirection::Out
764 && out.lib_par.name != "error"
765 && Some(index) != index_to_ignore
766 })
767 .map(|(_, out)| {
768 let mem_mode = c_type_mem_mode_lib(
769 env,
770 out.lib_par.typ,
771 out.lib_par.caller_allocates,
772 out.lib_par.transfer,
773 );
774 let value = self.generate_initialized_value(&out.lib_par.name, &uninitialized_vars);
775 if let OutMemMode::UninitializedNamed(_) = mem_mode {
776 value
777 } else {
778 let array_length_name = self.array_length(out).cloned();
779 self.remove_extra_assume_init(&array_length_name, &mut uninitialized_vars);
780 Chunk::FromGlibConversion {
781 mode: out.into(),
782 array_length_name,
783 value: Box::new(value),
784 }
785 }
786 })
787 .collect();
788
789 if let Some(ref ffi_ret) = trampoline.ffi_ret {
790 let mem_mode = c_type_mem_mode_lib(
791 env,
792 ffi_ret.lib_par.typ,
793 ffi_ret.lib_par.caller_allocates,
794 ffi_ret.lib_par.transfer,
795 );
796 let value = Chunk::Name("ret".to_string());
797 if let OutMemMode::UninitializedNamed(_) = mem_mode {
798 result.insert(0, value);
799 } else {
800 let array_length_name = self.array_length(ffi_ret).cloned();
801 self.remove_extra_assume_init(&array_length_name, &mut uninitialized_vars);
802 result.insert(
803 0,
804 Chunk::FromGlibConversion {
805 mode: ffi_ret.into(),
806 array_length_name,
807 value: Box::new(value),
808 },
809 );
810 }
811 }
812 let has_error_parameter = self
814 .async_trampoline
815 .as_ref()
816 .map(|a| a.has_error_parameter)
817 .unwrap_or_default();
818
819 let result = Chunk::Tuple(result, TupleMode::WithUnit);
820
821 let mut body = if has_error_parameter {
822 vec![Chunk::Let {
823 name: "error".to_string(),
824 is_mut: true,
825 value: Box::new(Chunk::NullMutPtr),
826 type_: None,
827 }]
828 } else {
829 vec![]
830 };
831 let output_vars = trampoline
832 .output_params
833 .iter()
834 .filter(|out| {
835 out.lib_par.direction == ParameterDirection::Out && out.lib_par.name != "error"
836 })
837 .map(|out| Chunk::Let {
838 name: out.lib_par.name.clone(),
839 is_mut: true,
840 value: Box::new(type_mem_mode(env, &out.lib_par)),
841 type_: None,
842 });
843 body.extend(output_vars);
844
845 if trampoline.ffi_ret.is_some() {
846 let ret_name = if has_error_parameter {
847 "ret"
848 } else {
849 "result" };
853
854 body.push(Chunk::Let {
855 name: ret_name.to_string(),
856 is_mut: false,
857 value: Box::new(Chunk::FfiCall {
858 name: trampoline.finish_func_name.clone(),
859 params: finish_args,
860 ignore_return: false,
861 }),
862 type_: None,
863 });
864 } else {
865 body.push(Chunk::FfiCall {
866 name: trampoline.finish_func_name.clone(),
867 params: finish_args,
868 ignore_return: true,
869 });
870 }
871
872 if has_error_parameter {
873 body.push(Chunk::Let {
874 name: "result".to_string(),
875 is_mut: false,
876 value: Box::new(Chunk::ErrorResultReturn {
877 ret: None,
878 value: Box::new(result),
879 }),
880 type_: None,
881 });
882 }
883 body.push(Chunk::Let {
884 name: "callback".to_string(),
885 is_mut: false,
886 value: Box::new(Chunk::Custom("Box_::from_raw(user_data as *mut _)".into())),
887 type_: Some(Box::new(Chunk::Custom(format!(
888 "Box_<{}<{}>>",
889 use_glib_type(env, "thread_guard::ThreadGuard"),
890 trampoline.bound_name
891 )))),
892 });
893 body.push(Chunk::Let {
894 name: "callback".to_string(),
895 is_mut: false,
896 value: Box::new(Chunk::Custom("callback.into_inner()".into())),
897 type_: Some(Box::new(Chunk::Custom(format!(
898 "{}",
899 trampoline.bound_name
900 )))),
901 });
902 body.push(Chunk::Call {
903 func_name: "callback".to_string(),
904 arguments: vec![Chunk::Name("result".to_string())],
905 });
906
907 let parameters = vec![
908 Param {
909 name: "_source_object".to_string(),
910 typ: format!("*mut {}", use_glib_type(env, "gobject_ffi::GObject")),
911 },
912 Param {
913 name: "res".to_string(),
914 typ: format!("*mut {}", use_gio_type(env, "ffi::GAsyncResult")),
915 },
916 Param {
917 name: "user_data".to_string(),
918 typ: use_glib_if_needed(env, "ffi::gpointer"),
919 },
920 ];
921
922 chunks.push(Chunk::ExternCFunc {
923 name: format!(
924 "{}<{}: {}>",
925 trampoline.name, trampoline.bound_name, trampoline.callback_type
926 ),
927 parameters,
928 body: Box::new(Chunk::Chunks(body.clone())),
929 return_value: None,
930 bounds: String::new(),
931 });
932 let chunk = Chunk::Let {
933 name: "callback".to_string(),
934 is_mut: false,
935 value: Box::new(Chunk::Name(format!(
936 "{}::<{}>",
937 trampoline.name, trampoline.bound_name
938 ))),
939 type_: None,
940 };
941 chunks.push(chunk);
942 }
943
944 fn array_length(&self, param: &analysis::Parameter) -> Option<&String> {
945 self.async_trampoline.as_ref().and_then(|trampoline| {
946 param
947 .lib_par
948 .array_length
949 .map(|index| &trampoline.output_params[index as usize].lib_par.name)
950 })
951 }
952
953 fn add_assertion(&self, chunks: &mut Vec<Chunk>) {
954 match self.assertion {
955 SafetyAssertionMode::None => (),
956 x => chunks.insert(0, Chunk::AssertInit(x)),
957 }
958 }
959
960 fn add_in_array_lengths(&self, chunks: &mut Vec<Chunk>) {
961 for trans in &self.transformations {
962 if let TransformationType::Length {
963 ref array_name,
964 ref array_length_name,
965 ..
966 } = trans.transformation_type
967 {
968 if let In = self.parameters[trans.ind_c] {
969 let value = Chunk::Custom(format!("{array_name}.len() as _"));
970 chunks.push(Chunk::Let {
971 name: array_length_name.clone(),
972 is_mut: false,
973 value: Box::new(value),
974 type_: None,
975 });
976 }
977 }
978 }
979 }
980
981 fn generate_call(&self, calls: &FuncParameters<'_>) -> Chunk {
982 let params = self.generate_func_parameters(calls);
983 Chunk::FfiCall {
984 name: self.glib_name.clone(),
985 params,
986 ignore_return: false,
987 }
988 }
989 fn generate_call_conversion(
990 &self,
991 call: Chunk,
992 uninitialized_vars: &mut Vec<(String, bool)>,
993 ) -> Chunk {
994 let array_length_name = self.find_array_length_name("");
995 self.remove_extra_assume_init(&array_length_name, uninitialized_vars);
996 Chunk::FfiCallConversion {
997 ret: self.ret.ret.clone(),
998 array_length_name,
999 call: Box::new(call),
1000 }
1001 }
1002 fn generate_func_parameters(&self, calls: &FuncParameters<'_>) -> Vec<Chunk> {
1003 let mut params = Vec::new();
1004 for trans in &self.transformations {
1005 if !trans.transformation_type.is_to_glib() {
1006 continue;
1007 }
1008 let par = &self.parameters[trans.ind_c];
1009 let chunk = match par {
1010 In => Chunk::FfiCallParameter {
1011 transformation_type: trans.transformation_type.clone(),
1012 },
1013 Out { parameter, .. } => Chunk::FfiCallOutParameter {
1014 par: parameter.clone(),
1015 },
1016 };
1017 params.push(chunk);
1018 }
1019 let mut to_insert = Vec::new();
1020 for (user_data_index, FuncParameter { pos, callbacks, .. }) in calls.iter() {
1021 let all_call = callbacks.iter().all(|c| c.scope.is_call());
1022 to_insert.push((
1023 *user_data_index,
1024 Chunk::FfiCallParameter {
1025 transformation_type: TransformationType::ToGlibDirect {
1026 name: if all_call {
1027 format!("super_callback{pos} as *mut _ as *mut _")
1028 } else {
1029 format!("Box_::into_raw(super_callback{pos}) as *mut _")
1030 },
1031 },
1032 },
1033 ));
1034 }
1035 for destroy in &self.destroys {
1036 to_insert.push((
1037 destroy.destroy_index,
1038 Chunk::FfiCallParameter {
1039 transformation_type: TransformationType::ToGlibDirect {
1040 name: format!("destroy_call{}", destroy.destroy_index),
1041 },
1042 },
1043 ));
1044 }
1045 to_insert.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
1046 for (pos, data) in to_insert {
1047 params.insert(pos, data);
1048 }
1049 params
1050 }
1051 fn get_outs(&self) -> Vec<&Parameter> {
1052 self.parameters
1053 .iter()
1054 .filter(|par| matches!(*par, Out { .. }))
1055 .collect()
1056 }
1057 fn get_outs_without_error(&self) -> Vec<&Parameter> {
1058 self.parameters
1059 .iter()
1060 .filter(|par| {
1061 if let Out { parameter, .. } = par {
1062 !parameter.is_error
1063 } else {
1064 false
1065 }
1066 })
1067 .collect()
1068 }
1069 fn check_if_need_glib_conversion(&self, env: &Env, typ: TypeId) -> bool {
1070 matches!(
1073 env.type_(typ),
1074 library::Type::Alias(a) if a.c_identifier == "GPid"
1075 )
1076 }
1077 fn write_out_variables(&self, body: &mut Vec<Chunk>, env: &Env) -> Vec<(String, bool)> {
1078 let mut uninitialized_vars = Vec::new();
1079 for par in self.get_outs() {
1080 if let Out {
1081 parameter,
1082 mem_mode,
1083 } = par
1084 {
1085 let val = self.get_uninitialized(mem_mode);
1086 if val.is_uninitialized() {
1087 uninitialized_vars.push((
1088 parameter.name.clone(),
1089 self.check_if_need_glib_conversion(env, parameter.typ),
1090 ));
1091 }
1092 let chunk = Chunk::Let {
1093 name: parameter.name.clone(),
1094 is_mut: true,
1095 value: Box::new(val),
1096 type_: None,
1097 };
1098 body.push(chunk);
1099 }
1100 }
1101 uninitialized_vars
1102 }
1103 fn get_uninitialized(&self, mem_mode: &OutMemMode) -> Chunk {
1104 use self::OutMemMode::*;
1105 match mem_mode {
1106 Uninitialized => Chunk::Uninitialized,
1107 UninitializedNamed(ref name) => Chunk::UninitializedNamed { name: name.clone() },
1108 NullPtr => Chunk::NullPtr,
1109 NullMutPtr => Chunk::NullMutPtr,
1110 }
1111 }
1112
1113 fn generate_out_return(&self, uninitialized_vars: &mut Vec<(String, bool)>) -> Option<Chunk> {
1114 if !self.outs_as_return {
1115 return None;
1116 }
1117 let outs = self.get_outs_without_error();
1118 let mut chs: Vec<Chunk> = Vec::with_capacity(outs.len());
1119 for par in outs {
1120 if let Out {
1121 parameter,
1122 mem_mode,
1123 } = par
1124 {
1125 if self.transformations.iter().any(|tr| {
1126 matches!(
1127 &tr.transformation_type,
1128 TransformationType::Length {
1129 array_length_name,
1130 ..
1131 } if array_length_name == ¶meter.name
1132 )
1133 }) {
1134 continue;
1135 }
1136
1137 chs.push(self.out_parameter_to_return(parameter, mem_mode, uninitialized_vars));
1138 }
1139 }
1140 let chunk = Chunk::Tuple(chs, TupleMode::Auto);
1141 Some(chunk)
1142 }
1143 fn out_parameter_to_return(
1144 &self,
1145 parameter: ¶meter_ffi_call_out::Parameter,
1146 mem_mode: &OutMemMode,
1147 uninitialized_vars: &mut Vec<(String, bool)>,
1148 ) -> Chunk {
1149 let value = self.generate_initialized_value(¶meter.name, uninitialized_vars);
1150 if let OutMemMode::UninitializedNamed(_) = mem_mode {
1151 value
1152 } else {
1153 let array_length_name = self.find_array_length_name(¶meter.name);
1154 self.remove_extra_assume_init(&array_length_name, uninitialized_vars);
1155 Chunk::FromGlibConversion {
1156 mode: parameter.into(),
1157 array_length_name,
1158 value: Box::new(value),
1159 }
1160 }
1161 }
1162 fn apply_outs_mode(
1163 &self,
1164 call: Chunk,
1165 ret: Option<Chunk>,
1166 uninitialized_vars: &mut Vec<(String, bool)>,
1167 ) -> (Chunk, Option<Chunk>) {
1168 use crate::analysis::out_parameters::Mode::*;
1169 match self.outs_mode {
1170 None => (call, ret),
1171 Normal => (call, ret),
1172 Optional => {
1173 let call = Chunk::Let {
1174 name: "ret".into(),
1175 is_mut: false,
1176 value: Box::new(call),
1177 type_: Option::None,
1178 };
1179 let ret = ret.expect("No return in optional outs mode");
1180 let ret = Chunk::OptionalReturn {
1181 condition: "ret".into(),
1182 value: Box::new(ret),
1183 };
1184 (call, Some(ret))
1185 }
1186 Combined => {
1187 let call = Chunk::Let {
1188 name: "ret".into(),
1189 is_mut: false,
1190 value: Box::new(call),
1191 type_: Option::None,
1192 };
1193 let mut ret = ret.expect("No return in combined outs mode");
1194 if let Chunk::Tuple(ref mut vec, _) = ret {
1195 vec.insert(0, Chunk::Custom("ret".into()));
1196 }
1197 (call, Some(ret))
1198 }
1199 Throws(return_strategy) => {
1200 let (boxed_call, array_length_name, ret_info) = if let Chunk::FfiCallConversion {
1202 call: inner,
1203 array_length_name,
1204 ret: ret_info,
1205 } = call
1206 {
1207 (inner, array_length_name, ret_info)
1208 } else {
1209 panic!("Call without Chunk::FfiCallConversion")
1210 };
1211 self.remove_extra_assume_init(&array_length_name, uninitialized_vars);
1212 let (name, assert_safe_ret) = match return_strategy {
1213 ThrowFunctionReturnStrategy::ReturnResult => ("ret", Option::None),
1214 ThrowFunctionReturnStrategy::CheckError => {
1215 ("is_ok", Some(Box::new(Chunk::AssertErrorSanity)))
1216 }
1217 ThrowFunctionReturnStrategy::Void => ("_", Option::None),
1218 };
1219 let call = Chunk::Let {
1220 name: name.into(),
1221 is_mut: false,
1222 value: boxed_call,
1223 type_: Option::None,
1224 };
1225 let mut ret = ret.expect("No return in throws outs mode");
1226
1227 if let Chunk::Tuple(vec, mode) = &mut ret {
1228 *mode = TupleMode::WithUnit;
1229 if return_strategy == ThrowFunctionReturnStrategy::ReturnResult {
1230 let val = Chunk::Custom("ret".into());
1231 let conv = Chunk::FfiCallConversion {
1232 call: Box::new(val),
1233 array_length_name,
1234 ret: ret_info,
1235 };
1236 vec.insert(0, conv);
1237 }
1238 } else {
1239 panic!("Return is not Tuple")
1240 }
1241 ret = Chunk::ErrorResultReturn {
1242 ret: assert_safe_ret,
1243 value: Box::new(ret),
1244 };
1245 (call, Some(ret))
1246 }
1247 }
1248 }
1249
1250 fn find_array_length_name(&self, array_name_: &str) -> Option<String> {
1251 self.transformations.iter().find_map(|tr| {
1252 if let TransformationType::Length {
1253 ref array_name,
1254 ref array_length_name,
1255 ..
1256 } = tr.transformation_type
1257 {
1258 if array_name == array_name_ {
1259 Some(array_length_name.clone())
1260 } else {
1261 None
1262 }
1263 } else {
1264 None
1265 }
1266 })
1267 }
1268}
1269
1270fn c_type_mem_mode_lib(
1271 env: &Env,
1272 typ: library::TypeId,
1273 caller_allocates: bool,
1274 transfer: library::Transfer,
1275) -> OutMemMode {
1276 use self::OutMemMode::*;
1277 match ConversionType::of(env, typ) {
1278 ConversionType::Pointer => {
1279 if caller_allocates {
1280 UninitializedNamed(RustType::try_new(env, typ).unwrap().into_string())
1281 } else {
1282 use crate::library::Type::*;
1283 let type_ = env.library.type_(typ);
1284 match type_ {
1285 Basic(
1286 library::Basic::Utf8 | library::Basic::OsString | library::Basic::Filename,
1287 ) => {
1288 if transfer == library::Transfer::Full {
1289 NullMutPtr
1290 } else {
1291 NullPtr
1292 }
1293 }
1294 _ => NullMutPtr,
1295 }
1296 }
1297 }
1298 _ => Uninitialized,
1299 }
1300}
1301
1302fn c_type_mem_mode(env: &Env, parameter: &AnalysisCParameter) -> OutMemMode {
1303 c_type_mem_mode_lib(
1304 env,
1305 parameter.typ,
1306 parameter.caller_allocates,
1307 parameter.transfer,
1308 )
1309}
1310
1311fn type_mem_mode(env: &Env, parameter: &library::Parameter) -> Chunk {
1312 match ConversionType::of(env, parameter.typ) {
1313 ConversionType::Pointer => {
1314 if parameter.caller_allocates {
1315 Chunk::UninitializedNamed {
1316 name: RustType::try_new(env, parameter.typ).unwrap().into_string(),
1317 }
1318 } else {
1319 use crate::library::Type::*;
1320 let type_ = env.library.type_(parameter.typ);
1321 match type_ {
1322 Basic(
1323 library::Basic::Utf8 | library::Basic::OsString | library::Basic::Filename,
1324 ) => {
1325 if parameter.transfer == library::Transfer::Full {
1326 Chunk::NullMutPtr
1327 } else {
1328 Chunk::NullPtr
1329 }
1330 }
1331 _ => Chunk::NullMutPtr,
1332 }
1333 }
1334 }
1335 _ => Chunk::Uninitialized,
1336 }
1337}
1338
1339fn add_chunk_for_type(
1340 env: &Env,
1341 typ_: library::TypeId,
1342 par: &trampoline_parameters::Transformation,
1343 body: &mut Vec<Chunk>,
1344 ty_name: &str,
1345 nullable: library::Nullable,
1346) -> bool {
1347 let type_ = env.type_(typ_);
1348 match type_ {
1349 library::Type::Basic(x) if !x.requires_conversion() => true,
1350 library::Type::Basic(library::Basic::Boolean) => {
1351 body.push(Chunk::Custom(format!(
1352 "let {0} = from_glib({0});",
1353 par.name
1354 )));
1355 true
1356 }
1357 library::Type::Basic(library::Basic::UniChar) => {
1358 body.push(Chunk::Custom(format!(
1359 "let {0} = std::convert::TryFrom::try_from({0})\
1360 .expect(\"conversion from an invalid Unicode value attempted\");",
1361 par.name
1362 )));
1363 true
1364 }
1365 library::Type::Alias(_) if ty_name == "glib::Pid" => {
1366 body.push(Chunk::Custom(format!(
1367 "let {0} = from_glib({0});",
1368 par.name
1369 )));
1370 true
1371 }
1372 library::Type::Alias(x) => add_chunk_for_type(env, x.typ, par, body, ty_name, nullable),
1373 x => {
1374 let (begin, end) =
1375 crate::codegen::trampoline_from_glib::from_glib_xxx(par.transfer, true);
1376
1377 let type_name;
1378 if is_gstring(ty_name) {
1379 if *nullable {
1380 if par.conversion_type == ConversionType::Borrow {
1381 type_name = String::from(": Borrowed<Option<glib::GString>>");
1382 } else {
1383 type_name = String::from(": Option<glib::GString>");
1384 }
1385 } else if par.conversion_type == ConversionType::Borrow {
1386 type_name = String::from(": Borrowed<glib::GString>");
1387 } else {
1388 type_name = String::from(": GString");
1389 }
1390 } else if par.transfer == library::Transfer::None && *nullable {
1391 if par.conversion_type == ConversionType::Borrow {
1392 type_name = format!(": Borrowed<Option<{ty_name}>>");
1393 } else {
1394 type_name = format!(": Option<{ty_name}>");
1395 }
1396 } else {
1397 type_name = String::new();
1398 }
1399
1400 body.push(Chunk::Custom(format!(
1401 "let {1}{3} = {0}{1}{2};",
1402 begin, par.name, end, type_name
1403 )));
1404 x.is_basic()
1405 }
1406 }
1407}