java-bin-name 0.3.0

Java binary name parser and writer
Documentation
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
use core::{
    convert::Infallible,
    fmt::{Debug, Display},
};

use alloc::{boxed::Box, vec::Vec};
use smallvec::SmallVec;

use crate::{
    Cursor, Parse, ReprForm, TypeParameter, TypeSignature, method::MethodDescriptor,
    strip_digits_prefix,
};

/// Binary name of a class or interface.
///
/// See [JLS 13.1](https://docs.oracle.com/javase/specs/jls/se25/html/jls-13.html#jls-13.1).
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ClassName<'a> {
    /// Binary name of a top level class or interface.
    TopLevel(CanonicalClassName<'a>),
    /// Binary name of a member class or interface, or
    /// a type variable declared by a generic class or interface.
    #[doc(alias = "Generic")]
    Member {
        /// The binary name of its immediately enclosing class or interface.
        parent: Box<Self>,
        /// The simple name of the member or the type variable.
        simple: &'a str,
    },
    /// Binary name of a local class or interface.
    Local {
        /// The binary name of its immediately enclosing class or interface.
        parent: Box<Self>,
        /// The simple name of the local class.
        simple: &'a str,
        /// A non-empty sequence of digits.
        index: u32,
    },
    /// Binary name of an anonymous class.
    Anonymous {
        /// The binary name of its immediately enclosing class or interface.
        parent: Box<Self>,
        /// A non-empty sequence of digits.
        index: u32,
    },
    /// Binary name of a type variable declared by a generic method, or
    /// a constructor.
    #[doc(alias = "ConstructorGeneric")]
    MethodGeneric {
        /// The binary name of the class or interface declaring the method or constructor.
        class: Box<Self>,
        /// The descriptor of the method or constructor.
        method: MethodDescriptor<'a>,
        /// The simple name of the type variable.
        simple: &'a str,
    },
}

impl Display for ClassName<'_> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            ClassName::TopLevel(canonical_class_name) => write!(f, "{canonical_class_name}"),
            ClassName::Member { parent, simple } => write!(f, "{parent}${simple}"),
            ClassName::Local {
                parent,
                simple,
                index,
            } => write!(f, "{parent}${index}{simple}"),
            ClassName::Anonymous { parent, index } => write!(f, "{parent}${index}"),
            ClassName::MethodGeneric {
                class,
                method,
                simple,
            } => write!(f, "{class}${method}${simple}"),
        }
    }
}

impl<'a> Parse<'a> for ClassName<'a> {
    type Error = Infallible;

    fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
        let s = cursor.get();
        if let Some((parent, simple)) = s.rsplit_once('$') {
            if let Some((parent, method)) = parent.rsplit_once('$')
                && method.chars().next().is_some_and(|c| c == '(')
                && let Ok(method) = MethodDescriptor::parse_from(&mut Cursor::new(method))
            {
                return Ok(Self::MethodGeneric {
                    class: Box::new(Self::parse_from(&mut Cursor::new(parent)).unwrap()),
                    method,
                    simple,
                });
            }

            let parent = Box::new(Self::parse_from(&mut Cursor::new(parent))?);
            let (digits, simple) = strip_digits_prefix(simple);
            cursor.clear();
            Ok(match (digits, simple.is_empty()) {
                // expected non-empty, but have to handle errors that way
                (None, _) => Self::Member { parent, simple },
                (Some(index), true) => Self::Anonymous { parent, index },
                (Some(index), false) => Self::Local {
                    parent,
                    simple,
                    index,
                },
            })
        } else {
            CanonicalClassName::parse_from(cursor).map(Self::TopLevel)
        }
    }
}

/// Canonical, or fully qualified name of a class or interface.
///
/// See [JLS 6.7](https://docs.oracle.com/javase/specs/jls/se25/html/jls-6.html#jls-6.7).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CanonicalClassName<'a> {
    /// The fully qualified name of the package.
    pub package: Option<&'a str>,
    /// The simple name of the class or interface.
    pub simple: &'a str,
    /// The representation form of this class name.
    pub form: ReprForm,
}

impl Display for CanonicalClassName<'_> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        if let Some(pkg) = self.package {
            write!(f, "{pkg}{}{}", self.form.package_separator(), self.simple)
        } else {
            write!(f, "{}", self.simple)
        }
    }
}

impl<'a> Parse<'a> for CanonicalClassName<'a> {
    type Error = Infallible;

    fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
        Ok(cursor.advance(|s| {
            let form = if s.contains('/') {
                ReprForm::Internal
            } else {
                ReprForm::JLS
            };
            let (package, c) = s.rsplit_once(form.package_separator()).unzip();
            (
                Self {
                    package,
                    simple: c.unwrap_or(s),
                    form,
                },
                "",
            )
        }))
    }
}

/// Signature of a class or interface declaration.
#[derive(PartialEq, Eq, Clone)]
pub struct ClassSignature<'a> {
    /// Generic parameters.
    pub params: SmallVec<[TypeParameter<'a>; 1]>,
    /// Super class.
    pub extends: TypeSignature<'a>,
    /// Super interfaces.
    pub impls: Box<[TypeSignature<'a>]>,
}

/// Errors encountered while parsing a class signature.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum InvalidClassSignature {
    /// Unclosed angles.
    UnclosedAngles,
    /// Unknown type signature tag.
    UnknownTypeTag(crate::UnknownTypeTag),
    /// Expected `ReferenceTypeSignature`.
    ExpectedReference,
    /// Expected `ClassTypeSignature`.
    ExpectedClass,
}

impl<'a> Parse<'a> for ClassSignature<'a> {
    type Error = InvalidClassSignature;

    fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
        let mut params = SmallVec::new();
        if cursor.0.starts_with('<') {
            cursor.get_char();
            let contents = cursor.try_advance(|s| {
                crate::angle_safe_split(s, &['>']).ok_or(InvalidClassSignature::UnclosedAngles)
            })?;
            cursor.get_char();
            let mut contents = Cursor(contents);
            crate::parse_type_params(&mut contents, |param| params.push(param))?;
        }

        Ok(Self {
            params,
            extends: {
                let sig = TypeSignature::parse_from(cursor)?;
                if !matches!(sig, TypeSignature::Class { .. }) {
                    return Err(InvalidClassSignature::ExpectedClass);
                }
                sig
            },
            impls: {
                let mut buf = Vec::new();
                while !cursor.0.is_empty() {
                    let sig = TypeSignature::parse_from(cursor)?;
                    if !matches!(sig, TypeSignature::Class { .. }) {
                        return Err(InvalidClassSignature::ExpectedClass);
                    }
                    buf.push(sig);
                }
                buf.into()
            },
        })
    }
}

impl Display for ClassSignature<'_> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        if !self.params.is_empty() {
            crate::display_type_params(&self.params, f)?;
        }
        write!(f, "{}", self.extends)?;
        for sig in &self.impls {
            write!(f, "{}", sig)?;
        }
        Ok(())
    }
}

impl Display for InvalidClassSignature {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::UnclosedAngles => write!(f, "unclosed angles"),
            Self::UnknownTypeTag(err) => write!(f, "{err}"),
            Self::ExpectedReference => {
                write!(f, "expected type signature to be `ReferenceTypeSignature`")
            }
            Self::ExpectedClass => write!(f, "expected type signature to be `ClassTypeSignature`"),
        }
    }
}

impl core::error::Error for InvalidClassSignature {
    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
        match self {
            Self::UnknownTypeTag(err) => Some(err),
            _ => None,
        }
    }
}

impl From<crate::UnknownTypeTag> for InvalidClassSignature {
    fn from(value: crate::UnknownTypeTag) -> Self {
        Self::UnknownTypeTag(value)
    }
}

impl From<crate::ParseTypeParamsError> for InvalidClassSignature {
    fn from(value: crate::ParseTypeParamsError) -> Self {
        match value {
            crate::ParseTypeParamsError::UnknownTypeTag(err) => Self::UnknownTypeTag(err),
            crate::ParseTypeParamsError::ExpectedReference => Self::ExpectedReference,
        }
    }
}

impl Debug for ClassSignature<'_> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        if !self.params.is_empty() {
            let mut iter = self.params.iter().peekable();
            write!(f, "<")?;
            while let Some(param) = iter.next() {
                write!(f, "{param:?}")?;
                if iter.peek().is_some() {
                    write!(f, ", ")?;
                }
            }
            write!(f, ">")?;
        }
        write!(f, " extends {:?}", self.extends)?;
        if !self.impls.is_empty() {
            let mut iter = self.impls.iter().peekable();
            write!(f, " implements ")?;
            while let Some(mom) = iter.next() {
                write!(f, "{mom:?}")?;
                if iter.peek().is_some() {
                    write!(f, ", ")?;
                }
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use alloc::boxed::Box;
    use smallvec::SmallVec;

    use crate::{
        CanonicalClassName, ClassName, ClassSignature, ReducedClassTypeSignature, ReprForm,
        TypeParameter, TypeSignature, parse, validate_rw,
    };

    #[test]
    fn top_level() {
        assert_eq!(
            parse::<'_, ClassName<'_>>("java.lang.String").unwrap(),
            ClassName::TopLevel(CanonicalClassName {
                package: Some("java.lang"),
                simple: "String",
                form: ReprForm::JLS,
            })
        );

        assert_eq!(
            parse::<'_, ClassName<'_>>("Foo").unwrap(),
            ClassName::TopLevel(CanonicalClassName {
                package: None,
                simple: "Foo",
                form: ReprForm::JLS,
            })
        );

        validate_rw::<'_, ClassName<'_>>("java.lang.String");
    }

    #[test]
    fn top_level_jvm() {
        assert_eq!(
            parse::<'_, ClassName<'_>>("java/lang/String").unwrap(),
            ClassName::TopLevel(CanonicalClassName {
                package: Some("java/lang"),
                simple: "String",
                form: ReprForm::Internal
            })
        );

        validate_rw::<'_, ClassName<'_>>("java/lang/String");
    }

    #[test]
    fn member() {
        assert_eq!(
            parse::<'_, ClassName<'_>>("java.util.Map$Entry").unwrap(),
            ClassName::Member {
                parent: Box::new(ClassName::TopLevel(CanonicalClassName {
                    package: Some("java.util"),
                    simple: "Map",
                    form: ReprForm::JLS
                })),
                simple: "Entry"
            }
        );

        validate_rw::<'_, ClassName<'_>>("java.util.Map$Entry");
    }

    #[test]
    fn local() {
        assert_eq!(
            parse::<'_, ClassName<'_>>("com.example.OuterClass$1LocalClass").unwrap(),
            ClassName::Local {
                parent: Box::new(ClassName::TopLevel(CanonicalClassName {
                    package: Some("com.example"),
                    simple: "OuterClass",
                    form: ReprForm::JLS
                })),
                index: 1,
                simple: "LocalClass"
            }
        );

        validate_rw::<'_, ClassName<'_>>("com.example.OuterClass$1LocalClass");
    }

    #[test]
    fn anonymous() {
        assert_eq!(
            parse::<'_, ClassName<'_>>("com.example.OuterClass$1").unwrap(),
            ClassName::Anonymous {
                parent: Box::new(ClassName::TopLevel(CanonicalClassName {
                    package: Some("com.example"),
                    simple: "OuterClass",
                    form: ReprForm::JLS
                })),
                index: 1,
            }
        );

        validate_rw::<'_, ClassName<'_>>("com.example.OuterClass$1");
    }

    #[test]
    fn class_sig() {
        assert_eq!(
            parse::<'_, ClassSignature<'_>>(
                "Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Cloneable;"
            )
            .unwrap(),
            ClassSignature {
                params: SmallVec::new(),
                extends: TypeSignature::Class {
                    sig: ReducedClassTypeSignature {
                        name: "java/lang/Object",
                        args: SmallVec::new()
                    },
                    suffix: None
                },
                impls: Box::new([
                    TypeSignature::Class {
                        sig: ReducedClassTypeSignature {
                            name: "java/io/Serializable",
                            args: SmallVec::new()
                        },
                        suffix: None
                    },
                    TypeSignature::Class {
                        sig: ReducedClassTypeSignature {
                            name: "java/lang/Cloneable",
                            args: SmallVec::new()
                        },
                        suffix: None
                    },
                ])
            }
        );
        validate_rw::<'_, ClassSignature<'_>>(
            "Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Cloneable;",
        );

        assert_eq!(
            parse::<'_, ClassSignature<'_>>("<T:Ljava/lang/Object;K:V:>Ljava/lang/Object;")
                .unwrap(),
            ClassSignature {
                params: smallvec::smallvec![
                    TypeParameter {
                        name: "T",
                        bound_class: Some(TypeSignature::Class {
                            sig: ReducedClassTypeSignature {
                                name: "java/lang/Object",
                                args: SmallVec::new()
                            },
                            suffix: None
                        }),
                        bound_interface: Box::new([])
                    },
                    TypeParameter {
                        name: "K",
                        bound_class: None,
                        bound_interface: Box::new([])
                    },
                    TypeParameter {
                        name: "V",
                        bound_class: None,
                        bound_interface: Box::new([])
                    }
                ],
                extends: TypeSignature::Class {
                    sig: ReducedClassTypeSignature {
                        name: "java/lang/Object",
                        args: SmallVec::new()
                    },
                    suffix: None
                },
                impls: Box::new([])
            }
        );
        validate_rw::<'_, ClassSignature<'_>>("<T:Ljava/lang/Object;K:V:>Ljava/lang/Object;");
    }
}