1use std::fmt::Write;
2
3use itertools::Itertools;
4use sha2::{Digest, Sha256};
5
6use crate::{
7 ZngurTrait, ZngurWellknownTrait, ZngurWellknownTraitData,
8 cpp::{CppFnSig, CppLayoutPolicy, CppPath, CppTraitDefinition, CppTraitMethod, CppType},
9};
10
11use zngur_def::*;
12
13pub trait IntoCpp {
14 fn into_cpp(&self, namespace: &str, crate_name: &str) -> CppType;
15}
16
17impl IntoCpp for RustPathAndGenerics {
18 fn into_cpp(&self, namespace: &str, crate_name: &str) -> CppType {
19 let RustPathAndGenerics {
20 path,
21 generics,
22 named_generics,
23 } = self;
24 let named_generics = named_generics.iter().sorted_by_key(|x| &x.0).map(|x| &x.1);
25 CppType {
26 path: CppPath::from_rust_path(path, namespace, crate_name),
27 generic_args: generics
28 .iter()
29 .chain(named_generics)
30 .map(|x| x.into_cpp(namespace, crate_name))
31 .collect(),
32 tail: None,
33 }
34 }
35}
36
37impl IntoCpp for RustTrait {
38 fn into_cpp(&self, namespace: &str, crate_name: &str) -> CppType {
39 match self {
40 RustTrait::Normal(pg) => pg.into_cpp(namespace, crate_name),
41 RustTrait::Fn {
42 name,
43 inputs,
44 output,
45 } => CppType {
46 path: CppPath::from(&*format!("{namespace}::{name}")),
47 generic_args: inputs
48 .iter()
49 .chain(Some(&**output))
50 .map(|x| x.into_cpp(namespace, crate_name))
51 .collect(),
52 tail: None,
53 },
54 }
55 }
56}
57
58impl IntoCpp for RustType {
59 fn into_cpp(&self, namespace: &str, crate_name: &str) -> CppType {
60 fn for_builtin(this: &RustType, namespace: &str, crate_name: &str) -> Option<CppType> {
61 match this {
62 RustType::Primitive(s) => match s {
63 PrimitiveRustType::Uint(s) => Some(CppType::from(&*format!("uint{s}_t"))),
64 PrimitiveRustType::Int(s) => Some(CppType::from(&*format!("int{s}_t"))),
65 PrimitiveRustType::Float(32) => Some(CppType::from("float_t")),
66 PrimitiveRustType::Float(64) => Some(CppType::from("double_t")),
67 PrimitiveRustType::Float(_) => unreachable!(),
68 PrimitiveRustType::Usize => Some(CppType::from("size_t")),
69 PrimitiveRustType::Bool | PrimitiveRustType::Str | PrimitiveRustType::Char => {
70 None
71 }
72 },
73 RustType::Raw(Mutability::Mut, t) => Some(CppType::from(&*format!(
74 "{}*",
75 for_builtin(t, namespace, crate_name)?
76 .to_string()
77 .strip_prefix("::")?
78 ))),
79 RustType::Raw(Mutability::Not, t) => Some(CppType::from(&*format!(
80 "{} const*",
81 for_builtin(t, namespace, crate_name)?
82 .to_string()
83 .strip_prefix("::")?
84 ))),
85 _ => None,
86 }
87 }
88 if let Some(builtin) = for_builtin(self, namespace, crate_name) {
89 return builtin;
90 }
91 match self {
92 RustType::Primitive(s) => match s {
93 PrimitiveRustType::Bool => CppType::from(&*format!("{namespace}::Bool")),
94 PrimitiveRustType::Str => CppType::from(&*format!("{namespace}::Str")),
95 PrimitiveRustType::Char => CppType::from(&*format!("{namespace}::Char")),
96 _ => unreachable!(),
97 },
98 RustType::Boxed(t) => CppType {
99 path: CppPath::from(&*format!("{namespace}::Box")),
100 generic_args: vec![t.into_cpp(namespace, crate_name)],
101 tail: None,
102 },
103 RustType::Ref(m, t) => CppType {
104 path: match m {
105 Mutability::Mut => CppPath::from(&*format!("{}::RefMut", namespace)),
106 Mutability::Not => CppPath::from(&*format!("{}::Ref", namespace)),
107 },
108 generic_args: vec![t.into_cpp(namespace, crate_name)],
109 tail: None,
110 },
111 RustType::Slice(s) => CppType {
112 path: CppPath::from(&*format!("{namespace}::Slice")),
113 generic_args: vec![s.into_cpp(namespace, crate_name)],
114 tail: None,
115 },
116 RustType::Raw(m, t) => CppType {
117 path: match m {
118 Mutability::Mut => CppPath::from(&*format!("{namespace}::RawMut")),
119 Mutability::Not => CppPath::from(&*format!("{namespace}::Raw")),
120 },
121 generic_args: vec![t.into_cpp(namespace, crate_name)],
122 tail: None,
123 },
124 RustType::Adt(pg) => pg.into_cpp(namespace, crate_name),
125 RustType::Tuple(v) => {
126 if v.is_empty() {
127 return CppType::from(&*format!("{namespace}::Unit"));
128 }
129 CppType {
130 path: CppPath::from(&*format!("{namespace}::Tuple")),
131 generic_args: v
132 .into_iter()
133 .map(|x| x.into_cpp(namespace, crate_name))
134 .collect(),
135 tail: None,
136 }
137 }
138 RustType::Dyn(tr, marker_bounds) => {
139 let tr_as_cpp_type = tr.into_cpp(namespace, crate_name);
140 CppType {
141 path: CppPath::from(&*format!("{namespace}::Dyn")),
142 generic_args: [tr_as_cpp_type]
143 .into_iter()
144 .chain(
145 marker_bounds
146 .iter()
147 .map(|x| CppType::from(&*format!("{namespace}::{x}"))),
148 )
149 .collect(),
150 tail: None,
151 }
152 }
153 RustType::Impl(_, _) => panic!("impl Trait is invalid in C++"),
154 RustType::TypeVar(_) => {
155 unreachable!("should not attempt to generate definition for unbound TypeVar")
156 }
157 }
158 }
159}
160
161pub struct RustFile {
162 pub text: String,
163 pub panic_to_exception: bool,
164 pub mangling_base: String,
165}
166
167impl RustFile {
168 pub fn new(mangling_base: &str) -> Self {
169 Self {
170 text: r#"
171macro_rules! __zngur_str_as_array {
172 ($s:expr) => {{
173 const VAL: &str = $s;
174 // SAFETY: `VAL` has at least size `N` because it's const len is right there.
175 const ARR: [u8; VAL.len()] = unsafe { *(VAL.as_bytes() as *const [u8]).cast() };
176 ARR
177 }};
178}
179
180pub const fn __zngur_usize_num_digits(val: usize) -> usize {
181 // docs currently say 64bit only but that's a bug
182 if val == 0 { 1 } else { val.ilog10() as usize + 1 }
183}
184
185pub const fn __zngur_usize_digit(val: usize, digit: usize) -> u8 {
186 let mut temp = val;
187 let mut i = 0;
188 while i < digit {
189 temp /= 10;
190 i += 1;
191 }
192 if temp == 0 && val > 0 {
193 ::core::panic!("no such digit!")
194 } else {
195 (temp % 10) as u8
196 }
197}
198
199pub const fn __zngur_digit_to_ascii(digit: u8) -> u8 {
200 ::core::assert!(digit <= 9);
201 digit + b'0'
202}
203
204pub const fn __zngur_usize_to_digit_array<const N: usize>(val: usize) -> [u8; N] {
205 let mut arr: [u8; N] = [0; N];
206 let mut i = 0;
207 while i < N {
208 arr[N - 1 - i] = __zngur_digit_to_ascii(__zngur_usize_digit(val, i));
209 i += 1;
210 }
211 arr
212}
213
214macro_rules! __zngur_usize_to_str {
215 ($x:expr) => {{
216 const VAL: usize = $x;
217 const ARR: [u8; __zngur_usize_num_digits(VAL)] = __zngur_usize_to_digit_array(VAL);
218 // SAFETY: `ARR` is an ascii byte array which is utf8 compliant
219 const STR: &str = unsafe { str::from_utf8_unchecked(&ARR) };
220 STR
221 }};
222}
223
224pub const fn __zngur_const_str_array_concat<const T: usize, const N: usize, const M: usize>(
225 x: [u8; N],
226 y: [u8; M],
227) -> [u8; T] {
228 ::core::assert!(N + M == T);
229 let mut arr: [u8; T] = [0; T];
230 let mut i = 0;
231 while i < N {
232 arr[i] = x[i];
233 i += 1;
234 }
235 while i - N < M {
236 arr[i] = y[i - N];
237 i += 1;
238 }
239 arr
240}
241
242macro_rules! __zngur_const_str_concat {
243
244 ( $x:expr, $y:expr $(,)? ) => {{
245 const X: &str = $x;
246 const Y: &str = $y;
247 const LEN: usize = X.len() + Y.len();
248 const ARR: [u8; LEN] = __zngur_const_str_array_concat::<LEN, {X.len()}, {Y.len()}>(
249 __zngur_str_as_array!(X),
250 __zngur_str_as_array!(Y),
251 );
252 // SAFETY: `ARR` is an concatenated utf8 byte array built from validated `const str&`
253 const STR: &str = unsafe { str::from_utf8_unchecked(&ARR) };
254 STR
255 }};
256 ( $x:expr, $y:expr, $($rest:expr),+ $(,)? ) => {
257 __zngur_const_str_concat!($x, __zngur_const_str_concat!( $y, $($rest),+ ))
258 };
259
260}
261
262macro_rules! __zngur_assert_is_copy {
263 ($x:ty $(,)?) => {
264 const _: () = {
265 const fn static_assert_is_copy<T: Copy>() {}
266 static_assert_is_copy::<$x>();
267 };
268 };
269}
270
271macro_rules! __zngur_assert_size {
272 ($x:ty, $size:expr $(,)?) => {
273 const _: () = ::core::assert!(
274 $size == ::core::mem::size_of::<$x>(),
275 "{}",
276 __zngur_const_str_concat!(
277 "zngur declared size of ",
278 stringify!($x),
279 " is incorrect: expected ",
280 __zngur_usize_to_str!($size),
281 " , real size is ",
282 __zngur_usize_to_str!(::core::mem::size_of::<$x>()),
283 )
284 );
285 };
286}
287
288macro_rules! __zngur_assert_align {
289 ($x:ty, $align:expr $(,)?) => {
290 const _: () = ::core::assert!(
291 $align == ::core::mem::align_of::<$x>(),
292 "{}",
293 __zngur_const_str_concat!(
294 "zngur declared align of ",
295 stringify!($x),
296 " is incorrect: expected ",
297 __zngur_usize_to_str!($align),
298 " , real align is ",
299 __zngur_usize_to_str!(::core::mem::align_of::<$x>()),
300 )
301 );
302 };
303}
304
305macro_rules! __zngur_assert_size_conservative {
306 ($x:ty, $size:expr $(,)?) => {
307 const _: () = ::core::assert!(
308 $size >= ::core::mem::size_of::<$x>(),
309 "{}",
310 __zngur_const_str_concat!(
311 "zngur declared conservative size of ",
312 stringify!($x),
313 " is incorrect: expected size less than or equal to ",
314 __zngur_usize_to_str!($size),
315 " , real size is ",
316 __zngur_usize_to_str!(::core::mem::size_of::<$x>()),
317 )
318 );
319 };
320}
321
322macro_rules! __zngur_assert_align_conservative {
323 ($x:ty, $align:expr $(,)?) => {
324 const _: () = ::core::assert!(
325 $align >= ::core::mem::align_of::<$x>(),
326 "{}",
327 __zngur_const_str_concat!(
328 "zngur declared conservative align of ",
329 stringify!($x),
330 " is incorrect: expected align less than or equal to ",
331 __zngur_usize_to_str!($align),
332 " , real align is ",
333 __zngur_usize_to_str!(::core::mem::align_of::<$x>()),
334 )
335 );
336 };
337}
338
339macro_rules! __zngur_assert_has_field {
340 ($x:ty, $y:ty, $($field:tt)+ $(,)?) => {
341 const _: () = {
342 #[allow(dead_code)]
343 #[allow(mismatched_lifetime_syntaxes)]
344 fn check_field(value: $x) -> $y {
345 value.$($field)+
346 }
347 };
348 };
349}
350
351macro_rules! __zngur_assert_field_offset {
352 ($x:ty, $offset:expr, $($field:tt)+ $(,)?) => {
353 const _: () = ::core::assert!(
354 $offset == ::core::mem::offset_of!($x, $($field)+),
355 "{}",
356 __zngur_const_str_concat!(
357 "zngur declared offset of field ",
358 stringify!($($field)+),
359 " in ",
360 stringify!($x),
361 " is incorrect: expected offset of ",
362 __zngur_usize_to_str!($offset),
363 " , real offset is ",
364 __zngur_usize_to_str!(::core::mem::offset_of!($x, $($field)+)),
365 )
366 );
367 };
368}
369"#
370 .to_owned(),
371 panic_to_exception: false,
372 mangling_base: mangling_base.to_owned(),
373 }
374 }
375}
376
377impl Write for RustFile {
378 fn write_str(&mut self, s: &str) -> std::fmt::Result {
379 self.text.write_str(s)
380 }
381}
382
383macro_rules! w {
384 ($dst:expr, $($arg:tt)*) => {
385 { let _ = write!($dst, $($arg)*); }
386 };
387}
388
389macro_rules! wln {
390 ($dst:expr, $($arg:tt)*) => {
391 { let _ = writeln!($dst, $($arg)*); }
392 };
393}
394
395pub fn hash_of_sig(sig: &[RustType]) -> String {
396 let mut text = "".to_owned();
397 for elem in sig {
398 text += &format!("{elem}+");
399 }
400
401 let digset = Sha256::digest(&text);
402 hex::encode(&digset[..5])
403}
404
405fn mangle_name(name: &str, mangling_base: &str) -> String {
406 let mut name = "_zngur_"
407 .chars()
408 .chain(mangling_base.chars())
409 .chain(name.chars().filter(|c| !c.is_whitespace()))
410 .chain(Some('_'))
411 .collect::<String>();
412 let bads = [
413 (1, "::<", 'm'),
414 (1, ">::", 'n'),
415 (1, "->", 'a'),
416 (2, "&", 'r'),
417 (2, "=", 'e'),
418 (2, "<", 'x'),
419 (2, ">", 'y'),
420 (2, "[", 'j'),
421 (2, "]", 'k'),
422 (2, "::", 's'),
423 (2, ",", 'c'),
424 (2, "+", 'l'),
425 (2, "(", 'p'),
426 (2, ")", 'q'),
427 (2, "@", 'z'),
428 (2, "-", 'h'),
429 ];
430 while let Some((pos, which)) = bads.iter().filter_map(|x| Some((name.find(x.1)?, x))).min() {
431 name.replace_range(pos..pos + which.1.len(), "_");
432 w!(name, "{}{pos}", which.2);
433 }
434 name
435}
436
437impl RustFile {
438 fn mangle_name(&self, name: &str) -> String {
439 mangle_name(name, &self.mangling_base)
440 }
441
442 fn call_cpp_function(&mut self, name: &str, inputs: usize) {
443 for n in 0..inputs {
444 wln!(self, "let mut i{n} = ::core::mem::MaybeUninit::new(i{n});")
445 }
446 wln!(self, "let mut r = ::core::mem::MaybeUninit::uninit();");
447 w!(self, "{name}");
448 for n in 0..inputs {
449 w!(self, "i{n}.as_mut_ptr() as *mut u8, ");
450 }
451 wln!(self, "r.as_mut_ptr() as *mut u8);");
452 wln!(self, "r.assume_init()");
453 }
454
455 pub fn add_static_is_copy_assert(&mut self, ty: &RustType) {
456 wln!(self, r#"__zngur_assert_is_copy!({ty});"#);
457 }
458
459 pub fn add_static_size_assert(&mut self, ty: &RustType, size: usize) {
460 wln!(self, r#"__zngur_assert_size!({ty}, {size});"#);
461 }
462
463 pub fn add_static_align_assert(&mut self, ty: &RustType, align: usize) {
464 wln!(self, r#"__zngur_assert_align!({ty}, {align});"#);
465 }
466
467 pub fn add_static_size_upper_bound_assert(&mut self, ty: &RustType, size: usize) {
468 wln!(self, r#"__zngur_assert_size_conservative!({ty}, {size});"#);
469 }
470
471 pub fn add_static_align_upper_bound_assert(&mut self, ty: &RustType, align: usize) {
472 wln!(
473 self,
474 r#"__zngur_assert_align_conservative!({ty}, {align});"#
475 );
476 }
477
478 pub(crate) fn add_builder_for_dyn_trait(
479 &mut self,
480 tr: &ZngurTrait,
481 namespace: &str,
482 crate_name: &str,
483 ) -> CppTraitDefinition {
484 assert!(matches!(tr.tr, RustTrait::Normal { .. }));
485 let mut method_mangled_name = vec![];
486 wln!(self, r#"unsafe extern "C" {{"#);
487 for method in &tr.methods {
488 let name = self.mangle_name(&tr.tr.to_string())
489 + "_"
490 + &method.name
491 + "_"
492 + &hash_of_sig(&method.generics)
493 + "_"
494 + &hash_of_sig(&method.inputs);
495 wln!(
496 self,
497 r#"fn {name}(data: *mut u8, {} o: *mut u8);"#,
498 method
499 .inputs
500 .iter()
501 .enumerate()
502 .map(|(n, _)| format!("i{n}: *mut u8,"))
503 .join(" ")
504 );
505 method_mangled_name.push(name);
506 }
507 wln!(self, "}}");
508 let link_name = self.add_builder_for_dyn_trait_owned(tr, &method_mangled_name);
509 let link_name_ref = self.add_builder_for_dyn_trait_borrowed(tr, &method_mangled_name);
510 CppTraitDefinition::Normal {
511 as_ty: tr.tr.into_cpp(namespace, crate_name),
512 methods: tr
513 .methods
514 .clone()
515 .into_iter()
516 .zip(method_mangled_name)
517 .map(|(x, rust_link_name)| CppTraitMethod {
518 name: x.name,
519 rust_link_name,
520 inputs: x
521 .inputs
522 .into_iter()
523 .map(|x| x.into_cpp(namespace, crate_name))
524 .collect(),
525 output: x.output.into_cpp(namespace, crate_name),
526 })
527 .collect(),
528 link_name,
529 link_name_ref,
530 }
531 }
532
533 fn add_builder_for_dyn_trait_owned(
534 &mut self,
535 tr: &ZngurTrait,
536 method_mangled_name: &[String],
537 ) -> String {
538 let trait_name = tr.tr.to_string();
539 let (trait_without_assocs, assocs) = tr.tr.clone().take_assocs();
540 let mangled_name = self.mangle_name(&trait_name);
541 wln!(
542 self,
543 r#"
544#[allow(non_snake_case)]
545#[unsafe(no_mangle)]
546pub extern "C" fn {mangled_name}(
547 data: *mut u8,
548 destructor: extern "C" fn(*mut u8),
549 o: *mut u8,
550) {{
551 struct Wrapper {{
552 data: *mut u8,
553 destructor: extern "C" fn(*mut u8),
554 }}
555 impl Drop for Wrapper {{
556 fn drop(&mut self) {{
557 (self.destructor)(self.data)
558 }}
559 }}
560 impl {trait_without_assocs} for Wrapper {{
561"#
562 );
563 for (name, ty) in assocs {
564 wln!(self, " type {name} = {ty};");
565 }
566 for (method, rust_link_name) in tr.methods.iter().zip(method_mangled_name) {
567 w!(self, " fn {}(", method.name);
568 match method.receiver {
569 crate::ZngurMethodReceiver::Static => {
570 panic!("traits with static methods are not object safe");
571 }
572 crate::ZngurMethodReceiver::Ref(Mutability::Not) => w!(self, "&self"),
573 crate::ZngurMethodReceiver::Ref(Mutability::Mut) => w!(self, "&mut self"),
574 crate::ZngurMethodReceiver::Move => w!(self, "self"),
575 }
576 for (i, ty) in method.inputs.iter().enumerate() {
577 w!(self, ", i{i}: {ty}");
578 }
579 wln!(self, ") -> {} {{ unsafe {{", method.output);
580 wln!(self, " let data = self.data;");
581 self.call_cpp_function(&format!("{rust_link_name}(data, "), method.inputs.len());
582 wln!(self, " }} }}");
583 }
584 wln!(
585 self,
586 r#"
587 }}
588 unsafe {{
589 let this = Wrapper {{
590 data,
591 destructor,
592 }};
593 let r: Box<dyn {trait_name}> = Box::new(this);
594 std::ptr::write(o as *mut _, r)
595 }}
596}}"#
597 );
598 mangled_name
599 }
600
601 fn add_builder_for_dyn_trait_borrowed(
602 &mut self,
603 tr: &ZngurTrait,
604 method_mangled_name: &[String],
605 ) -> String {
606 let trait_name = tr.tr.to_string();
607 let (trait_without_assocs, assocs) = tr.tr.clone().take_assocs();
608 let mangled_name = self.mangle_name(&trait_name) + "_borrowed";
609 wln!(
610 self,
611 r#"
612#[allow(non_snake_case)]
613#[unsafe(no_mangle)]
614pub extern "C" fn {mangled_name}(
615 data: *mut u8,
616 o: *mut u8,
617) {{
618 struct Wrapper(());
619 impl {trait_without_assocs} for Wrapper {{
620"#
621 );
622 for (name, ty) in assocs {
623 wln!(self, " type {name} = {ty};");
624 }
625 for (method, rust_link_name) in tr.methods.iter().zip(method_mangled_name) {
626 w!(self, " fn {}(", method.name);
627 match method.receiver {
628 crate::ZngurMethodReceiver::Static => {
629 panic!("traits with static methods are not object safe");
630 }
631 crate::ZngurMethodReceiver::Ref(Mutability::Not) => w!(self, "&self"),
632 crate::ZngurMethodReceiver::Ref(Mutability::Mut) => w!(self, "&mut self"),
633 crate::ZngurMethodReceiver::Move => w!(self, "self"),
634 }
635 for (i, ty) in method.inputs.iter().enumerate() {
636 w!(self, ", i{i}: {ty}");
637 }
638 wln!(self, ") -> {} {{ unsafe {{", method.output);
639 wln!(
640 self,
641 " let data = ::std::mem::transmute::<_, *mut u8>(self);"
642 );
643 self.call_cpp_function(&format!("{rust_link_name}(data, "), method.inputs.len());
644 wln!(self, " }} }}");
645 }
646 wln!(
647 self,
648 r#"
649 }}
650 unsafe {{
651 let this = data as *mut Wrapper;
652 let r: &dyn {trait_name} = &*this;
653 std::ptr::write(o as *mut _, r)
654 }}
655}}"#
656 );
657 mangled_name
658 }
659
660 pub fn add_builder_for_dyn_fn(
661 &mut self,
662 name: &str,
663 inputs: &[RustType],
664 output: &RustType,
665 ) -> String {
666 let mangled_name = self.mangle_name(&inputs.iter().chain(Some(output)).join(", "));
667 let trait_str = format!("{name}({}) -> {output}", inputs.iter().join(", "));
668 wln!(
669 self,
670 r#"
671#[allow(non_snake_case)]
672#[unsafe(no_mangle)]
673pub extern "C" fn {mangled_name}(
674 data: *mut u8,
675 destructor: extern "C" fn(*mut u8),
676 call: extern "C" fn(data: *mut u8, {} o: *mut u8),
677 o: *mut u8,
678) {{
679 struct ClosureData {{
680 data: *mut u8,
681 destructor: extern "C" fn(*mut u8),
682 }}
683 impl Drop for ClosureData {{
684 fn drop(&mut self) {{
685 (self.destructor)(self.data)
686 }}
687 }}
688 let this = ClosureData {{ data, destructor }};
689 let r: Box<dyn {trait_str}> = Box::new(move |{}| unsafe {{
690 _ = &this;
691 let data = this.data;
692"#,
693 inputs
694 .iter()
695 .enumerate()
696 .map(|(n, _)| format!("i{n}: *mut u8, "))
697 .join(" "),
698 inputs
699 .iter()
700 .enumerate()
701 .map(|(n, ty)| format!("i{n}: {ty}"))
702 .join(", "),
703 );
704 self.call_cpp_function("call(data, ", inputs.len());
705 wln!(
706 self,
707 r#"
708 }});
709 unsafe {{ std::ptr::write(o as *mut _, r) }}
710}}"#
711 );
712 mangled_name
713 }
714
715 pub fn add_tuple_constructor(&mut self, fields: &[RustType]) -> String {
716 let constructor = self.mangle_name(&fields.iter().join("&"));
717 w!(
718 self,
719 r#"
720#[allow(non_snake_case)]
721#[unsafe(no_mangle)]
722pub extern "C" fn {constructor}("#
723 );
724 for name in 0..fields.len() {
725 w!(self, "f_{name}: *mut u8, ");
726 }
727 w!(
728 self,
729 r#"o: *mut u8) {{ unsafe {{
730 ::std::ptr::write(o as *mut _, ("#
731 );
732 for (name, ty) in fields.iter().enumerate() {
733 w!(self, "::std::ptr::read(f_{name} as *mut {ty}), ");
734 }
735 wln!(self, ")) }} }}");
736 constructor
737 }
738
739 pub fn add_constructor<'a>(
740 &mut self,
741 rust_name: &str,
742 args: impl IntoIterator<Item = (&'a String, &'a RustType)> + Clone,
743 ) -> String {
744 let constructor = self.mangle_name(rust_name);
745 w!(
746 self,
747 r#"
748#[allow(non_snake_case)]
749#[unsafe(no_mangle)]
750pub extern "C" fn {constructor}("#
751 );
752 for (name, _) in args.clone() {
753 w!(self, "f_{name}: *mut u8, ");
754 }
755 w!(
756 self,
757 r#"o: *mut u8) {{ unsafe {{
758 ::std::ptr::write(o as *mut _, {rust_name} {{ "#
759 );
760 for (name, ty) in args {
761 w!(self, "{name}: ::std::ptr::read(f_{name} as *mut {ty}), ");
762 }
763 wln!(self, "}}) }} }}");
764 constructor
765 }
766
767 pub(crate) fn add_match_check(&mut self, rust_name: &str) -> String {
768 let match_check = self.mangle_name(&format!("{rust_name}_check"));
769 w!(
770 self,
771 r#"
772#[allow(non_snake_case)]
773#[unsafe(no_mangle)]
774pub extern "C" fn {match_check}(i: *mut u8, o: *mut u8) {{ unsafe {{
775 *o = matches!(&*(i as *mut &_), {rust_name} {{ .. }}) as u8;
776}} }}"#
777 );
778 match_check
779 }
780
781 pub(crate) fn add_discriminant(
782 &mut self,
783 rust_name: &str,
784 variants: &[ZngurVariant],
785 ) -> Option<String> {
786 if variants.is_empty() {
787 return None;
788 }
789
790 let name = self.mangle_name(&format!("{rust_name}::match"));
791 w!(
792 self,
793 r#"
794#[allow(non_snake_case)]
795#[unsafe(no_mangle)]
796pub extern "C" fn {name}(i: *mut u8) -> u32 {{ unsafe {{
797 match &*(i as *mut {rust_name} as *const _) {{"#
798 );
799 for (n, variant) in variants.iter().enumerate() {
800 let name = &variant.name;
801 w!(
802 self,
803 r#"
804 {rust_name}::{name} {{ .. }} => {n},
805 "#
806 );
807 }
808 w!(
809 self,
810 r#"
811 }}
812}} }}
813 "#
814 );
815 Some(name)
816 }
817
818 pub(crate) fn add_field_assertions(
819 &mut self,
820 field: &ZngurField,
821 owner: &RustType,
822 ) -> Option<String> {
823 let ZngurField { name, ty, offset } = field;
824 wln!(self, r#"__zngur_assert_has_field!({owner}, {ty}, {name});"#);
825 if let Some(offset) = offset {
826 wln!(
827 self,
828 r#"__zngur_assert_field_offset!({owner}, {offset}, {name});"#
829 );
830 None
831 } else {
832 let mn = self.mangle_name(&format!("{}_field_{}_offset", &owner, &name));
833 wln!(
834 self,
835 r#"
836#[allow(non_snake_case)]
837#[unsafe(no_mangle)]
838pub static {mn}: usize = ::std::mem::offset_of!({owner}, {name});
839 "#
840 );
841 Some(mn)
842 }
843 }
844
845 pub(crate) fn add_variant_field_calculations(
846 &mut self,
847 field: &ZngurField,
848 owner: &RustType,
849 variant: &str,
850 ) -> String {
851 let ZngurField { name, .. } = field;
852 let mn = self.mangle_name(&format!("{owner}_{variant}_field_{name}_offset"));
853 wln!(
858 self,
859 r#"
860#[allow(non_snake_case)]
861#[unsafe(no_mangle)]
862pub extern "C" fn {mn}(i: *const u8) -> usize {{ unsafe {{
863 let base = &*(i as *const {owner});
864 match base {{
865 {owner}::{variant} {{ {name}: f, .. }} => {{
866 (f as *const _ as usize) - (base as *const _ as usize)
867 }}
868 _ => std::hint::unreachable_unchecked(),
869 }}
870}} }}
871 "#
872 );
873 mn
874 }
875
876 pub fn add_extern_cpp_impl(
877 &mut self,
878 owner: &RustType,
879 tr: Option<&RustTrait>,
880 methods: &[ZngurMethod],
881 ) -> Vec<String> {
882 let mut mangled_names = vec![];
883 w!(self, r#"unsafe extern "C" {{"#);
884 for method in methods {
885 let mn = self.mangle_name(&format!("{}_extern_method_{}", owner, method.name));
886 w!(
887 self,
888 r#"
889 fn {mn}("#
890 );
891 let input_offset = if method.receiver == ZngurMethodReceiver::Static {
892 0
893 } else {
894 1
895 };
896 for n in 0..method.inputs.len() + input_offset {
897 w!(self, "i{n}: *mut u8, ");
898 }
899 wln!(self, r#"o: *mut u8);"#);
900 mangled_names.push(mn);
901 }
902 w!(self, r#"}}"#);
903 match tr {
904 Some(tr) => {
905 let (tr, assocs) = tr.clone().take_assocs();
906 w!(self, r#"impl {tr} for {owner} {{"#);
907 for (name, ty) in assocs {
908 w!(self, r#"type {name} = {ty};"#);
909 }
910 }
911 None => w!(self, r#"impl {owner} {{"#),
912 }
913 for (mn, method) in mangled_names.iter().zip(methods) {
914 if tr.is_none() {
915 w!(self, "pub ");
916 }
917 w!(
918 self,
919 r#"{}fn {}("#,
920 if method.is_safe { "" } else { "unsafe " },
921 method.name
922 );
923 match method.receiver {
924 ZngurMethodReceiver::Static => (),
925 ZngurMethodReceiver::Ref(Mutability::Mut) => w!(self, "&mut self, "),
926 ZngurMethodReceiver::Ref(Mutability::Not) => w!(self, "&self, "),
927 ZngurMethodReceiver::Move => w!(self, "self, "),
928 }
929 let input_offset = if method.receiver == ZngurMethodReceiver::Static {
930 0
931 } else {
932 1
933 };
934 for (ty, n) in method.inputs.iter().zip(input_offset..) {
935 w!(self, "i{n}: {ty}, ");
936 }
937 wln!(self, ") -> {} {{ unsafe {{", method.output);
938 if method.receiver != ZngurMethodReceiver::Static {
939 wln!(self, "let i0 = self;");
940 }
941 self.call_cpp_function(&format!("{mn}("), method.inputs.len() + input_offset);
942 wln!(self, "}} }}");
943 }
944 w!(self, r#"}}"#);
945 mangled_names
946 }
947
948 pub fn add_extern_cpp_function(
949 &mut self,
950 rust_name: &str,
951 inputs: &[RustType],
952 output: &RustType,
953 is_safe: bool,
954 ) -> String {
955 let mangled_name = self.mangle_name(rust_name);
956 w!(
957 self,
958 r#"
959unsafe extern "C" {{ fn {mangled_name}("#
960 );
961 for (n, _) in inputs.iter().enumerate() {
962 w!(self, "i{n}: *mut u8, ");
963 }
964 wln!(self, r#"o: *mut u8); }}"#);
965 w!(
966 self,
967 r#"
968#[allow(non_snake_case)]
969pub {}fn {rust_name}("#,
970 if is_safe { "" } else { "unsafe " }
971 );
972 for (n, ty) in inputs.iter().enumerate() {
973 w!(self, "i{n}: {ty}, ");
974 }
975 wln!(self, ") -> {output} {{ unsafe {{");
976 self.call_cpp_function(&format!("{mangled_name}("), inputs.len());
977 wln!(self, "}} }}");
978 mangled_name
979 }
980
981 pub fn add_cpp_value_bridge(&mut self, ty: &RustType) -> String {
982 let type_name = ty.to_string().split("::").last().unwrap().to_string();
983 let mangled_name = self.mangle_name(&format!("{ty}_cpp_value"));
984 w!(
985 self,
986 r#"
987#[allow(non_snake_case)]
988#[unsafe(no_mangle)]
989pub extern "C" fn {mangled_name}(d: *mut u8) -> *mut cpp::{type_name} {{
990 d as *mut cpp::{type_name}
991}}"#
992 );
993 mangled_name
994 }
995
996 pub fn add_function(
997 &mut self,
998 cxx_name: &str,
999 rust_name: &str,
1000 inputs: &[RustType],
1001 output: &RustType,
1002 use_path: Option<Vec<String>>,
1003 deref: Option<Mutability>,
1004 namespace: &str,
1005 crate_name: &str,
1006 ) -> CppFnSig {
1007 let mut mangled_name =
1008 self.mangle_name(&format!("{cxx_name}={rust_name}")) + "_" + &hash_of_sig(&inputs);
1009 if deref.is_some() {
1010 mangled_name += "_deref";
1011 }
1012 w!(
1013 self,
1014 r#"
1015#[allow(non_snake_case)]
1016#[unsafe(no_mangle)]
1017#[allow(unused_parens)]
1018pub extern "C" fn {mangled_name}("#
1019 );
1020 for n in 0..inputs.len() {
1021 w!(self, "i{n}: *mut u8, ");
1022 }
1023 let (modified_output, is_impl_trait) = if let RustType::Impl(tr, bounds) = output {
1024 (
1025 RustType::Boxed(Box::new(RustType::Dyn(tr.clone(), bounds.clone()))),
1026 true,
1027 )
1028 } else {
1029 (output.clone(), false)
1030 };
1031 wln!(self, "o: *mut u8) {{ unsafe {{");
1032 self.wrap_in_catch_unwind(|this| {
1033 if let Some(use_path) = use_path {
1034 if use_path.first().is_some_and(|x| x == "crate") {
1035 wln!(this, " use {};", use_path.iter().join("::"));
1036 } else {
1037 wln!(this, " use ::{};", use_path.iter().join("::"));
1038 }
1039 }
1040
1041 w!(
1042 this,
1043 " ::std::ptr::write(o as *mut {modified_output}, {impl_trait} {rust_name}(",
1044 impl_trait = if is_impl_trait { "Box::new( " } else { "" },
1045 );
1046 match deref {
1047 Some(Mutability::Mut) => w!(this, "::std::ops::DerefMut::deref_mut"),
1048 Some(Mutability::Not) => w!(this, "::std::ops::Deref::deref"),
1049 None => {}
1050 }
1051 for (n, ty) in inputs.iter().enumerate() {
1052 w!(this, "(::std::ptr::read(i{n} as *mut {ty})), ");
1053 }
1054 if is_impl_trait {
1055 wln!(this, ")));");
1056 } else {
1057 wln!(this, "));");
1058 }
1059 });
1060 wln!(self, " }} }}");
1061 CppFnSig {
1062 rust_link_name: mangled_name,
1063 inputs: inputs
1064 .iter()
1065 .map(|ty| ty.into_cpp(namespace, crate_name))
1066 .collect(),
1067 output: modified_output.into_cpp(namespace, crate_name),
1068 }
1069 }
1070
1071 pub(crate) fn add_wellknown_trait(
1072 &mut self,
1073 ty: &RustType,
1074 wellknown_trait: ZngurWellknownTrait,
1075 is_unsized: bool,
1076 ) -> ZngurWellknownTraitData {
1077 match wellknown_trait {
1078 ZngurWellknownTrait::Unsized => ZngurWellknownTraitData::Unsized,
1079 ZngurWellknownTrait::Copy => ZngurWellknownTraitData::Copy,
1080 ZngurWellknownTrait::Drop => {
1081 let drop_in_place = self.mangle_name(&format!("{ty}=drop_in_place"));
1082 wln!(
1083 self,
1084 r#"
1085#[allow(non_snake_case)]
1086#[unsafe(no_mangle)]
1087pub extern "C" fn {drop_in_place}(v: *mut u8) {{ unsafe {{
1088 ::std::ptr::drop_in_place(v as *mut {ty});
1089}} }}"#
1090 );
1091 ZngurWellknownTraitData::Drop { drop_in_place }
1092 }
1093 ZngurWellknownTrait::Debug => {
1094 let pretty_print = self.mangle_name(&format!("{ty}=debug_pretty"));
1095 let debug_print = self.mangle_name(&format!("{ty}=debug_print"));
1096 let dbg_ty = if !is_unsized {
1097 format!("{ty}")
1098 } else {
1099 format!("&{ty}")
1100 };
1101 wln!(
1102 self,
1103 r#"
1104#[allow(non_snake_case)]
1105#[unsafe(no_mangle)]
1106pub extern "C" fn {pretty_print}(v: *mut u8) {{
1107 eprintln!("{{:#?}}", unsafe {{ &*(v as *mut {dbg_ty}) }});
1108}}"#
1109 );
1110 wln!(
1111 self,
1112 r#"
1113#[allow(non_snake_case)]
1114#[unsafe(no_mangle)]
1115pub extern "C" fn {debug_print}(v: *mut u8) {{
1116 eprintln!("{{:?}}", unsafe {{ &*(v as *mut {dbg_ty}) }});
1117}}"#
1118 );
1119 ZngurWellknownTraitData::Debug {
1120 pretty_print,
1121 debug_print,
1122 }
1123 }
1124 }
1125 }
1126
1127 fn wrap_in_catch_unwind(&mut self, f: impl FnOnce(&mut RustFile)) {
1128 if !self.panic_to_exception {
1129 f(self);
1130 } else {
1131 wln!(
1132 self,
1133 r#"unsafe extern "C" {{
1134 fn __zngur_mark_panicked();
1135 }}
1136 let e = ::std::panic::catch_unwind(|| {{"#
1137 );
1138 f(self);
1139 wln!(self, "}});");
1140 wln!(self, "if let Err(_) = e {{ __zngur_mark_panicked(); }}");
1141 }
1142 }
1143
1144 pub(crate) fn add_layout_policy_shim(
1145 &mut self,
1146 ty: &RustType,
1147 layout: LayoutPolicy,
1148 ) -> CppLayoutPolicy {
1149 match layout {
1150 LayoutPolicy::StackAllocated { size, align } => {
1151 CppLayoutPolicy::StackAllocated { size, align }
1152 }
1153 LayoutPolicy::Conservative { size, align } => {
1154 CppLayoutPolicy::StackAllocated { size, align }
1155 }
1156 LayoutPolicy::HeapAllocated => {
1157 let size_fn = self.mangle_name(&format!("{ty}_size_fn"));
1158 let alloc_fn = self.mangle_name(&format!("{ty}_alloc_fn"));
1159 let free_fn = self.mangle_name(&format!("{ty}_free_fn"));
1160 wln!(
1161 self,
1162 r#"
1163 #[allow(non_snake_case)]
1164 #[unsafe(no_mangle)]
1165 pub fn {size_fn}() -> usize {{
1166 ::std::mem::size_of::<{ty}>()
1167 }}
1168
1169 #[allow(non_snake_case)]
1170 #[unsafe(no_mangle)]
1171 pub fn {alloc_fn}() -> *mut u8 {{
1172 unsafe {{ ::std::alloc::alloc(::std::alloc::Layout::new::<{ty}>()) }}
1173 }}
1174
1175 #[allow(non_snake_case)]
1176 #[unsafe(no_mangle)]
1177 pub fn {free_fn}(p: *mut u8) {{
1178 unsafe {{ ::std::alloc::dealloc(p, ::std::alloc::Layout::new::<{ty}>()) }}
1179 }}
1180 "#
1181 );
1182 CppLayoutPolicy::HeapAllocated {
1183 size_fn,
1184 alloc_fn,
1185 free_fn,
1186 }
1187 }
1188 LayoutPolicy::OnlyByRef => CppLayoutPolicy::OnlyByRef,
1189 }
1190 }
1191}