blob: 73aa68d51d6a2671bebabc5abb5a1e27a814ffd7 [file] [log] [blame]
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00005use anyhow::{anyhow, Result};
Marcel Hlopko884ae7f2021-08-18 13:58:22 +00006use ffi_types::*;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00007use ir::*;
8use itertools::Itertools;
Marcel Hlopkob4b28742021-09-15 12:45:20 +00009use proc_macro2::TokenStream;
Marcel Hlopko42abfc82021-08-09 07:03:17 +000010use quote::format_ident;
11use quote::quote;
Marcel Hlopko42abfc82021-08-09 07:03:17 +000012use std::iter::Iterator;
13use std::panic::catch_unwind;
14use std::process;
Marcel Hlopko42abfc82021-08-09 07:03:17 +000015use syn::*;
16
Marcel Hlopko45fba972021-08-23 19:52:20 +000017/// FFI equivalent of `Bindings`.
18#[repr(C)]
19pub struct FfiBindings {
20 rs_api: FfiU8SliceBox,
21 rs_api_impl: FfiU8SliceBox,
22}
23
24/// Deserializes IR from `json` and generates bindings source code.
Marcel Hlopko42abfc82021-08-09 07:03:17 +000025///
26/// This function panics on error.
27///
28/// Ownership:
29/// * function doesn't take ownership of (in other words it borrows) the param `json`
30/// * function passes ownership of the returned value to the caller
31///
32/// Safety:
33/// * function expects that param `json` is a FfiU8Slice for a valid array of bytes with the
34/// given size.
35/// * function expects that param `json` doesn't change during the call.
36#[no_mangle]
Marcel Hlopko45fba972021-08-23 19:52:20 +000037pub unsafe extern "C" fn GenerateBindingsImpl(json: FfiU8Slice) -> FfiBindings {
Marcel Hlopko42abfc82021-08-09 07:03:17 +000038 catch_unwind(|| {
Marcel Hlopko45fba972021-08-23 19:52:20 +000039 // It is ok to abort here.
40 let Bindings { rs_api, rs_api_impl } = generate_bindings(json.as_slice()).unwrap();
41
42 FfiBindings {
43 rs_api: FfiU8SliceBox::from_boxed_slice(rs_api.into_bytes().into_boxed_slice()),
44 rs_api_impl: FfiU8SliceBox::from_boxed_slice(
45 rs_api_impl.into_bytes().into_boxed_slice(),
46 ),
47 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +000048 })
49 .unwrap_or_else(|_| process::abort())
50}
51
Marcel Hlopko45fba972021-08-23 19:52:20 +000052/// Source code for generated bindings.
53struct Bindings {
54 // Rust source code.
55 rs_api: String,
56 // C++ source code.
57 rs_api_impl: String,
Marcel Hlopko42abfc82021-08-09 07:03:17 +000058}
59
Marcel Hlopko45fba972021-08-23 19:52:20 +000060fn generate_bindings(json: &[u8]) -> Result<Bindings> {
61 let ir = deserialize_ir(json)?;
62 let rs_api = generate_rs_api(&ir)?;
63 let rs_api_impl = generate_rs_api_impl(&ir)?;
64 Ok(Bindings { rs_api, rs_api_impl })
65}
66
Marcel Hlopko3164eee2021-08-24 20:09:22 +000067/// If we know the original C++ function is codegenned and already compatible with `extern "C"`
68/// calling convention we skip creating/calling the C++ thunk since we can call the original C++
69/// directly.
70fn can_skip_cc_thunk(func: &Func) -> bool {
71 // Inline functions may not be codegenned in the C++ library since Clang doesn't know if Rust
72 // calls the function or not. Therefore in order to make inline functions callable from Rust we
73 // need to generate a C++ file that defines a thunk that delegates to the original inline
74 // function. When compiled, Clang will emit code for this thunk and Rust code will call the
75 // thunk when the user wants to call the original inline function.
76 //
77 // This is not great runtime-performance-wise in regular builds (inline function will not be
78 // inlined, there will always be a function call), but it is correct. ThinLTO builds will be
79 // able to see through the thunk and inline code across the language boundary. For non-ThinLTO
80 // builds we plan to implement <internal link> which removes the runtime performance overhead.
81 !func.is_inline
82}
83
Marcel Hlopkob4b28742021-09-15 12:45:20 +000084/// Generate Rust source code for a given Record.
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +000085fn generate_record(record: &Record) -> Result<TokenStream> {
Marcel Hlopkob4b28742021-09-15 12:45:20 +000086 let ident = make_ident(&record.identifier.identifier);
87 let field_idents =
88 record.fields.iter().map(|f| make_ident(&f.identifier.identifier)).collect_vec();
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +000089 let field_types =
90 record.fields.iter().map(|f| format_rs_type(&f.type_)).collect::<Result<Vec<_>>>()?;
Googlerec589eb2021-09-17 07:45:39 +000091 let field_accesses = record
92 .fields
93 .iter()
94 .map(|f| {
95 if f.access == AccessSpecifier::Public {
96 quote! { pub }
97 } else {
98 quote! {}
99 }
100 })
101 .collect_vec();
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000102 Ok(quote! {
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000103 #[repr(C)]
104 pub struct #ident {
Googlerec589eb2021-09-17 07:45:39 +0000105 #( #field_accesses #field_idents: #field_types, )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000106 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000107 })
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000108}
109
Marcel Hlopko45fba972021-08-23 19:52:20 +0000110fn generate_rs_api(ir: &IR) -> Result<String> {
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000111 let mut thunks = vec![];
112 let mut api_funcs = vec![];
Marcel Hlopko45fba972021-08-23 19:52:20 +0000113 for func in &ir.functions {
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000114 let mangled_name = &func.mangled_name;
115 let ident = make_ident(&func.identifier.identifier);
116 let thunk_ident = format_ident!("__rust_thunk__{}", &func.identifier.identifier);
Marcel Hlopko7d739792021-08-12 07:52:47 +0000117 // TODO(hlopko): do not emit `-> ()` when return type is void, it's implicit.
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000118 let return_type_name = format_rs_type(&func.return_type)?;
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000119
120 let param_idents =
121 func.params.iter().map(|p| make_ident(&p.identifier.identifier)).collect_vec();
122
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000123 let param_types =
124 func.params.iter().map(|p| format_rs_type(&p.type_)).collect::<Result<Vec<_>>>()?;
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000125
126 api_funcs.push(quote! {
127 #[inline(always)]
128 pub fn #ident( #( #param_idents: #param_types ),* ) -> #return_type_name {
129 unsafe { crate::detail::#thunk_ident( #( #param_idents ),* ) }
130 }
131 });
132
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000133 let thunk_attr = if can_skip_cc_thunk(&func) {
134 quote! {#[link_name = #mangled_name]}
135 } else {
136 quote! {}
137 };
138
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000139 thunks.push(quote! {
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000140 #thunk_attr
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000141 pub(crate) fn #thunk_ident( #( #param_idents: #param_types ),* ) -> #return_type_name ;
142 });
143 }
144
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000145 let records = ir.records.iter().map(generate_record).collect::<Result<Vec<_>>>()?;
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000146
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000147 let mod_detail = if thunks.is_empty() {
148 quote! {}
149 } else {
150 quote! {
151 mod detail {
152 extern "C" {
153 #( #thunks )*
154 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000155 }
156 }
157 };
158
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000159 let result = quote! {
160 #( #api_funcs )*
161 #( #records )*
162
163 #mod_detail
164 };
165
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000166 Ok(result.to_string())
167}
168
169fn make_ident(ident: &str) -> Ident {
170 format_ident!("{}", ident)
171}
172
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000173fn format_rs_type(ty: &ir::IRType) -> Result<TokenStream> {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000174 let ptr_fragment = match ty.rs_name.as_str() {
175 "*mut" => Some(quote! {*mut}),
176 "*const" => Some(quote! {*const}),
177 _ => None,
178 };
179 match ptr_fragment {
180 Some(ptr_fragment) => {
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000181 if ty.type_params.len() != 1 {
182 return Err(anyhow!(
183 "Invalid pointer type (need exactly 1 type parameter): {:?}",
184 ty
185 ));
186 }
187 let nested_type = format_rs_type(&ty.type_params[0])?;
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000188 Ok(quote! {#ptr_fragment #nested_type})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000189 }
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000190 None => {
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000191 if ty.type_params.len() > 0 {
192 return Err(anyhow!("Type not yet supported: {:?}", ty));
193 }
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000194 let ident = make_ident(&ty.rs_name);
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000195 Ok(quote! {#ident})
196 }
197 }
198}
199
200fn format_cc_type(ty: &ir::IRType) -> Result<TokenStream> {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000201 let const_fragment = if ty.cc_const {
202 quote! {const}
203 } else {
204 quote! {}
205 };
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000206 match ty.cc_name.as_str() {
207 "*" => {
208 if ty.type_params.len() != 1 {
209 return Err(anyhow!(
210 "Invalid pointer type (need exactly 1 type parameter): {:?}",
211 ty
212 ));
213 }
214 assert_eq!(ty.type_params.len(), 1);
215 let nested_type = format_cc_type(&ty.type_params[0])?;
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000216 Ok(quote! {#nested_type * #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000217 }
218 ident => {
219 if ty.type_params.len() > 0 {
220 return Err(anyhow!("Type not yet supported: {:?}", ty));
221 }
222 let ident = make_ident(ident);
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000223 Ok(quote! {#ident #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000224 }
225 }
226}
227
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000228fn generate_rs_api_impl(ir: &IR) -> Result<String> {
229 // This function uses quote! to generate C++ source code out of convenience. This is a bold idea
230 // so we have to continously evaluate if it still makes sense or the cost of working around
231 // differences in Rust and C++ tokens is greather than the value added.
232 //
233 // See rs_bindings_from_cc/token_stream_printer.rs for a list
234 // of supported placeholders.
235 let mut thunks = vec![];
236 for func in &ir.functions {
237 if can_skip_cc_thunk(&func) {
238 continue;
239 }
240
241 let thunk_ident = format_ident!("__rust_thunk__{}", &func.identifier.identifier);
242 let ident = make_ident(&func.identifier.identifier);
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000243 let return_type_name = format_cc_type(&func.return_type)?;
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000244
245 let param_idents =
246 func.params.iter().map(|p| make_ident(&p.identifier.identifier)).collect_vec();
247
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000248 let param_types =
249 func.params.iter().map(|p| format_cc_type(&p.type_)).collect::<Result<Vec<_>>>()?;
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000250
251 thunks.push(quote! {
252 extern "C" #return_type_name #thunk_ident( #( #param_types #param_idents ),* ) {
253 return #ident( #( #param_idents ),* );
254 }
255 });
256 }
257
258 // In order to generate C++ thunk in all the cases Clang needs to be able to access declarations
259 // from public headers of the C++ library.
260 let includes = ir.used_headers.iter().map(|i| &i.name);
261
262 let result = quote! {
263 #( __HASH_TOKEN__ include #includes __NEWLINE__)*
264
265 #( #thunks )*
266 };
267
268 token_stream_printer::cc_tokens_to_string(result)
269}
270
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000271#[cfg(test)]
272mod tests {
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000273 use super::Result;
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000274 use super::{generate_rs_api, generate_rs_api_impl};
275 use ir::*;
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000276 use quote::quote;
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000277 use token_stream_printer::cc_tokens_to_string;
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000278
279 #[test]
Marcel Hlopko45fba972021-08-23 19:52:20 +0000280 fn test_simple_function() -> Result<()> {
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000281 let ir = IR {
Marcel Hlopkof1123c82021-08-19 11:38:52 +0000282 used_headers: vec![],
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000283 records: vec![],
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000284 functions: vec![Func {
285 identifier: Identifier { identifier: "add".to_string() },
286 mangled_name: "_Z3Addii".to_string(),
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000287 return_type: IRType {
288 rs_name: "i32".to_string(),
289 cc_name: "int".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000290 cc_const: false,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000291 type_params: vec![],
292 },
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000293 params: vec![
294 FuncParam {
295 identifier: Identifier { identifier: "a".to_string() },
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000296 type_: IRType {
297 rs_name: "i32".to_string(),
298 cc_name: "int".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000299 cc_const: false,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000300 type_params: vec![],
301 },
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000302 },
303 FuncParam {
304 identifier: Identifier { identifier: "b".to_string() },
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000305 type_: IRType {
306 rs_name: "i32".to_string(),
307 cc_name: "int".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000308 cc_const: false,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000309 type_params: vec![],
310 },
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000311 },
312 ],
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000313 is_inline: false,
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000314 }],
315 };
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000316 assert_eq!(
Marcel Hlopko45fba972021-08-23 19:52:20 +0000317 generate_rs_api(&ir)?,
Marcel Hlopkof1123c82021-08-19 11:38:52 +0000318 quote! {
319 #[inline(always)]
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000320 pub fn add(a: i32, b: i32) -> i32 {
321 unsafe { crate::detail::__rust_thunk__add(a, b) }
322 }
323
324 mod detail {
325 extern "C" {
326 #[link_name = "_Z3Addii"]
327 pub(crate) fn __rust_thunk__add(a: i32, b: i32) -> i32;
328 } // extern
329 } // mod detail
330 }
331 .to_string()
332 );
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000333 assert_eq!(generate_rs_api_impl(&ir)?, "");
334 Ok(())
335 }
336
337 #[test]
338 fn test_inline_function() -> Result<()> {
339 let ir = IR {
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000340 records: vec![],
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000341 used_headers: vec![
342 HeaderName { name: "foo/bar.h".to_string() },
343 HeaderName { name: "foo/baz.h".to_string() },
344 ],
345 functions: vec![Func {
346 identifier: Identifier { identifier: "add".to_string() },
347 mangled_name: "_Z3Addii".to_string(),
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000348 return_type: IRType {
349 rs_name: "i32".to_string(),
350 cc_name: "int".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000351 cc_const: false,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000352 type_params: vec![],
353 },
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000354 params: vec![
355 FuncParam {
356 identifier: Identifier { identifier: "a".to_string() },
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000357 type_: IRType {
358 rs_name: "i32".to_string(),
359 cc_name: "int".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000360 cc_const: false,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000361 type_params: vec![],
362 },
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000363 },
364 FuncParam {
365 identifier: Identifier { identifier: "b".to_string() },
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000366 type_: IRType {
367 rs_name: "i32".to_string(),
368 cc_name: "int".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000369 cc_const: false,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000370 type_params: vec![],
371 },
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000372 },
373 ],
374 is_inline: true,
375 }],
376 };
377
378 assert_eq!(
379 generate_rs_api(&ir)?,
380 quote! {#[inline(always)]
381 pub fn add(a: i32, b: i32) -> i32 {
382 unsafe { crate::detail::__rust_thunk__add(a, b) }
383 }
384
385 mod detail {
386 extern "C" {
387 pub(crate) fn __rust_thunk__add(a: i32, b: i32) -> i32;
388 } // extern
389 } // mod detail
390 }
391 .to_string()
392 );
393
394 assert_eq!(
395 generate_rs_api_impl(&ir)?,
396 cc_tokens_to_string(quote! {
397 __HASH_TOKEN__ include "foo/bar.h" __NEWLINE__
398 __HASH_TOKEN__ include "foo/baz.h" __NEWLINE__
399
400 extern "C" int __rust_thunk__add(int a, int b) {
401 return add(a, b);
402 }
403 })?
404 );
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000405 Ok(())
406 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000407
408 #[test]
409 fn test_simple_struct() -> Result<()> {
410 let ir = IR {
411 used_headers: vec![],
412 records: vec![Record {
413 identifier: Identifier { identifier: "SomeStruct".to_string() },
414 fields: vec![
415 Field {
Googlerec589eb2021-09-17 07:45:39 +0000416 identifier: Identifier { identifier: "public_int".to_string() },
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000417 type_: IRType {
418 rs_name: "i32".to_string(),
419 cc_name: "int".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000420 cc_const: false,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000421 type_params: vec![],
422 },
Googler2294c702021-09-17 07:32:07 +0000423 access: AccessSpecifier::Public,
Googlere6e5f302021-09-17 13:56:40 +0000424 offset: 0,
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000425 },
426 Field {
Googlerec589eb2021-09-17 07:45:39 +0000427 identifier: Identifier { identifier: "protected_int".to_string() },
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000428 type_: IRType {
429 rs_name: "i32".to_string(),
430 cc_name: "int".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000431 cc_const: false,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000432 type_params: vec![],
433 },
Googlerec589eb2021-09-17 07:45:39 +0000434 access: AccessSpecifier::Protected,
Googlere6e5f302021-09-17 13:56:40 +0000435 offset: 32,
Googlerec589eb2021-09-17 07:45:39 +0000436 },
437 Field {
438 identifier: Identifier { identifier: "private_int".to_string() },
439 type_: IRType {
440 rs_name: "i32".to_string(),
441 cc_name: "int".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000442 cc_const: false,
Googlerec589eb2021-09-17 07:45:39 +0000443 type_params: vec![],
444 },
445 access: AccessSpecifier::Private,
Googlere6e5f302021-09-17 13:56:40 +0000446 offset: 64,
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000447 },
448 ],
Googlere6e5f302021-09-17 13:56:40 +0000449 size: 12,
450 alignment: 4,
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000451 }],
452 functions: vec![],
453 };
454 assert_eq!(
455 generate_rs_api(&ir)?,
456 quote! {
457 #[repr(C)]
458 pub struct SomeStruct {
Googlerec589eb2021-09-17 07:45:39 +0000459 pub public_int: i32,
460 protected_int: i32,
461 private_int: i32,
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000462 }
463 }
464 .to_string()
465 );
466 assert_eq!(generate_rs_api_impl(&ir)?, "");
467 Ok(())
468 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000469
470 #[test]
471 fn test_ptr_func() -> Result<()> {
472 let ir = IR {
473 used_headers: vec![],
474 records: vec![],
475 functions: vec![Func {
476 identifier: Identifier { identifier: "Deref".to_string() },
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000477 mangled_name: "_Z5DerefPKPi".to_string(),
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000478 return_type: IRType {
479 rs_name: "*mut".to_string(),
480 cc_name: "*".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000481 cc_const: false,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000482 type_params: vec![IRType {
483 rs_name: "i32".to_string(),
484 cc_name: "int".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000485 cc_const: false,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000486 type_params: vec![],
487 }],
488 },
489 params: vec![FuncParam {
490 identifier: Identifier { identifier: "p".to_string() },
491 type_: IRType {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000492 rs_name: "*const".to_string(),
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000493 cc_name: "*".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000494 cc_const: false,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000495 type_params: vec![IRType {
496 rs_name: "*mut".to_string(),
497 cc_name: "*".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000498 cc_const: true,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000499 type_params: vec![IRType {
500 rs_name: "i32".to_string(),
501 cc_name: "int".to_string(),
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000502 cc_const: false,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000503 type_params: vec![],
504 }],
505 }],
506 },
507 }],
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000508 is_inline: true,
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000509 }],
510 };
511 assert_eq!(
512 generate_rs_api(&ir)?,
513 quote! {
514 #[inline(always)]
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000515 pub fn Deref(p: *const *mut i32) -> *mut i32 {
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000516 unsafe { crate::detail::__rust_thunk__Deref(p) }
517 }
518
519 mod detail {
520 extern "C" {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000521 pub(crate) fn __rust_thunk__Deref(p: *const *mut i32) -> *mut i32;
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000522 } // extern
523 } // mod detail
524 }
525 .to_string()
526 );
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000527
528 assert_eq!(
529 generate_rs_api_impl(&ir)?,
530 cc_tokens_to_string(quote! {
531 extern "C" int* __rust_thunk__Deref(int* const * p) {
532 return Deref(p);
533 }
534 })?
535 );
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000536 Ok(())
537 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000538}