scala-native-demangle-0.0.6/.cargo_vcs_info.json0000644000000001360000000000100151600ustar { "git": { "sha1": "034bf5e003437d3f15bf685311e3146fa93932b3" }, "path_in_vcs": "" }scala-native-demangle-0.0.6/.github/workflows/ci.yml000064400000000000000000000004661046102023000204710ustar 00000000000000name: CI on: push: branches: [ main ] pull_request: branches: [ main ] env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose scala-native-demangle-0.0.6/.gitignore000064400000000000000000000000241046102023000157340ustar 00000000000000/target /Cargo.lock scala-native-demangle-0.0.6/Cargo.lock0000644000000137000000000000100131340ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anstream" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" [[package]] name = "anstyle-parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", "windows-sys", ] [[package]] name = "clap" version = "4.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84ed82781cea27b43c9b106a979fe450a13a31aab0500595fb3fc06616de08e6" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap_builder" version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_derive" version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "proc-macro2" version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "scala-native-demangle" version = "0.0.6" dependencies = [ "clap", ] [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" version = "2.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e02e55d62894af2a08aca894c6577281f76769ba47c94d5756bec8ac6e7373" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" scala-native-demangle-0.0.6/Cargo.toml0000644000000017430000000000100131630ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "scala-native-demangle" version = "0.0.6" description = "Demangle Scala Native identifiers" homepage = "https://github.com/indoorvivants/scala-native-demangle-rs" readme = "README.md" keywords = [ "scala", "scala-native", "demangler", ] license = "Apache-2.0" repository = "https://github.com/indoorvivants/scala-native-demangle-rs" [lib] name = "scala_native_demangle" path = "src/lib.rs" [[example]] name = "cli" [dev-dependencies.clap] version = "4.4.3" features = ["derive"] scala-native-demangle-0.0.6/Cargo.toml.orig000064400000000000000000000011521046102023000166360ustar 00000000000000[package] name = "scala-native-demangle" version = "0.0.6" edition = "2021" license = "Apache-2.0" description = "Demangle Scala Native identifiers" homepage = "https://github.com/indoorvivants/scala-native-demangle-rs" repository = "https://github.com/indoorvivants/scala-native-demangle-rs" readme = "README.md" keywords = ["scala", "scala-native", "demangler"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] name = "scala_native_demangle" path = "src/lib.rs" [[example]] name = "cli" [dev-dependencies] clap = { version = "4.4.3", features = ["derive"] } scala-native-demangle-0.0.6/README.md000064400000000000000000000003761046102023000152350ustar 00000000000000## Scala Native demangler for Rust Turn mangled Scala Native identifiers into a more readable form. 1. Name mangling rules: https://scala-native.org/en/latest/contrib/mangling.html 2. Scala implementation: https://github.com/indoorvivants/sn-demangler scala-native-demangle-0.0.6/examples/cli.rs000064400000000000000000000041541046102023000167070ustar 00000000000000use std::{ fs::File, io::{self, BufRead}, path::Path, }; use scala_native_demangle::{self, DemanglingConfig}; use clap::{Parser, Subcommand}; #[derive(Parser)] #[command(author, version, about, long_about = None)] #[command(propagate_version = true)] struct Cli { #[command(subcommand)] command: Commands, } #[derive(Subcommand)] enum Commands { /// Process a file of mangled identifiers, outputting results inline, separating mangled/unmangled names via ` = ` File { name: String, #[arg(long)] debug: bool, }, Id { name: String, #[arg(long)] debug: bool, }, } fn main() { let cli = Cli::parse(); match &cli.command { Commands::File { name, debug } => { if let Ok(lines) = read_lines(name) { for line in lines { if let Ok(ip) = line { match scala_native_demangle::demangle( &ip, &DemanglingConfig { debug: *debug, ..Default::default() }, ) { Ok(res) => println!("{} = {}", ip, res), Err(e) => println!("{} ERROR {}", ip, e), } } } } } Commands::Id { name, debug } => { println!("{}", name); println!( "{}", scala_native_demangle::demangle( name, &DemanglingConfig { debug: *debug, ..Default::default() } ) .unwrap() ) } } } // The output is wrapped in a Result to allow matching on errors // Returns an Iterator to the Reader of the lines of the file. fn read_lines

(filename: P) -> io::Result>> where P: AsRef, { let file = File::open(filename)?; Ok(io::BufReader::new(file).lines()) } scala-native-demangle-0.0.6/src/lib.rs000064400000000000000000000416641046102023000156660ustar 00000000000000//! Demangle Scala Native identifiers //! //! Turn mangled Scala Native identifiers into a more readable form. //! //! 1. Name mangling rules: https://scala-native.org/en/latest/contrib/mangling.html //! 2. Scala implementation: https://github.com/indoorvivants/sn-demangler pub type DemangleError = String; pub type ParsingResult = Result; pub struct DemanglingConfig { pub collapse_scala_names: bool, pub debug: bool, } static DEFAULT_CONFIG: DemanglingConfig = DemanglingConfig { collapse_scala_names: true, debug: false, }; impl Default for DemanglingConfig { fn default() -> Self { DemanglingConfig { collapse_scala_names: true, debug: false, } } } impl DemanglingConfig { fn log(&self, str: &str) -> () { if self.debug { println!("{str}") } } fn log_name(&self, name: &str, str: &str) -> () { if self.debug { println!("{name}: {str}") } } } pub fn demangle(input: &str, config: &DemanglingConfig) -> ParsingResult { if !input.starts_with("_S") { return Err("identifier doesn't start with _S".to_string()); } else { config.log_name("demangle", &input[2..]); return defn_name(&input[2..], config); } } pub fn demangle_with_defaults(input: &str) -> ParsingResult { return demangle(input, &DEFAULT_CONFIG); } // private sub parsers // ::= // T // top-level name // M // member name fn defn_name(input: &str, config: &DemanglingConfig) -> ParsingResult { config.log_name("defn_name", input); if input.starts_with("T") { return toplevel_name(&input[1..], config); } else if input.starts_with("M") { return member_name(&input[1..], config); } else { if input.len() > 0 { return Err(format!( "defn_name: unknown name modifier '{}'", input[0..0].to_string() )); } else { return Err("defn_name: unexpectedly empty rest of identifier".to_string()); } } } fn toplevel_name(input: &str, config: &DemanglingConfig) -> ParsingResult { config.log_name("toplevel_name", input); return name(input, config).map(|t| t.1); } fn member_name(input: &str, config: &DemanglingConfig) -> ParsingResult { config.log_name("member_name", input); let (consumed, owner) = name(input, config)?; let signature = sig_name(&input[consumed..], config); return signature.and_then(|s| return Ok(format!("{}.{}", owner, s))); } // ::= // F // field name // R + E // constructor name // D + E // method name // P + E // proxy name // C // c extern name // G // generated name // K + E // duplicate name fn sig_name(input: &str, config: &DemanglingConfig) -> ParsingResult { config.log_name("sig_name", input); if input.starts_with("C") || input.starts_with("G") { return Ok(name(&input[1..], config)?.1); } else if input.starts_with("I") { return Ok("".to_string()); } else if input.starts_with("F") { let (consumed, field_name) = name(&input[1..], config)?; // return field_name.and_then(|nm| { let rest = &input[(1 + consumed)..]; return scope(rest, config).map(|sc| format!("{}{}", render_scope(sc), field_name)); // }); } else if input.starts_with("R") { let type_names = read_type_names(&input[1..], config)?; return Ok(type_names.1.join(", ")); } else if input.starts_with("K") { // TODO: basically the same as D case below let after_tag = &input[1..]; let (consumed, nm) = name(after_tag, config)?; let after_name = &after_tag[consumed..]; let (_, type_names) = read_type_names(after_name, config)?; let signature = match type_names.len() { 1 => format!("{}: {}", nm, type_names.join(",")), n => format!( "{}({}): {}", nm, type_names[0..n - 2].join(","), type_names.get(n - 1).unwrap_or(&"???".to_string()) ), }; return Ok(signature); } else if input.starts_with("P") { // TODO: basically the same as D case below let after_tag = &input[1..]; let (consumed, nm) = name(after_tag, config)?; let after_name = &after_tag[consumed..]; let (_, type_names) = read_type_names(after_name, config)?; let signature = match type_names.len() { 1 => format!("{}: {}", nm, type_names.join(",")), n => format!( "{}({}): {}", nm, type_names[0..n - 2].join(","), type_names.get(n - 1).unwrap_or(&"???".to_string()) ), }; return Ok(signature); } else if input.starts_with("D") { let after_tag = &input[1..]; let (consumed, nm) = name(after_tag, config)?; let after_name = &after_tag[consumed..]; let (consumed, type_names) = read_type_names(after_name, config)?; let after_types = &after_name[consumed + 1..]; config.log_name( "sig_name:D", format!("type_names: {type_names:?}, after: {after_types}").as_str(), ); let sc = scope(&after_types, config)?; let signature = match type_names.len() { 1 => format!("{}{}: {}", render_scope(sc), nm, type_names.join(",")), n => format!( "{}{}({}): {}", render_scope(sc), nm, type_names[0..n - 1].join(","), type_names.get(n - 1).unwrap_or(&"???".to_string()) ), }; return Ok(signature); } else { return Err(format!( "sig_name: expected to start with F/R/D/P/C/G/K/I, {}", &input ) .to_string()); } } fn read_type_names(input: &str, config: &DemanglingConfig) -> ParsingResult<(usize, Vec)> { let mut pos = 0; let mut result = Vec::new(); while !input[pos..].starts_with("E") { let (consumed, nm) = type_name(&input[pos..], config)?; result.push(nm); pos += consumed; } return Ok((pos, result)); } fn scala_root_name(name: &str, config: &DemanglingConfig) -> String { if !config.collapse_scala_names { return format!("scala.{name}"); } else { return name.to_string(); }; } fn common_type_name(name: String, config: &DemanglingConfig) -> String { if !config.collapse_scala_names { return name; } else { let immut = "scala.collection.immutable."; if name == "java.lang.Object" { return "Object".to_string(); } else if name == "java.lang.String" { return "String".to_string(); } else if name == "java.lang.Throwable" { return "Throwable".to_string(); } else if name.starts_with(immut) { return name.strip_prefix(immut).unwrap_or(&name).to_string(); // return "Throwable".to_string(); } else { return name; } } } // ::= // v // c vararg // R _ // c pointer type-name // R + E // c function type-name // S + E // c anonymous struct type-name // A _ // c array type-name // // signed integer type-name // ::= // b // scala.Byte // s // scala.Short // i // scala.Int // j // scala.Long // z // scala.Boolean // c // scala.Char // f // scala.Float // d // scala.Double // u // scala.Unit // l // scala.Null // n // scala.Nothing // L // nullable type-name // A _ // nonnull array type-name // X // nonnull exact class type-name // // nonnull class type-name fn type_name(input: &str, config: &DemanglingConfig) -> ParsingResult<(usize, String)> { let mut chars = input.chars(); config.log(format!("type_name: {input}").as_str()); let scala_root_namer = |name: &str| scala_root_name(name, config); let common_type_namer = |name: String| common_type_name(name, config); let result = match chars.next() { Some('v') => Ok((1, "".to_string())), Some('z') => Ok((1, scala_root_namer("Boolean"))), Some('c') => Ok((1, scala_root_namer("Char"))), Some('f') => Ok((1, scala_root_namer("Float"))), Some('d') => Ok((1, scala_root_namer("Double"))), Some('u') => Ok((1, scala_root_namer("Unit"))), Some('l') => Ok((1, scala_root_namer("Null"))), Some('n') => Ok((1, scala_root_namer("Nothing"))), Some('b') => Ok((1, scala_root_namer("Byte"))), Some('s') => Ok((1, scala_root_namer("Short"))), Some('i') => Ok((1, scala_root_namer("Int"))), Some('j') => Ok((1, scala_root_namer("Long"))), Some('R') => match chars.next() { Some('_') => Ok((2, "".to_string())), Some(c) => Err(format!("type_name: after R expected _, got `{c}` instead").to_string()), None => Err("type_name: unexpected end of input".to_string()), }, Some('L') => { let (consumed, type_name) = nullable_type_name(&input[1..], config)?; Ok((consumed + 1, common_type_namer(type_name))) } Some('A') => { let (consumed, tn) = type_name(&input[1..], config)?; let after_type_name = &input[1 + consumed..]; let num = number(after_type_name); Ok(( consumed + num + 1, /* "_" at the end */ format!("CArray[{}]", tn), )) } Some('X') => { let (consumed, class_type_name) = name(&input[1..], config)?; Ok((consumed + 1, class_type_name)) } Some(other) => Err(format!("type_name: unexpected start character `{other}`").to_string()), None => Err("type_name: unexpected end of input".to_string()), }; return result; } fn number(input: &str) -> usize { return input.chars().take_while(|c| c.is_digit(10)).count(); } fn nullable_type_name(input: &str, config: &DemanglingConfig) -> ParsingResult<(usize, String)> { let mut chars = input.chars(); match chars.next() { Some('A') => { let (consumed, ar) = type_name(&input[1..], config)?; return Ok((consumed + 2, format!("Array[{}]", ar))); } Some('X') => { let (consumed, n) = name(input, config)?; return Ok((consumed + 1, n)); } Some(d) if d.is_digit(10) => { return name(input, config); } Some(a) => return Err(format!("nullable_type_name: unexpected start `{a}`")), None => return Err("nullable_type_name: unexpected end of input".to_string()), }; } enum Scope { Public, PublicStatic, Private(String), PrivateStatic(String), } fn render_scope(scope: Scope) -> String { return match scope { Scope::Public => "".to_string(), Scope::PublicStatic => "".to_string(), Scope::Private(inn) => format!("", inn), Scope::PrivateStatic(inn) => format!("", inn), }; } // ::= // P // private to defn-name // O // public fn scope(input: &str, config: &DemanglingConfig) -> Result { config.log_name("scope", input); if input.starts_with("O") { return Ok(Scope::Public); } else if input.starts_with("o") { return Ok(Scope::PublicStatic); } else if input.starts_with("P") { return defn_name(&input[1..], config).map(|i| return Scope::Private(i)); } else if input.starts_with("p") { return defn_name(&input[1..], config).map(|i| return Scope::PrivateStatic(i)); } else { return Err(format!("scope: cannot read `{}`", input).to_string()); } } fn name(input: &str, config: &DemanglingConfig) -> ParsingResult<(usize, String)> { //println!("name: {}", input); config.log_name("name", input); let mut number_end: usize = 0; for c in input.chars() { if c.is_digit(10) { number_end += 1 } else { break; } } if number_end >= 1 { let (length, rest) = input.split_at(number_end); match usize::from_str_radix(length, 10) { Ok(res) => { if rest.starts_with("-") { return Ok((length.len() + 1 + res, rest[1..(1 + res)].to_string())); } else { return Ok((length.len() + res, rest[0..res].to_string())); } } Err(_) => { return Err("name: invalid length".to_string()); } } } else { return Err(format!("name: invalid input `{}`", input.to_string())); } } #[cfg(test)] mod tests { use crate::demangle; fn run(s: &str) -> String { return demangle(s, &Default::default()).unwrap(); } fn run_raw(s: &str) -> String { return demangle( s, &crate::DemanglingConfig { collapse_scala_names: false, ..Default::default() }, ) .unwrap(); } #[test] fn it_works() { assert_eq!(run("_ST10__dispatch"), "__dispatch"); assert_eq!( run_raw("_SM42sttp.model.headers.CacheDirective$MinFreshD12productArityiEO"), "sttp.model.headers.CacheDirective$MinFresh.productArity: scala.Int" ); assert_eq!( run("_SM42sttp.model.headers.CacheDirective$MinFreshD12productArityiEO"), "sttp.model.headers.CacheDirective$MinFresh.productArity: Int" ); assert_eq!(run_raw("_SM42scala.scalanative.runtime.SymbolFormatter$D10inBounds$1L32scala.scalanative.unsigned.ULongizEPT42scala.scalanative.runtime.SymbolFormatter$"), "scala.scalanative.runtime.SymbolFormatter$.inBounds$1(scala.scalanative.unsigned.ULong,scala.Int): scala.Boolean"); assert_eq!(run("_SM42scala.scalanative.runtime.SymbolFormatter$D10inBounds$1L32scala.scalanative.unsigned.ULongizEPT42scala.scalanative.runtime.SymbolFormatter$"), "scala.scalanative.runtime.SymbolFormatter$.inBounds$1(scala.scalanative.unsigned.ULong,Int): Boolean"); assert_eq!(run_raw("_SM41scalaboot.template.scalatemplate$package$D10$anonfun$3L26scalaboot.template.ContextL15scala.Function1L23java.lang.StringBuilderL31scalaboot.template.UnsafeCursorL23scalaboot.template.MoveuEPT41scalaboot.template.scalatemplate$package$"), "scalaboot.template.scalatemplate$package$.$anonfun$3(scalaboot.template.Context,scala.Function1,java.lang.StringBuilder,scalaboot.template.UnsafeCursor,scalaboot.template.Move): scala.Unit"); assert_eq!(run("_SM41scalaboot.template.scalatemplate$package$D10$anonfun$3L26scalaboot.template.ContextL15scala.Function1L23java.lang.StringBuilderL31scalaboot.template.UnsafeCursorL23scalaboot.template.MoveuEPT41scalaboot.template.scalatemplate$package$"), "scalaboot.template.scalatemplate$package$.$anonfun$3(scalaboot.template.Context,scala.Function1,java.lang.StringBuilder,scalaboot.template.UnsafeCursor,scalaboot.template.Move): Unit"); assert_eq!(run("_SM33scala.scalanative.unsafe.package$D11fromCStringL28scala.scalanative.unsafe.PtrL24java.nio.charset.CharsetL16java.lang.StringEO"), "scala.scalanative.unsafe.package$.fromCString(scala.scalanative.unsafe.Ptr,java.nio.charset.Charset): String"); assert_eq!( run("_SM17java.lang.IntegerD7compareiiiEo"), "java.lang.Integer.compare(Int,Int): Int" ); assert_eq!( run("_SM38scala.scalanative.junit.JUnitFrameworkIE"), "scala.scalanative.junit.JUnitFramework." ); assert_eq!(run("_SM10fansi.TrieD17$init$$$anonfun$5cLAL10fansi.Trie_L12scala.Tuple2uEpT10fansi.Trie"), "fansi.Trie.$init$$$anonfun$5(Char,Array[fansi.Trie],scala.Tuple2): Unit") } }