Skip to content

Commit

Permalink
Handle mangling pointers.
Browse files Browse the repository at this point in the history
Ugly, but better than crashing.

Closes mozilla#506.
Closes mozilla#453.
  • Loading branch information
emilio committed Apr 10, 2020
1 parent a519f1b commit 6a18639
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 33 deletions.
103 changes: 70 additions & 33 deletions src/bindgen/mangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,61 +5,98 @@
use crate::bindgen::ir::{Path, Type};

pub fn mangle_path(path: &Path, generic_values: &[Type]) -> Path {
internal_mangle_path(path, generic_values, true)
Path::new(mangle_name(path.name(), generic_values))
}

pub fn mangle_name(name: &str, generic_values: &[Type]) -> String {
internal_mangle_name(name, generic_values, true)
Mangler::new(name, generic_values, /* last = */ true).mangle()
}

fn internal_mangle_path(path: &Path, generic_values: &[Type], last_in_parent: bool) -> Path {
let name = path.name();
let mangled_name = internal_mangle_name(name, generic_values, last_in_parent);
Path::new(mangled_name)
enum Separator {
OpeningAngleBracket = 1,
Comma,
ClosingAngleBracket,
BeginMutPtr,
BeginConstPtr,
}

fn internal_mangle_name(name: &str, generic_values: &[Type], last_in_parent: bool) -> String {
if generic_values.is_empty() {
return name.to_owned();
struct Mangler<'a> {
input: &'a str,
generic_values: &'a [Type],
output: String,
last: bool,
}

impl<'a> Mangler<'a> {
fn new(input: &'a str, generic_values: &'a [Type], last: bool) -> Self {
Self {
input,
generic_values,
output: String::new(),
last,
}
}

let mut mangled = name.to_owned();
fn mangle(mut self) -> String {
self.mangle_internal();
self.output
}

mangled.push_str("_"); // <
for (i, ty) in generic_values.iter().enumerate() {
if i != 0 {
mangled.push_str("__"); // ,
}
fn push(&mut self, id: Separator) {
let separator = '_';
let count = id as usize;
self.output.extend(std::iter::repeat(separator).take(count));
}

let is_last = i == generic_values.len() - 1;
fn append_mangled_type(&mut self, ty: &Type, last: bool) {
match *ty {
Type::Path(ref generic) => {
mangled.push_str(&internal_mangle_name(
generic.export_name(),
generic.generics(),
last_in_parent && is_last,
));
let sub_path =
Mangler::new(generic.export_name(), generic.generics(), last).mangle();
self.output.push_str(&sub_path);
}
Type::Primitive(ref primitive) => {
mangled.push_str(primitive.to_repr_rust());
self.output.push_str(primitive.to_repr_rust());
}
Type::Ptr(ref ty) | Type::MutRef(ref ty) => {
self.push(Separator::BeginMutPtr);
self.append_mangled_type(&**ty, last);
}
Type::ConstPtr(ref ty) | Type::Ref(ref ty) => {
self.push(Separator::BeginConstPtr);
self.append_mangled_type(&**ty, last);
}
Type::MutRef(..)
| Type::Ref(..)
| Type::ConstPtr(..)
| Type::Ptr(..)
| Type::Array(..)
| Type::FuncPtr(..) => {
panic!("Unable to mangle generic parameter {:?} for '{}'", ty, name);
Type::Array(..) | Type::FuncPtr(..) => {
unimplemented!(
"Unable to mangle generic parameter {:?} for '{}'",
ty,
self.input
);
}
}
}

fn mangle_internal(&mut self) {
debug_assert!(self.output.is_empty());
self.output = self.input.to_owned();
if self.generic_values.is_empty() {
return;
}

self.push(Separator::OpeningAngleBracket);
for (i, ty) in self.generic_values.iter().enumerate() {
if i != 0 {
self.push(Separator::Comma);
}
let last = self.last && i == self.generic_values.len() - 1;
self.append_mangled_type(ty, last);
}

// Skip writing the trailing '>' mangling when possible
if is_last && !last_in_parent {
mangled.push_str("___"); // >
if !self.last {
self.push(Separator::ClosingAngleBracket)
}
}

mangled
}

#[test]
Expand Down
12 changes: 12 additions & 0 deletions tests/expectations/both/generic-pointer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct Foo_____u8 {
uint8_t *a;
} Foo_____u8;

typedef Foo_____u8 Boo;

void root(Boo x);
20 changes: 20 additions & 0 deletions tests/expectations/both/generic-pointer.compat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct Foo_____u8 {
uint8_t *a;
} Foo_____u8;

typedef Foo_____u8 Boo;

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

void root(Boo x);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
12 changes: 12 additions & 0 deletions tests/expectations/generic-pointer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct {
uint8_t *a;
} Foo_____u8;

typedef Foo_____u8 Boo;

void root(Boo x);
20 changes: 20 additions & 0 deletions tests/expectations/generic-pointer.compat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct {
uint8_t *a;
} Foo_____u8;

typedef Foo_____u8 Boo;

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

void root(Boo x);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
17 changes: 17 additions & 0 deletions tests/expectations/generic-pointer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <new>

template<typename T>
struct Foo {
T a;
};

using Boo = Foo<uint8_t*>;

extern "C" {

void root(Boo x);

} // extern "C"
12 changes: 12 additions & 0 deletions tests/expectations/tag/generic-pointer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

struct Foo_____u8 {
uint8_t *a;
};

typedef struct Foo_____u8 Boo;

void root(Boo x);
20 changes: 20 additions & 0 deletions tests/expectations/tag/generic-pointer.compat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

struct Foo_____u8 {
uint8_t *a;
};

typedef struct Foo_____u8 Boo;

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

void root(Boo x);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
11 changes: 11 additions & 0 deletions tests/rust/generic-pointer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#[repr(C)]
pub struct Foo<T> {
a: T,
}

pub type Boo = Foo<*mut u8>;

#[no_mangle]
pub extern "C" fn root(
x: Boo,
) { }

0 comments on commit 6a18639

Please sign in to comment.