Skip to main content

java_bin_name/
ty.rs

1use core::fmt::{Debug, Display, Write as _};
2
3use alloc::boxed::Box;
4use smallvec::SmallVec;
5
6use crate::{Cursor, Parse, angle_safe_split};
7
8/// Error type marking the leading character of a type descriptor is invalid.
9#[derive(Debug, Clone)]
10pub struct UnknownTypeTag(char);
11
12impl core::error::Error for UnknownTypeTag {}
13
14impl Display for UnknownTypeTag {
15    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
16        write!(f, "unknown field type '{}'", self.0)
17    }
18}
19
20/// A primitive type of Java language.
21#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
22#[allow(missing_docs)]
23pub enum PrimitiveType {
24    Byte,
25    Char,
26    Double,
27    Float,
28    Int,
29    Long,
30    Short,
31    Boolean,
32}
33
34impl PrimitiveType {
35    /// Key character of this type.
36    #[inline]
37    pub const fn key(self) -> char {
38        match self {
39            Self::Byte => 'B',
40            Self::Char => 'C',
41            Self::Double => 'D',
42            Self::Float => 'F',
43            Self::Int => 'I',
44            Self::Long => 'J',
45            Self::Short => 'S',
46            Self::Boolean => 'Z',
47        }
48    }
49}
50
51impl Debug for PrimitiveType {
52    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53        match self {
54            Self::Byte => write!(f, "byte"),
55            Self::Char => write!(f, "char"),
56            Self::Double => write!(f, "double"),
57            Self::Float => write!(f, "float"),
58            Self::Int => write!(f, "int"),
59            Self::Long => write!(f, "long"),
60            Self::Short => write!(f, "short"),
61            Self::Boolean => write!(f, "boolean"),
62        }
63    }
64}
65
66/// The type of a field, parameter, local variable, or value.
67///
68/// See [JVMS 4.3.2](https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.3.2).
69#[derive(Clone, PartialEq, Eq, Hash)]
70#[allow(missing_docs)] // pointless
71pub enum FieldType<'a> {
72    Primitive(PrimitiveType),
73    Class(&'a str),
74    Array(Box<Self>),
75}
76
77impl Display for FieldType<'_> {
78    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
79        match self {
80            Self::Primitive(ty) => f.write_char(ty.key()),
81            Self::Class(class_name) => write!(f, "L{class_name};"),
82            Self::Array(field_ty) => write!(f, "[{field_ty}"),
83        }
84    }
85}
86
87impl<'a> Parse<'a> for FieldType<'a> {
88    type Error = UnknownTypeTag;
89
90    fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
91        let leading = cursor.get_char();
92        match leading {
93            'B' => Ok(Self::Primitive(PrimitiveType::Byte)),
94            'C' => Ok(Self::Primitive(PrimitiveType::Char)),
95            'D' => Ok(Self::Primitive(PrimitiveType::Double)),
96            'F' => Ok(Self::Primitive(PrimitiveType::Float)),
97            'I' => Ok(Self::Primitive(PrimitiveType::Int)),
98            'J' => Ok(Self::Primitive(PrimitiveType::Long)),
99            'S' => Ok(Self::Primitive(PrimitiveType::Short)),
100            'Z' => Ok(Self::Primitive(PrimitiveType::Boolean)),
101            'L' => cursor
102                .try_advance(|s| s.split_once(';').ok_or(UnknownTypeTag('L')))
103                .map(Self::Class),
104            '[' => Self::parse_from(cursor).map(|t| Self::Array(Box::new(t))),
105            _ => Err(UnknownTypeTag(leading)),
106        }
107    }
108}
109
110impl Debug for FieldType<'_> {
111    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112        match self {
113            Self::Primitive(ty) => Debug::fmt(ty, f),
114            Self::Class(arg0) => Debug::fmt(arg0, f),
115            Self::Array(arg0) => {
116                Debug::fmt(arg0, f)?;
117                write!(f, "[]")
118            }
119        }
120    }
121}
122
123/// Signature of a Java type.
124///
125/// See [JVMS 4.7.9.1](https://docs.oracle.com/javase/specs/jvms/se26/html/jvms-4.html#jvms-4.7.9.1).
126#[derive(Clone, PartialEq, Eq)]
127#[allow(missing_docs, variant_size_differences)]
128pub enum TypeSignature<'a> {
129    Primitive(PrimitiveType),
130    Class {
131        sig: ReducedClassTypeSignature<'a>,
132        suffix: Option<ReducedClassTypeSignature<'a>>,
133    },
134    Type(&'a str),
135    Array(Box<Self>),
136}
137
138/// Signature of a class type.
139///
140/// This differs from `ClassTypeSignature` in JVMS as this does not contain the suffix.
141#[derive(Clone, PartialEq, Eq)]
142#[doc(alias = "SimpleClassTypeSignature")]
143pub struct ReducedClassTypeSignature<'a> {
144    /// Simple or full name of the class, depending on its location.
145    pub name: &'a str,
146    /// Generic arguments.
147    ///
148    /// `*` is denoted by `None`.
149    pub args: SmallVec<[Option<Box<TypeArgument<'a>>>; 1]>,
150}
151
152/// Type of an (generic) argument.
153#[derive(Clone, PartialEq, Eq)]
154pub struct TypeArgument<'a> {
155    /// Wildcard variant.
156    pub kind: TypeArgumentKind,
157    /// Underlying type signature.
158    pub signature: TypeSignature<'a>,
159}
160
161/// Kind of a type argument that is not full wildcard.
162#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
163#[allow(missing_docs)]
164pub enum TypeArgumentKind {
165    #[default]
166    Exact,
167    /// `? extends T` as `+`.
168    Extends,
169    /// `? super T` as `-`.
170    Super,
171}
172
173impl Display for TypeSignature<'_> {
174    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
175        match self {
176            TypeSignature::Primitive(ty) => write!(f, "{}", ty.key()),
177            TypeSignature::Class { sig, suffix } => {
178                write!(f, "L{}", sig)?;
179                if let Some(suffix) = suffix {
180                    write!(f, ".{}", suffix)?;
181                }
182                write!(f, ";")?;
183                Ok(())
184            }
185            TypeSignature::Type(ty) => write!(f, "T{};", ty),
186            TypeSignature::Array(ty) => write!(f, "[{}", ty),
187        }
188    }
189}
190
191impl Display for ReducedClassTypeSignature<'_> {
192    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
193        write!(f, "{}", self.name)?;
194        if !self.args.is_empty() {
195            write!(f, "<")?;
196            for arg in &self.args {
197                if let Some(arg) = arg {
198                    write!(f, "{}", arg)?;
199                } else {
200                    write!(f, "*")?;
201                }
202            }
203            write!(f, ">")?;
204        }
205        Ok(())
206    }
207}
208
209impl Display for TypeArgument<'_> {
210    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
211        match self.kind {
212            TypeArgumentKind::Exact => (),
213            TypeArgumentKind::Extends => write!(f, "+")?,
214            TypeArgumentKind::Super => write!(f, "-")?,
215        }
216        write!(f, "{}", self.signature)?;
217        Ok(())
218    }
219}
220
221impl<'a> Parse<'a> for TypeSignature<'a> {
222    type Error = UnknownTypeTag;
223
224    fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
225        let leading = cursor.get_char();
226        match leading {
227            'B' => Ok(Self::Primitive(PrimitiveType::Byte)),
228            'C' => Ok(Self::Primitive(PrimitiveType::Char)),
229            'D' => Ok(Self::Primitive(PrimitiveType::Double)),
230            'F' => Ok(Self::Primitive(PrimitiveType::Float)),
231            'I' => Ok(Self::Primitive(PrimitiveType::Int)),
232            'J' => Ok(Self::Primitive(PrimitiveType::Long)),
233            'S' => Ok(Self::Primitive(PrimitiveType::Short)),
234            'Z' => Ok(Self::Primitive(PrimitiveType::Boolean)),
235            'L' => {
236                let major = cursor
237                    .try_advance(|s| angle_safe_split(s, &[';', '.']).ok_or(UnknownTypeTag('L')))?;
238                let suffix = match cursor.get_char() {
239                    '.' => {
240                        let suffix = cursor.try_advance(|s| {
241                            angle_safe_split(s, &[';']).ok_or(UnknownTypeTag('L'))
242                        })?;
243                        cursor.get_char();
244                        Some(suffix)
245                    }
246                    ';' => None,
247                    _ => unreachable!(),
248                };
249
250                Ok(Self::Class {
251                    sig: ReducedClassTypeSignature::parse_from(&mut Cursor(major))?,
252                    suffix: suffix
253                        .map(|src| ReducedClassTypeSignature::parse_from(&mut Cursor(src)))
254                        .transpose()?,
255                })
256            }
257            'T' => cursor
258                .try_advance(|s| s.split_once(';').ok_or(UnknownTypeTag('T')))
259                .map(Self::Type),
260            '[' => Self::parse_from(cursor).map(|t| Self::Array(Box::new(t))),
261            _ => Err(UnknownTypeTag(leading)),
262        }
263    }
264}
265
266impl<'a> Parse<'a> for ReducedClassTypeSignature<'a> {
267    type Error = UnknownTypeTag;
268
269    fn parse_from(input: &mut Cursor<'a>) -> Result<Self, UnknownTypeTag> {
270        let src = input.get();
271        input.0 = "";
272        let (name, rem) = src.split_once('<').unzip();
273        let name = name.unwrap_or(src);
274        let mut args = SmallVec::new();
275        if let Some(rem) = rem {
276            let mut cursor = Cursor(rem.strip_suffix('>').unwrap_or(rem));
277            while !cursor.0.is_empty() {
278                let mut c1 = Cursor(cursor.0);
279                if c1.get_char() == '*' {
280                    args.push(None);
281                    cursor = c1;
282                } else {
283                    args.push(Some(Box::new(TypeArgument::parse_from(&mut cursor)?)));
284                }
285            }
286        }
287        Ok(Self { name, args })
288    }
289}
290
291impl<'a> Parse<'a> for TypeArgument<'a> {
292    type Error = UnknownTypeTag;
293
294    fn parse_from(src: &mut Cursor<'a>) -> Result<Self, UnknownTypeTag> {
295        let kind = match src.0.as_bytes().first() {
296            Some(b'+') => {
297                src.get_char();
298                TypeArgumentKind::Extends
299            }
300            Some(b'-') => {
301                src.get_char();
302                TypeArgumentKind::Super
303            }
304            _ => TypeArgumentKind::Exact,
305        };
306        Ok(Self {
307            kind,
308            signature: TypeSignature::parse_from(src)?,
309        })
310    }
311}
312
313impl Debug for TypeSignature<'_> {
314    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
315        match self {
316            TypeSignature::Primitive(ty) => write!(f, "{ty:?}"),
317            TypeSignature::Class { sig, suffix } => {
318                write!(f, "{:?}", sig)?;
319                if let Some(suffix) = suffix {
320                    write!(f, ".{:?}", suffix)?;
321                }
322                Ok(())
323            }
324            TypeSignature::Type(ty) => write!(f, "{}", ty),
325            TypeSignature::Array(ty) => write!(f, "{:?}[]", ty),
326        }
327    }
328}
329
330impl Debug for ReducedClassTypeSignature<'_> {
331    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
332        write!(f, "{}", self.name)?;
333        if !self.args.is_empty() {
334            write!(f, "<")?;
335            let mut iter = self.args.iter().peekable();
336            while let Some(val) = iter.next() {
337                write!(f, "{:?}", val)?;
338                if iter.peek().is_some() {
339                    write!(f, ", ")?;
340                }
341            }
342            write!(f, ">")?;
343        }
344        Ok(())
345    }
346}
347
348impl Debug for TypeArgument<'_> {
349    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
350        match self.kind {
351            TypeArgumentKind::Exact => write!(f, "{:?}", self.signature),
352            TypeArgumentKind::Extends => write!(f, "? extends {:?}", self.signature),
353            TypeArgumentKind::Super => write!(f, "? super {:?}", self.signature),
354        }
355    }
356}
357
358/// Generic parameter of a class or method.
359#[derive(PartialEq, Eq, Clone)]
360pub struct TypeParameter<'a> {
361    /// Name of this generic parameter.
362    pub name: &'a str,
363    /// Class bounds.
364    pub bound_class: Option<TypeSignature<'a>>,
365    /// Interface bounds.
366    pub bound_interface: Box<[TypeSignature<'a>]>,
367}
368
369impl Debug for TypeParameter<'_> {
370    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
371        write!(f, "{}", self.name)?;
372        let mut iter = self
373            .bound_class
374            .iter()
375            .chain(&self.bound_interface)
376            .peekable();
377        if iter.peek().is_some() {
378            write!(f, ": ")?;
379        }
380        while let Some(bound) = iter.next() {
381            write!(f, "{bound:?}")?;
382            if iter.peek().is_some() {
383                write!(f, " + ")?;
384            }
385        }
386        Ok(())
387    }
388}
389
390pub(crate) enum ParseTypeParamsError {
391    UnknownTypeTag(UnknownTypeTag),
392    ExpectedReference,
393}
394
395impl From<UnknownTypeTag> for ParseTypeParamsError {
396    fn from(value: UnknownTypeTag) -> Self {
397        Self::UnknownTypeTag(value)
398    }
399}
400
401pub(crate) fn parse_type_params<'a, F>(
402    contents: &mut Cursor<'a>,
403    mut f: F,
404) -> Result<(), ParseTypeParamsError>
405where
406    F: FnMut(TypeParameter<'a>),
407{
408    struct UnbakedParam<'a> {
409        name: &'a str,
410        bounds: SmallVec<[TypeSignature<'a>; 1]>,
411    }
412
413    impl<'a> From<UnbakedParam<'a>> for TypeParameter<'a> {
414        fn from(value: UnbakedParam<'a>) -> Self {
415            let mut it = value.bounds.into_iter();
416            TypeParameter {
417                name: value.name,
418                bound_class: it.next(),
419                bound_interface: it.collect(),
420            }
421        }
422    }
423
424    let mut unbaked: Option<UnbakedParam<'a>> = None;
425    while let Some(ident) = contents
426        .try_advance(|s| crate::angle_safe_rsplit(s, &[':', ';']).ok_or(()))
427        .ok()
428        .or_else(|| {
429            Some(contents.0)
430                .filter(|s| !s.is_empty())
431                .inspect(|_| contents.clear())
432        })
433    {
434        let ident = ident.strip_suffix(':').unwrap_or(ident);
435        if let Some(current) = unbaked.as_mut()
436            && ident.ends_with(';')
437        {
438            let bound = crate::parse(ident)?;
439            if !matches!(
440                bound,
441                TypeSignature::Class { .. } | TypeSignature::Type(_) | TypeSignature::Array(_)
442            ) {
443                return Err(ParseTypeParamsError::ExpectedReference);
444            }
445            current.bounds.push(bound);
446        } else {
447            if let Some(prev) = unbaked.replace(UnbakedParam {
448                name: ident,
449                bounds: SmallVec::new(),
450            }) {
451                f(prev.into());
452            }
453        }
454    }
455    if let Some(last) = unbaked {
456        f(last.into());
457    }
458    Ok(())
459}
460
461pub(crate) fn display_type_params(
462    params: &[TypeParameter<'_>],
463    f: &mut core::fmt::Formatter<'_>,
464) -> core::fmt::Result {
465    write!(f, "<")?;
466    for param in params {
467        write!(f, "{}:", param.name)?;
468        if let Some(bound) = &param.bound_class {
469            write!(f, "{bound}")?;
470            for bound in &param.bound_interface {
471                write!(f, ":{bound}")?;
472            }
473        }
474    }
475    write!(f, ">")
476}
477
478#[cfg(test)]
479mod tests {
480    use alloc::boxed::Box;
481    use smallvec::{SmallVec, smallvec};
482
483    use crate::{
484        FieldType, PrimitiveType, ReducedClassTypeSignature, TypeArgument, TypeArgumentKind,
485        TypeSignature, parse, validate_rw,
486    };
487
488    #[test]
489    fn primitives() {
490        macro_rules! primitives {
491            ($($v:ident,$l:expr),*$(,)?) => {
492                $(
493                assert_eq!(parse::<'_, FieldType<'_>>($l).unwrap(), FieldType::Primitive(PrimitiveType::$v));
494                validate_rw::<'_, FieldType<'_>>($l);
495
496                assert_eq!(parse::<'_, TypeSignature<'_>>($l).unwrap(), TypeSignature::Primitive(PrimitiveType::$v));
497                validate_rw::<'_, TypeSignature<'_>>($l);
498                )*
499            };
500        }
501
502        primitives! {
503            Byte, "B",
504            Char, "C",
505            Double, "D",
506            Float, "F",
507            Int, "I",
508            Long, "J",
509            Short, "S",
510            Boolean, "Z",
511        }
512    }
513
514    #[test]
515    fn class() {
516        assert_eq!(
517            parse::<'_, FieldType<'_>>("Ljava/lang/Object;").unwrap(),
518            FieldType::Class("java/lang/Object")
519        );
520        validate_rw::<'_, FieldType<'_>>("Ljava/lang/Object;");
521
522        assert_eq!(
523            parse::<'_, TypeSignature<'_>>("Ljava/lang/Object;").unwrap(),
524            TypeSignature::Class {
525                sig: ReducedClassTypeSignature {
526                    name: "java/lang/Object",
527                    args: SmallVec::new()
528                },
529                suffix: None
530            }
531        );
532        validate_rw::<'_, TypeSignature<'_>>("Ljava/lang/Object;");
533    }
534
535    #[test]
536    fn array_primitive() {
537        assert_eq!(
538            parse::<'_, FieldType<'_>>("[I").unwrap(),
539            FieldType::Array(Box::new(FieldType::Primitive(PrimitiveType::Int)))
540        );
541        validate_rw::<'_, FieldType<'_>>("[I");
542
543        assert_eq!(
544            parse::<'_, TypeSignature<'_>>("[I").unwrap(),
545            TypeSignature::Array(Box::new(TypeSignature::Primitive(PrimitiveType::Int)))
546        );
547        validate_rw::<'_, TypeSignature<'_>>("[I");
548    }
549
550    #[test]
551    fn array_primitive_2d() {
552        assert_eq!(
553            parse::<'_, FieldType<'_>>("[[I").unwrap(),
554            FieldType::Array(Box::new(FieldType::Array(Box::new(FieldType::Primitive(
555                PrimitiveType::Int
556            )))))
557        );
558        validate_rw::<'_, FieldType<'_>>("[[I");
559        assert_eq!(
560            parse::<'_, TypeSignature<'_>>("[[I").unwrap(),
561            TypeSignature::Array(Box::new(TypeSignature::Array(Box::new(
562                TypeSignature::Primitive(PrimitiveType::Int)
563            ))))
564        );
565        validate_rw::<'_, TypeSignature<'_>>("[[I");
566    }
567
568    #[test]
569    fn array_class() {
570        assert_eq!(
571            parse::<'_, FieldType<'_>>("[Ljava/lang/Object;").unwrap(),
572            FieldType::Array(Box::new(FieldType::Class("java/lang/Object")))
573        );
574        validate_rw::<'_, FieldType<'_>>("[Ljava/lang/Object;");
575        validate_rw::<'_, TypeSignature<'_>>("[Ljava/lang/Object;");
576    }
577
578    #[test]
579    fn array_class_2d() {
580        assert_eq!(
581            parse::<'_, FieldType<'_>>("[[Ljava/lang/Object;").unwrap(),
582            FieldType::Array(Box::new(FieldType::Array(Box::new(FieldType::Class(
583                "java/lang/Object"
584            )))))
585        );
586        validate_rw::<'_, FieldType<'_>>("[[Ljava/lang/Object;");
587        validate_rw::<'_, TypeSignature<'_>>("[[Ljava/lang/Object;");
588    }
589
590    #[test]
591    fn class_generics() {
592        assert_eq!(
593            parse::<'_, TypeSignature<'_>>(
594                "Ljava/util/Map<Ljava/lang/String;+Ljava/lang/Integer;>;"
595            )
596            .unwrap(),
597            TypeSignature::Class {
598                sig: ReducedClassTypeSignature {
599                    name: "java/util/Map",
600                    args: smallvec![
601                        Some(Box::new(TypeArgument {
602                            kind: TypeArgumentKind::Exact,
603                            signature: TypeSignature::Class {
604                                sig: ReducedClassTypeSignature {
605                                    name: "java/lang/String",
606                                    args: SmallVec::new(),
607                                },
608                                suffix: None
609                            }
610                        })),
611                        Some(Box::new(TypeArgument {
612                            kind: TypeArgumentKind::Extends,
613                            signature: TypeSignature::Class {
614                                sig: ReducedClassTypeSignature {
615                                    name: "java/lang/Integer",
616                                    args: SmallVec::new(),
617                                },
618                                suffix: None
619                            }
620                        })),
621                    ]
622                },
623                suffix: None
624            }
625        );
626        validate_rw::<'_, TypeSignature<'_>>(
627            "Ljava/util/Map<Ljava/lang/String;Ljava/lang/Integer;>;",
628        );
629    }
630
631    #[test]
632    fn class_generics_any() {
633        assert_eq!(
634            parse::<'_, TypeSignature<'_>>("Ljava/util/concurrent/Future<*>;").unwrap(),
635            TypeSignature::Class {
636                sig: ReducedClassTypeSignature {
637                    name: "java/util/concurrent/Future",
638                    args: smallvec![None]
639                },
640                suffix: None
641            }
642        );
643        validate_rw::<'_, TypeSignature<'_>>("Ljava/util/concurrent/Future<*>;");
644    }
645
646    #[test]
647    fn class_generics_suffix() {
648        assert_eq!(
649            parse::<'_, TypeSignature<'_>>("LOuter<TT;>.Inner<TU;>;").unwrap(),
650            TypeSignature::Class {
651                sig: ReducedClassTypeSignature {
652                    name: "Outer",
653                    args: smallvec![Some(Box::new(TypeArgument {
654                        kind: TypeArgumentKind::Exact,
655                        signature: TypeSignature::Type("T"),
656                    }))]
657                },
658                suffix: Some(ReducedClassTypeSignature {
659                    name: "Inner",
660                    args: smallvec![Some(Box::new(TypeArgument {
661                        kind: TypeArgumentKind::Exact,
662                        signature: TypeSignature::Type("U"),
663                    }))]
664                },)
665            }
666        );
667        validate_rw::<'_, TypeSignature<'_>>("LOuter<TT;>.Inner<TU;>;");
668    }
669
670    #[test]
671    fn type_sig() {
672        assert_eq!(
673            parse::<'_, TypeSignature<'_>>("TT;").unwrap(),
674            TypeSignature::Type("T")
675        );
676        validate_rw::<'_, TypeSignature<'_>>("TT;");
677    }
678}