Skip to content

Commit d15662f

Browse files
authored
Translate function pointers passed in variadic arguments (#83)
In unsafe, the function pointer is first converted to void* when passed to a variadic function. Then, inside the variadic function, when calling `va_arg(ap, fn_ptr)`, the function is first retrieved as void*, then transmuted to function pointer. The `function pointer -calling variadic function-> void*, void* -va_arg(ap, fn_ptr)-> transmute<function pointer>` is needed so that we don't add the unsafe transmute in libcc2rs. In refcount, this dance is not needed. VaArg and VaArgGet are implemented for FnPtr safely.
1 parent 9c40bf1 commit d15662f

8 files changed

Lines changed: 252 additions & 1 deletion

File tree

cpp2rust/converter/converter.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,16 @@ std::optional<std::string> Converter::TryPluginConvert(clang::CallExpr *call) {
13611361
return std::nullopt;
13621362
}
13631363

1364+
void Converter::ConvertVariadicArg(clang::Expr *arg) {
1365+
if (arg->getType()->isFunctionPointerType()) {
1366+
PushParen p(*this);
1367+
Convert(arg);
1368+
StrCat(".map_or(::std::ptr::null_mut(), |f| f as *mut ::libc::c_void)");
1369+
return;
1370+
}
1371+
Convert(arg);
1372+
}
1373+
13641374
void Converter::ConvertVAArgCall(clang::CallExpr *expr) {
13651375
if (IsBuiltinVaStart(expr)) {
13661376
StrCat(ToString(expr->getArg(0)->IgnoreImpCasts()),
@@ -1571,7 +1581,7 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {
15711581
StrCat("& [");
15721582
for (unsigned i = num_named_params; i < num_args; ++i) {
15731583
auto *arg = expr->getArg(i + arg_begin);
1574-
Convert(arg);
1584+
ConvertVariadicArg(arg);
15751585
StrCat(".into()", token::kComma);
15761586
}
15771587
StrCat(']');
@@ -2598,6 +2608,18 @@ bool Converter::VisitVAArgExpr(clang::VAArgExpr *expr) {
25982608
if (auto *cast = clang::dyn_cast<clang::ImplicitCastExpr>(va_list_expr)) {
25992609
va_list_expr = cast->getSubExpr();
26002610
}
2611+
if (expr->getType()->isFunctionPointerType()) {
2612+
StrCat("std::mem::transmute::<*mut ::libc::c_void", token::kComma);
2613+
Convert(expr->getType());
2614+
StrCat('>');
2615+
PushParen paren(*this);
2616+
{
2617+
PushExprKind push(*this, ExprKind::RValue);
2618+
Convert(va_list_expr);
2619+
}
2620+
StrCat(".arg::<*mut ::libc::c_void>()");
2621+
return false;
2622+
}
26012623
Convert(va_list_expr);
26022624
StrCat(".arg::<");
26032625
Convert(expr->getType());

cpp2rust/converter/converter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
224224

225225
void ConvertVAArgCall(clang::CallExpr *expr);
226226

227+
virtual void ConvertVariadicArg(clang::Expr *arg);
228+
227229
virtual bool VisitCallExpr(clang::CallExpr *expr);
228230

229231
virtual bool VisitIntegerLiteral(clang::IntegerLiteral *expr);

cpp2rust/converter/models/converter_refcount.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,6 +1602,8 @@ bool ConverterRefCount::VisitImplicitValueInitExpr(
16021602
return Converter::VisitImplicitValueInitExpr(expr);
16031603
}
16041604

1605+
void ConverterRefCount::ConvertVariadicArg(clang::Expr *arg) { Convert(arg); }
1606+
16051607
bool ConverterRefCount::VisitVAArgExpr(clang::VAArgExpr *expr) {
16061608
auto va_list_expr = expr->getSubExpr();
16071609
if (auto *cast = clang::dyn_cast<clang::ImplicitCastExpr>(va_list_expr)) {

cpp2rust/converter/models/converter_refcount.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ class ConverterRefCount final : public Converter {
116116

117117
bool VisitVAArgExpr(clang::VAArgExpr *expr) override;
118118

119+
void ConvertVariadicArg(clang::Expr *arg) override;
120+
119121
void ConvertArrayCXXConstructExpr(clang::CXXConstructExpr *expr) override;
120122

121123
bool VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) override;

libcc2rs/src/va_args.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,18 @@ impl<T: 'static> VaArgGet for crate::rc::Ptr<T> {
121121
}
122122
}
123123
}
124+
125+
impl<T: 'static> From<crate::FnPtr<T>> for VaArg {
126+
fn from(v: crate::FnPtr<T>) -> Self {
127+
VaArg::Ptr(v.to_any())
128+
}
129+
}
130+
131+
impl<T: 'static> VaArgGet for crate::FnPtr<T> {
132+
fn get(v: &VaArg) -> Self {
133+
match v {
134+
VaArg::Ptr(any) => any.cast_fn::<T>().expect("VaArgGet: FnPtr type mismatch"),
135+
_ => panic!("VaArgGet: expected FnPtr"),
136+
}
137+
}
138+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
extern crate libcc2rs;
2+
use libcc2rs::*;
3+
use std::cell::RefCell;
4+
use std::collections::BTreeMap;
5+
use std::io::prelude::*;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::AsFd;
8+
use std::rc::{Rc, Weak};
9+
pub fn square_0(x: i32) -> i32 {
10+
let x: Value<i32> = Rc::new(RefCell::new(x));
11+
return ((*x.borrow()) * (*x.borrow()));
12+
}
13+
pub fn negate_1(x: i32) -> i32 {
14+
let x: Value<i32> = Rc::new(RefCell::new(x));
15+
return -(*x.borrow());
16+
}
17+
pub fn add_2(a: i32, b: i32) -> i32 {
18+
let a: Value<i32> = Rc::new(RefCell::new(a));
19+
let b: Value<i32> = Rc::new(RefCell::new(b));
20+
return ((*a.borrow()) + (*b.borrow()));
21+
}
22+
pub fn apply_unary_3(x: i32, __args: &[VaArg]) -> i32 {
23+
let x: Value<i32> = Rc::new(RefCell::new(x));
24+
let ap: Value<VaList> = Rc::new(RefCell::new(VaList::default()));
25+
(*ap.borrow_mut()) = VaList::new(__args);
26+
let fn_: Value<FnPtr<fn(i32) -> i32>> = Rc::new(RefCell::new(
27+
((*ap.borrow_mut()).arg::<FnPtr<fn(i32) -> i32>>()).clone(),
28+
));
29+
let result: Value<i32> = Rc::new(RefCell::new(
30+
({
31+
let _arg0: i32 = (*x.borrow());
32+
(*(*fn_.borrow()))(_arg0)
33+
}),
34+
));
35+
return (*result.borrow());
36+
}
37+
pub fn apply_binary_4(a: i32, b: i32, __args: &[VaArg]) -> i32 {
38+
let a: Value<i32> = Rc::new(RefCell::new(a));
39+
let b: Value<i32> = Rc::new(RefCell::new(b));
40+
let ap: Value<VaList> = Rc::new(RefCell::new(VaList::default()));
41+
(*ap.borrow_mut()) = VaList::new(__args);
42+
let fn_: Value<FnPtr<fn(i32, i32) -> i32>> = Rc::new(RefCell::new(
43+
((*ap.borrow_mut()).arg::<FnPtr<fn(i32, i32) -> i32>>()).clone(),
44+
));
45+
let result: Value<i32> = Rc::new(RefCell::new(
46+
({
47+
let _arg0: i32 = (*a.borrow());
48+
let _arg1: i32 = (*b.borrow());
49+
(*(*fn_.borrow()))(_arg0, _arg1)
50+
}),
51+
));
52+
return (*result.borrow());
53+
}
54+
pub fn main() {
55+
std::process::exit(main_0());
56+
}
57+
fn main_0() -> i32 {
58+
assert!(
59+
(((({
60+
let _x: i32 = 5;
61+
apply_unary_3(_x, &[FnPtr::<fn(i32) -> i32>::new(square_0).into()])
62+
}) == 25) as i32)
63+
!= 0)
64+
);
65+
assert!(
66+
(((({
67+
let _x: i32 = 7;
68+
apply_unary_3(_x, &[FnPtr::<fn(i32) -> i32>::new(negate_1).into()])
69+
}) == -7_i32) as i32)
70+
!= 0)
71+
);
72+
assert!(
73+
(((({
74+
let _a: i32 = 3;
75+
let _b: i32 = 4;
76+
apply_binary_4(_a, _b, &[FnPtr::<fn(i32, i32) -> i32>::new(add_2).into()])
77+
}) == 7) as i32)
78+
!= 0)
79+
);
80+
return 0;
81+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
extern crate libc;
2+
use libc::*;
3+
extern crate libcc2rs;
4+
use libcc2rs::*;
5+
use std::collections::BTreeMap;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
8+
use std::rc::Rc;
9+
pub unsafe fn square_0(mut x: i32) -> i32 {
10+
return ((x) * (x));
11+
}
12+
pub unsafe fn negate_1(mut x: i32) -> i32 {
13+
return -x;
14+
}
15+
pub unsafe fn add_2(mut a: i32, mut b: i32) -> i32 {
16+
return ((a) + (b));
17+
}
18+
pub unsafe fn apply_unary_3(mut x: i32, __args: &[VaArg]) -> i32 {
19+
let mut ap: VaList = VaList::default();
20+
ap = VaList::new(__args);
21+
let mut fn_: Option<unsafe fn(i32) -> i32> = std::mem::transmute::<
22+
*mut ::libc::c_void,
23+
Option<unsafe fn(i32) -> i32>,
24+
>(ap.arg::<*mut ::libc::c_void>());
25+
let mut result: i32 = (unsafe {
26+
let _arg0: i32 = x;
27+
(fn_).unwrap()(_arg0)
28+
});
29+
return result;
30+
}
31+
pub unsafe fn apply_binary_4(mut a: i32, mut b: i32, __args: &[VaArg]) -> i32 {
32+
let mut ap: VaList = VaList::default();
33+
ap = VaList::new(__args);
34+
let mut fn_: Option<unsafe fn(i32, i32) -> i32> = std::mem::transmute::<
35+
*mut ::libc::c_void,
36+
Option<unsafe fn(i32, i32) -> i32>,
37+
>(ap.arg::<*mut ::libc::c_void>());
38+
let mut result: i32 = (unsafe {
39+
let _arg0: i32 = a;
40+
let _arg1: i32 = b;
41+
(fn_).unwrap()(_arg0, _arg1)
42+
});
43+
return result;
44+
}
45+
pub fn main() {
46+
unsafe {
47+
std::process::exit(main_0() as i32);
48+
}
49+
}
50+
unsafe fn main_0() -> i32 {
51+
assert!(
52+
((((unsafe {
53+
let _x: i32 = 5;
54+
apply_unary_3(
55+
_x,
56+
&[
57+
(Some(square_0).map_or(::std::ptr::null_mut(), |f| f as *mut ::libc::c_void))
58+
.into(),
59+
],
60+
)
61+
}) == (25)) as i32)
62+
!= 0)
63+
);
64+
assert!(
65+
((((unsafe {
66+
let _x: i32 = 7;
67+
apply_unary_3(
68+
_x,
69+
&[
70+
(Some(negate_1).map_or(::std::ptr::null_mut(), |f| f as *mut ::libc::c_void))
71+
.into(),
72+
],
73+
)
74+
}) == (-7_i32)) as i32)
75+
!= 0)
76+
);
77+
assert!(
78+
((((unsafe {
79+
let _a: i32 = 3;
80+
let _b: i32 = 4;
81+
apply_binary_4(
82+
_a,
83+
_b,
84+
&[
85+
(Some(add_2).map_or(::std::ptr::null_mut(), |f| f as *mut ::libc::c_void))
86+
.into(),
87+
],
88+
)
89+
}) == (7)) as i32)
90+
!= 0)
91+
);
92+
return 0;
93+
}

tests/unit/va_arg_fn_ptr.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <assert.h>
2+
#include <stdarg.h>
3+
4+
typedef int (*unary)(int);
5+
typedef int (*binary)(int, int);
6+
7+
int square(int x) { return x * x; }
8+
int negate(int x) { return -x; }
9+
int add(int a, int b) { return a + b; }
10+
11+
int apply_unary(int x, ...) {
12+
va_list ap;
13+
va_start(ap, x);
14+
unary fn = va_arg(ap, unary);
15+
int result = fn(x);
16+
va_end(ap);
17+
return result;
18+
}
19+
20+
int apply_binary(int a, int b, ...) {
21+
va_list ap;
22+
va_start(ap, b);
23+
binary fn = va_arg(ap, binary);
24+
int result = fn(a, b);
25+
va_end(ap);
26+
return result;
27+
}
28+
29+
int main() {
30+
assert(apply_unary(5, square) == 25);
31+
assert(apply_unary(7, negate) == -7);
32+
assert(apply_binary(3, 4, add) == 7);
33+
return 0;
34+
}

0 commit comments

Comments
 (0)