blob: d6c0c83b3cd5767ca9484e520899ac01a988b03c [file] [log] [blame]
David Tolnaya52602b2020-03-06 10:24:34 -08001use crate::syntax::atom::Atom::{self, *};
David Tolnay08419302020-04-19 20:38:20 -07002use crate::syntax::namespace::Namespace;
David Tolnay891061b2020-04-19 22:42:33 -07003use crate::syntax::symbol::Symbol;
David Tolnay3caa50a2020-04-19 21:25:34 -07004use crate::syntax::{
5 self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types,
6};
David Tolnay7db73692019-10-20 14:51:12 -04007use proc_macro2::{Ident, Span, TokenStream};
8use quote::{format_ident, quote, quote_spanned};
David Tolnay75dca2e2020-03-25 20:17:52 -07009use syn::{parse_quote, Error, ItemMod, Result, Token};
David Tolnay7db73692019-10-20 14:51:12 -040010
11pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result<TokenStream> {
12 let ident = &ffi.ident;
13 let content = ffi.content.ok_or(Error::new(
14 Span::call_site(),
15 "#[cxx::bridge] module must have inline contents",
16 ))?;
17 let apis = syntax::parse_items(content.1)?;
18 let ref types = Types::collect(&apis)?;
19 check::typecheck(&apis, types)?;
20
21 let mut expanded = TokenStream::new();
22 let mut hidden = TokenStream::new();
23 let mut has_rust_type = false;
24
25 for api in &apis {
26 if let Api::RustType(ety) = api {
27 expanded.extend(expand_rust_type(ety));
28 if !has_rust_type {
David Tolnay199d7352020-01-20 18:40:10 -080029 hidden.extend(quote!(
30 const fn __assert_sized<T>() {}
31 ));
David Tolnay7db73692019-10-20 14:51:12 -040032 has_rust_type = true;
33 }
34 let ident = &ety.ident;
35 hidden.extend(quote!(__assert_sized::<#ident>();));
36 }
37 }
38
39 for api in &apis {
40 match api {
41 Api::Include(_) | Api::RustType(_) => {}
42 Api::Struct(strct) => expanded.extend(expand_struct(strct)),
43 Api::CxxType(ety) => expanded.extend(expand_cxx_type(ety)),
44 Api::CxxFunction(efn) => {
45 expanded.extend(expand_cxx_function_shim(namespace, efn, types));
46 }
47 Api::RustFunction(efn) => {
48 hidden.extend(expand_rust_function_shim(namespace, efn, types))
49 }
50 }
51 }
52
53 for ty in types {
54 if let Type::RustBox(ty) = ty {
55 if let Type::Ident(ident) = &ty.inner {
56 if Atom::from(ident).is_none() {
57 hidden.extend(expand_rust_box(namespace, ident));
58 }
59 }
60 } else if let Type::UniquePtr(ptr) = ty {
61 if let Type::Ident(ident) = &ptr.inner {
62 if Atom::from(ident).is_none() {
David Tolnay53838912020-04-09 20:56:44 -070063 expanded.extend(expand_unique_ptr(namespace, ident, types));
David Tolnay7db73692019-10-20 14:51:12 -040064 }
65 }
66 }
67 }
68
69 // Work around https://github.com/rust-lang/rust/issues/67851.
70 if !hidden.is_empty() {
71 expanded.extend(quote! {
72 #[doc(hidden)]
73 const _: () = {
74 #hidden
75 };
76 });
77 }
78
79 let attrs = ffi
80 .attrs
81 .into_iter()
82 .filter(|attr| attr.path.is_ident("doc"));
83 let vis = &ffi.vis;
84
85 Ok(quote! {
86 #(#attrs)*
87 #[deny(improper_ctypes)]
88 #[allow(non_snake_case)]
89 #vis mod #ident {
90 #expanded
91 }
92 })
93}
94
95fn expand_struct(strct: &Struct) -> TokenStream {
96 let ident = &strct.ident;
97 let doc = &strct.doc;
98 let derives = &strct.derives;
99 let fields = strct.fields.iter().map(|field| {
100 // This span on the pub makes "private type in public interface" errors
101 // appear in the right place.
102 let vis = Token![pub](field.ident.span());
103 quote!(#vis #field)
104 });
105 quote! {
106 #doc
107 #[derive(#(#derives),*)]
108 #[repr(C)]
109 pub struct #ident {
110 #(#fields,)*
111 }
112 }
113}
114
115fn expand_cxx_type(ety: &ExternType) -> TokenStream {
116 let ident = &ety.ident;
117 let doc = &ety.doc;
118 quote! {
119 #doc
120 #[repr(C)]
121 pub struct #ident {
122 _private: ::cxx::private::Opaque,
123 }
124 }
125}
126
127fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
128 let ident = &efn.ident;
David Tolnaye439c772020-04-20 00:23:55 -0700129 let receiver = efn.receiver.iter().map(|receiver| {
130 let ident = &receiver.ident;
131 match receiver.mutability {
Joel Galenson3d4f6122020-04-07 15:54:05 -0700132 None => quote!(_: &#ident),
133 Some(_) => quote!(_: &mut #ident),
134 }
135 });
David Tolnay39d575f2020-03-03 00:10:56 -0800136 let args = efn.args.iter().map(|arg| {
137 let ident = &arg.ident;
138 let ty = expand_extern_type(&arg.ty);
David Tolnaya46a2372020-03-06 10:03:48 -0800139 if arg.ty == RustString {
140 quote!(#ident: *const #ty)
David Tolnay75dca2e2020-03-25 20:17:52 -0700141 } else if let Type::Fn(_) = arg.ty {
142 quote!(#ident: ::cxx::private::FatFunction)
David Tolnaya46a2372020-03-06 10:03:48 -0800143 } else if types.needs_indirect_abi(&arg.ty) {
David Tolnay39d575f2020-03-03 00:10:56 -0800144 quote!(#ident: *mut #ty)
145 } else {
146 quote!(#ident: #ty)
147 }
148 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700149 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700150 let ret = if efn.throws {
151 quote!(-> ::cxx::private::Result)
152 } else {
153 expand_extern_return_type(&efn.ret, types)
154 };
David Tolnay7db73692019-10-20 14:51:12 -0400155 let mut outparam = None;
David Tolnay1e548172020-03-16 13:37:09 -0700156 if indirect_return(efn, types) {
David Tolnay7db73692019-10-20 14:51:12 -0400157 let ret = expand_extern_type(efn.ret.as_ref().unwrap());
158 outparam = Some(quote!(__return: *mut #ret));
159 }
David Tolnay3caa50a2020-04-19 21:25:34 -0700160 let link_name = mangle::extern_fn(namespace, efn);
David Tolnay7db73692019-10-20 14:51:12 -0400161 let local_name = format_ident!("__{}", ident);
162 quote! {
163 #[link_name = #link_name]
Joel Galenson3d4f6122020-04-07 15:54:05 -0700164 fn #local_name(#(#all_args,)* #outparam) #ret;
David Tolnay7db73692019-10-20 14:51:12 -0400165 }
166}
167
168fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
169 let ident = &efn.ident;
170 let doc = &efn.doc;
171 let decl = expand_cxx_function_decl(namespace, efn, types);
David Tolnaye439c772020-04-20 00:23:55 -0700172 let receiver = efn
173 .receiver
174 .iter()
175 .map(|receiver| match receiver.mutability {
176 None => quote!(&self),
177 Some(_) => quote!(&mut self),
178 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700179 let args = efn.args.iter().map(|arg| quote!(#arg));
180 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700181 let ret = if efn.throws {
182 let ok = match &efn.ret {
183 Some(ret) => quote!(#ret),
184 None => quote!(()),
185 };
186 quote!(-> ::std::result::Result<#ok, ::cxx::Exception>)
187 } else {
188 expand_return_type(&efn.ret)
189 };
David Tolnay1e548172020-03-16 13:37:09 -0700190 let indirect_return = indirect_return(efn, types);
Joel Galenson3d4f6122020-04-07 15:54:05 -0700191 let receiver_var = efn.receiver.iter().map(|_| quote!(self));
192 let arg_vars = efn.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400193 let var = &arg.ident;
194 match &arg.ty {
David Tolnaya52602b2020-03-06 10:24:34 -0800195 Type::Ident(ident) if ident == RustString => {
David Tolnaya46a2372020-03-06 10:03:48 -0800196 quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString)
David Tolnay7db73692019-10-20 14:51:12 -0400197 }
198 Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)),
199 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)),
200 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800201 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400202 quote!(::cxx::private::RustString::from_ref(#var))
203 }
204 _ => quote!(#var),
205 },
206 Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700207 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)),
David Tolnay7db73692019-10-20 14:51:12 -0400208 ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()),
209 _ => quote!(#var),
210 }
211 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700212 let vars = receiver_var.chain(arg_vars);
David Tolnay75dca2e2020-03-25 20:17:52 -0700213 let trampolines = efn
214 .args
215 .iter()
216 .filter_map(|arg| {
217 if let Type::Fn(f) = &arg.ty {
218 let var = &arg.ident;
219 Some(expand_function_pointer_trampoline(
220 namespace, efn, var, f, types,
221 ))
222 } else {
223 None
224 }
225 })
226 .collect::<TokenStream>();
David Tolnay7db73692019-10-20 14:51:12 -0400227 let mut setup = efn
228 .args
229 .iter()
230 .filter(|arg| types.needs_indirect_abi(&arg.ty))
231 .map(|arg| {
232 let var = &arg.ident;
233 // These are arguments for which C++ has taken ownership of the data
234 // behind the mut reference it received.
235 quote! {
236 let mut #var = std::mem::MaybeUninit::new(#var);
237 }
238 })
239 .collect::<TokenStream>();
240 let local_name = format_ident!("__{}", ident);
241 let call = if indirect_return {
242 let ret = expand_extern_type(efn.ret.as_ref().unwrap());
243 setup.extend(quote! {
244 let mut __return = ::std::mem::MaybeUninit::<#ret>::uninit();
David Tolnay7db73692019-10-20 14:51:12 -0400245 });
David Tolnayebef4a22020-03-17 15:33:47 -0700246 if efn.throws {
247 setup.extend(quote! {
248 #local_name(#(#vars,)* __return.as_mut_ptr()).exception()?;
249 });
250 quote!(::std::result::Result::Ok(__return.assume_init()))
251 } else {
252 setup.extend(quote! {
253 #local_name(#(#vars,)* __return.as_mut_ptr());
254 });
255 quote!(__return.assume_init())
256 }
257 } else if efn.throws {
David Tolnay7db73692019-10-20 14:51:12 -0400258 quote! {
David Tolnayebef4a22020-03-17 15:33:47 -0700259 #local_name(#(#vars),*).exception()
David Tolnay7db73692019-10-20 14:51:12 -0400260 }
261 } else {
262 quote! {
263 #local_name(#(#vars),*)
264 }
265 };
Myron Ahn84849302020-03-25 22:00:58 +0700266 let expr = if efn.throws {
267 efn.ret.as_ref().and_then(|ret| match ret {
268 Type::Ident(ident) if ident == RustString => {
269 Some(quote!(#call.map(|r| r.into_string())))
270 }
271 Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))),
272 Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))),
273 Type::Ref(ty) => match &ty.inner {
274 Type::Ident(ident) if ident == RustString => {
275 Some(quote!(#call.map(|r| r.as_string())))
276 }
277 _ => None,
278 },
279 Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700280 Type::SliceRefU8(_) => Some(quote!(#call.map(|r| r.as_slice()))),
Myron Ahn84849302020-03-25 22:00:58 +0700281 _ => None,
282 })
283 } else {
284 efn.ret.as_ref().and_then(|ret| match ret {
David Tolnaya52602b2020-03-06 10:24:34 -0800285 Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())),
David Tolnay7db73692019-10-20 14:51:12 -0400286 Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))),
287 Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))),
288 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800289 Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())),
David Tolnay7db73692019-10-20 14:51:12 -0400290 _ => None,
291 },
292 Type::Str(_) => Some(quote!(#call.as_str())),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700293 Type::SliceRefU8(_) => Some(quote!(#call.as_slice())),
David Tolnay7db73692019-10-20 14:51:12 -0400294 _ => None,
295 })
Myron Ahn84849302020-03-25 22:00:58 +0700296 }
297 .unwrap_or(call);
David Tolnaye439c772020-04-20 00:23:55 -0700298 let receiver_type = efn.receiver.as_ref().map(|receiver| &receiver.ident);
299 match receiver_type {
Joel Galenson3d4f6122020-04-07 15:54:05 -0700300 None => quote! {
301 #doc
302 pub fn #ident(#(#all_args,)*) #ret {
303 extern "C" {
304 #decl
305 }
306 #trampolines
307 unsafe {
308 #setup
309 #expr
310 }
David Tolnay7db73692019-10-20 14:51:12 -0400311 }
Joel Galenson3d4f6122020-04-07 15:54:05 -0700312 },
David Tolnaye439c772020-04-20 00:23:55 -0700313 Some(receiver_type) => quote! {
Joel Galenson3d4f6122020-04-07 15:54:05 -0700314 #doc
David Tolnaye439c772020-04-20 00:23:55 -0700315 impl #receiver_type {
Joel Galenson3d4f6122020-04-07 15:54:05 -0700316 pub fn #ident(#(#all_args,)*) #ret {
317 extern "C" {
318 #decl
319 }
320 #trampolines
321 unsafe {
322 #setup
323 #expr
324 }
325 }
David Tolnay7db73692019-10-20 14:51:12 -0400326 }
Joel Galenson3d4f6122020-04-07 15:54:05 -0700327 },
David Tolnay7db73692019-10-20 14:51:12 -0400328 }
329}
330
David Tolnay75dca2e2020-03-25 20:17:52 -0700331fn expand_function_pointer_trampoline(
332 namespace: &Namespace,
333 efn: &ExternFn,
334 var: &Ident,
335 sig: &Signature,
336 types: &Types,
337) -> TokenStream {
David Tolnay891061b2020-04-19 22:42:33 -0700338 let c_trampoline = mangle::c_trampoline(namespace, efn, var);
339 let r_trampoline = mangle::r_trampoline(namespace, efn, var);
David Tolnay75dca2e2020-03-25 20:17:52 -0700340 let local_name = parse_quote!(__);
341 let catch_unwind_label = format!("::{}::{}", efn.ident, var);
342 let shim = expand_rust_function_shim_impl(
343 sig,
344 types,
345 &r_trampoline,
346 local_name,
347 catch_unwind_label,
348 None,
349 );
350
351 quote! {
352 let #var = ::cxx::private::FatFunction {
353 trampoline: {
354 extern "C" {
355 #[link_name = #c_trampoline]
356 fn trampoline();
357 }
358 #shim
359 trampoline as usize as *const ()
360 },
361 ptr: #var as usize as *const (),
362 };
363 }
364}
365
David Tolnay7db73692019-10-20 14:51:12 -0400366fn expand_rust_type(ety: &ExternType) -> TokenStream {
367 let ident = &ety.ident;
368 quote! {
369 use super::#ident;
370 }
371}
372
373fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
374 let ident = &efn.ident;
David Tolnay3caa50a2020-04-19 21:25:34 -0700375 let link_name = mangle::extern_fn(namespace, efn);
David Tolnay75dca2e2020-03-25 20:17:52 -0700376 let local_name = format_ident!("__{}", ident);
377 let catch_unwind_label = format!("::{}", ident);
378 let invoke = Some(ident);
379 expand_rust_function_shim_impl(
380 efn,
381 types,
382 &link_name,
383 local_name,
384 catch_unwind_label,
385 invoke,
386 )
387}
388
389fn expand_rust_function_shim_impl(
390 sig: &Signature,
391 types: &Types,
David Tolnay891061b2020-04-19 22:42:33 -0700392 link_name: &Symbol,
David Tolnay75dca2e2020-03-25 20:17:52 -0700393 local_name: Ident,
394 catch_unwind_label: String,
395 invoke: Option<&Ident>,
396) -> TokenStream {
David Tolnaye439c772020-04-20 00:23:55 -0700397 let receiver = sig.receiver.iter().map(|receiver| {
398 let ident = &receiver.ident;
399 match receiver.mutability {
David Tolnay26804bd2020-04-19 20:06:51 -0700400 None => quote!(__self: &#ident),
401 Some(_) => quote!(__self: &mut #ident),
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700402 }
403 });
David Tolnay75dca2e2020-03-25 20:17:52 -0700404 let args = sig.args.iter().map(|arg| {
David Tolnay39d575f2020-03-03 00:10:56 -0800405 let ident = &arg.ident;
406 let ty = expand_extern_type(&arg.ty);
407 if types.needs_indirect_abi(&arg.ty) {
408 quote!(#ident: *mut #ty)
409 } else {
410 quote!(#ident: #ty)
411 }
412 });
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700413 let all_args = receiver.chain(args);
David Tolnay75dca2e2020-03-25 20:17:52 -0700414
415 let vars = sig.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400416 let ident = &arg.ident;
David Tolnay17955e22020-01-20 17:58:24 -0800417 match &arg.ty {
David Tolnaycc3767f2020-03-06 10:41:51 -0800418 Type::Ident(i) if i == RustString => {
419 quote!(::std::mem::take((*#ident).as_mut_string()))
420 }
David Tolnay40226ab2020-03-03 00:05:35 -0800421 Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)),
422 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)),
David Tolnay17955e22020-01-20 17:58:24 -0800423 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800424 Type::Ident(i) if i == RustString => quote!(#ident.as_string()),
David Tolnay40226ab2020-03-03 00:05:35 -0800425 _ => quote!(#ident),
David Tolnay17955e22020-01-20 17:58:24 -0800426 },
David Tolnay40226ab2020-03-03 00:05:35 -0800427 Type::Str(_) => quote!(#ident.as_str()),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700428 Type::SliceRefU8(_) => quote!(#ident.as_slice()),
David Tolnay40226ab2020-03-03 00:05:35 -0800429 ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
430 _ => quote!(#ident),
David Tolnay7db73692019-10-20 14:51:12 -0400431 }
432 });
David Tolnay75dca2e2020-03-25 20:17:52 -0700433
434 let mut call = match invoke {
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700435 Some(ident) => match sig.receiver {
436 None => quote!(super::#ident),
David Tolnay26804bd2020-04-19 20:06:51 -0700437 Some(_) => quote!(__self.#ident),
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700438 },
David Tolnay75dca2e2020-03-25 20:17:52 -0700439 None => quote!(__extern),
440 };
441 call.extend(quote! { (#(#vars),*) });
442
443 let mut expr = sig
David Tolnay7db73692019-10-20 14:51:12 -0400444 .ret
445 .as_ref()
446 .and_then(|ret| match ret {
David Tolnaya52602b2020-03-06 10:24:34 -0800447 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400448 Some(quote!(::cxx::private::RustString::from(#call)))
449 }
450 Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))),
451 Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))),
452 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800453 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400454 Some(quote!(::cxx::private::RustString::from_ref(#call)))
455 }
456 _ => None,
457 },
458 Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700459 Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))),
David Tolnay7db73692019-10-20 14:51:12 -0400460 _ => None,
461 })
462 .unwrap_or(call);
David Tolnay75dca2e2020-03-25 20:17:52 -0700463
464 let mut outparam = None;
465 let indirect_return = indirect_return(sig, types);
David Tolnay1e548172020-03-16 13:37:09 -0700466 if indirect_return {
David Tolnay75dca2e2020-03-25 20:17:52 -0700467 let ret = expand_extern_type(sig.ret.as_ref().unwrap());
468 outparam = Some(quote!(__return: *mut #ret,));
David Tolnay1e548172020-03-16 13:37:09 -0700469 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700470 if sig.throws {
471 let out = match sig.ret {
David Tolnaycecada62020-03-17 01:45:58 -0700472 Some(_) => quote!(__return),
473 None => quote!(&mut ()),
474 };
475 expr = quote!(::cxx::private::r#try(#out, #expr));
David Tolnay1e548172020-03-16 13:37:09 -0700476 } else if indirect_return {
David Tolnay7db73692019-10-20 14:51:12 -0400477 expr = quote!(::std::ptr::write(__return, #expr));
478 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700479
David Tolnay1e548172020-03-16 13:37:09 -0700480 expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr));
David Tolnay75dca2e2020-03-25 20:17:52 -0700481
482 let ret = if sig.throws {
David Tolnay486b6ec2020-03-17 01:19:57 -0700483 quote!(-> ::cxx::private::Result)
David Tolnay1e548172020-03-16 13:37:09 -0700484 } else {
David Tolnay75dca2e2020-03-25 20:17:52 -0700485 expand_extern_return_type(&sig.ret, types)
David Tolnay1e548172020-03-16 13:37:09 -0700486 };
David Tolnay75dca2e2020-03-25 20:17:52 -0700487
488 let pointer = match invoke {
489 None => Some(quote!(__extern: #sig)),
490 Some(_) => None,
491 };
492
David Tolnay7db73692019-10-20 14:51:12 -0400493 quote! {
494 #[doc(hidden)]
495 #[export_name = #link_name]
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700496 unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
David Tolnay7db73692019-10-20 14:51:12 -0400497 let __fn = concat!(module_path!(), #catch_unwind_label);
498 #expr
499 }
500 }
501}
502
503fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream {
David Tolnay8c730492020-03-13 01:29:06 -0700504 let link_prefix = format!("cxxbridge02$box${}{}$", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400505 let link_uninit = format!("{}uninit", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400506 let link_drop = format!("{}drop", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400507
508 let local_prefix = format_ident!("{}__box_", ident);
509 let local_uninit = format_ident!("{}uninit", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400510 let local_drop = format_ident!("{}drop", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400511
512 let span = ident.span();
513 quote_spanned! {span=>
514 #[doc(hidden)]
515 #[export_name = #link_uninit]
516 unsafe extern "C" fn #local_uninit(
517 this: *mut ::std::boxed::Box<::std::mem::MaybeUninit<#ident>>,
518 ) {
519 ::std::ptr::write(
520 this,
521 ::std::boxed::Box::new(::std::mem::MaybeUninit::uninit()),
522 );
523 }
524 #[doc(hidden)]
David Tolnay7db73692019-10-20 14:51:12 -0400525 #[export_name = #link_drop]
526 unsafe extern "C" fn #local_drop(this: *mut ::std::boxed::Box<#ident>) {
527 ::std::ptr::drop_in_place(this);
528 }
David Tolnay7db73692019-10-20 14:51:12 -0400529 }
530}
531
David Tolnay53838912020-04-09 20:56:44 -0700532fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream {
David Tolnayad266772020-04-09 23:42:29 -0700533 let name = ident.to_string();
David Tolnay8c730492020-03-13 01:29:06 -0700534 let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400535 let link_null = format!("{}null", prefix);
536 let link_new = format!("{}new", prefix);
537 let link_raw = format!("{}raw", prefix);
538 let link_get = format!("{}get", prefix);
539 let link_release = format!("{}release", prefix);
540 let link_drop = format!("{}drop", prefix);
541
David Tolnay53838912020-04-09 20:56:44 -0700542 let new_method = if types.structs.contains_key(ident) {
543 Some(quote! {
544 fn __new(mut value: Self) -> *mut ::std::ffi::c_void {
545 extern "C" {
546 #[link_name = #link_new]
547 fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident);
548 }
549 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
550 unsafe { __new(&mut repr, &mut value) }
551 repr
552 }
553 })
554 } else {
555 None
556 };
557
David Tolnay7db73692019-10-20 14:51:12 -0400558 quote! {
559 unsafe impl ::cxx::private::UniquePtrTarget for #ident {
David Tolnayad266772020-04-09 23:42:29 -0700560 const __NAME: &'static str = #name;
David Tolnay7db73692019-10-20 14:51:12 -0400561 fn __null() -> *mut ::std::ffi::c_void {
562 extern "C" {
563 #[link_name = #link_null]
564 fn __null(this: *mut *mut ::std::ffi::c_void);
565 }
566 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
567 unsafe { __null(&mut repr) }
568 repr
569 }
David Tolnay53838912020-04-09 20:56:44 -0700570 #new_method
David Tolnay7db73692019-10-20 14:51:12 -0400571 unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void {
572 extern "C" {
573 #[link_name = #link_raw]
574 fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident);
575 }
576 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
577 __raw(&mut repr, raw);
578 repr
579 }
580 unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self {
581 extern "C" {
582 #[link_name = #link_get]
583 fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident;
584 }
585 __get(&repr)
586 }
587 unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self {
588 extern "C" {
589 #[link_name = #link_release]
590 fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident;
591 }
592 __release(&mut repr)
593 }
594 unsafe fn __drop(mut repr: *mut ::std::ffi::c_void) {
595 extern "C" {
596 #[link_name = #link_drop]
597 fn __drop(this: *mut *mut ::std::ffi::c_void);
598 }
599 __drop(&mut repr);
600 }
601 }
602 }
603}
604
605fn expand_return_type(ret: &Option<Type>) -> TokenStream {
606 match ret {
607 Some(ret) => quote!(-> #ret),
608 None => TokenStream::new(),
609 }
610}
611
David Tolnay75dca2e2020-03-25 20:17:52 -0700612fn indirect_return(sig: &Signature, types: &Types) -> bool {
613 sig.ret
David Tolnay1e548172020-03-16 13:37:09 -0700614 .as_ref()
David Tolnay75dca2e2020-03-25 20:17:52 -0700615 .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret))
David Tolnay7db73692019-10-20 14:51:12 -0400616}
617
618fn expand_extern_type(ty: &Type) -> TokenStream {
619 match ty {
David Tolnaya52602b2020-03-06 10:24:34 -0800620 Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString),
David Tolnay7db73692019-10-20 14:51:12 -0400621 Type::RustBox(ty) | Type::UniquePtr(ty) => {
622 let inner = &ty.inner;
623 quote!(*mut #inner)
624 }
625 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800626 Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString),
David Tolnay7db73692019-10-20 14:51:12 -0400627 _ => quote!(#ty),
628 },
629 Type::Str(_) => quote!(::cxx::private::RustStr),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700630 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
David Tolnay7db73692019-10-20 14:51:12 -0400631 _ => quote!(#ty),
632 }
633}
634
635fn expand_extern_return_type(ret: &Option<Type>, types: &Types) -> TokenStream {
636 let ret = match ret {
637 Some(ret) if !types.needs_indirect_abi(ret) => ret,
638 _ => return TokenStream::new(),
639 };
640 let ty = expand_extern_type(ret);
641 quote!(-> #ty)
642}