1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use bindgen::ir::{Path, Type};

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

pub fn mangle_name(name: &str, generic_values: &[Type]) -> String {
    internal_mangle_name(name, generic_values, true)
}

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)
}

fn internal_mangle_name(name: &str, generic_values: &[Type], last_in_parent: bool) -> String {
    if generic_values.is_empty() {
        return name.to_owned();
    }

    let mut mangled = name.to_owned();

    mangled.push_str("_"); // <
    for (i, ty) in generic_values.iter().enumerate() {
        if i != 0 {
            mangled.push_str("__"); // ,
        }

        let is_last = i == generic_values.len() - 1;
        match *ty {
            Type::Path(ref generic) => {
                mangled.push_str(&internal_mangle_name(
                    generic.export_name(),
                    generic.generics(),
                    last_in_parent && is_last,
                ));
            }
            Type::Primitive(ref primitive) => {
                mangled.push_str(primitive.to_repr_rust());
            }
            Type::MutRef(..)
            | Type::Ref(..)
            | Type::ConstPtr(..)
            | Type::Ptr(..)
            | Type::Array(..)
            | Type::FuncPtr(..) => {
                panic!("Unable to mangle generic parameter {:?} for '{}'", ty, name);
            }
        }

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

    mangled
}

#[test]
fn generics() {
    use bindgen::ir::{GenericPath, PrimitiveType};

    fn float() -> Type {
        Type::Primitive(PrimitiveType::Float)
    }

    fn path(path: &str) -> Type {
        generic_path(path, &vec![])
    }

    fn generic_path(path: &str, generics: &[Type]) -> Type {
        let path = Path::new(path);
        let generic_path = GenericPath::new(path, generics.to_owned());
        Type::Path(generic_path)
    }

    // Foo<f32> => Foo_f32
    assert_eq!(
        mangle_path(&Path::new("Foo"), &vec![float()]),
        Path::new("Foo_f32")
    );

    // Foo<Bar<f32>> => Foo_Bar_f32
    assert_eq!(
        mangle_path(&Path::new("Foo"), &vec![generic_path("Bar", &[float()])]),
        Path::new("Foo_Bar_f32")
    );

    // Foo<Bar> => Foo_Bar
    assert_eq!(
        mangle_path(&Path::new("Foo"), &[path("Bar")]),
        Path::new("Foo_Bar")
    );

    // Foo<Bar<T>> => Foo_Bar_T
    assert_eq!(
        mangle_path(&Path::new("Foo"), &[generic_path("Bar", &[path("T")])]),
        Path::new("Foo_Bar_T")
    );

    // Foo<Bar<T>, E> => Foo_Bar_T_____E
    assert_eq!(
        mangle_path(
            &Path::new("Foo"),
            &[generic_path("Bar", &[path("T")]), path("E")]
        ),
        Path::new("Foo_Bar_T_____E")
    );

    // Foo<Bar<T>, Bar<E>> => Foo_Bar_T_____Bar_E
    assert_eq!(
        mangle_path(
            &Path::new("Foo"),
            &[
                generic_path("Bar", &[path("T")]),
                generic_path("Bar", &[path("E")]),
            ]
        ),
        Path::new("Foo_Bar_T_____Bar_E")
    );
}