| /* 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 crate::bindgen::config::MangleConfig; |
| use crate::bindgen::ir::{ConstExpr, GenericArgument, GenericPath, Path, Type}; |
| use crate::bindgen::rename::IdentifierType; |
| |
| pub fn mangle_path(path: &Path, generic_values: &[GenericArgument], config: &MangleConfig) -> Path { |
| Path::new(mangle_name(path.name(), generic_values, config)) |
| } |
| |
| pub fn mangle_name( |
| name: &str, |
| generic_values: &[GenericArgument], |
| config: &MangleConfig, |
| ) -> String { |
| Mangler::new(name, generic_values, /* last = */ true, config).mangle() |
| } |
| |
| enum Separator { |
| OpeningAngleBracket = 1, |
| Comma, |
| ClosingAngleBracket, |
| BeginMutPtr, |
| BeginConstPtr, |
| BeginFn, |
| BetweenFnArg, |
| EndFn, |
| } |
| |
| struct Mangler<'a> { |
| input: &'a str, |
| generic_values: &'a [GenericArgument], |
| output: String, |
| last: bool, |
| config: &'a MangleConfig, |
| } |
| |
| impl<'a> Mangler<'a> { |
| fn new( |
| input: &'a str, |
| generic_values: &'a [GenericArgument], |
| last: bool, |
| config: &'a MangleConfig, |
| ) -> Self { |
| Self { |
| input, |
| generic_values, |
| output: String::new(), |
| last, |
| config, |
| } |
| } |
| |
| fn mangle(mut self) -> String { |
| self.mangle_internal(); |
| self.output |
| } |
| |
| fn push(&mut self, id: Separator) { |
| let count = id as usize; |
| let separator = if self.config.remove_underscores { |
| "" |
| } else { |
| "_" |
| }; |
| self.output.extend(std::iter::repeat(separator).take(count)); |
| } |
| |
| fn append_mangled_argument(&mut self, arg: &GenericArgument, last: bool) { |
| match *arg { |
| GenericArgument::Type(ref ty) => self.append_mangled_type(ty, last), |
| GenericArgument::Const(ConstExpr::Name(ref name)) => { |
| // This must behave the same as a GenericArgument::Type, |
| // because const arguments are commonly represented as Types; |
| // see the comment on `enum GenericArgument`. |
| let fake_ty = Type::Path(GenericPath::new(Path::new(name), vec![])); |
| self.append_mangled_type(&fake_ty, last); |
| } |
| GenericArgument::Const(ConstExpr::Value(ref val)) => self.output.push_str(val), |
| } |
| } |
| |
| fn append_mangled_type(&mut self, ty: &Type, last: bool) { |
| match *ty { |
| Type::Path(ref generic) => { |
| let sub_path = |
| Mangler::new(generic.export_name(), generic.generics(), last, self.config) |
| .mangle(); |
| |
| self.output.push_str( |
| &self |
| .config |
| .rename_types |
| .apply(&sub_path, IdentifierType::Type), |
| ); |
| } |
| Type::Primitive(ref primitive) => { |
| self.output.push_str( |
| &self |
| .config |
| .rename_types |
| .apply(primitive.to_repr_rust(), IdentifierType::Type), |
| ); |
| } |
| Type::Ptr { |
| ref ty, is_const, .. |
| } => { |
| self.push(if is_const { |
| Separator::BeginConstPtr |
| } else { |
| Separator::BeginMutPtr |
| }); |
| self.append_mangled_type(ty, last); |
| } |
| Type::FuncPtr { |
| ref ret, ref args, .. |
| } => { |
| self.push(Separator::BeginFn); |
| self.append_mangled_type(ret, args.is_empty()); |
| for (i, arg) in args.iter().enumerate() { |
| self.push(Separator::BetweenFnArg); |
| let last = last && i == args.len() - 1; |
| self.append_mangled_type(&arg.1, last); |
| } |
| if !self.last { |
| self.push(Separator::EndFn); |
| } |
| } |
| Type::Array(..) => { |
| 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, arg) 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_argument(arg, last); |
| } |
| |
| // Skip writing the trailing '>' mangling when possible |
| if !self.last { |
| self.push(Separator::ClosingAngleBracket) |
| } |
| } |
| } |
| |
| #[test] |
| fn generics() { |
| use crate::bindgen::ir::{GenericPath, PrimitiveType}; |
| use crate::bindgen::rename::RenameRule::{self, PascalCase}; |
| |
| fn float() -> GenericArgument { |
| GenericArgument::Type(Type::Primitive(PrimitiveType::Float)) |
| } |
| |
| fn c_char() -> GenericArgument { |
| GenericArgument::Type(Type::Primitive(PrimitiveType::Char)) |
| } |
| |
| fn path(path: &str) -> GenericArgument { |
| generic_path(path, &[]) |
| } |
| |
| fn generic_path(path: &str, arguments: &[GenericArgument]) -> GenericArgument { |
| let path = Path::new(path); |
| let generic_path = GenericPath::new(path, arguments.to_owned()); |
| GenericArgument::Type(Type::Path(generic_path)) |
| } |
| |
| // Foo<f32> => Foo_f32 |
| assert_eq!( |
| mangle_path(&Path::new("Foo"), &[float()], &MangleConfig::default()), |
| Path::new("Foo_f32") |
| ); |
| |
| // Foo<Bar<f32>> => Foo_Bar_f32 |
| assert_eq!( |
| mangle_path( |
| &Path::new("Foo"), |
| &[generic_path("Bar", &[float()])], |
| &MangleConfig::default(), |
| ), |
| Path::new("Foo_Bar_f32") |
| ); |
| |
| // Foo<Bar> => Foo_Bar |
| assert_eq!( |
| mangle_path(&Path::new("Foo"), &[path("Bar")], &MangleConfig::default()), |
| Path::new("Foo_Bar") |
| ); |
| |
| // Foo<Bar> => FooBar |
| assert_eq!( |
| mangle_path( |
| &Path::new("Foo"), |
| &[path("Bar")], |
| &MangleConfig { |
| remove_underscores: true, |
| rename_types: RenameRule::None, |
| } |
| ), |
| Path::new("FooBar") |
| ); |
| |
| // Foo<Bar<f32>> => FooBarF32 |
| assert_eq!( |
| mangle_path( |
| &Path::new("Foo"), |
| &[generic_path("Bar", &[float()])], |
| &MangleConfig { |
| remove_underscores: true, |
| rename_types: PascalCase, |
| }, |
| ), |
| Path::new("FooBarF32") |
| ); |
| |
| // Foo<Bar<c_char>> => FooBarCChar |
| assert_eq!( |
| mangle_path( |
| &Path::new("Foo"), |
| &[generic_path("Bar", &[c_char()])], |
| &MangleConfig { |
| remove_underscores: true, |
| rename_types: PascalCase, |
| }, |
| ), |
| Path::new("FooBarCChar") |
| ); |
| |
| // Foo<Bar<T>> => Foo_Bar_T |
| assert_eq!( |
| mangle_path( |
| &Path::new("Foo"), |
| &[generic_path("Bar", &[path("T")])], |
| &MangleConfig::default(), |
| ), |
| 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")], |
| &MangleConfig::default(), |
| ), |
| 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")]), |
| ], |
| &MangleConfig::default(), |
| ), |
| Path::new("Foo_Bar_T_____Bar_E") |
| ); |
| |
| // Foo<Bar<T>, E> => FooBarTE |
| assert_eq!( |
| mangle_path( |
| &Path::new("Foo"), |
| &[generic_path("Bar", &[path("T")]), path("E")], |
| &MangleConfig { |
| remove_underscores: true, |
| rename_types: PascalCase, |
| }, |
| ), |
| Path::new("FooBarTE") |
| ); |
| |
| // Foo<Bar<T>, Bar<E>> => FooBarTBarE |
| assert_eq!( |
| mangle_path( |
| &Path::new("Foo"), |
| &[ |
| generic_path("Bar", &[path("T")]), |
| generic_path("Bar", &[path("E")]), |
| ], |
| &MangleConfig { |
| remove_underscores: true, |
| rename_types: PascalCase, |
| }, |
| ), |
| Path::new("FooBarTBarE") |
| ); |
| |
| assert_eq!( |
| mangle_path( |
| &Path::new("Top"), |
| &[GenericArgument::Const(ConstExpr::Value("40".to_string()))], |
| &MangleConfig::default(), |
| ), |
| Path::new("Top_40") |
| ); |
| |
| assert_eq!( |
| mangle_path( |
| &Path::new("Top"), |
| &[GenericArgument::Const(ConstExpr::Name("N".to_string()))], |
| &MangleConfig::default(), |
| ), |
| Path::new("Top_N") |
| ); |
| |
| assert_eq!( |
| mangle_path( |
| &Path::new("Top"), |
| &[generic_path("N", &[])], |
| &MangleConfig::default(), |
| ), |
| Path::new("Top_N") |
| ); |
| |
| assert_eq!( |
| mangle_path( |
| &Path::new("Foo"), |
| &[ |
| float(), |
| GenericArgument::Const(ConstExpr::Value("40".to_string())) |
| ], |
| &MangleConfig::default(), |
| ), |
| Path::new("Foo_f32__40") |
| ); |
| } |