diff options
Diffstat (limited to 'src/bindgen/mangle.rs')
-rw-r--r-- | src/bindgen/mangle.rs | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/src/bindgen/mangle.rs b/src/bindgen/mangle.rs new file mode 100644 index 0000000..c8769f8 --- /dev/null +++ b/src/bindgen/mangle.rs @@ -0,0 +1,348 @@ +/* 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") + ); +} |