blob: 4841794404eab9a7ebc3c5918360016c2952a53d [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) => {
David Tolnay05e11cc2020-04-20 02:13:56 -0700305 let receiver_type = &receiver.ty;
David Tolnayc66cdbb2020-04-20 01:41:15 -0700306 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
David Tolnay3a45f2d2020-04-20 01:51:12 -0700392 let receiver_var = sig.receiver.iter().map(|_| quote!(__self));
393 let arg_vars = sig.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400394 let ident = &arg.ident;
David Tolnay17955e22020-01-20 17:58:24 -0800395 match &arg.ty {
David Tolnaycc3767f2020-03-06 10:41:51 -0800396 Type::Ident(i) if i == RustString => {
397 quote!(::std::mem::take((*#ident).as_mut_string()))
398 }
David Tolnay40226ab2020-03-03 00:05:35 -0800399 Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)),
400 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)),
David Tolnay17955e22020-01-20 17:58:24 -0800401 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800402 Type::Ident(i) if i == RustString => quote!(#ident.as_string()),
David Tolnay40226ab2020-03-03 00:05:35 -0800403 _ => quote!(#ident),
David Tolnay17955e22020-01-20 17:58:24 -0800404 },
David Tolnay40226ab2020-03-03 00:05:35 -0800405 Type::Str(_) => quote!(#ident.as_str()),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700406 Type::SliceRefU8(_) => quote!(#ident.as_slice()),
David Tolnay40226ab2020-03-03 00:05:35 -0800407 ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
408 _ => quote!(#ident),
David Tolnay7db73692019-10-20 14:51:12 -0400409 }
410 });
David Tolnay3a45f2d2020-04-20 01:51:12 -0700411 let vars = receiver_var.chain(arg_vars);
David Tolnay75dca2e2020-03-25 20:17:52 -0700412
413 let mut call = match invoke {
David Tolnay3a45f2d2020-04-20 01:51:12 -0700414 Some(ident) => match &sig.receiver {
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700415 None => quote!(super::#ident),
David Tolnay3a45f2d2020-04-20 01:51:12 -0700416 Some(receiver) => {
David Tolnay05e11cc2020-04-20 02:13:56 -0700417 let receiver_type = &receiver.ty;
David Tolnay3a45f2d2020-04-20 01:51:12 -0700418 quote!(#receiver_type::#ident)
419 }
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700420 },
David Tolnay75dca2e2020-03-25 20:17:52 -0700421 None => quote!(__extern),
422 };
423 call.extend(quote! { (#(#vars),*) });
424
425 let mut expr = sig
David Tolnay7db73692019-10-20 14:51:12 -0400426 .ret
427 .as_ref()
428 .and_then(|ret| match ret {
David Tolnaya52602b2020-03-06 10:24:34 -0800429 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400430 Some(quote!(::cxx::private::RustString::from(#call)))
431 }
432 Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))),
433 Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))),
434 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800435 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400436 Some(quote!(::cxx::private::RustString::from_ref(#call)))
437 }
438 _ => None,
439 },
440 Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700441 Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))),
David Tolnay7db73692019-10-20 14:51:12 -0400442 _ => None,
443 })
444 .unwrap_or(call);
David Tolnay75dca2e2020-03-25 20:17:52 -0700445
446 let mut outparam = None;
447 let indirect_return = indirect_return(sig, types);
David Tolnay1e548172020-03-16 13:37:09 -0700448 if indirect_return {
David Tolnay75dca2e2020-03-25 20:17:52 -0700449 let ret = expand_extern_type(sig.ret.as_ref().unwrap());
450 outparam = Some(quote!(__return: *mut #ret,));
David Tolnay1e548172020-03-16 13:37:09 -0700451 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700452 if sig.throws {
453 let out = match sig.ret {
David Tolnaycecada62020-03-17 01:45:58 -0700454 Some(_) => quote!(__return),
455 None => quote!(&mut ()),
456 };
457 expr = quote!(::cxx::private::r#try(#out, #expr));
David Tolnay1e548172020-03-16 13:37:09 -0700458 } else if indirect_return {
David Tolnay7db73692019-10-20 14:51:12 -0400459 expr = quote!(::std::ptr::write(__return, #expr));
460 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700461
David Tolnay1e548172020-03-16 13:37:09 -0700462 expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr));
David Tolnay75dca2e2020-03-25 20:17:52 -0700463
464 let ret = if sig.throws {
David Tolnay486b6ec2020-03-17 01:19:57 -0700465 quote!(-> ::cxx::private::Result)
David Tolnay1e548172020-03-16 13:37:09 -0700466 } else {
David Tolnay75dca2e2020-03-25 20:17:52 -0700467 expand_extern_return_type(&sig.ret, types)
David Tolnay1e548172020-03-16 13:37:09 -0700468 };
David Tolnay75dca2e2020-03-25 20:17:52 -0700469
470 let pointer = match invoke {
471 None => Some(quote!(__extern: #sig)),
472 Some(_) => None,
473 };
474
David Tolnay7db73692019-10-20 14:51:12 -0400475 quote! {
476 #[doc(hidden)]
477 #[export_name = #link_name]
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700478 unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
David Tolnay7db73692019-10-20 14:51:12 -0400479 let __fn = concat!(module_path!(), #catch_unwind_label);
480 #expr
481 }
482 }
483}
484
485fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream {
David Tolnay8c730492020-03-13 01:29:06 -0700486 let link_prefix = format!("cxxbridge02$box${}{}$", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400487 let link_uninit = format!("{}uninit", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400488 let link_drop = format!("{}drop", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400489
490 let local_prefix = format_ident!("{}__box_", ident);
491 let local_uninit = format_ident!("{}uninit", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400492 let local_drop = format_ident!("{}drop", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400493
494 let span = ident.span();
495 quote_spanned! {span=>
496 #[doc(hidden)]
497 #[export_name = #link_uninit]
498 unsafe extern "C" fn #local_uninit(
499 this: *mut ::std::boxed::Box<::std::mem::MaybeUninit<#ident>>,
500 ) {
501 ::std::ptr::write(
502 this,
503 ::std::boxed::Box::new(::std::mem::MaybeUninit::uninit()),
504 );
505 }
506 #[doc(hidden)]
David Tolnay7db73692019-10-20 14:51:12 -0400507 #[export_name = #link_drop]
508 unsafe extern "C" fn #local_drop(this: *mut ::std::boxed::Box<#ident>) {
509 ::std::ptr::drop_in_place(this);
510 }
David Tolnay7db73692019-10-20 14:51:12 -0400511 }
512}
513
David Tolnay53838912020-04-09 20:56:44 -0700514fn expand_unique_ptr(namespace: &Namespace, ident: &Ident, types: &Types) -> TokenStream {
David Tolnayad266772020-04-09 23:42:29 -0700515 let name = ident.to_string();
David Tolnay8c730492020-03-13 01:29:06 -0700516 let prefix = format!("cxxbridge02$unique_ptr${}{}$", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400517 let link_null = format!("{}null", prefix);
518 let link_new = format!("{}new", prefix);
519 let link_raw = format!("{}raw", prefix);
520 let link_get = format!("{}get", prefix);
521 let link_release = format!("{}release", prefix);
522 let link_drop = format!("{}drop", prefix);
523
David Tolnay53838912020-04-09 20:56:44 -0700524 let new_method = if types.structs.contains_key(ident) {
525 Some(quote! {
526 fn __new(mut value: Self) -> *mut ::std::ffi::c_void {
527 extern "C" {
528 #[link_name = #link_new]
529 fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident);
530 }
531 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
532 unsafe { __new(&mut repr, &mut value) }
533 repr
534 }
535 })
536 } else {
537 None
538 };
539
David Tolnay7db73692019-10-20 14:51:12 -0400540 quote! {
541 unsafe impl ::cxx::private::UniquePtrTarget for #ident {
David Tolnayad266772020-04-09 23:42:29 -0700542 const __NAME: &'static str = #name;
David Tolnay7db73692019-10-20 14:51:12 -0400543 fn __null() -> *mut ::std::ffi::c_void {
544 extern "C" {
545 #[link_name = #link_null]
546 fn __null(this: *mut *mut ::std::ffi::c_void);
547 }
548 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
549 unsafe { __null(&mut repr) }
550 repr
551 }
David Tolnay53838912020-04-09 20:56:44 -0700552 #new_method
David Tolnay7db73692019-10-20 14:51:12 -0400553 unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void {
554 extern "C" {
555 #[link_name = #link_raw]
556 fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident);
557 }
558 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
559 __raw(&mut repr, raw);
560 repr
561 }
562 unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self {
563 extern "C" {
564 #[link_name = #link_get]
565 fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident;
566 }
567 __get(&repr)
568 }
569 unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self {
570 extern "C" {
571 #[link_name = #link_release]
572 fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident;
573 }
574 __release(&mut repr)
575 }
576 unsafe fn __drop(mut repr: *mut ::std::ffi::c_void) {
577 extern "C" {
578 #[link_name = #link_drop]
579 fn __drop(this: *mut *mut ::std::ffi::c_void);
580 }
581 __drop(&mut repr);
582 }
583 }
584 }
585}
586
587fn expand_return_type(ret: &Option<Type>) -> TokenStream {
588 match ret {
589 Some(ret) => quote!(-> #ret),
590 None => TokenStream::new(),
591 }
592}
593
David Tolnay75dca2e2020-03-25 20:17:52 -0700594fn indirect_return(sig: &Signature, types: &Types) -> bool {
595 sig.ret
David Tolnay1e548172020-03-16 13:37:09 -0700596 .as_ref()
David Tolnay75dca2e2020-03-25 20:17:52 -0700597 .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret))
David Tolnay7db73692019-10-20 14:51:12 -0400598}
599
600fn expand_extern_type(ty: &Type) -> TokenStream {
601 match ty {
David Tolnaya52602b2020-03-06 10:24:34 -0800602 Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString),
David Tolnay7db73692019-10-20 14:51:12 -0400603 Type::RustBox(ty) | Type::UniquePtr(ty) => {
604 let inner = &ty.inner;
605 quote!(*mut #inner)
606 }
607 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800608 Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString),
David Tolnay7db73692019-10-20 14:51:12 -0400609 _ => quote!(#ty),
610 },
611 Type::Str(_) => quote!(::cxx::private::RustStr),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700612 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
David Tolnay7db73692019-10-20 14:51:12 -0400613 _ => quote!(#ty),
614 }
615}
616
617fn expand_extern_return_type(ret: &Option<Type>, types: &Types) -> TokenStream {
618 let ret = match ret {
619 Some(ret) if !types.needs_indirect_abi(ret) => ret,
620 _ => return TokenStream::new(),
621 };
622 let ty = expand_extern_type(ret);
623 quote!(-> #ty)
624}