blob: 998b47e856a0302042636d0f9a14704b404aa361 [file] [log] [blame]
David Tolnay7db73692019-10-20 14:51:12 -04001use crate::namespace::Namespace;
David Tolnaya52602b2020-03-06 10:24:34 -08002use crate::syntax::atom::Atom::{self, *};
David Tolnay75dca2e2020-03-25 20:17:52 -07003use crate::syntax::{self, check, Api, ExternFn, ExternType, Signature, Struct, Type, Types};
David Tolnay7db73692019-10-20 14:51:12 -04004use proc_macro2::{Ident, Span, TokenStream};
5use quote::{format_ident, quote, quote_spanned};
David Tolnay75dca2e2020-03-25 20:17:52 -07006use syn::{parse_quote, Error, ItemMod, Result, Token};
David Tolnay7db73692019-10-20 14:51:12 -04007
8pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result<TokenStream> {
9 let ident = &ffi.ident;
10 let content = ffi.content.ok_or(Error::new(
11 Span::call_site(),
12 "#[cxx::bridge] module must have inline contents",
13 ))?;
14 let apis = syntax::parse_items(content.1)?;
15 let ref types = Types::collect(&apis)?;
16 check::typecheck(&apis, types)?;
17
18 let mut expanded = TokenStream::new();
19 let mut hidden = TokenStream::new();
20 let mut has_rust_type = false;
21
22 for api in &apis {
23 if let Api::RustType(ety) = api {
24 expanded.extend(expand_rust_type(ety));
25 if !has_rust_type {
David Tolnay199d7352020-01-20 18:40:10 -080026 hidden.extend(quote!(
27 const fn __assert_sized<T>() {}
28 ));
David Tolnay7db73692019-10-20 14:51:12 -040029 has_rust_type = true;
30 }
31 let ident = &ety.ident;
32 hidden.extend(quote!(__assert_sized::<#ident>();));
33 }
34 }
35
36 for api in &apis {
37 match api {
38 Api::Include(_) | Api::RustType(_) => {}
39 Api::Struct(strct) => expanded.extend(expand_struct(strct)),
40 Api::CxxType(ety) => expanded.extend(expand_cxx_type(ety)),
41 Api::CxxFunction(efn) => {
42 expanded.extend(expand_cxx_function_shim(namespace, efn, types));
43 }
44 Api::RustFunction(efn) => {
45 hidden.extend(expand_rust_function_shim(namespace, efn, types))
46 }
47 }
48 }
49
50 for ty in types {
51 if let Type::RustBox(ty) = ty {
52 if let Type::Ident(ident) = &ty.inner {
53 if Atom::from(ident).is_none() {
54 hidden.extend(expand_rust_box(namespace, ident));
55 }
56 }
57 } else if let Type::UniquePtr(ptr) = ty {
58 if let Type::Ident(ident) = &ptr.inner {
59 if Atom::from(ident).is_none() {
David Tolnay53838912020-04-09 20:56:44 -070060 expanded.extend(expand_unique_ptr(namespace, ident, types));
David Tolnay7db73692019-10-20 14:51:12 -040061 }
62 }
63 }
64 }
65
66 // Work around https://github.com/rust-lang/rust/issues/67851.
67 if !hidden.is_empty() {
68 expanded.extend(quote! {
69 #[doc(hidden)]
70 const _: () = {
71 #hidden
72 };
73 });
74 }
75
76 let attrs = ffi
77 .attrs
78 .into_iter()
79 .filter(|attr| attr.path.is_ident("doc"));
80 let vis = &ffi.vis;
81
82 Ok(quote! {
83 #(#attrs)*
84 #[deny(improper_ctypes)]
85 #[allow(non_snake_case)]
86 #vis mod #ident {
87 #expanded
88 }
89 })
90}
91
92fn expand_struct(strct: &Struct) -> TokenStream {
93 let ident = &strct.ident;
94 let doc = &strct.doc;
95 let derives = &strct.derives;
96 let fields = strct.fields.iter().map(|field| {
97 // This span on the pub makes "private type in public interface" errors
98 // appear in the right place.
99 let vis = Token![pub](field.ident.span());
100 quote!(#vis #field)
101 });
102 quote! {
103 #doc
104 #[derive(#(#derives),*)]
105 #[repr(C)]
106 pub struct #ident {
107 #(#fields,)*
108 }
109 }
110}
111
112fn expand_cxx_type(ety: &ExternType) -> TokenStream {
113 let ident = &ety.ident;
114 let doc = &ety.doc;
115 quote! {
116 #doc
117 #[repr(C)]
118 pub struct #ident {
119 _private: ::cxx::private::Opaque,
120 }
121 }
122}
123
124fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
125 let ident = &efn.ident;
Joel Galenson3d4f6122020-04-07 15:54:05 -0700126 let receiver = efn.receiver.iter().map(|base| {
127 let ident = &base.ident;
128 match base.mutability {
129 None => quote!(_: &#ident),
130 Some(_) => quote!(_: &mut #ident),
131 }
132 });
David Tolnay39d575f2020-03-03 00:10:56 -0800133 let args = efn.args.iter().map(|arg| {
134 let ident = &arg.ident;
135 let ty = expand_extern_type(&arg.ty);
David Tolnaya46a2372020-03-06 10:03:48 -0800136 if arg.ty == RustString {
137 quote!(#ident: *const #ty)
David Tolnay75dca2e2020-03-25 20:17:52 -0700138 } else if let Type::Fn(_) = arg.ty {
139 quote!(#ident: ::cxx::private::FatFunction)
David Tolnaya46a2372020-03-06 10:03:48 -0800140 } else if types.needs_indirect_abi(&arg.ty) {
David Tolnay39d575f2020-03-03 00:10:56 -0800141 quote!(#ident: *mut #ty)
142 } else {
143 quote!(#ident: #ty)
144 }
145 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700146 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700147 let ret = if efn.throws {
148 quote!(-> ::cxx::private::Result)
149 } else {
150 expand_extern_return_type(&efn.ret, types)
151 };
David Tolnay7db73692019-10-20 14:51:12 -0400152 let mut outparam = None;
David Tolnay1e548172020-03-16 13:37:09 -0700153 if indirect_return(efn, types) {
David Tolnay7db73692019-10-20 14:51:12 -0400154 let ret = expand_extern_type(efn.ret.as_ref().unwrap());
155 outparam = Some(quote!(__return: *mut #ret));
156 }
David Tolnay8c730492020-03-13 01:29:06 -0700157 let link_name = format!("{}cxxbridge02${}", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400158 let local_name = format_ident!("__{}", ident);
159 quote! {
160 #[link_name = #link_name]
Joel Galenson3d4f6122020-04-07 15:54:05 -0700161 fn #local_name(#(#all_args,)* #outparam) #ret;
David Tolnay7db73692019-10-20 14:51:12 -0400162 }
163}
164
165fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
166 let ident = &efn.ident;
167 let doc = &efn.doc;
168 let decl = expand_cxx_function_decl(namespace, efn, types);
Joel Galenson3d4f6122020-04-07 15:54:05 -0700169 let receiver = efn.receiver.iter().map(|base| match base.mutability {
170 None => quote!(&self),
171 Some(_) => quote!(&mut self),
172 });
173 let args = efn.args.iter().map(|arg| quote!(#arg));
174 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700175 let ret = if efn.throws {
176 let ok = match &efn.ret {
177 Some(ret) => quote!(#ret),
178 None => quote!(()),
179 };
180 quote!(-> ::std::result::Result<#ok, ::cxx::Exception>)
181 } else {
182 expand_return_type(&efn.ret)
183 };
David Tolnay1e548172020-03-16 13:37:09 -0700184 let indirect_return = indirect_return(efn, types);
Joel Galenson3d4f6122020-04-07 15:54:05 -0700185 let receiver_var = efn.receiver.iter().map(|_| quote!(self));
186 let arg_vars = efn.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400187 let var = &arg.ident;
188 match &arg.ty {
David Tolnaya52602b2020-03-06 10:24:34 -0800189 Type::Ident(ident) if ident == RustString => {
David Tolnaya46a2372020-03-06 10:03:48 -0800190 quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString)
David Tolnay7db73692019-10-20 14:51:12 -0400191 }
192 Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)),
193 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)),
194 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800195 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400196 quote!(::cxx::private::RustString::from_ref(#var))
197 }
198 _ => quote!(#var),
199 },
200 Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700201 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)),
David Tolnay7db73692019-10-20 14:51:12 -0400202 ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()),
203 _ => quote!(#var),
204 }
205 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700206 let vars = receiver_var.chain(arg_vars);
David Tolnay75dca2e2020-03-25 20:17:52 -0700207 let trampolines = efn
208 .args
209 .iter()
210 .filter_map(|arg| {
211 if let Type::Fn(f) = &arg.ty {
212 let var = &arg.ident;
213 Some(expand_function_pointer_trampoline(
214 namespace, efn, var, f, types,
215 ))
216 } else {
217 None
218 }
219 })
220 .collect::<TokenStream>();
David Tolnay7db73692019-10-20 14:51:12 -0400221 let mut setup = efn
222 .args
223 .iter()
224 .filter(|arg| types.needs_indirect_abi(&arg.ty))
225 .map(|arg| {
226 let var = &arg.ident;
227 // These are arguments for which C++ has taken ownership of the data
228 // behind the mut reference it received.
229 quote! {
230 let mut #var = std::mem::MaybeUninit::new(#var);
231 }
232 })
233 .collect::<TokenStream>();
234 let local_name = format_ident!("__{}", ident);
235 let call = if indirect_return {
236 let ret = expand_extern_type(efn.ret.as_ref().unwrap());
237 setup.extend(quote! {
238 let mut __return = ::std::mem::MaybeUninit::<#ret>::uninit();
David Tolnay7db73692019-10-20 14:51:12 -0400239 });
David Tolnayebef4a22020-03-17 15:33:47 -0700240 if efn.throws {
241 setup.extend(quote! {
242 #local_name(#(#vars,)* __return.as_mut_ptr()).exception()?;
243 });
244 quote!(::std::result::Result::Ok(__return.assume_init()))
245 } else {
246 setup.extend(quote! {
247 #local_name(#(#vars,)* __return.as_mut_ptr());
248 });
249 quote!(__return.assume_init())
250 }
251 } else if efn.throws {
David Tolnay7db73692019-10-20 14:51:12 -0400252 quote! {
David Tolnayebef4a22020-03-17 15:33:47 -0700253 #local_name(#(#vars),*).exception()
David Tolnay7db73692019-10-20 14:51:12 -0400254 }
255 } else {
256 quote! {
257 #local_name(#(#vars),*)
258 }
259 };
Myron Ahn84849302020-03-25 22:00:58 +0700260 let expr = if efn.throws {
261 efn.ret.as_ref().and_then(|ret| match ret {
262 Type::Ident(ident) if ident == RustString => {
263 Some(quote!(#call.map(|r| r.into_string())))
264 }
265 Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))),
266 Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))),
267 Type::Ref(ty) => match &ty.inner {
268 Type::Ident(ident) if ident == RustString => {
269 Some(quote!(#call.map(|r| r.as_string())))
270 }
271 _ => None,
272 },
273 Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700274 Type::SliceRefU8(_) => Some(quote!(#call.map(|r| r.as_slice()))),
Myron Ahn84849302020-03-25 22:00:58 +0700275 _ => None,
276 })
277 } else {
278 efn.ret.as_ref().and_then(|ret| match ret {
David Tolnaya52602b2020-03-06 10:24:34 -0800279 Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())),
David Tolnay7db73692019-10-20 14:51:12 -0400280 Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))),
281 Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))),
282 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800283 Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())),
David Tolnay7db73692019-10-20 14:51:12 -0400284 _ => None,
285 },
286 Type::Str(_) => Some(quote!(#call.as_str())),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700287 Type::SliceRefU8(_) => Some(quote!(#call.as_slice())),
David Tolnay7db73692019-10-20 14:51:12 -0400288 _ => None,
289 })
Myron Ahn84849302020-03-25 22:00:58 +0700290 }
291 .unwrap_or(call);
Joel Galenson3d4f6122020-04-07 15:54:05 -0700292 let receiver_ident = efn.receiver.as_ref().map(|base| &base.ident);
293 match receiver_ident {
294 None => quote! {
295 #doc
296 pub fn #ident(#(#all_args,)*) #ret {
297 extern "C" {
298 #decl
299 }
300 #trampolines
301 unsafe {
302 #setup
303 #expr
304 }
David Tolnay7db73692019-10-20 14:51:12 -0400305 }
Joel Galenson3d4f6122020-04-07 15:54:05 -0700306 },
307 Some(base_ident) => quote! {
308 #doc
309 impl #base_ident {
310 pub fn #ident(#(#all_args,)*) #ret {
311 extern "C" {
312 #decl
313 }
314 #trampolines
315 unsafe {
316 #setup
317 #expr
318 }
319 }
David Tolnay7db73692019-10-20 14:51:12 -0400320 }
Joel Galenson3d4f6122020-04-07 15:54:05 -0700321 },
David Tolnay7db73692019-10-20 14:51:12 -0400322 }
323}
324
David Tolnay75dca2e2020-03-25 20:17:52 -0700325fn expand_function_pointer_trampoline(
326 namespace: &Namespace,
327 efn: &ExternFn,
328 var: &Ident,
329 sig: &Signature,
330 types: &Types,
331) -> TokenStream {
332 let c_trampoline = format!("{}cxxbridge02${}${}$0", namespace, efn.ident, var);
333 let r_trampoline = format!("{}cxxbridge02${}${}$1", namespace, efn.ident, var);
334 let local_name = parse_quote!(__);
335 let catch_unwind_label = format!("::{}::{}", efn.ident, var);
336 let shim = expand_rust_function_shim_impl(
337 sig,
338 types,
339 &r_trampoline,
340 local_name,
341 catch_unwind_label,
342 None,
343 );
344
345 quote! {
346 let #var = ::cxx::private::FatFunction {
347 trampoline: {
348 extern "C" {
349 #[link_name = #c_trampoline]
350 fn trampoline();
351 }
352 #shim
353 trampoline as usize as *const ()
354 },
355 ptr: #var as usize as *const (),
356 };
357 }
358}
359
David Tolnay7db73692019-10-20 14:51:12 -0400360fn expand_rust_type(ety: &ExternType) -> TokenStream {
361 let ident = &ety.ident;
362 quote! {
363 use super::#ident;
364 }
365}
366
367fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
368 let ident = &efn.ident;
David Tolnay75dca2e2020-03-25 20:17:52 -0700369 let link_name = format!("{}cxxbridge02${}", namespace, ident);
370 let local_name = format_ident!("__{}", ident);
371 let catch_unwind_label = format!("::{}", ident);
372 let invoke = Some(ident);
373 expand_rust_function_shim_impl(
374 efn,
375 types,
376 &link_name,
377 local_name,
378 catch_unwind_label,
379 invoke,
380 )
381}
382
383fn expand_rust_function_shim_impl(
384 sig: &Signature,
385 types: &Types,
386 link_name: &str,
387 local_name: Ident,
388 catch_unwind_label: String,
389 invoke: Option<&Ident>,
390) -> TokenStream {
391 let args = sig.args.iter().map(|arg| {
David Tolnay39d575f2020-03-03 00:10:56 -0800392 let ident = &arg.ident;
393 let ty = expand_extern_type(&arg.ty);
394 if types.needs_indirect_abi(&arg.ty) {
395 quote!(#ident: *mut #ty)
396 } else {
397 quote!(#ident: #ty)
398 }
399 });
David Tolnay75dca2e2020-03-25 20:17:52 -0700400
401 let vars = sig.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400402 let ident = &arg.ident;
David Tolnay17955e22020-01-20 17:58:24 -0800403 match &arg.ty {
David Tolnaycc3767f2020-03-06 10:41:51 -0800404 Type::Ident(i) if i == RustString => {
405 quote!(::std::mem::take((*#ident).as_mut_string()))
406 }
David Tolnay40226ab2020-03-03 00:05:35 -0800407 Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)),
408 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)),
David Tolnay17955e22020-01-20 17:58:24 -0800409 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800410 Type::Ident(i) if i == RustString => quote!(#ident.as_string()),
David Tolnay40226ab2020-03-03 00:05:35 -0800411 _ => quote!(#ident),
David Tolnay17955e22020-01-20 17:58:24 -0800412 },
David Tolnay40226ab2020-03-03 00:05:35 -0800413 Type::Str(_) => quote!(#ident.as_str()),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700414 Type::SliceRefU8(_) => quote!(#ident.as_slice()),
David Tolnay40226ab2020-03-03 00:05:35 -0800415 ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
416 _ => quote!(#ident),
David Tolnay7db73692019-10-20 14:51:12 -0400417 }
418 });
David Tolnay75dca2e2020-03-25 20:17:52 -0700419
420 let mut call = match invoke {
421 Some(ident) => quote!(super::#ident),
422 None => quote!(__extern),
423 };
424 call.extend(quote! { (#(#vars),*) });
425
426 let mut expr = sig
David Tolnay7db73692019-10-20 14:51:12 -0400427 .ret
428 .as_ref()
429 .and_then(|ret| match ret {
David Tolnaya52602b2020-03-06 10:24:34 -0800430 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400431 Some(quote!(::cxx::private::RustString::from(#call)))
432 }
433 Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))),
434 Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))),
435 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800436 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400437 Some(quote!(::cxx::private::RustString::from_ref(#call)))
438 }
439 _ => None,
440 },
441 Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700442 Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))),
David Tolnay7db73692019-10-20 14:51:12 -0400443 _ => None,
444 })
445 .unwrap_or(call);
David Tolnay75dca2e2020-03-25 20:17:52 -0700446
447 let mut outparam = None;
448 let indirect_return = indirect_return(sig, types);
David Tolnay1e548172020-03-16 13:37:09 -0700449 if indirect_return {
David Tolnay75dca2e2020-03-25 20:17:52 -0700450 let ret = expand_extern_type(sig.ret.as_ref().unwrap());
451 outparam = Some(quote!(__return: *mut #ret,));
David Tolnay1e548172020-03-16 13:37:09 -0700452 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700453 if sig.throws {
454 let out = match sig.ret {
David Tolnaycecada62020-03-17 01:45:58 -0700455 Some(_) => quote!(__return),
456 None => quote!(&mut ()),
457 };
458 expr = quote!(::cxx::private::r#try(#out, #expr));
David Tolnay1e548172020-03-16 13:37:09 -0700459 } else if indirect_return {
David Tolnay7db73692019-10-20 14:51:12 -0400460 expr = quote!(::std::ptr::write(__return, #expr));
461 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700462
David Tolnay1e548172020-03-16 13:37:09 -0700463 expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr));
David Tolnay75dca2e2020-03-25 20:17:52 -0700464
465 let ret = if sig.throws {
David Tolnay486b6ec2020-03-17 01:19:57 -0700466 quote!(-> ::cxx::private::Result)
David Tolnay1e548172020-03-16 13:37:09 -0700467 } else {
David Tolnay75dca2e2020-03-25 20:17:52 -0700468 expand_extern_return_type(&sig.ret, types)
David Tolnay1e548172020-03-16 13:37:09 -0700469 };
David Tolnay75dca2e2020-03-25 20:17:52 -0700470
471 let pointer = match invoke {
472 None => Some(quote!(__extern: #sig)),
473 Some(_) => None,
474 };
475
David Tolnay7db73692019-10-20 14:51:12 -0400476 quote! {
477 #[doc(hidden)]
478 #[export_name = #link_name]
David Tolnay75dca2e2020-03-25 20:17:52 -0700479 unsafe extern "C" fn #local_name(#(#args,)* #outparam #pointer) #ret {
David Tolnay7db73692019-10-20 14:51:12 -0400480 let __fn = concat!(module_path!(), #catch_unwind_label);
481 #expr
482 }
483 }
484}
485
486fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream {
David Tolnay8c730492020-03-13 01:29:06 -0700487 let link_prefix = format!("cxxbridge02$box${}{}$", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400488 let link_uninit = format!("{}uninit", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400489 let link_drop = format!("{}drop", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400490
491 let local_prefix = format_ident!("{}__box_", ident);
492 let local_uninit = format_ident!("{}uninit", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400493 let local_drop = format_ident!("{}drop", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400494
495 let span = ident.span();
496 quote_spanned! {span=>
497 #[doc(hidden)]
498 #[export_name = #link_uninit]
499 unsafe extern "C" fn #local_uninit(
500 this: *mut ::std::boxed::Box<::std::mem::MaybeUninit<#ident>>,
501 ) {
502 ::std::ptr::write(
503 this,
504 ::std::boxed::Box::new(::std::mem::MaybeUninit::uninit()),
505 );
506 }
507 #[doc(hidden)]
David Tolnay7db73692019-10-20 14:51:12 -0400508 #[export_name = #link_drop]
509 unsafe extern "C" fn #local_drop(this: *mut ::std::boxed::Box<#ident>) {
510 ::std::ptr::drop_in_place(this);
511 }
David Tolnay7db73692019-10-20 14:51:12 -0400512 }
513}
514
David Tolnay53838912020-04-09 20:56:44 -0700515fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream {
David Tolnayad266772020-04-09 23:42:29 -0700516 let name = ident.to_string();
David Tolnay8c730492020-03-13 01:29:06 -0700517 let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400518 let link_null = format!("{}null", prefix);
519 let link_new = format!("{}new", prefix);
520 let link_raw = format!("{}raw", prefix);
521 let link_get = format!("{}get", prefix);
522 let link_release = format!("{}release", prefix);
523 let link_drop = format!("{}drop", prefix);
524
David Tolnay53838912020-04-09 20:56:44 -0700525 let new_method = if types.structs.contains_key(ident) {
526 Some(quote! {
527 fn __new(mut value: Self) -> *mut ::std::ffi::c_void {
528 extern "C" {
529 #[link_name = #link_new]
530 fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident);
531 }
532 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
533 unsafe { __new(&mut repr, &mut value) }
534 repr
535 }
536 })
537 } else {
538 None
539 };
540
David Tolnay7db73692019-10-20 14:51:12 -0400541 quote! {
542 unsafe impl ::cxx::private::UniquePtrTarget for #ident {
David Tolnayad266772020-04-09 23:42:29 -0700543 const __NAME: &'static str = #name;
David Tolnay7db73692019-10-20 14:51:12 -0400544 fn __null() -> *mut ::std::ffi::c_void {
545 extern "C" {
546 #[link_name = #link_null]
547 fn __null(this: *mut *mut ::std::ffi::c_void);
548 }
549 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
550 unsafe { __null(&mut repr) }
551 repr
552 }
David Tolnay53838912020-04-09 20:56:44 -0700553 #new_method
David Tolnay7db73692019-10-20 14:51:12 -0400554 unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void {
555 extern "C" {
556 #[link_name = #link_raw]
557 fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident);
558 }
559 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
560 __raw(&mut repr, raw);
561 repr
562 }
563 unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self {
564 extern "C" {
565 #[link_name = #link_get]
566 fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident;
567 }
568 __get(&repr)
569 }
570 unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self {
571 extern "C" {
572 #[link_name = #link_release]
573 fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident;
574 }
575 __release(&mut repr)
576 }
577 unsafe fn __drop(mut repr: *mut ::std::ffi::c_void) {
578 extern "C" {
579 #[link_name = #link_drop]
580 fn __drop(this: *mut *mut ::std::ffi::c_void);
581 }
582 __drop(&mut repr);
583 }
584 }
585 }
586}
587
588fn expand_return_type(ret: &Option<Type>) -> TokenStream {
589 match ret {
590 Some(ret) => quote!(-> #ret),
591 None => TokenStream::new(),
592 }
593}
594
David Tolnay75dca2e2020-03-25 20:17:52 -0700595fn indirect_return(sig: &Signature, types: &Types) -> bool {
596 sig.ret
David Tolnay1e548172020-03-16 13:37:09 -0700597 .as_ref()
David Tolnay75dca2e2020-03-25 20:17:52 -0700598 .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret))
David Tolnay7db73692019-10-20 14:51:12 -0400599}
600
601fn expand_extern_type(ty: &Type) -> TokenStream {
602 match ty {
David Tolnaya52602b2020-03-06 10:24:34 -0800603 Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString),
David Tolnay7db73692019-10-20 14:51:12 -0400604 Type::RustBox(ty) | Type::UniquePtr(ty) => {
605 let inner = &ty.inner;
606 quote!(*mut #inner)
607 }
608 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800609 Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString),
David Tolnay7db73692019-10-20 14:51:12 -0400610 _ => quote!(#ty),
611 },
612 Type::Str(_) => quote!(::cxx::private::RustStr),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700613 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
David Tolnay7db73692019-10-20 14:51:12 -0400614 _ => quote!(#ty),
615 }
616}
617
618fn expand_extern_return_type(ret: &Option<Type>, types: &Types) -> TokenStream {
619 let ret = match ret {
620 Some(ret) if !types.needs_indirect_abi(ret) => ret,
621 _ => return TokenStream::new(),
622 };
623 let ty = expand_extern_type(ret);
624 quote!(-> #ty)
625}