blob: 782da0505dda07057f5c8ae982693862fd33f147 [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();
David Tolnay7db73692019-10-20 14:51:12 -040025
Myron Ahneba35cf2020-02-05 19:41:51 +070026 // "Header" to define newtypes locally so we can implement
27 // traits on them.
28 expanded.extend(quote! {
David Tolnaye90be1d2020-04-24 11:45:57 -070029 pub struct Vector<T>(pub ::cxx::CxxVector<T>);
Myron Ahneba35cf2020-02-05 19:41:51 +070030 impl<T: cxx::private::VectorTarget<T>> Vector<T> {
31 pub fn size(&self) -> usize {
32 self.0.size()
33 }
34 pub fn get(&self, pos: usize) -> Option<&T> {
35 self.0.get(pos)
36 }
37 pub fn get_unchecked(&self, pos: usize) -> &T {
38 self.0.get_unchecked(pos)
39 }
40 pub fn is_empty(&self) -> bool {
41 self.0.is_empty()
42 }
43 pub fn push_back(&mut self, item: &T) {
44 self.0.push_back(item)
45 }
46 }
47 impl<'a, T: cxx::private::VectorTarget<T>> IntoIterator for &'a Vector<T> {
48 type Item = &'a T;
David Tolnaye90be1d2020-04-24 11:45:57 -070049 type IntoIter = <&'a ::cxx::CxxVector<T> as IntoIterator>::IntoIter;
Myron Ahneba35cf2020-02-05 19:41:51 +070050
51 fn into_iter(self) -> Self::IntoIter {
52 self.0.into_iter()
53 }
54 }
55 unsafe impl<T> Send for Vector<T> where T: Send + cxx::private::VectorTarget<T> {}
56 });
57
David Tolnay7db73692019-10-20 14:51:12 -040058 for api in &apis {
59 if let Api::RustType(ety) = api {
60 expanded.extend(expand_rust_type(ety));
David Tolnay7db73692019-10-20 14:51:12 -040061 let ident = &ety.ident;
David Tolnay4b07ab92020-04-22 19:50:21 -070062 let span = ident.span();
David Tolnay1044d442020-04-22 19:58:30 -070063 hidden.extend(quote_spanned! {span=>
64 let _ = ::std::ptr::read::<#ident>;
65 });
David Tolnay7db73692019-10-20 14:51:12 -040066 }
67 }
68
69 for api in &apis {
70 match api {
71 Api::Include(_) | Api::RustType(_) => {}
72 Api::Struct(strct) => expanded.extend(expand_struct(strct)),
73 Api::CxxType(ety) => expanded.extend(expand_cxx_type(ety)),
74 Api::CxxFunction(efn) => {
75 expanded.extend(expand_cxx_function_shim(namespace, efn, types));
76 }
77 Api::RustFunction(efn) => {
78 hidden.extend(expand_rust_function_shim(namespace, efn, types))
79 }
80 }
81 }
82
83 for ty in types {
84 if let Type::RustBox(ty) = ty {
85 if let Type::Ident(ident) = &ty.inner {
86 if Atom::from(ident).is_none() {
87 hidden.extend(expand_rust_box(namespace, ident));
88 }
89 }
Myron Ahneba35cf2020-02-05 19:41:51 +070090 } else if let Type::RustVec(ty) = ty {
91 if let Type::Ident(ident) = &ty.inner {
92 hidden.extend(expand_rust_vec(namespace, &ty.inner, ident));
93 }
David Tolnay7db73692019-10-20 14:51:12 -040094 } else if let Type::UniquePtr(ptr) = ty {
95 if let Type::Ident(ident) = &ptr.inner {
96 if Atom::from(ident).is_none() {
Myron Ahneba35cf2020-02-05 19:41:51 +070097 expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types));
98 }
99 } else if let Type::Vector(_) = &ptr.inner {
100 // Generate code for unique_ptr<vector<T>> if T is not an atom
101 // or if T is a primitive.
102 // Code for primitives is already generated
103 match Atom::from(ident) {
104 None => expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types)),
105 Some(atom) => {
106 if atom.is_valid_vector_target() {
107 expanded.extend(expand_unique_ptr(namespace, &ptr.inner, types));
108 }
109 }
110 }
111 }
112 } else if let Type::Vector(ptr) = ty {
113 if let Type::Ident(ident) = &ptr.inner {
114 if Atom::from(ident).is_none() {
David Tolnaye90be1d2020-04-24 11:45:57 -0700115 // Generate code for CxxVector<T> if T is not an atom
Myron Ahneba35cf2020-02-05 19:41:51 +0700116 // Code for atoms is already generated
117 expanded.extend(expand_vector(namespace, &ptr.inner));
David Tolnay7db73692019-10-20 14:51:12 -0400118 }
119 }
120 }
121 }
122
123 // Work around https://github.com/rust-lang/rust/issues/67851.
124 if !hidden.is_empty() {
125 expanded.extend(quote! {
126 #[doc(hidden)]
127 const _: () = {
128 #hidden
129 };
130 });
131 }
132
133 let attrs = ffi
134 .attrs
135 .into_iter()
136 .filter(|attr| attr.path.is_ident("doc"));
137 let vis = &ffi.vis;
138
139 Ok(quote! {
140 #(#attrs)*
141 #[deny(improper_ctypes)]
142 #[allow(non_snake_case)]
143 #vis mod #ident {
144 #expanded
145 }
146 })
147}
148
149fn expand_struct(strct: &Struct) -> TokenStream {
150 let ident = &strct.ident;
151 let doc = &strct.doc;
152 let derives = &strct.derives;
153 let fields = strct.fields.iter().map(|field| {
154 // This span on the pub makes "private type in public interface" errors
155 // appear in the right place.
156 let vis = Token![pub](field.ident.span());
157 quote!(#vis #field)
158 });
159 quote! {
160 #doc
161 #[derive(#(#derives),*)]
162 #[repr(C)]
163 pub struct #ident {
164 #(#fields,)*
165 }
166 }
167}
168
169fn expand_cxx_type(ety: &ExternType) -> TokenStream {
170 let ident = &ety.ident;
171 let doc = &ety.doc;
172 quote! {
173 #doc
174 #[repr(C)]
175 pub struct #ident {
176 _private: ::cxx::private::Opaque,
177 }
178 }
179}
180
181fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
182 let ident = &efn.ident;
David Tolnay18ba92c2020-04-22 16:17:30 -0700183 let receiver = efn.receiver.iter().map(|receiver| {
184 let receiver_type = receiver.ty();
185 quote!(_: #receiver_type)
186 });
David Tolnay39d575f2020-03-03 00:10:56 -0800187 let args = efn.args.iter().map(|arg| {
188 let ident = &arg.ident;
189 let ty = expand_extern_type(&arg.ty);
David Tolnaya46a2372020-03-06 10:03:48 -0800190 if arg.ty == RustString {
191 quote!(#ident: *const #ty)
David Tolnay75dca2e2020-03-25 20:17:52 -0700192 } else if let Type::Fn(_) = arg.ty {
193 quote!(#ident: ::cxx::private::FatFunction)
David Tolnaya46a2372020-03-06 10:03:48 -0800194 } else if types.needs_indirect_abi(&arg.ty) {
David Tolnay39d575f2020-03-03 00:10:56 -0800195 quote!(#ident: *mut #ty)
196 } else {
197 quote!(#ident: #ty)
198 }
199 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700200 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700201 let ret = if efn.throws {
202 quote!(-> ::cxx::private::Result)
203 } else {
204 expand_extern_return_type(&efn.ret, types)
205 };
David Tolnay7db73692019-10-20 14:51:12 -0400206 let mut outparam = None;
David Tolnay1e548172020-03-16 13:37:09 -0700207 if indirect_return(efn, types) {
David Tolnay7db73692019-10-20 14:51:12 -0400208 let ret = expand_extern_type(efn.ret.as_ref().unwrap());
209 outparam = Some(quote!(__return: *mut #ret));
210 }
David Tolnay3caa50a2020-04-19 21:25:34 -0700211 let link_name = mangle::extern_fn(namespace, efn);
David Tolnay7db73692019-10-20 14:51:12 -0400212 let local_name = format_ident!("__{}", ident);
213 quote! {
214 #[link_name = #link_name]
Joel Galenson3d4f6122020-04-07 15:54:05 -0700215 fn #local_name(#(#all_args,)* #outparam) #ret;
David Tolnay7db73692019-10-20 14:51:12 -0400216 }
217}
218
219fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
220 let ident = &efn.ident;
221 let doc = &efn.doc;
222 let decl = expand_cxx_function_decl(namespace, efn, types);
David Tolnayfb6e3862020-04-20 01:33:23 -0700223 let receiver = efn.receiver.iter().map(|receiver| {
David Tolnayf9ffb932020-04-20 02:22:57 -0700224 let ampersand = receiver.ampersand;
David Tolnayfb6e3862020-04-20 01:33:23 -0700225 let mutability = receiver.mutability;
David Tolnayf9ffb932020-04-20 02:22:57 -0700226 let var = receiver.var;
227 quote!(#ampersand #mutability #var)
David Tolnayfb6e3862020-04-20 01:33:23 -0700228 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700229 let args = efn.args.iter().map(|arg| quote!(#arg));
230 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700231 let ret = if efn.throws {
232 let ok = match &efn.ret {
233 Some(ret) => quote!(#ret),
234 None => quote!(()),
235 };
236 quote!(-> ::std::result::Result<#ok, ::cxx::Exception>)
237 } else {
238 expand_return_type(&efn.ret)
239 };
David Tolnay1e548172020-03-16 13:37:09 -0700240 let indirect_return = indirect_return(efn, types);
David Tolnayf9ffb932020-04-20 02:22:57 -0700241 let receiver_var = efn
242 .receiver
243 .iter()
244 .map(|receiver| receiver.var.to_token_stream());
Joel Galenson3d4f6122020-04-07 15:54:05 -0700245 let arg_vars = efn.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400246 let var = &arg.ident;
247 match &arg.ty {
David Tolnaya52602b2020-03-06 10:24:34 -0800248 Type::Ident(ident) if ident == RustString => {
David Tolnaya46a2372020-03-06 10:03:48 -0800249 quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString)
David Tolnay7db73692019-10-20 14:51:12 -0400250 }
251 Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)),
252 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::into_raw(#var)),
David Tolnay6c6b7e02020-04-24 11:42:59 -0700253 Type::RustVec(_) => quote!(::cxx::private::RustVec::from(#var)),
David Tolnay7db73692019-10-20 14:51:12 -0400254 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800255 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400256 quote!(::cxx::private::RustString::from_ref(#var))
257 }
David Tolnay6c6b7e02020-04-24 11:42:59 -0700258 Type::RustVec(_) => quote!(::cxx::private::RustVec::from_ref(#var)),
David Tolnay7db73692019-10-20 14:51:12 -0400259 _ => quote!(#var),
260 },
261 Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700262 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)),
David Tolnay7db73692019-10-20 14:51:12 -0400263 ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()),
264 _ => quote!(#var),
265 }
266 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700267 let vars = receiver_var.chain(arg_vars);
David Tolnay75dca2e2020-03-25 20:17:52 -0700268 let trampolines = efn
269 .args
270 .iter()
271 .filter_map(|arg| {
272 if let Type::Fn(f) = &arg.ty {
273 let var = &arg.ident;
274 Some(expand_function_pointer_trampoline(
275 namespace, efn, var, f, types,
276 ))
277 } else {
278 None
279 }
280 })
281 .collect::<TokenStream>();
David Tolnay7db73692019-10-20 14:51:12 -0400282 let mut setup = efn
283 .args
284 .iter()
285 .filter(|arg| types.needs_indirect_abi(&arg.ty))
286 .map(|arg| {
287 let var = &arg.ident;
288 // These are arguments for which C++ has taken ownership of the data
289 // behind the mut reference it received.
290 quote! {
David Tolnaycb4ee4b2020-04-24 11:28:08 -0700291 let mut #var = ::std::mem::MaybeUninit::new(#var);
David Tolnay7db73692019-10-20 14:51:12 -0400292 }
293 })
294 .collect::<TokenStream>();
295 let local_name = format_ident!("__{}", ident);
296 let call = if indirect_return {
297 let ret = expand_extern_type(efn.ret.as_ref().unwrap());
298 setup.extend(quote! {
299 let mut __return = ::std::mem::MaybeUninit::<#ret>::uninit();
David Tolnay7db73692019-10-20 14:51:12 -0400300 });
David Tolnayebef4a22020-03-17 15:33:47 -0700301 if efn.throws {
302 setup.extend(quote! {
303 #local_name(#(#vars,)* __return.as_mut_ptr()).exception()?;
304 });
305 quote!(::std::result::Result::Ok(__return.assume_init()))
306 } else {
307 setup.extend(quote! {
308 #local_name(#(#vars,)* __return.as_mut_ptr());
309 });
310 quote!(__return.assume_init())
311 }
312 } else if efn.throws {
David Tolnay7db73692019-10-20 14:51:12 -0400313 quote! {
David Tolnayebef4a22020-03-17 15:33:47 -0700314 #local_name(#(#vars),*).exception()
David Tolnay7db73692019-10-20 14:51:12 -0400315 }
316 } else {
317 quote! {
318 #local_name(#(#vars),*)
319 }
320 };
Myron Ahn84849302020-03-25 22:00:58 +0700321 let expr = if efn.throws {
322 efn.ret.as_ref().and_then(|ret| match ret {
323 Type::Ident(ident) if ident == RustString => {
324 Some(quote!(#call.map(|r| r.into_string())))
325 }
326 Type::RustBox(_) => Some(quote!(#call.map(|r| ::std::boxed::Box::from_raw(r)))),
327 Type::UniquePtr(_) => Some(quote!(#call.map(|r| ::cxx::UniquePtr::from_raw(r)))),
328 Type::Ref(ty) => match &ty.inner {
329 Type::Ident(ident) if ident == RustString => {
330 Some(quote!(#call.map(|r| r.as_string())))
331 }
332 _ => None,
333 },
334 Type::Str(_) => Some(quote!(#call.map(|r| r.as_str()))),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700335 Type::SliceRefU8(_) => Some(quote!(#call.map(|r| r.as_slice()))),
Myron Ahn84849302020-03-25 22:00:58 +0700336 _ => None,
337 })
338 } else {
339 efn.ret.as_ref().and_then(|ret| match ret {
David Tolnaya52602b2020-03-06 10:24:34 -0800340 Type::Ident(ident) if ident == RustString => Some(quote!(#call.into_string())),
David Tolnay7db73692019-10-20 14:51:12 -0400341 Type::RustBox(_) => Some(quote!(::std::boxed::Box::from_raw(#call))),
342 Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::from_raw(#call))),
343 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800344 Type::Ident(ident) if ident == RustString => Some(quote!(#call.as_string())),
David Tolnay7db73692019-10-20 14:51:12 -0400345 _ => None,
346 },
347 Type::Str(_) => Some(quote!(#call.as_str())),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700348 Type::SliceRefU8(_) => Some(quote!(#call.as_slice())),
David Tolnay7db73692019-10-20 14:51:12 -0400349 _ => None,
350 })
Myron Ahn84849302020-03-25 22:00:58 +0700351 }
352 .unwrap_or(call);
David Tolnayc66cdbb2020-04-20 01:41:15 -0700353 let function_shim = quote! {
354 #doc
355 pub fn #ident(#(#all_args,)*) #ret {
356 extern "C" {
357 #decl
David Tolnay7db73692019-10-20 14:51:12 -0400358 }
David Tolnayc66cdbb2020-04-20 01:41:15 -0700359 #trampolines
360 unsafe {
361 #setup
362 #expr
David Tolnay7db73692019-10-20 14:51:12 -0400363 }
David Tolnayc66cdbb2020-04-20 01:41:15 -0700364 }
365 };
366 match &efn.receiver {
367 None => function_shim,
368 Some(receiver) => {
David Tolnay05e11cc2020-04-20 02:13:56 -0700369 let receiver_type = &receiver.ty;
David Tolnayc66cdbb2020-04-20 01:41:15 -0700370 quote!(impl #receiver_type { #function_shim })
371 }
David Tolnay7db73692019-10-20 14:51:12 -0400372 }
373}
374
David Tolnay75dca2e2020-03-25 20:17:52 -0700375fn expand_function_pointer_trampoline(
376 namespace: &Namespace,
377 efn: &ExternFn,
378 var: &Ident,
379 sig: &Signature,
380 types: &Types,
381) -> TokenStream {
David Tolnay891061b2020-04-19 22:42:33 -0700382 let c_trampoline = mangle::c_trampoline(namespace, efn, var);
383 let r_trampoline = mangle::r_trampoline(namespace, efn, var);
David Tolnay75dca2e2020-03-25 20:17:52 -0700384 let local_name = parse_quote!(__);
385 let catch_unwind_label = format!("::{}::{}", efn.ident, var);
386 let shim = expand_rust_function_shim_impl(
387 sig,
388 types,
389 &r_trampoline,
390 local_name,
391 catch_unwind_label,
392 None,
393 );
394
395 quote! {
396 let #var = ::cxx::private::FatFunction {
397 trampoline: {
398 extern "C" {
399 #[link_name = #c_trampoline]
400 fn trampoline();
401 }
402 #shim
403 trampoline as usize as *const ()
404 },
405 ptr: #var as usize as *const (),
406 };
407 }
408}
409
David Tolnay7db73692019-10-20 14:51:12 -0400410fn expand_rust_type(ety: &ExternType) -> TokenStream {
411 let ident = &ety.ident;
412 quote! {
413 use super::#ident;
414 }
415}
416
417fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream {
418 let ident = &efn.ident;
David Tolnay3caa50a2020-04-19 21:25:34 -0700419 let link_name = mangle::extern_fn(namespace, efn);
David Tolnay75dca2e2020-03-25 20:17:52 -0700420 let local_name = format_ident!("__{}", ident);
421 let catch_unwind_label = format!("::{}", ident);
422 let invoke = Some(ident);
423 expand_rust_function_shim_impl(
424 efn,
425 types,
426 &link_name,
427 local_name,
428 catch_unwind_label,
429 invoke,
430 )
431}
432
433fn expand_rust_function_shim_impl(
434 sig: &Signature,
435 types: &Types,
David Tolnay891061b2020-04-19 22:42:33 -0700436 link_name: &Symbol,
David Tolnay75dca2e2020-03-25 20:17:52 -0700437 local_name: Ident,
438 catch_unwind_label: String,
439 invoke: Option<&Ident>,
440) -> TokenStream {
David Tolnayf9ffb932020-04-20 02:22:57 -0700441 let receiver_var = sig
442 .receiver
443 .as_ref()
444 .map(|receiver| quote_spanned!(receiver.var.span=> __self));
David Tolnay18ba92c2020-04-22 16:17:30 -0700445 let receiver = sig.receiver.as_ref().map(|receiver| {
446 let receiver_type = receiver.ty();
447 quote!(#receiver_var: #receiver_type)
448 });
David Tolnay75dca2e2020-03-25 20:17:52 -0700449 let args = sig.args.iter().map(|arg| {
David Tolnay39d575f2020-03-03 00:10:56 -0800450 let ident = &arg.ident;
451 let ty = expand_extern_type(&arg.ty);
452 if types.needs_indirect_abi(&arg.ty) {
453 quote!(#ident: *mut #ty)
454 } else {
455 quote!(#ident: #ty)
456 }
457 });
David Tolnayf9ffb932020-04-20 02:22:57 -0700458 let all_args = receiver.into_iter().chain(args);
David Tolnay75dca2e2020-03-25 20:17:52 -0700459
David Tolnay3a45f2d2020-04-20 01:51:12 -0700460 let arg_vars = sig.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400461 let ident = &arg.ident;
David Tolnay17955e22020-01-20 17:58:24 -0800462 match &arg.ty {
David Tolnaycc3767f2020-03-06 10:41:51 -0800463 Type::Ident(i) if i == RustString => {
464 quote!(::std::mem::take((*#ident).as_mut_string()))
465 }
David Tolnay40226ab2020-03-03 00:05:35 -0800466 Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)),
467 Type::UniquePtr(_) => quote!(::cxx::UniquePtr::from_raw(#ident)),
David Tolnay17955e22020-01-20 17:58:24 -0800468 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800469 Type::Ident(i) if i == RustString => quote!(#ident.as_string()),
David Tolnay40226ab2020-03-03 00:05:35 -0800470 _ => quote!(#ident),
David Tolnay17955e22020-01-20 17:58:24 -0800471 },
David Tolnay40226ab2020-03-03 00:05:35 -0800472 Type::Str(_) => quote!(#ident.as_str()),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700473 Type::SliceRefU8(_) => quote!(#ident.as_slice()),
David Tolnay40226ab2020-03-03 00:05:35 -0800474 ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
475 _ => quote!(#ident),
David Tolnay7db73692019-10-20 14:51:12 -0400476 }
477 });
David Tolnayf9ffb932020-04-20 02:22:57 -0700478 let vars = receiver_var.into_iter().chain(arg_vars);
David Tolnay75dca2e2020-03-25 20:17:52 -0700479
480 let mut call = match invoke {
David Tolnay3a45f2d2020-04-20 01:51:12 -0700481 Some(ident) => match &sig.receiver {
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700482 None => quote!(super::#ident),
David Tolnay3a45f2d2020-04-20 01:51:12 -0700483 Some(receiver) => {
David Tolnay05e11cc2020-04-20 02:13:56 -0700484 let receiver_type = &receiver.ty;
David Tolnay3a45f2d2020-04-20 01:51:12 -0700485 quote!(#receiver_type::#ident)
486 }
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700487 },
David Tolnay75dca2e2020-03-25 20:17:52 -0700488 None => quote!(__extern),
489 };
490 call.extend(quote! { (#(#vars),*) });
491
492 let mut expr = sig
David Tolnay7db73692019-10-20 14:51:12 -0400493 .ret
494 .as_ref()
495 .and_then(|ret| match ret {
David Tolnaya52602b2020-03-06 10:24:34 -0800496 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400497 Some(quote!(::cxx::private::RustString::from(#call)))
498 }
499 Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))),
500 Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))),
501 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800502 Type::Ident(ident) if ident == RustString => {
David Tolnay7db73692019-10-20 14:51:12 -0400503 Some(quote!(::cxx::private::RustString::from_ref(#call)))
504 }
505 _ => None,
506 },
507 Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700508 Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))),
David Tolnay7db73692019-10-20 14:51:12 -0400509 _ => None,
510 })
511 .unwrap_or(call);
David Tolnay75dca2e2020-03-25 20:17:52 -0700512
513 let mut outparam = None;
514 let indirect_return = indirect_return(sig, types);
David Tolnay1e548172020-03-16 13:37:09 -0700515 if indirect_return {
David Tolnay75dca2e2020-03-25 20:17:52 -0700516 let ret = expand_extern_type(sig.ret.as_ref().unwrap());
517 outparam = Some(quote!(__return: *mut #ret,));
David Tolnay1e548172020-03-16 13:37:09 -0700518 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700519 if sig.throws {
520 let out = match sig.ret {
David Tolnaycecada62020-03-17 01:45:58 -0700521 Some(_) => quote!(__return),
522 None => quote!(&mut ()),
523 };
524 expr = quote!(::cxx::private::r#try(#out, #expr));
David Tolnay1e548172020-03-16 13:37:09 -0700525 } else if indirect_return {
David Tolnay7db73692019-10-20 14:51:12 -0400526 expr = quote!(::std::ptr::write(__return, #expr));
527 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700528
David Tolnay1e548172020-03-16 13:37:09 -0700529 expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr));
David Tolnay75dca2e2020-03-25 20:17:52 -0700530
531 let ret = if sig.throws {
David Tolnay486b6ec2020-03-17 01:19:57 -0700532 quote!(-> ::cxx::private::Result)
David Tolnay1e548172020-03-16 13:37:09 -0700533 } else {
David Tolnay75dca2e2020-03-25 20:17:52 -0700534 expand_extern_return_type(&sig.ret, types)
David Tolnay1e548172020-03-16 13:37:09 -0700535 };
David Tolnay75dca2e2020-03-25 20:17:52 -0700536
537 let pointer = match invoke {
538 None => Some(quote!(__extern: #sig)),
539 Some(_) => None,
540 };
541
David Tolnay7db73692019-10-20 14:51:12 -0400542 quote! {
543 #[doc(hidden)]
544 #[export_name = #link_name]
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700545 unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
David Tolnay7db73692019-10-20 14:51:12 -0400546 let __fn = concat!(module_path!(), #catch_unwind_label);
547 #expr
548 }
549 }
550}
551
552fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream {
David Tolnay8c730492020-03-13 01:29:06 -0700553 let link_prefix = format!("cxxbridge02$box${}{}$", namespace, ident);
David Tolnay7db73692019-10-20 14:51:12 -0400554 let link_uninit = format!("{}uninit", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400555 let link_drop = format!("{}drop", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400556
557 let local_prefix = format_ident!("{}__box_", ident);
558 let local_uninit = format_ident!("{}uninit", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400559 let local_drop = format_ident!("{}drop", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400560
561 let span = ident.span();
562 quote_spanned! {span=>
563 #[doc(hidden)]
564 #[export_name = #link_uninit]
565 unsafe extern "C" fn #local_uninit(
566 this: *mut ::std::boxed::Box<::std::mem::MaybeUninit<#ident>>,
567 ) {
568 ::std::ptr::write(
569 this,
570 ::std::boxed::Box::new(::std::mem::MaybeUninit::uninit()),
571 );
572 }
573 #[doc(hidden)]
David Tolnay7db73692019-10-20 14:51:12 -0400574 #[export_name = #link_drop]
575 unsafe extern "C" fn #local_drop(this: *mut ::std::boxed::Box<#ident>) {
576 ::std::ptr::drop_in_place(this);
577 }
David Tolnay7db73692019-10-20 14:51:12 -0400578 }
579}
580
Myron Ahneba35cf2020-02-05 19:41:51 +0700581fn expand_rust_vec(namespace: &Namespace, ty: &Type, ident: &Ident) -> TokenStream {
582 let inner = ty;
583 let mangled = ty.to_mangled(&namespace.segments) + "$";
584 let link_prefix = format!("cxxbridge02$rust_vec${}", mangled);
585 let link_drop = format!("{}drop", link_prefix);
Myron Ahneba35cf2020-02-05 19:41:51 +0700586 let link_len = format!("{}len", link_prefix);
587
588 let local_prefix = format_ident!("{}__vec_", ident);
589 let local_drop = format_ident!("{}drop", local_prefix);
Myron Ahneba35cf2020-02-05 19:41:51 +0700590 let local_len = format_ident!("{}len", local_prefix);
591
592 let span = ty.span();
593 quote_spanned! {span=>
594 #[doc(hidden)]
595 #[export_name = #link_drop]
David Tolnay6c6b7e02020-04-24 11:42:59 -0700596 unsafe extern "C" fn #local_drop(this: *mut ::cxx::private::RustVec<#inner>) {
David Tolnay4c64afb2020-04-24 11:30:18 -0700597 ::std::ptr::drop_in_place(this);
Myron Ahneba35cf2020-02-05 19:41:51 +0700598 }
David Tolnay85db5a02020-04-25 13:17:27 -0700599 #[doc(hidden)]
Myron Ahneba35cf2020-02-05 19:41:51 +0700600 #[export_name = #link_len]
David Tolnay6c6b7e02020-04-24 11:42:59 -0700601 unsafe extern "C" fn #local_len(this: *const ::cxx::private::RustVec<#inner>) -> usize {
David Tolnay2cef5df2020-04-24 11:33:58 -0700602 (*this).len()
Myron Ahneba35cf2020-02-05 19:41:51 +0700603 }
604 }
605}
606
607fn expand_unique_ptr(namespace: &Namespace, ty: &Type, types: &Types) -> TokenStream {
608 let name = ty.to_typename(&namespace.segments);
609 let inner = ty;
610 let mangled = ty.to_mangled(&namespace.segments) + "$";
611 let prefix = format!("cxxbridge02$unique_ptr${}", mangled);
David Tolnay7db73692019-10-20 14:51:12 -0400612 let link_null = format!("{}null", prefix);
613 let link_new = format!("{}new", prefix);
614 let link_raw = format!("{}raw", prefix);
615 let link_get = format!("{}get", prefix);
616 let link_release = format!("{}release", prefix);
617 let link_drop = format!("{}drop", prefix);
618
Myron Ahneba35cf2020-02-05 19:41:51 +0700619 let new_method = match ty {
620 Type::Ident(ident) if types.structs.contains_key(ident) => Some(quote! {
David Tolnay53838912020-04-09 20:56:44 -0700621 fn __new(mut value: Self) -> *mut ::std::ffi::c_void {
622 extern "C" {
623 #[link_name = #link_new]
624 fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident);
625 }
626 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
627 unsafe { __new(&mut repr, &mut value) }
628 repr
629 }
Myron Ahneba35cf2020-02-05 19:41:51 +0700630 }),
631 _ => None,
David Tolnay53838912020-04-09 20:56:44 -0700632 };
633
David Tolnay7db73692019-10-20 14:51:12 -0400634 quote! {
Myron Ahneba35cf2020-02-05 19:41:51 +0700635 unsafe impl ::cxx::private::UniquePtrTarget for #inner {
David Tolnayad266772020-04-09 23:42:29 -0700636 const __NAME: &'static str = #name;
David Tolnay7db73692019-10-20 14:51:12 -0400637 fn __null() -> *mut ::std::ffi::c_void {
638 extern "C" {
639 #[link_name = #link_null]
640 fn __null(this: *mut *mut ::std::ffi::c_void);
641 }
642 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
643 unsafe { __null(&mut repr) }
644 repr
645 }
David Tolnay53838912020-04-09 20:56:44 -0700646 #new_method
David Tolnay7db73692019-10-20 14:51:12 -0400647 unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void {
648 extern "C" {
649 #[link_name = #link_raw]
Myron Ahneba35cf2020-02-05 19:41:51 +0700650 fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #inner);
David Tolnay7db73692019-10-20 14:51:12 -0400651 }
652 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
653 __raw(&mut repr, raw);
654 repr
655 }
656 unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self {
657 extern "C" {
658 #[link_name = #link_get]
Myron Ahneba35cf2020-02-05 19:41:51 +0700659 fn __get(this: *const *mut ::std::ffi::c_void) -> *const #inner;
David Tolnay7db73692019-10-20 14:51:12 -0400660 }
661 __get(&repr)
662 }
663 unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self {
664 extern "C" {
665 #[link_name = #link_release]
Myron Ahneba35cf2020-02-05 19:41:51 +0700666 fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #inner;
David Tolnay7db73692019-10-20 14:51:12 -0400667 }
668 __release(&mut repr)
669 }
670 unsafe fn __drop(mut repr: *mut ::std::ffi::c_void) {
671 extern "C" {
672 #[link_name = #link_drop]
673 fn __drop(this: *mut *mut ::std::ffi::c_void);
674 }
675 __drop(&mut repr);
676 }
677 }
678 }
679}
680
Myron Ahneba35cf2020-02-05 19:41:51 +0700681fn expand_vector(namespace: &Namespace, ty: &Type) -> TokenStream {
682 let inner = ty;
683 let mangled = ty.to_mangled(&namespace.segments) + "$";
684 let prefix = format!("cxxbridge02$std$vector${}", mangled);
685 let link_length = format!("{}length", prefix);
686 let link_get_unchecked = format!("{}get_unchecked", prefix);
687 let link_push_back = format!("{}push_back", prefix);
688
689 quote! {
690 impl ::cxx::private::VectorTarget<#inner> for #inner {
David Tolnaye90be1d2020-04-24 11:45:57 -0700691 fn get_unchecked(v: &::cxx::CxxVector<#inner>, pos: usize) -> &#inner {
Myron Ahneba35cf2020-02-05 19:41:51 +0700692 extern "C" {
693 #[link_name = #link_get_unchecked]
David Tolnaye90be1d2020-04-24 11:45:57 -0700694 fn __get_unchecked(_: &::cxx::CxxVector<#inner>, _: usize) -> &#inner;
Myron Ahneba35cf2020-02-05 19:41:51 +0700695 }
696 unsafe {
697 __get_unchecked(v, pos)
698 }
699 }
David Tolnaye90be1d2020-04-24 11:45:57 -0700700 fn vector_length(v: &::cxx::CxxVector<#inner>) -> usize {
Myron Ahneba35cf2020-02-05 19:41:51 +0700701 unsafe {
702 extern "C" {
703 #[link_name = #link_length]
David Tolnaye90be1d2020-04-24 11:45:57 -0700704 fn __vector_length(_: &::cxx::CxxVector<#inner>) -> usize;
Myron Ahneba35cf2020-02-05 19:41:51 +0700705 }
706 __vector_length(v)
707 }
708 }
David Tolnaye90be1d2020-04-24 11:45:57 -0700709 fn push_back(v: &::cxx::CxxVector<#inner>, item: &#inner) {
Myron Ahneba35cf2020-02-05 19:41:51 +0700710 unsafe {
711 extern "C" {
712 #[link_name = #link_push_back]
David Tolnaye90be1d2020-04-24 11:45:57 -0700713 fn __push_back(_: &::cxx::CxxVector<#inner>, _: &#inner) -> usize;
Myron Ahneba35cf2020-02-05 19:41:51 +0700714 }
715 __push_back(v, item);
716 }
717 }
718 }
719 }
720}
721
722pub fn expand_vector_builtin(ident: Ident) -> TokenStream {
723 let ty = Type::Ident(ident);
724 let inner = &ty;
725 let namespace = Namespace { segments: vec![] };
726 let mangled = ty.to_mangled(&namespace.segments) + "$";
727 let prefix = format!("cxxbridge02$std$vector${}", mangled);
728 let link_length = format!("{}length", prefix);
729 let link_get_unchecked = format!("{}get_unchecked", prefix);
730 let link_push_back = format!("{}push_back", prefix);
731
732 quote! {
733 impl VectorTarget<#inner> for #inner {
David Tolnaye90be1d2020-04-24 11:45:57 -0700734 fn get_unchecked(v: &CxxVector<#inner>, pos: usize) -> &#inner {
Myron Ahneba35cf2020-02-05 19:41:51 +0700735 extern "C" {
736 #[link_name = #link_get_unchecked]
David Tolnaye90be1d2020-04-24 11:45:57 -0700737 fn __get_unchecked(_: &CxxVector<#inner>, _: usize) -> &#inner;
Myron Ahneba35cf2020-02-05 19:41:51 +0700738 }
739 unsafe {
740 __get_unchecked(v, pos)
741 }
742 }
David Tolnaye90be1d2020-04-24 11:45:57 -0700743 fn vector_length(v: &CxxVector<#inner>) -> usize {
Myron Ahneba35cf2020-02-05 19:41:51 +0700744 unsafe {
745 extern "C" {
746 #[link_name = #link_length]
David Tolnaye90be1d2020-04-24 11:45:57 -0700747 fn __vector_length(_: &CxxVector<#inner>) -> usize;
Myron Ahneba35cf2020-02-05 19:41:51 +0700748 }
749 __vector_length(v)
750 }
751 }
David Tolnaye90be1d2020-04-24 11:45:57 -0700752 fn push_back(v: &CxxVector<#inner>, item: &#inner) {
Myron Ahneba35cf2020-02-05 19:41:51 +0700753 unsafe {
754 extern "C" {
755 #[link_name = #link_push_back]
David Tolnaye90be1d2020-04-24 11:45:57 -0700756 fn __push_back(_: &CxxVector<#inner>, _: &#inner) -> usize;
Myron Ahneba35cf2020-02-05 19:41:51 +0700757 }
758 __push_back(v, item);
759 }
760 }
761 }
762 }
763}
764
David Tolnay7db73692019-10-20 14:51:12 -0400765fn expand_return_type(ret: &Option<Type>) -> TokenStream {
766 match ret {
767 Some(ret) => quote!(-> #ret),
768 None => TokenStream::new(),
769 }
770}
771
David Tolnay75dca2e2020-03-25 20:17:52 -0700772fn indirect_return(sig: &Signature, types: &Types) -> bool {
773 sig.ret
David Tolnay1e548172020-03-16 13:37:09 -0700774 .as_ref()
David Tolnay75dca2e2020-03-25 20:17:52 -0700775 .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret))
David Tolnay7db73692019-10-20 14:51:12 -0400776}
777
778fn expand_extern_type(ty: &Type) -> TokenStream {
779 match ty {
David Tolnaya52602b2020-03-06 10:24:34 -0800780 Type::Ident(ident) if ident == RustString => quote!(::cxx::private::RustString),
David Tolnay7db73692019-10-20 14:51:12 -0400781 Type::RustBox(ty) | Type::UniquePtr(ty) => {
Myron Ahneba35cf2020-02-05 19:41:51 +0700782 let inner = expand_extern_type(&ty.inner);
David Tolnay7db73692019-10-20 14:51:12 -0400783 quote!(*mut #inner)
784 }
David Tolnay6c6b7e02020-04-24 11:42:59 -0700785 Type::RustVec(ty) => quote!(::cxx::private::RustVec<#ty>),
David Tolnay7db73692019-10-20 14:51:12 -0400786 Type::Ref(ty) => match &ty.inner {
David Tolnaya52602b2020-03-06 10:24:34 -0800787 Type::Ident(ident) if ident == RustString => quote!(&::cxx::private::RustString),
Myron Ahneba35cf2020-02-05 19:41:51 +0700788 Type::RustVec(ty) => {
789 let inner = expand_extern_type(&ty.inner);
David Tolnay6c6b7e02020-04-24 11:42:59 -0700790 quote!(&::cxx::private::RustVec<#inner>)
Myron Ahneba35cf2020-02-05 19:41:51 +0700791 }
David Tolnay7db73692019-10-20 14:51:12 -0400792 _ => quote!(#ty),
793 },
794 Type::Str(_) => quote!(::cxx::private::RustStr),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700795 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
David Tolnay7db73692019-10-20 14:51:12 -0400796 _ => quote!(#ty),
797 }
798}
799
800fn expand_extern_return_type(ret: &Option<Type>, types: &Types) -> TokenStream {
801 let ret = match ret {
802 Some(ret) if !types.needs_indirect_abi(ret) => ret,
803 _ => return TokenStream::new(),
804 };
805 let ty = expand_extern_type(ret);
806 quote!(-> #ty)
807}