blob: 8b3602ab65f46a32d502aa1ebaba12045c3ce070 [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};
David Tolnayf9ffb932020-04-20 02:22:57 -07008use quote::{format_ident, quote, quote_spanned, ToTokens};
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;
David Tolnay4b07ab92020-04-22 19:50:21 -070035 let span = ident.span();
36 hidden.extend(quote_spanned!(span=> __assert_sized::<#ident>();));
David Tolnay7db73692019-10-20 14:51:12 -040037 }
38 }
39
40 for api in &apis {
41 match api {
42 Api::Include(_) | Api::RustType(_) => {}
43 Api::Struct(strct) => expanded.extend(expand_struct(strct)),
44 Api::CxxType(ety) => expanded.extend(expand_cxx_type(ety)),
45 Api::CxxFunction(efn) => {
46 expanded.extend(expand_cxx_function_shim(namespace, efn, types));
47 }
48 Api::RustFunction(efn) => {
49 hidden.extend(expand_rust_function_shim(namespace, efn, types))
50 }
51 }
52 }
53
54 for ty in types {
55 if let Type::RustBox(ty) = ty {
56 if let Type::Ident(ident) = &ty.inner {
57 if Atom::from(ident).is_none() {
58 hidden.extend(expand_rust_box(namespace, ident));
59 }
60 }
61 } else if let Type::UniquePtr(ptr) = ty {
62 if let Type::Ident(ident) = &ptr.inner {
63 if Atom::from(ident).is_none() {
David Tolnay53838912020-04-09 20:56:44 -070064 expanded.extend(expand_unique_ptr(namespace, ident, types));
David Tolnay7db73692019-10-20 14:51:12 -040065 }
66 }
67 }
68 }
69
70 // Work around https://github.com/rust-lang/rust/issues/67851.
71 if !hidden.is_empty() {
72 expanded.extend(quote! {
73 #[doc(hidden)]
74 const _: () = {
75 #hidden
76 };
77 });
78 }
79
80 let attrs = ffi
81 .attrs
82 .into_iter()
83 .filter(|attr| attr.path.is_ident("doc"));
84 let vis = &ffi.vis;
85
86 Ok(quote! {
87 #(#attrs)*
88 #[deny(improper_ctypes)]
89 #[allow(non_snake_case)]
90 #vis mod #ident {
91 #expanded
92 }
93 })
94}
95
96fn expand_struct(strct: &Struct) -> TokenStream {
97 let ident = &strct.ident;
98 let doc = &strct.doc;
99 let derives = &strct.derives;
100 let fields = strct.fields.iter().map(|field| {
101 // This span on the pub makes "private type in public interface" errors
102 // appear in the right place.
103 let vis = Token![pub](field.ident.span());
104 quote!(#vis #field)
105 });
106 quote! {
107 #doc
108 #[derive(#(#derives),*)]
109 #[repr(C)]
110 pub struct #ident {
111 #(#fields,)*
112 }
113 }
114}
115
116fn expand_cxx_type(ety: &ExternType) -> TokenStream {
117 let ident = &ety.ident;
118 let doc = &ety.doc;
119 quote! {
120 #doc
121 #[repr(C)]
122 pub struct #ident {
123 _private: ::cxx::private::Opaque,
124 }
125 }
126}
127
128fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
129 let ident = &efn.ident;
David Tolnay18ba92c2020-04-22 16:17:30 -0700130 let receiver = efn.receiver.iter().map(|receiver| {
131 let receiver_type = receiver.ty();
132 quote!(_: #receiver_type)
133 });
David Tolnay39d575f2020-03-03 00:10:56 -0800134 let args = efn.args.iter().map(|arg| {
135 let ident = &arg.ident;
136 let ty = expand_extern_type(&arg.ty);
David Tolnaya46a2372020-03-06 10:03:48 -0800137 if arg.ty == RustString {
138 quote!(#ident: *const #ty)
David Tolnay75dca2e2020-03-25 20:17:52 -0700139 } else if let Type::Fn(_) = arg.ty {
140 quote!(#ident: ::cxx::private::FatFunction)
David Tolnaya46a2372020-03-06 10:03:48 -0800141 } else if types.needs_indirect_abi(&arg.ty) {
David Tolnay39d575f2020-03-03 00:10:56 -0800142 quote!(#ident: *mut #ty)
143 } else {
144 quote!(#ident: #ty)
145 }
146 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700147 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700148 let ret = if efn.throws {
149 quote!(-> ::cxx::private::Result)
150 } else {
151 expand_extern_return_type(&efn.ret, types)
152 };
David Tolnay7db73692019-10-20 14:51:12 -0400153 let mut outparam = None;
David Tolnay1e548172020-03-16 13:37:09 -0700154 if indirect_return(efn, types) {
David Tolnay7db73692019-10-20 14:51:12 -0400155 let ret = expand_extern_type(efn.ret.as_ref().unwrap());
156 outparam = Some(quote!(__return: *mut #ret));
157 }
David Tolnay3caa50a2020-04-19 21:25:34 -0700158 let link_name = mangle::extern_fn(namespace, efn);
David Tolnay7db73692019-10-20 14:51:12 -0400159 let local_name = format_ident!("__{}", ident);
160 quote! {
161 #[link_name = #link_name]
Joel Galenson3d4f6122020-04-07 15:54:05 -0700162 fn #local_name(#(#all_args,)* #outparam) #ret;
David Tolnay7db73692019-10-20 14:51:12 -0400163 }
164}
165
166fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
167 let ident = &efn.ident;
168 let doc = &efn.doc;
169 let decl = expand_cxx_function_decl(namespace, efn, types);
David Tolnayfb6e3862020-04-20 01:33:23 -0700170 let receiver = efn.receiver.iter().map(|receiver| {
David Tolnayf9ffb932020-04-20 02:22:57 -0700171 let ampersand = receiver.ampersand;
David Tolnayfb6e3862020-04-20 01:33:23 -0700172 let mutability = receiver.mutability;
David Tolnayf9ffb932020-04-20 02:22:57 -0700173 let var = receiver.var;
174 quote!(#ampersand #mutability #var)
David Tolnayfb6e3862020-04-20 01:33:23 -0700175 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700176 let args = efn.args.iter().map(|arg| quote!(#arg));
177 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700178 let ret = if efn.throws {
179 let ok = match &efn.ret {
180 Some(ret) => quote!(#ret),
181 None => quote!(()),
182 };
183 quote!(-> ::std::result::Result<#ok, ::cxx::Exception>)
184 } else {
185 expand_return_type(&efn.ret)
186 };
David Tolnay1e548172020-03-16 13:37:09 -0700187 let indirect_return = indirect_return(efn, types);
David Tolnayf9ffb932020-04-20 02:22:57 -0700188 let receiver_var = efn
189 .receiver
190 .iter()
191 .map(|receiver| receiver.var.to_token_stream());
Joel Galenson3d4f6122020-04-07 15:54:05 -0700192 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 Tolnayc66cdbb2020-04-20 01:41:15 -0700298 let function_shim = quote! {
299 #doc
300 pub fn #ident(#(#all_args,)*) #ret {
301 extern "C" {
302 #decl
David Tolnay7db73692019-10-20 14:51:12 -0400303 }
David Tolnayc66cdbb2020-04-20 01:41:15 -0700304 #trampolines
305 unsafe {
306 #setup
307 #expr
David Tolnay7db73692019-10-20 14:51:12 -0400308 }
David Tolnayc66cdbb2020-04-20 01:41:15 -0700309 }
310 };
311 match &efn.receiver {
312 None => function_shim,
313 Some(receiver) => {
David Tolnay05e11cc2020-04-20 02:13:56 -0700314 let receiver_type = &receiver.ty;
David Tolnayc66cdbb2020-04-20 01:41:15 -0700315 quote!(impl #receiver_type { #function_shim })
316 }
David Tolnay7db73692019-10-20 14:51:12 -0400317 }
318}
319
David Tolnay75dca2e2020-03-25 20:17:52 -0700320fn expand_function_pointer_trampoline(
321 namespace: &Namespace,
322 efn: &ExternFn,
323 var: &Ident,
324 sig: &Signature,
325 types: &Types,
326) -> TokenStream {
David Tolnay891061b2020-04-19 22:42:33 -0700327 let c_trampoline = mangle::c_trampoline(namespace, efn, var);
328 let r_trampoline = mangle::r_trampoline(namespace, efn, var);
David Tolnay75dca2e2020-03-25 20:17:52 -0700329 let local_name = parse_quote!(__);
330 let catch_unwind_label = format!("::{}::{}", efn.ident, var);
331 let shim = expand_rust_function_shim_impl(
332 sig,
333 types,
334 &r_trampoline,
335 local_name,
336 catch_unwind_label,
337 None,
338 );
339
340 quote! {
341 let #var = ::cxx::private::FatFunction {
342 trampoline: {
343 extern "C" {
344 #[link_name = #c_trampoline]
345 fn trampoline();
346 }
347 #shim
348 trampoline as usize as *const ()
349 },
350 ptr: #var as usize as *const (),
351 };
352 }
353}
354
David Tolnay7db73692019-10-20 14:51:12 -0400355fn expand_rust_type(ety: &ExternType) -> TokenStream {
356 let ident = &ety.ident;
357 quote! {
358 use super::#ident;
359 }
360}
361
362fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
363 let ident = &efn.ident;
David Tolnay3caa50a2020-04-19 21:25:34 -0700364 let link_name = mangle::extern_fn(namespace, efn);
David Tolnay75dca2e2020-03-25 20:17:52 -0700365 let local_name = format_ident!("__{}", ident);
366 let catch_unwind_label = format!("::{}", ident);
367 let invoke = Some(ident);
368 expand_rust_function_shim_impl(
369 efn,
370 types,
371 &link_name,
372 local_name,
373 catch_unwind_label,
374 invoke,
375 )
376}
377
378fn expand_rust_function_shim_impl(
379 sig: &Signature,
380 types: &Types,
David Tolnay891061b2020-04-19 22:42:33 -0700381 link_name: &Symbol,
David Tolnay75dca2e2020-03-25 20:17:52 -0700382 local_name: Ident,
383 catch_unwind_label: String,
384 invoke: Option<&Ident>,
385) -> TokenStream {
David Tolnayf9ffb932020-04-20 02:22:57 -0700386 let receiver_var = sig
387 .receiver
388 .as_ref()
389 .map(|receiver| quote_spanned!(receiver.var.span=> __self));
David Tolnay18ba92c2020-04-22 16:17:30 -0700390 let receiver = sig.receiver.as_ref().map(|receiver| {
391 let receiver_type = receiver.ty();
392 quote!(#receiver_var: #receiver_type)
393 });
David Tolnay75dca2e2020-03-25 20:17:52 -0700394 let args = sig.args.iter().map(|arg| {
David Tolnay39d575f2020-03-03 00:10:56 -0800395 let ident = &arg.ident;
396 let ty = expand_extern_type(&arg.ty);
397 if types.needs_indirect_abi(&arg.ty) {
398 quote!(#ident: *mut #ty)
399 } else {
400 quote!(#ident: #ty)
401 }
402 });
David Tolnayf9ffb932020-04-20 02:22:57 -0700403 let all_args = receiver.into_iter().chain(args);
David Tolnay75dca2e2020-03-25 20:17:52 -0700404
David Tolnay3a45f2d2020-04-20 01:51:12 -0700405 let arg_vars = sig.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400406 let ident = &arg.ident;
David Tolnay17955e22020-01-20 17:58:24 -0800407 match &arg.ty {
David Tolnaycc3767f2020-03-06 10:41:51 -0800408 Type::Ident(i) if i == RustString => {
409 quote!(::std::mem::take((*#ident).as_mut_string()))
410 }
David Tolnay40226ab2020-03-03 00:05:35 -0800411 Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)),
412 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)),
David Tolnay17955e22020-01-20 17:58:24 -0800413 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800414 Type::Ident(i) if i == RustString => quote!(#ident.as_string()),
David Tolnay40226ab2020-03-03 00:05:35 -0800415 _ => quote!(#ident),
David Tolnay17955e22020-01-20 17:58:24 -0800416 },
David Tolnay40226ab2020-03-03 00:05:35 -0800417 Type::Str(_) => quote!(#ident.as_str()),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700418 Type::SliceRefU8(_) => quote!(#ident.as_slice()),
David Tolnay40226ab2020-03-03 00:05:35 -0800419 ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
420 _ => quote!(#ident),
David Tolnay7db73692019-10-20 14:51:12 -0400421 }
422 });
David Tolnayf9ffb932020-04-20 02:22:57 -0700423 let vars = receiver_var.into_iter().chain(arg_vars);
David Tolnay75dca2e2020-03-25 20:17:52 -0700424
425 let mut call = match invoke {
David Tolnay3a45f2d2020-04-20 01:51:12 -0700426 Some(ident) => match &sig.receiver {
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700427 None => quote!(super::#ident),
David Tolnay3a45f2d2020-04-20 01:51:12 -0700428 Some(receiver) => {
David Tolnay05e11cc2020-04-20 02:13:56 -0700429 let receiver_type = &receiver.ty;
David Tolnay3a45f2d2020-04-20 01:51:12 -0700430 quote!(#receiver_type::#ident)
431 }
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700432 },
David Tolnay75dca2e2020-03-25 20:17:52 -0700433 None => quote!(__extern),
434 };
435 call.extend(quote! { (#(#vars),*) });
436
437 let mut expr = sig
David Tolnay7db73692019-10-20 14:51:12 -0400438 .ret
439 .as_ref()
440 .and_then(|ret| match ret {
David Tolnaya52602b2020-03-06 10:24:34 -0800441 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400442 Some(quote!(::cxx::private::RustString::from(#call)))
443 }
444 Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))),
445 Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))),
446 Type::Ref(ty) => match &ty.inner {
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_ref(#call)))
449 }
450 _ => None,
451 },
452 Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700453 Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))),
David Tolnay7db73692019-10-20 14:51:12 -0400454 _ => None,
455 })
456 .unwrap_or(call);
David Tolnay75dca2e2020-03-25 20:17:52 -0700457
458 let mut outparam = None;
459 let indirect_return = indirect_return(sig, types);
David Tolnay1e548172020-03-16 13:37:09 -0700460 if indirect_return {
David Tolnay75dca2e2020-03-25 20:17:52 -0700461 let ret = expand_extern_type(sig.ret.as_ref().unwrap());
462 outparam = Some(quote!(__return: *mut #ret,));
David Tolnay1e548172020-03-16 13:37:09 -0700463 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700464 if sig.throws {
465 let out = match sig.ret {
David Tolnaycecada62020-03-17 01:45:58 -0700466 Some(_) => quote!(__return),
467 None => quote!(&mut ()),
468 };
469 expr = quote!(::cxx::private::r#try(#out, #expr));
David Tolnay1e548172020-03-16 13:37:09 -0700470 } else if indirect_return {
David Tolnay7db73692019-10-20 14:51:12 -0400471 expr = quote!(::std::ptr::write(__return, #expr));
472 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700473
David Tolnay1e548172020-03-16 13:37:09 -0700474 expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr));
David Tolnay75dca2e2020-03-25 20:17:52 -0700475
476 let ret = if sig.throws {
David Tolnay486b6ec2020-03-17 01:19:57 -0700477 quote!(-> ::cxx::private::Result)
David Tolnay1e548172020-03-16 13:37:09 -0700478 } else {
David Tolnay75dca2e2020-03-25 20:17:52 -0700479 expand_extern_return_type(&sig.ret, types)
David Tolnay1e548172020-03-16 13:37:09 -0700480 };
David Tolnay75dca2e2020-03-25 20:17:52 -0700481
482 let pointer = match invoke {
483 None => Some(quote!(__extern: #sig)),
484 Some(_) => None,
485 };
486
David Tolnay7db73692019-10-20 14:51:12 -0400487 quote! {
488 #[doc(hidden)]
489 #[export_name = #link_name]
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700490 unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
David Tolnay7db73692019-10-20 14:51:12 -0400491 let __fn = concat!(module_path!(), #catch_unwind_label);
492 #expr
493 }
494 }
495}
496
497fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream {
David Tolnay8c730492020-03-13 01:29:06 -0700498 let link_prefix = format!("cxxbridge02$box${}{}$", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400499 let link_uninit = format!("{}uninit", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400500 let link_drop = format!("{}drop", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400501
502 let local_prefix = format_ident!("{}__box_", ident);
503 let local_uninit = format_ident!("{}uninit", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400504 let local_drop = format_ident!("{}drop", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400505
506 let span = ident.span();
507 quote_spanned! {span=>
508 #[doc(hidden)]
509 #[export_name = #link_uninit]
510 unsafe extern "C" fn #local_uninit(
511 this: *mut ::std::boxed::Box<::std::mem::MaybeUninit<#ident>>,
512 ) {
513 ::std::ptr::write(
514 this,
515 ::std::boxed::Box::new(::std::mem::MaybeUninit::uninit()),
516 );
517 }
518 #[doc(hidden)]
David Tolnay7db73692019-10-20 14:51:12 -0400519 #[export_name = #link_drop]
520 unsafe extern "C" fn #local_drop(this: *mut ::std::boxed::Box<#ident>) {
521 ::std::ptr::drop_in_place(this);
522 }
David Tolnay7db73692019-10-20 14:51:12 -0400523 }
524}
525
David Tolnay53838912020-04-09 20:56:44 -0700526fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream {
David Tolnayad266772020-04-09 23:42:29 -0700527 let name = ident.to_string();
David Tolnay8c730492020-03-13 01:29:06 -0700528 let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400529 let link_null = format!("{}null", prefix);
530 let link_new = format!("{}new", prefix);
531 let link_raw = format!("{}raw", prefix);
532 let link_get = format!("{}get", prefix);
533 let link_release = format!("{}release", prefix);
534 let link_drop = format!("{}drop", prefix);
535
David Tolnay53838912020-04-09 20:56:44 -0700536 let new_method = if types.structs.contains_key(ident) {
537 Some(quote! {
538 fn __new(mut value: Self) -> *mut ::std::ffi::c_void {
539 extern "C" {
540 #[link_name = #link_new]
541 fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident);
542 }
543 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
544 unsafe { __new(&mut repr, &mut value) }
545 repr
546 }
547 })
548 } else {
549 None
550 };
551
David Tolnay7db73692019-10-20 14:51:12 -0400552 quote! {
553 unsafe impl ::cxx::private::UniquePtrTarget for #ident {
David Tolnayad266772020-04-09 23:42:29 -0700554 const __NAME: &'static str = #name;
David Tolnay7db73692019-10-20 14:51:12 -0400555 fn __null() -> *mut ::std::ffi::c_void {
556 extern "C" {
557 #[link_name = #link_null]
558 fn __null(this: *mut *mut ::std::ffi::c_void);
559 }
560 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
561 unsafe { __null(&mut repr) }
562 repr
563 }
David Tolnay53838912020-04-09 20:56:44 -0700564 #new_method
David Tolnay7db73692019-10-20 14:51:12 -0400565 unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void {
566 extern "C" {
567 #[link_name = #link_raw]
568 fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident);
569 }
570 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
571 __raw(&mut repr, raw);
572 repr
573 }
574 unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self {
575 extern "C" {
576 #[link_name = #link_get]
577 fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident;
578 }
579 __get(&repr)
580 }
581 unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self {
582 extern "C" {
583 #[link_name = #link_release]
584 fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident;
585 }
586 __release(&mut repr)
587 }
588 unsafe fn __drop(mut repr: *mut ::std::ffi::c_void) {
589 extern "C" {
590 #[link_name = #link_drop]
591 fn __drop(this: *mut *mut ::std::ffi::c_void);
592 }
593 __drop(&mut repr);
594 }
595 }
596 }
597}
598
599fn expand_return_type(ret: &Option<Type>) -> TokenStream {
600 match ret {
601 Some(ret) => quote!(-> #ret),
602 None => TokenStream::new(),
603 }
604}
605
David Tolnay75dca2e2020-03-25 20:17:52 -0700606fn indirect_return(sig: &Signature, types: &Types) -> bool {
607 sig.ret
David Tolnay1e548172020-03-16 13:37:09 -0700608 .as_ref()
David Tolnay75dca2e2020-03-25 20:17:52 -0700609 .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret))
David Tolnay7db73692019-10-20 14:51:12 -0400610}
611
612fn expand_extern_type(ty: &Type) -> TokenStream {
613 match ty {
David Tolnaya52602b2020-03-06 10:24:34 -0800614 Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString),
David Tolnay7db73692019-10-20 14:51:12 -0400615 Type::RustBox(ty) | Type::UniquePtr(ty) => {
616 let inner = &ty.inner;
617 quote!(*mut #inner)
618 }
619 Type::Ref(ty) => match &ty.inner {
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 _ => quote!(#ty),
622 },
623 Type::Str(_) => quote!(::cxx::private::RustStr),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700624 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
David Tolnay7db73692019-10-20 14:51:12 -0400625 _ => quote!(#ty),
626 }
627}
628
629fn expand_extern_return_type(ret: &Option<Type>, types: &Types) -> TokenStream {
630 let ret = match ret {
631 Some(ret) if !types.needs_indirect_abi(ret) => ret,
632 _ => return TokenStream::new(),
633 };
634 let ty = expand_extern_type(ret);
635 quote!(-> #ty)
636}