1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! Name substitutions for JNI name mangling. See
16 //! <https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names>
17 //! for spec details.
18
substitute_method_chars(s: &str) -> String19 pub fn substitute_method_chars(s: &str) -> String {
20 s.chars()
21 .flat_map(|c| -> CharIter {
22 match c {
23 '$' => "_00024".into(),
24 '_' => "_1".into(),
25 _ => c.into(),
26 }
27 })
28 .collect()
29 }
30
substitute_package_chars(s: &str) -> String31 pub fn substitute_package_chars(s: &str) -> String {
32 s.chars()
33 .flat_map(|c| -> CharIter {
34 match c {
35 '.' => "_".into(),
36 '$' => "_00024".into(),
37 '_' => "_1".into(),
38 _ => c.into(),
39 }
40 })
41 .collect()
42 }
43
substitute_class_chars(s: &str) -> String44 pub fn substitute_class_chars(s: &str) -> String {
45 s.chars()
46 .flat_map(|c| -> CharIter {
47 match c {
48 // Use dot or dollar for inner classes: `'.' -> '$' -> "_00024"`
49 '.' | '$' => "_00024".into(),
50 '_' => "_1".into(),
51 _ => c.into(),
52 }
53 })
54 .collect()
55 }
56
57 /// A `char` iterator that can be created from either a `char` or a `&'static str`.
58 enum CharIter {
59 One(core::option::IntoIter<char>),
60 Many(core::str::Chars<'static>),
61 }
62
63 impl Iterator for CharIter {
64 type Item = char;
next(&mut self) -> Option<char>65 fn next(&mut self) -> Option<char> {
66 match *self {
67 Self::One(ref mut iter) => iter.next(),
68 Self::Many(ref mut iter) => iter.next(),
69 }
70 }
71 }
72
73 impl From<char> for CharIter {
from(c: char) -> Self74 fn from(c: char) -> Self {
75 Self::One(Some(c).into_iter())
76 }
77 }
78
79 impl From<&'static str> for CharIter {
from(s: &'static str) -> Self80 fn from(s: &'static str) -> Self {
81 Self::Many(s.chars())
82 }
83 }
84
85 #[cfg(test)]
86 mod tests {
87 use super::*;
88
89 #[test]
test_char_iter_one()90 fn test_char_iter_one() {
91 let seq = CharIter::from('a').collect::<String>();
92 assert_eq!("a", &seq);
93 }
94
95 #[test]
test_char_iter_many()96 fn test_char_iter_many() {
97 let seq = CharIter::from("asdf").collect::<String>();
98 assert_eq!("asdf", &seq);
99 }
100
101 #[test]
test_substitute_method_chars()102 fn test_substitute_method_chars() {
103 let mangled = substitute_method_chars("method_with_under$cores");
104 assert_eq!("method_1with_1under_00024cores", &mangled);
105 }
106
107 #[test]
test_substitute_package_chars()108 fn test_substitute_package_chars() {
109 let mangled = substitute_package_chars("com.weird_name.java");
110 assert_eq!("com_weird_1name_java", &mangled);
111 }
112
113 #[test]
test_substitute_class_chars()114 fn test_substitute_class_chars() {
115 // Both dot and dollar should work here
116
117 let mangled = substitute_class_chars("Foo.Inner");
118 assert_eq!("Foo_00024Inner", &mangled);
119
120 let mangled = substitute_class_chars("Foo$Inner");
121 assert_eq!("Foo_00024Inner", &mangled);
122 }
123 }
124