blob: acc74d5c8917b08373d998d94b861010869307b1 [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 Tolnayfb6e3862020-04-20 01:33:23 -0700129 let receiver = efn.receiver.iter().map(|receiver| quote!(_: #receiver));
David Tolnay39d575f2020-03-03 00:10:56 -0800130 let args = efn.args.iter().map(|arg| {
131 let ident = &arg.ident;
132 let ty = expand_extern_type(&arg.ty);
David Tolnaya46a2372020-03-06 10:03:48 -0800133 if arg.ty == RustString {
134 quote!(#ident: *const #ty)
David Tolnay75dca2e2020-03-25 20:17:52 -0700135 } else if let Type::Fn(_) = arg.ty {
136 quote!(#ident: ::cxx::private::FatFunction)
David Tolnaya46a2372020-03-06 10:03:48 -0800137 } else if types.needs_indirect_abi(&arg.ty) {
David Tolnay39d575f2020-03-03 00:10:56 -0800138 quote!(#ident: *mut #ty)
139 } else {
140 quote!(#ident: #ty)
141 }
142 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700143 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700144 let ret = if efn.throws {
145 quote!(-> ::cxx::private::Result)
146 } else {
147 expand_extern_return_type(&efn.ret, types)
148 };
David Tolnay7db73692019-10-20 14:51:12 -0400149 let mut outparam = None;
David Tolnay1e548172020-03-16 13:37:09 -0700150 if indirect_return(efn, types) {
David Tolnay7db73692019-10-20 14:51:12 -0400151 let ret = expand_extern_type(efn.ret.as_ref().unwrap());
152 outparam = Some(quote!(__return: *mut #ret));
153 }
David Tolnay3caa50a2020-04-19 21:25:34 -0700154 let link_name = mangle::extern_fn(namespace, efn);
David Tolnay7db73692019-10-20 14:51:12 -0400155 let local_name = format_ident!("__{}", ident);
156 quote! {
157 #[link_name = #link_name]
Joel Galenson3d4f6122020-04-07 15:54:05 -0700158 fn #local_name(#(#all_args,)* #outparam) #ret;
David Tolnay7db73692019-10-20 14:51:12 -0400159 }
160}
161
162fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
163 let ident = &efn.ident;
164 let doc = &efn.doc;
165 let decl = expand_cxx_function_decl(namespace, efn, types);
David Tolnayfb6e3862020-04-20 01:33:23 -0700166 let receiver = efn.receiver.iter().map(|receiver| {
167 let mutability = receiver.mutability;
168 quote!(&#mutability self)
169 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700170 let args = efn.args.iter().map(|arg| quote!(#arg));
171 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700172 let ret = if efn.throws {
173 let ok = match &efn.ret {
174 Some(ret) => quote!(#ret),
175 None => quote!(()),
176 };
177 quote!(-> ::std::result::Result<#ok, ::cxx::Exception>)
178 } else {
179 expand_return_type(&efn.ret)
180 };
David Tolnay1e548172020-03-16 13:37:09 -0700181 let indirect_return = indirect_return(efn, types);
Joel Galenson3d4f6122020-04-07 15:54:05 -0700182 let receiver_var = efn.receiver.iter().map(|_| quote!(self));
183 let arg_vars = efn.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400184 let var = &arg.ident;
185 match &arg.ty {
David Tolnaya52602b2020-03-06 10:24:34 -0800186 Type::Ident(ident) if ident == RustString => {
David Tolnaya46a2372020-03-06 10:03:48 -0800187 quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString)
David Tolnay7db73692019-10-20 14:51:12 -0400188 }
189 Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)),
190 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)),
191 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800192 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400193 quote!(::cxx::private::RustString::from_ref(#var))
194 }
195 _ => quote!(#var),
196 },
197 Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700198 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)),
David Tolnay7db73692019-10-20 14:51:12 -0400199 ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()),
200 _ => quote!(#var),
201 }
202 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700203 let vars = receiver_var.chain(arg_vars);
David Tolnay75dca2e2020-03-25 20:17:52 -0700204 let trampolines = efn
205 .args
206 .iter()
207 .filter_map(|arg| {
208 if let Type::Fn(f) = &arg.ty {
209 let var = &arg.ident;
210 Some(expand_function_pointer_trampoline(
211 namespace, efn, var, f, types,
212 ))
213 } else {
214 None
215 }
216 })
217 .collect::<TokenStream>();
David Tolnay7db73692019-10-20 14:51:12 -0400218 let mut setup = efn
219 .args
220 .iter()
221 .filter(|arg| types.needs_indirect_abi(&arg.ty))
222 .map(|arg| {
223 let var = &arg.ident;
224 // These are arguments for which C++ has taken ownership of the data
225 // behind the mut reference it received.
226 quote! {
227 let mut #var = std::mem::MaybeUninit::new(#var);
228 }
229 })
230 .collect::<TokenStream>();
231 let local_name = format_ident!("__{}", ident);
232 let call = if indirect_return {
233 let ret = expand_extern_type(efn.ret.as_ref().unwrap());
234 setup.extend(quote! {
235 let mut __return = ::std::mem::MaybeUninit::<#ret>::uninit();
David Tolnay7db73692019-10-20 14:51:12 -0400236 });
David Tolnayebef4a22020-03-17 15:33:47 -0700237 if efn.throws {
238 setup.extend(quote! {
239 #local_name(#(#vars,)* __return.as_mut_ptr()).exception()?;
240 });
241 quote!(::std::result::Result::Ok(__return.assume_init()))
242 } else {
243 setup.extend(quote! {
244 #local_name(#(#vars,)* __return.as_mut_ptr());
245 });
246 quote!(__return.assume_init())
247 }
248 } else if efn.throws {
David Tolnay7db73692019-10-20 14:51:12 -0400249 quote! {
David Tolnayebef4a22020-03-17 15:33:47 -0700250 #local_name(#(#vars),*).exception()
David Tolnay7db73692019-10-20 14:51:12 -0400251 }
252 } else {
253 quote! {
254 #local_name(#(#vars),*)
255 }
256 };
Myron Ahn84849302020-03-25 22:00:58 +0700257 let expr = if efn.throws {
258 efn.ret.as_ref().and_then(|ret| match ret {
259 Type::Ident(ident) if ident == RustString => {
260 Some(quote!(#call.map(|r| r.into_string())))
261 }
262 Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))),
263 Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))),
264 Type::Ref(ty) => match &ty.inner {
265 Type::Ident(ident) if ident == RustString => {
266 Some(quote!(#call.map(|r| r.as_string())))
267 }
268 _ => None,
269 },
270 Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700271 Type::SliceRefU8(_) => Some(quote!(#call.map(|r| r.as_slice()))),
Myron Ahn84849302020-03-25 22:00:58 +0700272 _ => None,
273 })
274 } else {
275 efn.ret.as_ref().and_then(|ret| match ret {
David Tolnaya52602b2020-03-06 10:24:34 -0800276 Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())),
David Tolnay7db73692019-10-20 14:51:12 -0400277 Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))),
278 Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))),
279 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800280 Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())),
David Tolnay7db73692019-10-20 14:51:12 -0400281 _ => None,
282 },
283 Type::Str(_) => Some(quote!(#call.as_str())),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700284 Type::SliceRefU8(_) => Some(quote!(#call.as_slice())),
David Tolnay7db73692019-10-20 14:51:12 -0400285 _ => None,
286 })
Myron Ahn84849302020-03-25 22:00:58 +0700287 }
288 .unwrap_or(call);
David Tolnayc66cdbb2020-04-20 01:41:15 -0700289 let function_shim = quote! {
290 #doc
291 pub fn #ident(#(#all_args,)*) #ret {
292 extern "C" {
293 #decl
David Tolnay7db73692019-10-20 14:51:12 -0400294 }
David Tolnayc66cdbb2020-04-20 01:41:15 -0700295 #trampolines
296 unsafe {
297 #setup
298 #expr
David Tolnay7db73692019-10-20 14:51:12 -0400299 }
David Tolnayc66cdbb2020-04-20 01:41:15 -0700300 }
301 };
302 match &efn.receiver {
303 None => function_shim,
304 Some(receiver) => {
305 let receiver_type = &receiver.ident;
306 quote!(impl #receiver_type { #function_shim })
307 }
David Tolnay7db73692019-10-20 14:51:12 -0400308 }
309}
310
David Tolnay75dca2e2020-03-25 20:17:52 -0700311fn expand_function_pointer_trampoline(
312 namespace: &Namespace,
313 efn: &ExternFn,
314 var: &Ident,
315 sig: &Signature,
316 types: &Types,
317) -> TokenStream {
David Tolnay891061b2020-04-19 22:42:33 -0700318 let c_trampoline = mangle::c_trampoline(namespace, efn, var);
319 let r_trampoline = mangle::r_trampoline(namespace, efn, var);
David Tolnay75dca2e2020-03-25 20:17:52 -0700320 let local_name = parse_quote!(__);
321 let catch_unwind_label = format!("::{}::{}", efn.ident, var);
322 let shim = expand_rust_function_shim_impl(
323 sig,
324 types,
325 &r_trampoline,
326 local_name,
327 catch_unwind_label,
328 None,
329 );
330
331 quote! {
332 let #var = ::cxx::private::FatFunction {
333 trampoline: {
334 extern "C" {
335 #[link_name = #c_trampoline]
336 fn trampoline();
337 }
338 #shim
339 trampoline as usize as *const ()
340 },
341 ptr: #var as usize as *const (),
342 };
343 }
344}
345
David Tolnay7db73692019-10-20 14:51:12 -0400346fn expand_rust_type(ety: &ExternType) -> TokenStream {
347 let ident = &ety.ident;
348 quote! {
349 use super::#ident;
350 }
351}
352
353fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
354 let ident = &efn.ident;
David Tolnay3caa50a2020-04-19 21:25:34 -0700355 let link_name = mangle::extern_fn(namespace, efn);
David Tolnay75dca2e2020-03-25 20:17:52 -0700356 let local_name = format_ident!("__{}", ident);
357 let catch_unwind_label = format!("::{}", ident);
358 let invoke = Some(ident);
359 expand_rust_function_shim_impl(
360 efn,
361 types,
362 &link_name,
363 local_name,
364 catch_unwind_label,
365 invoke,
366 )
367}
368
369fn expand_rust_function_shim_impl(
370 sig: &Signature,
371 types: &Types,
David Tolnay891061b2020-04-19 22:42:33 -0700372 link_name: &Symbol,
David Tolnay75dca2e2020-03-25 20:17:52 -0700373 local_name: Ident,
374 catch_unwind_label: String,
375 invoke: Option<&Ident>,
376) -> TokenStream {
David Tolnayfb6e3862020-04-20 01:33:23 -0700377 let receiver = sig
378 .receiver
379 .iter()
380 .map(|receiver| quote!(__self: #receiver));
David Tolnay75dca2e2020-03-25 20:17:52 -0700381 let args = sig.args.iter().map(|arg| {
David Tolnay39d575f2020-03-03 00:10:56 -0800382 let ident = &arg.ident;
383 let ty = expand_extern_type(&arg.ty);
384 if types.needs_indirect_abi(&arg.ty) {
385 quote!(#ident: *mut #ty)
386 } else {
387 quote!(#ident: #ty)
388 }
389 });
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700390 let all_args = receiver.chain(args);
David Tolnay75dca2e2020-03-25 20:17:52 -0700391
392 let vars = sig.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400393 let ident = &arg.ident;
David Tolnay17955e22020-01-20 17:58:24 -0800394 match &arg.ty {
David Tolnaycc3767f2020-03-06 10:41:51 -0800395 Type::Ident(i) if i == RustString => {
396 quote!(::std::mem::take((*#ident).as_mut_string()))
397 }
David Tolnay40226ab2020-03-03 00:05:35 -0800398 Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)),
399 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)),
David Tolnay17955e22020-01-20 17:58:24 -0800400 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800401 Type::Ident(i) if i == RustString => quote!(#ident.as_string()),
David Tolnay40226ab2020-03-03 00:05:35 -0800402 _ => quote!(#ident),
David Tolnay17955e22020-01-20 17:58:24 -0800403 },
David Tolnay40226ab2020-03-03 00:05:35 -0800404 Type::Str(_) => quote!(#ident.as_str()),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700405 Type::SliceRefU8(_) => quote!(#ident.as_slice()),
David Tolnay40226ab2020-03-03 00:05:35 -0800406 ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
407 _ => quote!(#ident),
David Tolnay7db73692019-10-20 14:51:12 -0400408 }
409 });
David Tolnay75dca2e2020-03-25 20:17:52 -0700410
411 let mut call = match invoke {
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700412 Some(ident) => match sig.receiver {
413 None => quote!(super::#ident),
David Tolnay26804bd2020-04-19 20:06:51 -0700414 Some(_) => quote!(__self.#ident),
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700415 },
David Tolnay75dca2e2020-03-25 20:17:52 -0700416 None => quote!(__extern),
417 };
418 call.extend(quote! { (#(#vars),*) });
419
420 let mut expr = sig
David Tolnay7db73692019-10-20 14:51:12 -0400421 .ret
422 .as_ref()
423 .and_then(|ret| match ret {
David Tolnaya52602b2020-03-06 10:24:34 -0800424 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400425 Some(quote!(::cxx::private::RustString::from(#call)))
426 }
427 Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))),
428 Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))),
429 Type::Ref(ty) => match &ty.inner {
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_ref(#call)))
432 }
433 _ => None,
434 },
435 Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700436 Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))),
David Tolnay7db73692019-10-20 14:51:12 -0400437 _ => None,
438 })
439 .unwrap_or(call);
David Tolnay75dca2e2020-03-25 20:17:52 -0700440
441 let mut outparam = None;
442 let indirect_return = indirect_return(sig, types);
David Tolnay1e548172020-03-16 13:37:09 -0700443 if indirect_return {
David Tolnay75dca2e2020-03-25 20:17:52 -0700444 let ret = expand_extern_type(sig.ret.as_ref().unwrap());
445 outparam = Some(quote!(__return: *mut #ret,));
David Tolnay1e548172020-03-16 13:37:09 -0700446 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700447 if sig.throws {
448 let out = match sig.ret {
David Tolnaycecada62020-03-17 01:45:58 -0700449 Some(_) => quote!(__return),
450 None => quote!(&mut ()),
451 };
452 expr = quote!(::cxx::private::r#try(#out, #expr));
David Tolnay1e548172020-03-16 13:37:09 -0700453 } else if indirect_return {
David Tolnay7db73692019-10-20 14:51:12 -0400454 expr = quote!(::std::ptr::write(__return, #expr));
455 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700456
David Tolnay1e548172020-03-16 13:37:09 -0700457 expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr));
David Tolnay75dca2e2020-03-25 20:17:52 -0700458
459 let ret = if sig.throws {
David Tolnay486b6ec2020-03-17 01:19:57 -0700460 quote!(-> ::cxx::private::Result)
David Tolnay1e548172020-03-16 13:37:09 -0700461 } else {
David Tolnay75dca2e2020-03-25 20:17:52 -0700462 expand_extern_return_type(&sig.ret, types)
David Tolnay1e548172020-03-16 13:37:09 -0700463 };
David Tolnay75dca2e2020-03-25 20:17:52 -0700464
465 let pointer = match invoke {
466 None => Some(quote!(__extern: #sig)),
467 Some(_) => None,
468 };
469
David Tolnay7db73692019-10-20 14:51:12 -0400470 quote! {
471 #[doc(hidden)]
472 #[export_name = #link_name]
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700473 unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
David Tolnay7db73692019-10-20 14:51:12 -0400474 let __fn = concat!(module_path!(), #catch_unwind_label);
475 #expr
476 }
477 }
478}
479
480fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream {
David Tolnay8c730492020-03-13 01:29:06 -0700481 let link_prefix = format!("cxxbridge02$box${}{}$", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400482 let link_uninit = format!("{}uninit", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400483 let link_drop = format!("{}drop", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400484
485 let local_prefix = format_ident!("{}__box_", ident);
486 let local_uninit = format_ident!("{}uninit", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400487 let local_drop = format_ident!("{}drop", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400488
489 let span = ident.span();
490 quote_spanned! {span=>
491 #[doc(hidden)]
492 #[export_name = #link_uninit]
493 unsafe extern "C" fn #local_uninit(
494 this: *mut ::std::boxed::Box<::std::mem::MaybeUninit<#ident>>,
495 ) {
496 ::std::ptr::write(
497 this,
498 ::std::boxed::Box::new(::std::mem::MaybeUninit::uninit()),
499 );
500 }
501 #[doc(hidden)]
David Tolnay7db73692019-10-20 14:51:12 -0400502 #[export_name = #link_drop]
503 unsafe extern "C" fn #local_drop(this: *mut ::std::boxed::Box<#ident>) {
504 ::std::ptr::drop_in_place(this);
505 }
David Tolnay7db73692019-10-20 14:51:12 -0400506 }
507}
508
David Tolnay53838912020-04-09 20:56:44 -0700509fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream {
David Tolnayad266772020-04-09 23:42:29 -0700510 let name = ident.to_string();
David Tolnay8c730492020-03-13 01:29:06 -0700511 let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400512 let link_null = format!("{}null", prefix);
513 let link_new = format!("{}new", prefix);
514 let link_raw = format!("{}raw", prefix);
515 let link_get = format!("{}get", prefix);
516 let link_release = format!("{}release", prefix);
517 let link_drop = format!("{}drop", prefix);
518
David Tolnay53838912020-04-09 20:56:44 -0700519 let new_method = if types.structs.contains_key(ident) {
520 Some(quote! {
521 fn __new(mut value: Self) -> *mut ::std::ffi::c_void {
522 extern "C" {
523 #[link_name = #link_new]
524 fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident);
525 }
526 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
527 unsafe { __new(&mut repr, &mut value) }
528 repr
529 }
530 })
531 } else {
532 None
533 };
534
David Tolnay7db73692019-10-20 14:51:12 -0400535 quote! {
536 unsafe impl ::cxx::private::UniquePtrTarget for #ident {
David Tolnayad266772020-04-09 23:42:29 -0700537 const __NAME: &'static str = #name;
David Tolnay7db73692019-10-20 14:51:12 -0400538 fn __null() -> *mut ::std::ffi::c_void {
539 extern "C" {
540 #[link_name = #link_null]
541 fn __null(this: *mut *mut ::std::ffi::c_void);
542 }
543 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
544 unsafe { __null(&mut repr) }
545 repr
546 }
David Tolnay53838912020-04-09 20:56:44 -0700547 #new_method
David Tolnay7db73692019-10-20 14:51:12 -0400548 unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void {
549 extern "C" {
550 #[link_name = #link_raw]
551 fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident);
552 }
553 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
554 __raw(&mut repr, raw);
555 repr
556 }
557 unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self {
558 extern "C" {
559 #[link_name = #link_get]
560 fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident;
561 }
562 __get(&repr)
563 }
564 unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self {
565 extern "C" {
566 #[link_name = #link_release]
567 fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident;
568 }
569 __release(&mut repr)
570 }
571 unsafe fn __drop(mut repr: *mut ::std::ffi::c_void) {
572 extern "C" {
573 #[link_name = #link_drop]
574 fn __drop(this: *mut *mut ::std::ffi::c_void);
575 }
576 __drop(&mut repr);
577 }
578 }
579 }
580}
581
582fn expand_return_type(ret: &Option<Type>) -> TokenStream {
583 match ret {
584 Some(ret) => quote!(-> #ret),
585 None => TokenStream::new(),
586 }
587}
588
David Tolnay75dca2e2020-03-25 20:17:52 -0700589fn indirect_return(sig: &Signature, types: &Types) -> bool {
590 sig.ret
David Tolnay1e548172020-03-16 13:37:09 -0700591 .as_ref()
David Tolnay75dca2e2020-03-25 20:17:52 -0700592 .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret))
David Tolnay7db73692019-10-20 14:51:12 -0400593}
594
595fn expand_extern_type(ty: &Type) -> TokenStream {
596 match ty {
David Tolnaya52602b2020-03-06 10:24:34 -0800597 Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString),
David Tolnay7db73692019-10-20 14:51:12 -0400598 Type::RustBox(ty) | Type::UniquePtr(ty) => {
599 let inner = &ty.inner;
600 quote!(*mut #inner)
601 }
602 Type::Ref(ty) => match &ty.inner {
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 _ => quote!(#ty),
605 },
606 Type::Str(_) => quote!(::cxx::private::RustStr),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700607 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
David Tolnay7db73692019-10-20 14:51:12 -0400608 _ => quote!(#ty),
609 }
610}
611
612fn expand_extern_return_type(ret: &Option<Type>, types: &Types) -> TokenStream {
613 let ret = match ret {
614 Some(ret) if !types.needs_indirect_abi(ret) => ret,
615 _ => return TokenStream::new(),
616 };
617 let ty = expand_extern_type(ret);
618 quote!(-> #ty)
619}