blob: 97fdc4802d8e3eec65e70af0252a0efe32afdce1 [file] [log] [blame]
David Tolnay7db73692019-10-20 14:51:12 -04001use crate::gen::include;
2use crate::gen::out::OutFile;
3use crate::syntax::atom::Atom::{self, *};
4use crate::syntax::{Api, ExternFn, Struct, Type, Types, Var};
5use proc_macro2::Ident;
6
7pub(super) fn gen(namespace: Vec<String>, apis: &[Api], types: &Types, header: bool) -> OutFile {
8 let mut out_file = OutFile::new(namespace.clone(), header);
9 let out = &mut out_file;
10
11 if header {
12 writeln!(out, "#pragma once");
13 }
14
15 for api in apis {
16 if let Api::Include(include) = api {
17 writeln!(out, "#include \"{}\"", include.value().escape_default());
18 }
19 }
20
21 write_includes(out, types);
22 write_include_cxxbridge(out, types);
23
24 if !header {
25 out.next_section();
26 write_namespace_alias(out, types);
27 }
28
29 out.next_section();
30 for name in &namespace {
31 writeln!(out, "namespace {} {{", name);
32 }
33
34 if header {
35 out.next_section();
36 write_namespace_alias(out, types);
37 }
38
39 out.next_section();
40 for api in apis {
41 match api {
42 Api::Struct(strct) => write_struct_decl(out, &strct.ident),
David Tolnay8861bee2020-01-20 18:39:24 -080043 Api::CxxType(ety) => write_struct_using(out, &ety.ident),
44 Api::RustType(ety) => write_struct_decl(out, &ety.ident),
David Tolnay7db73692019-10-20 14:51:12 -040045 _ => {}
46 }
47 }
48
49 for api in apis {
50 if let Api::Struct(strct) = api {
51 out.next_section();
52 write_struct(out, strct);
53 }
54 }
55
56 if !header {
57 out.begin_block("extern \"C\"");
58 for api in apis {
59 let (efn, write): (_, fn(_, _, _)) = match api {
60 Api::CxxFunction(efn) => (efn, write_cxx_function_shim),
61 Api::RustFunction(efn) => (efn, write_rust_function_decl),
62 _ => continue,
63 };
64 out.next_section();
65 write(out, efn, types);
66 }
67 out.end_block();
68 }
69
70 for api in apis {
71 if let Api::RustFunction(efn) = api {
72 out.next_section();
73 write_rust_function_shim(out, efn, types);
74 }
75 }
76
77 out.next_section();
78 for name in namespace.iter().rev() {
79 writeln!(out, "}} // namespace {}", name);
80 }
81
82 if !header {
83 out.next_section();
84 write_generic_instantiations(out, types);
85 }
86
87 out_file
88}
89
90fn write_includes(out: &mut OutFile, types: &Types) {
91 let mut has_int = false;
92 let mut has_unique_ptr = false;
93 let mut has_string = false;
94
95 for ty in types {
96 match ty {
97 Type::Ident(ident) => match Atom::from(ident) {
98 Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8)
99 | Some(I16) | Some(I32) | Some(I64) | Some(Isize) => has_int = true,
100 Some(CxxString) => has_string = true,
101 Some(Bool) | Some(RustString) | None => {}
102 },
103 Type::UniquePtr(_) => has_unique_ptr = true,
104 _ => {}
105 }
106 }
107
108 if has_int {
109 writeln!(out, "#include <cstdint>");
110 }
111 if has_unique_ptr {
112 writeln!(out, "#include <memory>");
113 }
114 if has_string {
115 writeln!(out, "#include <string>");
116 }
117}
118
119fn write_include_cxxbridge(out: &mut OutFile, types: &Types) {
120 let mut needs_rust_box = false;
121 for ty in types {
122 if let Type::RustBox(_) = ty {
123 needs_rust_box = true;
124 break;
125 }
126 }
127
David Tolnaye43b7372020-01-08 08:46:20 -0800128 out.begin_block("namespace cxxbridge01");
David Tolnay7db73692019-10-20 14:51:12 -0400129 if needs_rust_box {
130 writeln!(out, "// #include \"cxxbridge.h\"");
David Tolnaye43b7372020-01-08 08:46:20 -0800131 for line in include::get("CXXBRIDGE01_RUST_BOX").lines() {
David Tolnay7db73692019-10-20 14:51:12 -0400132 if !line.trim_start().starts_with("//") {
133 writeln!(out, "{}", line);
134 }
135 }
136 }
137 out.end_block();
138}
139
140fn write_namespace_alias(out: &mut OutFile, types: &Types) {
141 let mut needs_namespace_alias = false;
142 for ty in types {
143 if let Type::RustBox(_) = ty {
144 needs_namespace_alias = true;
145 break;
146 }
147 }
148
149 if needs_namespace_alias {
David Tolnaye43b7372020-01-08 08:46:20 -0800150 writeln!(out, "namespace cxxbridge = cxxbridge01;");
David Tolnay7db73692019-10-20 14:51:12 -0400151 }
152}
153
154fn write_struct(out: &mut OutFile, strct: &Struct) {
155 for line in strct.doc.to_string().lines() {
156 writeln!(out, "//{}", line);
157 }
158 writeln!(out, "struct {} final {{", strct.ident);
159 for field in &strct.fields {
160 write!(out, " ");
161 write_type_space(out, &field.ty);
162 writeln!(out, "{};", field.ident);
163 }
164 writeln!(out, "}};");
165}
166
167fn write_struct_decl(out: &mut OutFile, ident: &Ident) {
168 writeln!(out, "struct {};", ident);
169}
170
David Tolnay8861bee2020-01-20 18:39:24 -0800171fn write_struct_using(out: &mut OutFile, ident: &Ident) {
172 writeln!(out, "using {} = {};", ident, ident);
173}
174
David Tolnay7db73692019-10-20 14:51:12 -0400175fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
176 let indirect_return = efn
177 .ret
178 .as_ref()
179 .map_or(false, |ret| types.needs_indirect_abi(ret));
180 write_extern_return_type(out, &efn.ret, types);
181 for name in out.namespace.clone() {
182 write!(out, "{}$", name);
183 }
David Tolnaye43b7372020-01-08 08:46:20 -0800184 write!(out, "cxxbridge01${}(", efn.ident);
David Tolnay7db73692019-10-20 14:51:12 -0400185 for (i, arg) in efn.args.iter().enumerate() {
186 if i > 0 {
187 write!(out, ", ");
188 }
189 write_extern_arg(out, arg, types);
190 }
191 if indirect_return {
192 if !efn.args.is_empty() {
193 write!(out, ", ");
194 }
195 write_return_type(out, &efn.ret);
196 write!(out, "*return$");
197 }
198 writeln!(out, ") noexcept {{");
199 write!(out, " ");
200 write_return_type(out, &efn.ret);
201 write!(out, "(*{}$)(", efn.ident);
202 for (i, arg) in efn.args.iter().enumerate() {
203 if i > 0 {
204 write!(out, ", ");
205 }
206 write_type(out, &arg.ty);
207 }
208 writeln!(out, ") = {};", efn.ident);
209 write!(out, " ");
210 if indirect_return {
211 write!(out, "new (return$) ");
212 write_type(out, efn.ret.as_ref().unwrap());
213 write!(out, "(");
214 } else if efn.ret.is_some() {
215 write!(out, "return ");
216 }
217 write!(out, "{}$(", efn.ident);
218 for (i, arg) in efn.args.iter().enumerate() {
219 if i > 0 {
220 write!(out, ", ");
221 }
222 if let Type::RustBox(_) = &arg.ty {
223 write_type(out, &arg.ty);
224 write!(out, "::from_raw({})", arg.ident);
225 } else if let Type::UniquePtr(_) = &arg.ty {
226 write_type(out, &arg.ty);
227 write!(out, "({})", arg.ident);
228 } else if types.needs_indirect_abi(&arg.ty) {
229 write!(out, "std::move(*{})", arg.ident);
230 } else {
231 write!(out, "{}", arg.ident);
232 }
233 }
234 write!(out, ")");
235 match &efn.ret {
236 Some(Type::RustBox(_)) => write!(out, ".into_raw()"),
237 Some(Type::UniquePtr(_)) => write!(out, ".release()"),
238 _ => {}
239 }
240 if indirect_return {
241 write!(out, ")");
242 }
243 writeln!(out, ";");
244 writeln!(out, "}}");
245}
246
247fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) {
248 write_extern_return_type(out, &efn.ret, types);
249 for name in out.namespace.clone() {
250 write!(out, "{}$", name);
251 }
David Tolnaye43b7372020-01-08 08:46:20 -0800252 write!(out, "cxxbridge01${}(", efn.ident);
David Tolnay7db73692019-10-20 14:51:12 -0400253 for (i, arg) in efn.args.iter().enumerate() {
254 if i > 0 {
255 write!(out, ", ");
256 }
257 write_extern_arg(out, arg, types);
258 }
259 if efn
260 .ret
261 .as_ref()
262 .map_or(false, |ret| types.needs_indirect_abi(ret))
263 {
264 if !efn.args.is_empty() {
265 write!(out, ", ");
266 }
267 write_return_type(out, &efn.ret);
268 write!(out, "*return$");
269 }
270 writeln!(out, ") noexcept;");
271}
272
273fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
274 let indirect_return = efn
275 .ret
276 .as_ref()
277 .map_or(false, |ret| types.needs_indirect_abi(ret));
278 for line in efn.doc.to_string().lines() {
279 writeln!(out, "//{}", line);
280 }
281 write_return_type(out, &efn.ret);
282 write!(out, "{}(", efn.ident);
283 for (i, arg) in efn.args.iter().enumerate() {
284 if i > 0 {
285 write!(out, ", ");
286 }
287 write_type_space(out, &arg.ty);
288 write!(out, "{}", arg.ident);
289 }
290 write!(out, ") noexcept");
291 if out.header {
292 writeln!(out, ";");
293 } else {
294 writeln!(out, " {{");
295 write!(out, " ");
296 if indirect_return {
297 write!(out, "char return$[sizeof(");
298 write_type(out, efn.ret.as_ref().unwrap());
299 writeln!(out, ")];");
300 write!(out, " ");
301 } else if efn.ret.is_some() {
302 write!(out, "return ");
303 }
304 for name in out.namespace.clone() {
305 write!(out, "{}$", name);
306 }
David Tolnaye43b7372020-01-08 08:46:20 -0800307 write!(out, "cxxbridge01${}(", efn.ident);
David Tolnay7db73692019-10-20 14:51:12 -0400308 for (i, arg) in efn.args.iter().enumerate() {
309 if i > 0 {
310 write!(out, ", ");
311 }
312 if types.needs_indirect_abi(&arg.ty) {
313 write!(out, "&");
314 }
315 write!(out, "{}", arg.ident);
David Tolnay17955e22020-01-20 17:58:24 -0800316 match arg.ty {
317 Type::RustBox(_) => write!(out, ".into_raw()"),
318 Type::UniquePtr(_) => write!(out, ".release()"),
319 _ => {}
320 }
David Tolnay7db73692019-10-20 14:51:12 -0400321 }
322 if indirect_return {
323 if !efn.args.is_empty() {
324 write!(out, ", ");
325 }
326 write!(out, "reinterpret_cast<");
327 write_return_type(out, &efn.ret);
328 write!(out, "*>(return$)");
329 }
330 writeln!(out, ");");
331 if indirect_return {
332 write!(out, " return ");
333 write_type(out, efn.ret.as_ref().unwrap());
334 write!(out, "(*reinterpret_cast<");
335 write_return_type(out, &efn.ret);
336 writeln!(out, "*>(return$));");
337 }
338 writeln!(out, "}}");
339 }
340}
341
342fn write_return_type(out: &mut OutFile, ty: &Option<Type>) {
343 match ty {
344 None => write!(out, "void "),
345 Some(ty) => write_type_space(out, ty),
346 }
347}
348
349fn write_extern_return_type(out: &mut OutFile, ty: &Option<Type>, types: &Types) {
350 match ty {
351 Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => {
352 write_type_space(out, &ty.inner);
353 write!(out, "*");
354 }
355 Some(Type::Str(_)) => write!(out, "cxxbridge::RustStr::Repr "),
356 Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "),
357 _ => write_return_type(out, ty),
358 }
359}
360
361fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) {
362 match &arg.ty {
363 Type::RustBox(ty) | Type::UniquePtr(ty) => {
364 write_type_space(out, &ty.inner);
365 write!(out, "*");
366 }
367 Type::Str(_) => write!(out, "cxxbridge::RustStr::Repr "),
368 _ => write_type_space(out, &arg.ty),
369 }
370 if types.needs_indirect_abi(&arg.ty) {
371 write!(out, "*");
372 }
373 write!(out, "{}", arg.ident);
374}
375
376fn write_type(out: &mut OutFile, ty: &Type) {
377 match ty {
378 Type::Ident(ident) => match Atom::from(ident) {
379 Some(Bool) => write!(out, "bool"),
380 Some(U8) => write!(out, "uint8_t"),
381 Some(U16) => write!(out, "uint16_t"),
382 Some(U32) => write!(out, "uint32_t"),
383 Some(U64) => write!(out, "uint64_t"),
384 Some(Usize) => write!(out, "size_t"),
385 Some(I8) => write!(out, "int8_t"),
386 Some(I16) => write!(out, "int16_t"),
387 Some(I32) => write!(out, "int32_t"),
388 Some(I64) => write!(out, "int64_t"),
389 Some(Isize) => write!(out, "ssize_t"),
390 Some(CxxString) => write!(out, "std::string"),
391 Some(RustString) => write!(out, "cxxbridge::RustString"),
392 None => write!(out, "{}", ident),
393 },
394 Type::RustBox(ty) => {
395 write!(out, "cxxbridge::RustBox<");
396 write_type(out, &ty.inner);
397 write!(out, ">");
398 }
399 Type::UniquePtr(ptr) => {
400 write!(out, "std::unique_ptr<");
401 write_type(out, &ptr.inner);
402 write!(out, ">");
403 }
404 Type::Ref(r) => {
405 if r.mutability.is_none() {
406 write!(out, "const ");
407 }
408 write_type(out, &r.inner);
409 write!(out, " &");
410 }
411 Type::Str(_) => {
412 write!(out, "cxxbridge::RustStr");
413 }
414 }
415}
416
417fn write_type_space(out: &mut OutFile, ty: &Type) {
418 write_type(out, ty);
419 match ty {
420 Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) => write!(out, " "),
421 Type::Ref(_) => {}
422 }
423}
424
425fn write_generic_instantiations(out: &mut OutFile, types: &Types) {
426 fn allow_unique_ptr(ident: &Ident) -> bool {
427 Atom::from(ident).is_none()
428 }
429
430 out.begin_block("extern \"C\"");
431 for ty in types {
432 if let Type::RustBox(ty) = ty {
433 if let Type::Ident(inner) = &ty.inner {
434 out.next_section();
435 write_rust_box_extern(out, inner);
436 }
437 } else if let Type::UniquePtr(ptr) = ty {
438 if let Type::Ident(inner) = &ptr.inner {
439 if allow_unique_ptr(inner) {
440 out.next_section();
441 write_unique_ptr(out, inner);
442 }
443 }
444 }
445 }
446 out.end_block();
447
David Tolnaye43b7372020-01-08 08:46:20 -0800448 out.begin_block("namespace cxxbridge01");
David Tolnay7db73692019-10-20 14:51:12 -0400449 for ty in types {
450 if let Type::RustBox(ty) = ty {
451 if let Type::Ident(inner) = &ty.inner {
452 write_rust_box_impl(out, inner);
453 }
454 }
455 }
456 out.end_block();
457}
458
459fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) {
460 let mut inner = String::new();
461 for name in &out.namespace {
462 inner += name;
463 inner += "::";
464 }
465 inner += &ident.to_string();
466 let instance = inner.replace("::", "$");
467
David Tolnaye43b7372020-01-08 08:46:20 -0800468 writeln!(out, "#ifndef CXXBRIDGE01_RUST_BOX_{}", instance);
469 writeln!(out, "#define CXXBRIDGE01_RUST_BOX_{}", instance);
David Tolnay7db73692019-10-20 14:51:12 -0400470 writeln!(
471 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800472 "void cxxbridge01$rust_box${}$uninit(cxxbridge::RustBox<{}> *ptr) noexcept;",
David Tolnay7db73692019-10-20 14:51:12 -0400473 instance, inner,
474 );
475 writeln!(
476 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800477 "void cxxbridge01$rust_box${}$set_raw(cxxbridge::RustBox<{}> *ptr, {} *raw) noexcept;",
David Tolnay7db73692019-10-20 14:51:12 -0400478 instance, inner, inner
479 );
480 writeln!(
481 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800482 "void cxxbridge01$rust_box${}$drop(cxxbridge::RustBox<{}> *ptr) noexcept;",
David Tolnay7db73692019-10-20 14:51:12 -0400483 instance, inner,
484 );
485 writeln!(
486 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800487 "const {} *cxxbridge01$rust_box${}$deref(const cxxbridge::RustBox<{}> *ptr) noexcept;",
David Tolnay7db73692019-10-20 14:51:12 -0400488 inner, instance, inner,
489 );
490 writeln!(
491 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800492 "{} *cxxbridge01$rust_box${}$deref_mut(cxxbridge::RustBox<{}> *ptr) noexcept;",
David Tolnay7db73692019-10-20 14:51:12 -0400493 inner, instance, inner,
494 );
David Tolnaye43b7372020-01-08 08:46:20 -0800495 writeln!(out, "#endif // CXXBRIDGE01_RUST_BOX_{}", instance);
David Tolnay7db73692019-10-20 14:51:12 -0400496}
497
498fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) {
499 let mut inner = String::new();
500 for name in &out.namespace {
501 inner += name;
502 inner += "::";
503 }
504 inner += &ident.to_string();
505 let instance = inner.replace("::", "$");
506
507 writeln!(out, "template <>");
508 writeln!(out, "void RustBox<{}>::uninit() noexcept {{", inner);
509 writeln!(
510 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800511 " return cxxbridge01$rust_box${}$uninit(this);",
David Tolnay7db73692019-10-20 14:51:12 -0400512 instance
513 );
514 writeln!(out, "}}");
515
516 writeln!(out, "template <>");
517 writeln!(
518 out,
519 "void RustBox<{}>::set_raw({} *raw) noexcept {{",
520 inner, inner,
521 );
522 writeln!(
523 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800524 " return cxxbridge01$rust_box${}$set_raw(this, raw);",
David Tolnay7db73692019-10-20 14:51:12 -0400525 instance
526 );
527 writeln!(out, "}}");
528
529 writeln!(out, "template <>");
530 writeln!(out, "void RustBox<{}>::drop() noexcept {{", inner);
531 writeln!(
532 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800533 " return cxxbridge01$rust_box${}$drop(this);",
David Tolnay7db73692019-10-20 14:51:12 -0400534 instance
535 );
536 writeln!(out, "}}");
537
538 writeln!(out, "template <>");
539 writeln!(
540 out,
541 "const {} *RustBox<{}>::deref() const noexcept {{",
542 inner, inner,
543 );
544 writeln!(
545 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800546 " return cxxbridge01$rust_box${}$deref(this);",
David Tolnay7db73692019-10-20 14:51:12 -0400547 instance
548 );
549 writeln!(out, "}}");
550
551 writeln!(out, "template <>");
552 writeln!(
553 out,
554 "{} *RustBox<{}>::deref_mut() noexcept {{",
555 inner, inner,
556 );
557 writeln!(
558 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800559 " return cxxbridge01$rust_box${}$deref_mut(this);",
David Tolnay7db73692019-10-20 14:51:12 -0400560 instance
561 );
562 writeln!(out, "}}");
563}
564
565fn write_unique_ptr(out: &mut OutFile, ident: &Ident) {
566 let mut inner = String::new();
567 for name in &out.namespace {
568 inner += name;
569 inner += "::";
570 }
571 inner += &ident.to_string();
572 let instance = inner.replace("::", "$");
573
David Tolnaye43b7372020-01-08 08:46:20 -0800574 writeln!(out, "#ifndef CXXBRIDGE01_UNIQUE_PTR_{}", instance);
575 writeln!(out, "#define CXXBRIDGE01_UNIQUE_PTR_{}", instance);
David Tolnay7db73692019-10-20 14:51:12 -0400576 writeln!(
577 out,
578 "static_assert(sizeof(std::unique_ptr<{}>) == sizeof(void *), \"\");",
579 inner,
580 );
581 writeln!(
582 out,
583 "static_assert(alignof(std::unique_ptr<{}>) == alignof(void *), \"\");",
584 inner,
585 );
586 writeln!(
587 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800588 "void cxxbridge01$unique_ptr${}$null(std::unique_ptr<{}> *ptr) noexcept {{",
David Tolnay7db73692019-10-20 14:51:12 -0400589 instance, inner,
590 );
591 writeln!(out, " new (ptr) std::unique_ptr<{}>();", inner);
592 writeln!(out, "}}");
593 writeln!(
594 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800595 "void cxxbridge01$unique_ptr${}$new(std::unique_ptr<{}> *ptr, {} *value) noexcept {{",
David Tolnay7db73692019-10-20 14:51:12 -0400596 instance, inner, inner,
597 );
598 writeln!(
599 out,
600 " new (ptr) std::unique_ptr<{}>(new {}(std::move(*value)));",
601 inner, inner,
602 );
603 writeln!(out, "}}");
604 writeln!(
605 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800606 "void cxxbridge01$unique_ptr${}$raw(std::unique_ptr<{}> *ptr, {} *raw) noexcept {{",
David Tolnay7db73692019-10-20 14:51:12 -0400607 instance, inner, inner,
608 );
609 writeln!(out, " new (ptr) std::unique_ptr<{}>(raw);", inner);
610 writeln!(out, "}}");
611 writeln!(
612 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800613 "const {} *cxxbridge01$unique_ptr${}$get(const std::unique_ptr<{}>& ptr) noexcept {{",
David Tolnay7db73692019-10-20 14:51:12 -0400614 inner, instance, inner,
615 );
616 writeln!(out, " return ptr.get();");
617 writeln!(out, "}}");
618 writeln!(
619 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800620 "{} *cxxbridge01$unique_ptr${}$release(std::unique_ptr<{}>& ptr) noexcept {{",
David Tolnay7db73692019-10-20 14:51:12 -0400621 inner, instance, inner,
622 );
623 writeln!(out, " return ptr.release();");
624 writeln!(out, "}}");
625 writeln!(
626 out,
David Tolnaye43b7372020-01-08 08:46:20 -0800627 "void cxxbridge01$unique_ptr${}$drop(std::unique_ptr<{}> *ptr) noexcept {{",
David Tolnay7db73692019-10-20 14:51:12 -0400628 instance, inner,
629 );
630 writeln!(out, " ptr->~unique_ptr();");
631 writeln!(out, "}}");
David Tolnaye43b7372020-01-08 08:46:20 -0800632 writeln!(out, "#endif // CXXBRIDGE01_UNIQUE_PTR_{}", instance);
David Tolnay7db73692019-10-20 14:51:12 -0400633}