blob: b329276e28c3c84e6242a6299cb99d20f3ba3756 [file] [log] [blame]
David Tolnaya52602b2020-03-06 10:24:34 -08001use crate::syntax::atom::Atom::{self, *};
Myron Ahneba35cf2020-02-05 19:41:51 +07002use crate::syntax::mangled::ToMangled;
David Tolnay08419302020-04-19 20:38:20 -07003use crate::syntax::namespace::Namespace;
David Tolnay891061b2020-04-19 22:42:33 -07004use crate::syntax::symbol::Symbol;
Myron Ahneba35cf2020-02-05 19:41:51 +07005use crate::syntax::typename::ToTypename;
David Tolnay3caa50a2020-04-19 21:25:34 -07006use crate::syntax::{
7 self, check, mangle, Api, ExternFn, ExternType, Signature, Struct, Type, Types,
8};
David Tolnay7db73692019-10-20 14:51:12 -04009use proc_macro2::{Ident, Span, TokenStream};
David Tolnayf9ffb932020-04-20 02:22:57 -070010use quote::{format_ident, quote, quote_spanned, ToTokens};
Myron Ahneba35cf2020-02-05 19:41:51 +070011use syn::{parse_quote, spanned::Spanned, Error, ItemMod, Result, Token};
David Tolnay7db73692019-10-20 14:51:12 -040012
13pub fn bridge(namespace: &Namespace, ffi: ItemMod) -> Result<TokenStream> {
14 let ident = &ffi.ident;
15 let content = ffi.content.ok_or(Error::new(
16 Span::call_site(),
17 "#[cxx::bridge] module must have inline contents",
18 ))?;
19 let apis = syntax::parse_items(content.1)?;
20 let ref types = Types::collect(&apis)?;
21 check::typecheck(&apis, types)?;
22
23 let mut expanded = TokenStream::new();
24 let mut hidden = TokenStream::new();
25 let mut has_rust_type = false;
26
Myron Ahneba35cf2020-02-05 19:41:51 +070027 // "Header" to define newtypes locally so we can implement
28 // traits on them.
29 expanded.extend(quote! {
30 pub struct Vector<T>(pub ::cxx::RealVector<T>);
31 impl<T: cxx::private::VectorTarget<T>> Vector<T> {
32 pub fn size(&self) -> usize {
33 self.0.size()
34 }
35 pub fn get(&self, pos: usize) -> Option<&T> {
36 self.0.get(pos)
37 }
38 pub fn get_unchecked(&self, pos: usize) -> &T {
39 self.0.get_unchecked(pos)
40 }
41 pub fn is_empty(&self) -> bool {
42 self.0.is_empty()
43 }
44 pub fn push_back(&mut self, item: &T) {
45 self.0.push_back(item)
46 }
47 }
48 impl<'a, T: cxx::private::VectorTarget<T>> IntoIterator for &'a Vector<T> {
49 type Item = &'a T;
50 type IntoIter = <&'a ::cxx::RealVector<T> as IntoIterator>::IntoIter;
51
52 fn into_iter(self) -> Self::IntoIter {
53 self.0.into_iter()
54 }
55 }
56 unsafe impl<T> Send for Vector<T> where T: Send + cxx::private::VectorTarget<T> {}
57 });
58
David Tolnay7db73692019-10-20 14:51:12 -040059 for api in &apis {
60 if let Api::RustType(ety) = api {
61 expanded.extend(expand_rust_type(ety));
62 if !has_rust_type {
David Tolnay199d7352020-01-20 18:40:10 -080063 hidden.extend(quote!(
64 const fn __assert_sized<T>() {}
65 ));
David Tolnay7db73692019-10-20 14:51:12 -040066 has_rust_type = true;
67 }
68 let ident = &ety.ident;
David Tolnay4b07ab92020-04-22 19:50:21 -070069 let span = ident.span();
70 hidden.extend(quote_spanned!(span=> __assert_sized::<#ident>();));
David Tolnay7db73692019-10-20 14:51:12 -040071 }
72 }
73
74 for api in &apis {
75 match api {
76 Api::Include(_) | Api::RustType(_) => {}
77 Api::Struct(strct) => expanded.extend(expand_struct(strct)),
78 Api::CxxType(ety) => expanded.extend(expand_cxx_type(ety)),
79 Api::CxxFunction(efn) => {
80 expanded.extend(expand_cxx_function_shim(namespace, efn, types));
81 }
82 Api::RustFunction(efn) => {
83 hidden.extend(expand_rust_function_shim(namespace, efn, types))
84 }
85 }
86 }
87
88 for ty in types {
89 if let Type::RustBox(ty) = ty {
90 if let Type::Ident(ident) = &ty.inner {
91 if Atom::from(ident).is_none() {
92 hidden.extend(expand_rust_box(namespace, ident));
93 }
94 }
Myron Ahneba35cf2020-02-05 19:41:51 +070095 } else if let Type::RustVec(ty) = ty {
96 if let Type::Ident(ident) = &ty.inner {
97 hidden.extend(expand_rust_vec(namespace, &ty.inner, ident));
98 }
David Tolnay7db73692019-10-20 14:51:12 -040099 } else if let Type::UniquePtr(ptr) = ty {
100 if let Type::Ident(ident) = &ptr.inner {
101 if Atom::from(ident).is_none() {
Myron Ahneba35cf2020-02-05 19:41:51 +0700102 expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types));
103 }
104 } else if let Type::Vector(_) = &ptr.inner {
105 // Generate code for unique_ptr<vector<T>> if T is not an atom
106 // or if T is a primitive.
107 // Code for primitives is already generated
108 match Atom::from(ident) {
109 None => expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)),
110 Some(atom) => {
111 if atom.is_valid_vector_target() {
112 expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types));
113 }
114 }
115 }
116 }
117 } else if let Type::Vector(ptr) = ty {
118 if let Type::Ident(ident) = &ptr.inner {
119 if Atom::from(ident).is_none() {
120 // Generate code for Vector<T> if T is not an atom
121 // Code for atoms is already generated
122 expanded.extend(expand_vector(namespace, &ptr.inner));
David Tolnay7db73692019-10-20 14:51:12 -0400123 }
124 }
125 }
126 }
127
128 // Work around https://github.com/rust-lang/rust/issues/67851.
129 if !hidden.is_empty() {
130 expanded.extend(quote! {
131 #[doc(hidden)]
132 const _: () = {
133 #hidden
134 };
135 });
136 }
137
138 let attrs = ffi
139 .attrs
140 .into_iter()
141 .filter(|attr| attr.path.is_ident("doc"));
142 let vis = &ffi.vis;
143
144 Ok(quote! {
145 #(#attrs)*
146 #[deny(improper_ctypes)]
147 #[allow(non_snake_case)]
148 #vis mod #ident {
149 #expanded
150 }
151 })
152}
153
154fn expand_struct(strct: &Struct) -> TokenStream {
155 let ident = &strct.ident;
156 let doc = &strct.doc;
157 let derives = &strct.derives;
158 let fields = strct.fields.iter().map(|field| {
159 // This span on the pub makes "private type in public interface" errors
160 // appear in the right place.
161 let vis = Token![pub](field.ident.span());
162 quote!(#vis #field)
163 });
164 quote! {
165 #doc
166 #[derive(#(#derives),*)]
167 #[repr(C)]
168 pub struct #ident {
169 #(#fields,)*
170 }
171 }
172}
173
174fn expand_cxx_type(ety: &ExternType) -> TokenStream {
175 let ident = &ety.ident;
176 let doc = &ety.doc;
177 quote! {
178 #doc
179 #[repr(C)]
180 pub struct #ident {
181 _private: ::cxx::private::Opaque,
182 }
183 }
184}
185
186fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
187 let ident = &efn.ident;
David Tolnay18ba92c2020-04-22 16:17:30 -0700188 let receiver = efn.receiver.iter().map(|receiver| {
189 let receiver_type = receiver.ty();
190 quote!(_: #receiver_type)
191 });
David Tolnay39d575f2020-03-03 00:10:56 -0800192 let args = efn.args.iter().map(|arg| {
193 let ident = &arg.ident;
194 let ty = expand_extern_type(&arg.ty);
David Tolnaya46a2372020-03-06 10:03:48 -0800195 if arg.ty == RustString {
196 quote!(#ident: *const #ty)
David Tolnay75dca2e2020-03-25 20:17:52 -0700197 } else if let Type::Fn(_) = arg.ty {
198 quote!(#ident: ::cxx::private::FatFunction)
David Tolnaya46a2372020-03-06 10:03:48 -0800199 } else if types.needs_indirect_abi(&arg.ty) {
David Tolnay39d575f2020-03-03 00:10:56 -0800200 quote!(#ident: *mut #ty)
201 } else {
202 quote!(#ident: #ty)
203 }
204 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700205 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700206 let ret = if efn.throws {
207 quote!(-> ::cxx::private::Result)
208 } else {
209 expand_extern_return_type(&efn.ret, types)
210 };
David Tolnay7db73692019-10-20 14:51:12 -0400211 let mut outparam = None;
David Tolnay1e548172020-03-16 13:37:09 -0700212 if indirect_return(efn, types) {
David Tolnay7db73692019-10-20 14:51:12 -0400213 let ret = expand_extern_type(efn.ret.as_ref().unwrap());
214 outparam = Some(quote!(__return: *mut #ret));
215 }
David Tolnay3caa50a2020-04-19 21:25:34 -0700216 let link_name = mangle::extern_fn(namespace, efn);
David Tolnay7db73692019-10-20 14:51:12 -0400217 let local_name = format_ident!("__{}", ident);
218 quote! {
219 #[link_name = #link_name]
Joel Galenson3d4f6122020-04-07 15:54:05 -0700220 fn #local_name(#(#all_args,)* #outparam) #ret;
David Tolnay7db73692019-10-20 14:51:12 -0400221 }
222}
223
224fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
225 let ident = &efn.ident;
226 let doc = &efn.doc;
227 let decl = expand_cxx_function_decl(namespace, efn, types);
David Tolnayfb6e3862020-04-20 01:33:23 -0700228 let receiver = efn.receiver.iter().map(|receiver| {
David Tolnayf9ffb932020-04-20 02:22:57 -0700229 let ampersand = receiver.ampersand;
David Tolnayfb6e3862020-04-20 01:33:23 -0700230 let mutability = receiver.mutability;
David Tolnayf9ffb932020-04-20 02:22:57 -0700231 let var = receiver.var;
232 quote!(#ampersand #mutability #var)
David Tolnayfb6e3862020-04-20 01:33:23 -0700233 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700234 let args = efn.args.iter().map(|arg| quote!(#arg));
235 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700236 let ret = if efn.throws {
237 let ok = match &efn.ret {
238 Some(ret) => quote!(#ret),
239 None => quote!(()),
240 };
241 quote!(-> ::std::result::Result<#ok, ::cxx::Exception>)
242 } else {
243 expand_return_type(&efn.ret)
244 };
David Tolnay1e548172020-03-16 13:37:09 -0700245 let indirect_return = indirect_return(efn, types);
David Tolnayf9ffb932020-04-20 02:22:57 -0700246 let receiver_var = efn
247 .receiver
248 .iter()
249 .map(|receiver| receiver.var.to_token_stream());
Joel Galenson3d4f6122020-04-07 15:54:05 -0700250 let arg_vars = efn.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400251 let var = &arg.ident;
252 match &arg.ty {
David Tolnaya52602b2020-03-06 10:24:34 -0800253 Type::Ident(ident) if ident == RustString => {
David Tolnaya46a2372020-03-06 10:03:48 -0800254 quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString)
David Tolnay7db73692019-10-20 14:51:12 -0400255 }
256 Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)),
257 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)),
Myron Ahneba35cf2020-02-05 19:41:51 +0700258 Type::RustVec(_) => quote!(::cxx::RustVec::from(#var)),
David Tolnay7db73692019-10-20 14:51:12 -0400259 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800260 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400261 quote!(::cxx::private::RustString::from_ref(#var))
262 }
Myron Ahneba35cf2020-02-05 19:41:51 +0700263 Type::RustVec(_) => quote!(::cxx::RustVec::from_ref(#var)),
David Tolnay7db73692019-10-20 14:51:12 -0400264 _ => quote!(#var),
265 },
266 Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700267 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)),
David Tolnay7db73692019-10-20 14:51:12 -0400268 ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()),
269 _ => quote!(#var),
270 }
271 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700272 let vars = receiver_var.chain(arg_vars);
David Tolnay75dca2e2020-03-25 20:17:52 -0700273 let trampolines = efn
274 .args
275 .iter()
276 .filter_map(|arg| {
277 if let Type::Fn(f) = &arg.ty {
278 let var = &arg.ident;
279 Some(expand_function_pointer_trampoline(
280 namespace, efn, var, f, types,
281 ))
282 } else {
283 None
284 }
285 })
286 .collect::<TokenStream>();
David Tolnay7db73692019-10-20 14:51:12 -0400287 let mut setup = efn
288 .args
289 .iter()
290 .filter(|arg| types.needs_indirect_abi(&arg.ty))
291 .map(|arg| {
292 let var = &arg.ident;
293 // These are arguments for which C++ has taken ownership of the data
294 // behind the mut reference it received.
295 quote! {
296 let mut #var = std::mem::MaybeUninit::new(#var);
297 }
298 })
299 .collect::<TokenStream>();
300 let local_name = format_ident!("__{}", ident);
301 let call = if indirect_return {
302 let ret = expand_extern_type(efn.ret.as_ref().unwrap());
303 setup.extend(quote! {
304 let mut __return = ::std::mem::MaybeUninit::<#ret>::uninit();
David Tolnay7db73692019-10-20 14:51:12 -0400305 });
David Tolnayebef4a22020-03-17 15:33:47 -0700306 if efn.throws {
307 setup.extend(quote! {
308 #local_name(#(#vars,)* __return.as_mut_ptr()).exception()?;
309 });
310 quote!(::std::result::Result::Ok(__return.assume_init()))
311 } else {
312 setup.extend(quote! {
313 #local_name(#(#vars,)* __return.as_mut_ptr());
314 });
315 quote!(__return.assume_init())
316 }
317 } else if efn.throws {
David Tolnay7db73692019-10-20 14:51:12 -0400318 quote! {
David Tolnayebef4a22020-03-17 15:33:47 -0700319 #local_name(#(#vars),*).exception()
David Tolnay7db73692019-10-20 14:51:12 -0400320 }
321 } else {
322 quote! {
323 #local_name(#(#vars),*)
324 }
325 };
Myron Ahn84849302020-03-25 22:00:58 +0700326 let expr = if efn.throws {
327 efn.ret.as_ref().and_then(|ret| match ret {
328 Type::Ident(ident) if ident == RustString => {
329 Some(quote!(#call.map(|r| r.into_string())))
330 }
331 Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))),
332 Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))),
333 Type::Ref(ty) => match &ty.inner {
334 Type::Ident(ident) if ident == RustString => {
335 Some(quote!(#call.map(|r| r.as_string())))
336 }
337 _ => None,
338 },
339 Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700340 Type::SliceRefU8(_) => Some(quote!(#call.map(|r| r.as_slice()))),
Myron Ahn84849302020-03-25 22:00:58 +0700341 _ => None,
342 })
343 } else {
344 efn.ret.as_ref().and_then(|ret| match ret {
David Tolnaya52602b2020-03-06 10:24:34 -0800345 Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())),
David Tolnay7db73692019-10-20 14:51:12 -0400346 Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))),
347 Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))),
348 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800349 Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())),
David Tolnay7db73692019-10-20 14:51:12 -0400350 _ => None,
351 },
352 Type::Str(_) => Some(quote!(#call.as_str())),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700353 Type::SliceRefU8(_) => Some(quote!(#call.as_slice())),
David Tolnay7db73692019-10-20 14:51:12 -0400354 _ => None,
355 })
Myron Ahn84849302020-03-25 22:00:58 +0700356 }
357 .unwrap_or(call);
David Tolnayc66cdbb2020-04-20 01:41:15 -0700358 let function_shim = quote! {
359 #doc
360 pub fn #ident(#(#all_args,)*) #ret {
361 extern "C" {
362 #decl
David Tolnay7db73692019-10-20 14:51:12 -0400363 }
David Tolnayc66cdbb2020-04-20 01:41:15 -0700364 #trampolines
365 unsafe {
366 #setup
367 #expr
David Tolnay7db73692019-10-20 14:51:12 -0400368 }
David Tolnayc66cdbb2020-04-20 01:41:15 -0700369 }
370 };
371 match &efn.receiver {
372 None => function_shim,
373 Some(receiver) => {
David Tolnay05e11cc2020-04-20 02:13:56 -0700374 let receiver_type = &receiver.ty;
David Tolnayc66cdbb2020-04-20 01:41:15 -0700375 quote!(impl #receiver_type { #function_shim })
376 }
David Tolnay7db73692019-10-20 14:51:12 -0400377 }
378}
379
David Tolnay75dca2e2020-03-25 20:17:52 -0700380fn expand_function_pointer_trampoline(
381 namespace: &Namespace,
382 efn: &ExternFn,
383 var: &Ident,
384 sig: &Signature,
385 types: &Types,
386) -> TokenStream {
David Tolnay891061b2020-04-19 22:42:33 -0700387 let c_trampoline = mangle::c_trampoline(namespace, efn, var);
388 let r_trampoline = mangle::r_trampoline(namespace, efn, var);
David Tolnay75dca2e2020-03-25 20:17:52 -0700389 let local_name = parse_quote!(__);
390 let catch_unwind_label = format!("::{}::{}", efn.ident, var);
391 let shim = expand_rust_function_shim_impl(
392 sig,
393 types,
394 &r_trampoline,
395 local_name,
396 catch_unwind_label,
397 None,
398 );
399
400 quote! {
401 let #var = ::cxx::private::FatFunction {
402 trampoline: {
403 extern "C" {
404 #[link_name = #c_trampoline]
405 fn trampoline();
406 }
407 #shim
408 trampoline as usize as *const ()
409 },
410 ptr: #var as usize as *const (),
411 };
412 }
413}
414
David Tolnay7db73692019-10-20 14:51:12 -0400415fn expand_rust_type(ety: &ExternType) -> TokenStream {
416 let ident = &ety.ident;
417 quote! {
418 use super::#ident;
419 }
420}
421
422fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
423 let ident = &efn.ident;
David Tolnay3caa50a2020-04-19 21:25:34 -0700424 let link_name = mangle::extern_fn(namespace, efn);
David Tolnay75dca2e2020-03-25 20:17:52 -0700425 let local_name = format_ident!("__{}", ident);
426 let catch_unwind_label = format!("::{}", ident);
427 let invoke = Some(ident);
428 expand_rust_function_shim_impl(
429 efn,
430 types,
431 &link_name,
432 local_name,
433 catch_unwind_label,
434 invoke,
435 )
436}
437
438fn expand_rust_function_shim_impl(
439 sig: &Signature,
440 types: &Types,
David Tolnay891061b2020-04-19 22:42:33 -0700441 link_name: &Symbol,
David Tolnay75dca2e2020-03-25 20:17:52 -0700442 local_name: Ident,
443 catch_unwind_label: String,
444 invoke: Option<&Ident>,
445) -> TokenStream {
David Tolnayf9ffb932020-04-20 02:22:57 -0700446 let receiver_var = sig
447 .receiver
448 .as_ref()
449 .map(|receiver| quote_spanned!(receiver.var.span=> __self));
David Tolnay18ba92c2020-04-22 16:17:30 -0700450 let receiver = sig.receiver.as_ref().map(|receiver| {
451 let receiver_type = receiver.ty();
452 quote!(#receiver_var: #receiver_type)
453 });
David Tolnay75dca2e2020-03-25 20:17:52 -0700454 let args = sig.args.iter().map(|arg| {
David Tolnay39d575f2020-03-03 00:10:56 -0800455 let ident = &arg.ident;
456 let ty = expand_extern_type(&arg.ty);
457 if types.needs_indirect_abi(&arg.ty) {
458 quote!(#ident: *mut #ty)
459 } else {
460 quote!(#ident: #ty)
461 }
462 });
David Tolnayf9ffb932020-04-20 02:22:57 -0700463 let all_args = receiver.into_iter().chain(args);
David Tolnay75dca2e2020-03-25 20:17:52 -0700464
David Tolnay3a45f2d2020-04-20 01:51:12 -0700465 let arg_vars = sig.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400466 let ident = &arg.ident;
David Tolnay17955e22020-01-20 17:58:24 -0800467 match &arg.ty {
David Tolnaycc3767f2020-03-06 10:41:51 -0800468 Type::Ident(i) if i == RustString => {
469 quote!(::std::mem::take((*#ident).as_mut_string()))
470 }
David Tolnay40226ab2020-03-03 00:05:35 -0800471 Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)),
472 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)),
David Tolnay17955e22020-01-20 17:58:24 -0800473 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800474 Type::Ident(i) if i == RustString => quote!(#ident.as_string()),
David Tolnay40226ab2020-03-03 00:05:35 -0800475 _ => quote!(#ident),
David Tolnay17955e22020-01-20 17:58:24 -0800476 },
David Tolnay40226ab2020-03-03 00:05:35 -0800477 Type::Str(_) => quote!(#ident.as_str()),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700478 Type::SliceRefU8(_) => quote!(#ident.as_slice()),
David Tolnay40226ab2020-03-03 00:05:35 -0800479 ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
480 _ => quote!(#ident),
David Tolnay7db73692019-10-20 14:51:12 -0400481 }
482 });
David Tolnayf9ffb932020-04-20 02:22:57 -0700483 let vars = receiver_var.into_iter().chain(arg_vars);
David Tolnay75dca2e2020-03-25 20:17:52 -0700484
485 let mut call = match invoke {
David Tolnay3a45f2d2020-04-20 01:51:12 -0700486 Some(ident) => match &sig.receiver {
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700487 None => quote!(super::#ident),
David Tolnay3a45f2d2020-04-20 01:51:12 -0700488 Some(receiver) => {
David Tolnay05e11cc2020-04-20 02:13:56 -0700489 let receiver_type = &receiver.ty;
David Tolnay3a45f2d2020-04-20 01:51:12 -0700490 quote!(#receiver_type::#ident)
491 }
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700492 },
David Tolnay75dca2e2020-03-25 20:17:52 -0700493 None => quote!(__extern),
494 };
495 call.extend(quote! { (#(#vars),*) });
496
497 let mut expr = sig
David Tolnay7db73692019-10-20 14:51:12 -0400498 .ret
499 .as_ref()
500 .and_then(|ret| match ret {
David Tolnaya52602b2020-03-06 10:24:34 -0800501 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400502 Some(quote!(::cxx::private::RustString::from(#call)))
503 }
504 Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))),
505 Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))),
506 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800507 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400508 Some(quote!(::cxx::private::RustString::from_ref(#call)))
509 }
510 _ => None,
511 },
512 Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700513 Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))),
David Tolnay7db73692019-10-20 14:51:12 -0400514 _ => None,
515 })
516 .unwrap_or(call);
David Tolnay75dca2e2020-03-25 20:17:52 -0700517
518 let mut outparam = None;
519 let indirect_return = indirect_return(sig, types);
David Tolnay1e548172020-03-16 13:37:09 -0700520 if indirect_return {
David Tolnay75dca2e2020-03-25 20:17:52 -0700521 let ret = expand_extern_type(sig.ret.as_ref().unwrap());
522 outparam = Some(quote!(__return: *mut #ret,));
David Tolnay1e548172020-03-16 13:37:09 -0700523 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700524 if sig.throws {
525 let out = match sig.ret {
David Tolnaycecada62020-03-17 01:45:58 -0700526 Some(_) => quote!(__return),
527 None => quote!(&mut ()),
528 };
529 expr = quote!(::cxx::private::r#try(#out, #expr));
David Tolnay1e548172020-03-16 13:37:09 -0700530 } else if indirect_return {
David Tolnay7db73692019-10-20 14:51:12 -0400531 expr = quote!(::std::ptr::write(__return, #expr));
532 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700533
David Tolnay1e548172020-03-16 13:37:09 -0700534 expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr));
David Tolnay75dca2e2020-03-25 20:17:52 -0700535
536 let ret = if sig.throws {
David Tolnay486b6ec2020-03-17 01:19:57 -0700537 quote!(-> ::cxx::private::Result)
David Tolnay1e548172020-03-16 13:37:09 -0700538 } else {
David Tolnay75dca2e2020-03-25 20:17:52 -0700539 expand_extern_return_type(&sig.ret, types)
David Tolnay1e548172020-03-16 13:37:09 -0700540 };
David Tolnay75dca2e2020-03-25 20:17:52 -0700541
542 let pointer = match invoke {
543 None => Some(quote!(__extern: #sig)),
544 Some(_) => None,
545 };
546
David Tolnay7db73692019-10-20 14:51:12 -0400547 quote! {
548 #[doc(hidden)]
549 #[export_name = #link_name]
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700550 unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
David Tolnay7db73692019-10-20 14:51:12 -0400551 let __fn = concat!(module_path!(), #catch_unwind_label);
552 #expr
553 }
554 }
555}
556
557fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream {
David Tolnay8c730492020-03-13 01:29:06 -0700558 let link_prefix = format!("cxxbridge02$box${}{}$", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400559 let link_uninit = format!("{}uninit", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400560 let link_drop = format!("{}drop", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400561
562 let local_prefix = format_ident!("{}__box_", ident);
563 let local_uninit = format_ident!("{}uninit", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400564 let local_drop = format_ident!("{}drop", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400565
566 let span = ident.span();
567 quote_spanned! {span=>
568 #[doc(hidden)]
569 #[export_name = #link_uninit]
570 unsafe extern "C" fn #local_uninit(
571 this: *mut ::std::boxed::Box<::std::mem::MaybeUninit<#ident>>,
572 ) {
573 ::std::ptr::write(
574 this,
575 ::std::boxed::Box::new(::std::mem::MaybeUninit::uninit()),
576 );
577 }
578 #[doc(hidden)]
David Tolnay7db73692019-10-20 14:51:12 -0400579 #[export_name = #link_drop]
580 unsafe extern "C" fn #local_drop(this: *mut ::std::boxed::Box<#ident>) {
581 ::std::ptr::drop_in_place(this);
582 }
David Tolnay7db73692019-10-20 14:51:12 -0400583 }
584}
585
Myron Ahneba35cf2020-02-05 19:41:51 +0700586fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStream {
587 let inner = ty;
588 let mangled = ty.to_mangled(&namespace.segments) + "$";
589 let link_prefix = format!("cxxbridge02$rust_vec${}", mangled);
590 let link_drop = format!("{}drop", link_prefix);
591 let link_vector_from = format!("{}vector_from", link_prefix);
592 let link_len = format!("{}len", link_prefix);
593
594 let local_prefix = format_ident!("{}__vec_", ident);
595 let local_drop = format_ident!("{}drop", local_prefix);
596 let local_vector_from = format_ident!("{}vector_from", local_prefix);
597 let local_len = format_ident!("{}len", local_prefix);
598
599 let span = ty.span();
600 quote_spanned! {span=>
601 #[doc(hidden)]
602 #[export_name = #link_drop]
603 unsafe extern "C" fn #local_drop(this: *mut ::cxx::RustVec<#inner>) {
604 std::ptr::drop_in_place(this);
605 }
606 #[export_name = #link_vector_from]
607 unsafe extern "C" fn #local_vector_from(this: *mut ::cxx::RustVec<#inner>, vector: *mut ::cxx::RealVector<#inner>) {
608 this.as_ref().unwrap().into_vector(vector.as_mut().unwrap());
609 }
610 #[export_name = #link_len]
611 unsafe extern "C" fn #local_len(this: *const ::cxx::RustVec<#inner>) -> usize {
612 this.as_ref().unwrap().len()
613 }
614 }
615}
616
617fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenStream {
618 let name = ty.to_typename(&namespace.segments);
619 let inner = ty;
620 let mangled = ty.to_mangled(&namespace.segments) + "$";
621 let prefix = format!("cxxbridge02$unique_ptr${}", mangled);
David Tolnay7db73692019-10-20 14:51:12 -0400622 let link_null = format!("{}null", prefix);
623 let link_new = format!("{}new", prefix);
624 let link_raw = format!("{}raw", prefix);
625 let link_get = format!("{}get", prefix);
626 let link_release = format!("{}release", prefix);
627 let link_drop = format!("{}drop", prefix);
628
Myron Ahneba35cf2020-02-05 19:41:51 +0700629 let new_method = match ty {
630 Type::Ident(ident) if types.structs.contains_key(ident) => Some(quote! {
David Tolnay53838912020-04-09 20:56:44 -0700631 fn __new(mut value: Self) -> *mut ::std::ffi::c_void {
632 extern "C" {
633 #[link_name = #link_new]
634 fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident);
635 }
636 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
637 unsafe { __new(&mut repr, &mut value) }
638 repr
639 }
Myron Ahneba35cf2020-02-05 19:41:51 +0700640 }),
641 _ => None,
David Tolnay53838912020-04-09 20:56:44 -0700642 };
643
David Tolnay7db73692019-10-20 14:51:12 -0400644 quote! {
Myron Ahneba35cf2020-02-05 19:41:51 +0700645 unsafe impl ::cxx::private::UniquePtrTarget for #inner {
David Tolnayad266772020-04-09 23:42:29 -0700646 const __NAME: &'static str = #name;
David Tolnay7db73692019-10-20 14:51:12 -0400647 fn __null() -> *mut ::std::ffi::c_void {
648 extern "C" {
649 #[link_name = #link_null]
650 fn __null(this: *mut *mut ::std::ffi::c_void);
651 }
652 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
653 unsafe { __null(&mut repr) }
654 repr
655 }
David Tolnay53838912020-04-09 20:56:44 -0700656 #new_method
David Tolnay7db73692019-10-20 14:51:12 -0400657 unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void {
658 extern "C" {
659 #[link_name = #link_raw]
Myron Ahneba35cf2020-02-05 19:41:51 +0700660 fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #inner);
David Tolnay7db73692019-10-20 14:51:12 -0400661 }
662 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
663 __raw(&mut repr, raw);
664 repr
665 }
666 unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self {
667 extern "C" {
668 #[link_name = #link_get]
Myron Ahneba35cf2020-02-05 19:41:51 +0700669 fn __get(this: *const *mut ::std::ffi::c_void) -> *const #inner;
David Tolnay7db73692019-10-20 14:51:12 -0400670 }
671 __get(&repr)
672 }
673 unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self {
674 extern "C" {
675 #[link_name = #link_release]
Myron Ahneba35cf2020-02-05 19:41:51 +0700676 fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #inner;
David Tolnay7db73692019-10-20 14:51:12 -0400677 }
678 __release(&mut repr)
679 }
680 unsafe fn __drop(mut repr: *mut ::std::ffi::c_void) {
681 extern "C" {
682 #[link_name = #link_drop]
683 fn __drop(this: *mut *mut ::std::ffi::c_void);
684 }
685 __drop(&mut repr);
686 }
687 }
688 }
689}
690
Myron Ahneba35cf2020-02-05 19:41:51 +0700691fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream {
692 let inner = ty;
693 let mangled = ty.to_mangled(&namespace.segments) + "$";
694 let prefix = format!("cxxbridge02$std$vector${}", mangled);
695 let link_length = format!("{}length", prefix);
696 let link_get_unchecked = format!("{}get_unchecked", prefix);
697 let link_push_back = format!("{}push_back", prefix);
698
699 quote! {
700 impl ::cxx::private::VectorTarget<#inner> for #inner {
701 fn get_unchecked(v: &::cxx::RealVector<#inner>, pos: usize) -> &#inner {
702 extern "C" {
703 #[link_name = #link_get_unchecked]
704 fn __get_unchecked(_: &::cxx::RealVector<#inner>, _: usize) -> &#inner;
705 }
706 unsafe {
707 __get_unchecked(v, pos)
708 }
709 }
710 fn vector_length(v: &::cxx::RealVector<#inner>) -> usize {
711 unsafe {
712 extern "C" {
713 #[link_name = #link_length]
714 fn __vector_length(_: &::cxx::RealVector<#inner>) -> usize;
715 }
716 __vector_length(v)
717 }
718 }
719 fn push_back(v: &::cxx::RealVector<#inner>, item: &#inner) {
720 unsafe {
721 extern "C" {
722 #[link_name = #link_push_back]
723 fn __push_back(_: &::cxx::RealVector<#inner>, _: &#inner) -> usize;
724 }
725 __push_back(v, item);
726 }
727 }
728 }
729 }
730}
731
732pub fn expand_vector_builtin(ident: Ident) -> TokenStream {
733 let ty = Type::Ident(ident);
734 let inner = &ty;
735 let namespace = Namespace { segments: vec![] };
736 let mangled = ty.to_mangled(&namespace.segments) + "$";
737 let prefix = format!("cxxbridge02$std$vector${}", mangled);
738 let link_length = format!("{}length", prefix);
739 let link_get_unchecked = format!("{}get_unchecked", prefix);
740 let link_push_back = format!("{}push_back", prefix);
741
742 quote! {
743 impl VectorTarget<#inner> for #inner {
744 fn get_unchecked(v: &RealVector<#inner>, pos: usize) -> &#inner {
745 extern "C" {
746 #[link_name = #link_get_unchecked]
747 fn __get_unchecked(_: &RealVector<#inner>, _: usize) -> &#inner;
748 }
749 unsafe {
750 __get_unchecked(v, pos)
751 }
752 }
753 fn vector_length(v: &RealVector<#inner>) -> usize {
754 unsafe {
755 extern "C" {
756 #[link_name = #link_length]
757 fn __vector_length(_: &RealVector<#inner>) -> usize;
758 }
759 __vector_length(v)
760 }
761 }
762 fn push_back(v: &RealVector<#inner>, item: &#inner) {
763 unsafe {
764 extern "C" {
765 #[link_name = #link_push_back]
766 fn __push_back(_: &RealVector<#inner>, _: &#inner) -> usize;
767 }
768 __push_back(v, item);
769 }
770 }
771 }
772 }
773}
774
David Tolnay7db73692019-10-20 14:51:12 -0400775fn expand_return_type(ret: &Option<Type>) -> TokenStream {
776 match ret {
777 Some(ret) => quote!(-> #ret),
778 None => TokenStream::new(),
779 }
780}
781
David Tolnay75dca2e2020-03-25 20:17:52 -0700782fn indirect_return(sig: &Signature, types: &Types) -> bool {
783 sig.ret
David Tolnay1e548172020-03-16 13:37:09 -0700784 .as_ref()
David Tolnay75dca2e2020-03-25 20:17:52 -0700785 .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret))
David Tolnay7db73692019-10-20 14:51:12 -0400786}
787
788fn expand_extern_type(ty: &Type) -> TokenStream {
789 match ty {
David Tolnaya52602b2020-03-06 10:24:34 -0800790 Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString),
David Tolnay7db73692019-10-20 14:51:12 -0400791 Type::RustBox(ty) | Type::UniquePtr(ty) => {
Myron Ahneba35cf2020-02-05 19:41:51 +0700792 let inner = expand_extern_type(&ty.inner);
David Tolnay7db73692019-10-20 14:51:12 -0400793 quote!(*mut #inner)
794 }
Myron Ahneba35cf2020-02-05 19:41:51 +0700795 Type::RustVec(ty) => quote!(::cxx::RustVec<#ty>),
David Tolnay7db73692019-10-20 14:51:12 -0400796 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800797 Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString),
Myron Ahneba35cf2020-02-05 19:41:51 +0700798 Type::RustVec(ty) => {
799 let inner = expand_extern_type(&ty.inner);
800 quote!(&::cxx::RustVec<#inner>)
801 }
David Tolnay7db73692019-10-20 14:51:12 -0400802 _ => quote!(#ty),
803 },
804 Type::Str(_) => quote!(::cxx::private::RustStr),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700805 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
David Tolnay7db73692019-10-20 14:51:12 -0400806 _ => quote!(#ty),
807 }
808}
809
810fn expand_extern_return_type(ret: &Option<Type>, types: &Types) -> TokenStream {
811 let ret = match ret {
812 Some(ret) if !types.needs_indirect_abi(ret) => ret,
813 _ => return TokenStream::new(),
814 };
815 let ty = expand_extern_type(ret);
816 quote!(-> #ty)
817}