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};
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: ClassTypeSignature<'a>,
132        suffix: Option<ClassTypeSignature<'a>>,
133    },
134    Type(&'a str),
135    Array(Box<Self>),
136}
137
138/// Signature of a class type.
139#[derive(Clone, PartialEq, Eq)]
140pub struct ClassTypeSignature<'a> {
141    /// Simple or full name of the class, depending on its location.
142    pub name: &'a str,
143    /// Generic arguments.
144    ///
145    /// `*` is denoted by `None`.
146    pub args: SmallVec<[Option<Box<TypeArgument<'a>>>; 1]>,
147}
148
149/// Type of an (generic) argument.
150#[derive(Clone, PartialEq, Eq)]
151pub struct TypeArgument<'a> {
152    /// Wildcard variant.
153    pub kind: TypeArgumentKind,
154    /// Underlying type signature.
155    pub signature: TypeSignature<'a>,
156}
157
158/// Kind of a type argument that is not full wildcard.
159#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
160#[allow(missing_docs)]
161pub enum TypeArgumentKind {
162    #[default]
163    Exact,
164    /// `? extends T` as `+`.
165    Extends,
166    /// `? super T` as `-`.
167    Super,
168}
169
170impl Display for TypeSignature<'_> {
171    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
172        match self {
173            TypeSignature::Primitive(ty) => write!(f, "{}", ty.key()),
174            TypeSignature::Class { sig, suffix } => {
175                write!(f, "L{}", sig)?;
176                if let Some(suffix) = suffix {
177                    write!(f, ".{}", suffix)?;
178                }
179                write!(f, ";")?;
180                Ok(())
181            }
182            TypeSignature::Type(ty) => write!(f, "T{};", ty),
183            TypeSignature::Array(ty) => write!(f, "[{}", ty),
184        }
185    }
186}
187
188impl Display for ClassTypeSignature<'_> {
189    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
190        write!(f, "{}", self.name)?;
191        if !self.args.is_empty() {
192            write!(f, "<")?;
193            for arg in &self.args {
194                if let Some(arg) = arg {
195                    write!(f, "{}", arg)?;
196                } else {
197                    write!(f, "*")?;
198                }
199            }
200            write!(f, ">")?;
201        }
202        Ok(())
203    }
204}
205
206impl Display for TypeArgument<'_> {
207    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
208        match self.kind {
209            TypeArgumentKind::Exact => (),
210            TypeArgumentKind::Extends => write!(f, "+")?,
211            TypeArgumentKind::Super => write!(f, "-")?,
212        }
213        write!(f, "{}", self.signature)?;
214        Ok(())
215    }
216}
217
218impl<'a> Parse<'a> for TypeSignature<'a> {
219    type Error = UnknownTypeTag;
220
221    fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
222        let leading = cursor.get_char();
223        match leading {
224            'B' => Ok(Self::Primitive(PrimitiveType::Byte)),
225            'C' => Ok(Self::Primitive(PrimitiveType::Char)),
226            'D' => Ok(Self::Primitive(PrimitiveType::Double)),
227            'F' => Ok(Self::Primitive(PrimitiveType::Float)),
228            'I' => Ok(Self::Primitive(PrimitiveType::Int)),
229            'J' => Ok(Self::Primitive(PrimitiveType::Long)),
230            'S' => Ok(Self::Primitive(PrimitiveType::Short)),
231            'Z' => Ok(Self::Primitive(PrimitiveType::Boolean)),
232            'L' => {
233                let mut offset0 = if let Some((a, b)) = cursor.0.split_once('<')
234                    && !a.contains(['.', ';'])
235                {
236                    let mut rem = b;
237                    let mut layers = 1usize;
238                    while layers > 0 {
239                        let pos = rem.find(['<', '>']).expect("unclosed angles");
240                        match rem.as_bytes()[pos] {
241                            b'<' => layers += 1,
242                            b'>' => layers -= 1,
243                            _ => unreachable!(),
244                        }
245                        rem = &rem[pos + 1..];
246                    }
247                    unsafe { rem.as_ptr().byte_offset_from_unsigned(cursor.0.as_ptr()) }
248                } else {
249                    0
250                };
251                offset0 += cursor.0[offset0..]
252                    .find([';', '.'])
253                    .ok_or(UnknownTypeTag('L'))?;
254                let major = cursor.advance(|s| s.split_at(offset0));
255                let suffix = match cursor.get_char() {
256                    '.' => {
257                        let mut offset1 = if let Some((a, b)) = cursor.0.split_once('<')
258                            && !a.contains(';')
259                        {
260                            let mut rem = b;
261                            let mut layers = 1usize;
262                            while layers > 0 {
263                                let pos = rem.find(['<', '>']).expect("unclosed angles");
264                                match rem.as_bytes()[pos] {
265                                    b'<' => layers += 1,
266                                    b'>' => layers -= 1,
267                                    _ => unreachable!(),
268                                }
269                                rem = &rem[pos + 1..];
270                            }
271                            unsafe { rem.as_ptr().byte_offset_from_unsigned(cursor.0.as_ptr()) }
272                        } else {
273                            0
274                        };
275                        offset1 += cursor.0[offset1..].find(';').ok_or(UnknownTypeTag('L'))?;
276                        let suffix = cursor.advance(|s| s.split_at(offset1));
277                        cursor.get_char();
278                        Some(suffix)
279                    }
280                    ';' => None,
281                    _ => unreachable!(),
282                };
283
284                Ok(Self::Class {
285                    sig: ClassTypeSignature::parse_from(&mut Cursor(major))?,
286                    suffix: suffix
287                        .map(|src| ClassTypeSignature::parse_from(&mut Cursor(src)))
288                        .transpose()?,
289                })
290            }
291            'T' => cursor
292                .try_advance(|s| s.split_once(';').ok_or(UnknownTypeTag('T')))
293                .map(Self::Type),
294            '[' => Self::parse_from(cursor).map(|t| Self::Array(Box::new(t))),
295            _ => Err(UnknownTypeTag(leading)),
296        }
297    }
298}
299
300impl<'a> Parse<'a> for ClassTypeSignature<'a> {
301    type Error = UnknownTypeTag;
302
303    fn parse_from(input: &mut Cursor<'a>) -> Result<Self, UnknownTypeTag> {
304        let src = input.get();
305        input.0 = "";
306        let (name, rem) = src.split_once('<').unzip();
307        let name = name.unwrap_or(src);
308        let mut args = SmallVec::new();
309        if let Some(rem) = rem {
310            let mut cursor = Cursor(rem.strip_suffix('>').unwrap_or(rem));
311            while !cursor.0.is_empty() {
312                let mut c1 = Cursor(cursor.0);
313                if c1.get_char() == '*' {
314                    args.push(None);
315                    cursor = c1;
316                } else {
317                    args.push(Some(Box::new(TypeArgument::parse_from(&mut cursor)?)));
318                }
319            }
320        }
321        Ok(Self { name, args })
322    }
323}
324
325impl<'a> Parse<'a> for TypeArgument<'a> {
326    type Error = UnknownTypeTag;
327
328    fn parse_from(src: &mut Cursor<'a>) -> Result<Self, UnknownTypeTag> {
329        let kind = match src.0.as_bytes().first() {
330            Some(b'+') => {
331                src.get_char();
332                TypeArgumentKind::Extends
333            }
334            Some(b'_') => {
335                src.get_char();
336                TypeArgumentKind::Super
337            }
338            _ => TypeArgumentKind::Exact,
339        };
340        Ok(Self {
341            kind,
342            signature: TypeSignature::parse_from(src)?,
343        })
344    }
345}
346
347impl Debug for TypeSignature<'_> {
348    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
349        match self {
350            TypeSignature::Primitive(ty) => write!(f, "{ty:?}"),
351            TypeSignature::Class { sig, suffix } => {
352                write!(f, "{:?}", sig)?;
353                if let Some(suffix) = suffix {
354                    write!(f, ".{:?}", suffix)?;
355                }
356                Ok(())
357            }
358            TypeSignature::Type(ty) => write!(f, "{}", ty),
359            TypeSignature::Array(ty) => write!(f, "{:?}[]", ty),
360        }
361    }
362}
363
364impl Debug for ClassTypeSignature<'_> {
365    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
366        write!(f, "{}", self.name)?;
367        if !self.args.is_empty() {
368            write!(f, "<")?;
369            let mut iter = self.args.iter().peekable();
370            while let Some(val) = iter.next() {
371                write!(f, "{:?}", val)?;
372                if iter.peek().is_some() {
373                    write!(f, ", ")?;
374                }
375            }
376            write!(f, ">")?;
377        }
378        Ok(())
379    }
380}
381
382impl Debug for TypeArgument<'_> {
383    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
384        match self.kind {
385            TypeArgumentKind::Exact => write!(f, "{:?}", self.signature),
386            TypeArgumentKind::Extends => write!(f, "? extends {:?}", self.signature),
387            TypeArgumentKind::Super => write!(f, "? super {:?}", self.signature),
388        }
389    }
390}
391
392#[cfg(test)]
393mod tests {
394    use alloc::boxed::Box;
395    use smallvec::{SmallVec, smallvec};
396
397    use crate::{
398        ClassTypeSignature, FieldType, PrimitiveType, TypeArgument, TypeArgumentKind,
399        TypeSignature, parse, validate_rw,
400    };
401
402    #[test]
403    fn primitives() {
404        macro_rules! primitives {
405            ($($v:ident,$l:expr),*$(,)?) => {
406                $(
407                assert_eq!(parse::<'_, FieldType<'_>>($l).unwrap(), FieldType::Primitive(PrimitiveType::$v));
408                validate_rw::<'_, FieldType<'_>>($l);
409
410                assert_eq!(parse::<'_, TypeSignature<'_>>($l).unwrap(), TypeSignature::Primitive(PrimitiveType::$v));
411                validate_rw::<'_, TypeSignature<'_>>($l);
412                )*
413            };
414        }
415
416        primitives! {
417            Byte, "B",
418            Char, "C",
419            Double, "D",
420            Float, "F",
421            Int, "I",
422            Long, "J",
423            Short, "S",
424            Boolean, "Z",
425        }
426    }
427
428    #[test]
429    fn class() {
430        assert_eq!(
431            parse::<'_, FieldType<'_>>("Ljava/lang/Object;").unwrap(),
432            FieldType::Class("java/lang/Object")
433        );
434        validate_rw::<'_, FieldType<'_>>("Ljava/lang/Object;");
435
436        assert_eq!(
437            parse::<'_, TypeSignature<'_>>("Ljava/lang/Object;").unwrap(),
438            TypeSignature::Class {
439                sig: ClassTypeSignature {
440                    name: "java/lang/Object",
441                    args: SmallVec::new()
442                },
443                suffix: None
444            }
445        );
446        validate_rw::<'_, TypeSignature<'_>>("Ljava/lang/Object;");
447    }
448
449    #[test]
450    fn array_primitive() {
451        assert_eq!(
452            parse::<'_, FieldType<'_>>("[I").unwrap(),
453            FieldType::Array(Box::new(FieldType::Primitive(PrimitiveType::Int)))
454        );
455        validate_rw::<'_, FieldType<'_>>("[I");
456
457        assert_eq!(
458            parse::<'_, TypeSignature<'_>>("[I").unwrap(),
459            TypeSignature::Array(Box::new(TypeSignature::Primitive(PrimitiveType::Int)))
460        );
461        validate_rw::<'_, TypeSignature<'_>>("[I");
462    }
463
464    #[test]
465    fn array_primitive_2d() {
466        assert_eq!(
467            parse::<'_, FieldType<'_>>("[[I").unwrap(),
468            FieldType::Array(Box::new(FieldType::Array(Box::new(FieldType::Primitive(
469                PrimitiveType::Int
470            )))))
471        );
472        validate_rw::<'_, FieldType<'_>>("[[I");
473        assert_eq!(
474            parse::<'_, TypeSignature<'_>>("[[I").unwrap(),
475            TypeSignature::Array(Box::new(TypeSignature::Array(Box::new(
476                TypeSignature::Primitive(PrimitiveType::Int)
477            ))))
478        );
479        validate_rw::<'_, TypeSignature<'_>>("[[I");
480    }
481
482    #[test]
483    fn array_class() {
484        assert_eq!(
485            parse::<'_, FieldType<'_>>("[Ljava/lang/Object;").unwrap(),
486            FieldType::Array(Box::new(FieldType::Class("java/lang/Object")))
487        );
488        validate_rw::<'_, FieldType<'_>>("[Ljava/lang/Object;");
489        validate_rw::<'_, TypeSignature<'_>>("[Ljava/lang/Object;");
490    }
491
492    #[test]
493    fn array_class_2d() {
494        assert_eq!(
495            parse::<'_, FieldType<'_>>("[[Ljava/lang/Object;").unwrap(),
496            FieldType::Array(Box::new(FieldType::Array(Box::new(FieldType::Class(
497                "java/lang/Object"
498            )))))
499        );
500        validate_rw::<'_, FieldType<'_>>("[[Ljava/lang/Object;");
501        validate_rw::<'_, TypeSignature<'_>>("[[Ljava/lang/Object;");
502    }
503
504    #[test]
505    fn class_generics() {
506        assert_eq!(
507            parse::<'_, TypeSignature<'_>>(
508                "Ljava/util/Map<Ljava/lang/String;+Ljava/lang/Integer;>;"
509            )
510            .unwrap(),
511            TypeSignature::Class {
512                sig: ClassTypeSignature {
513                    name: "java/util/Map",
514                    args: smallvec![
515                        Some(Box::new(TypeArgument {
516                            kind: TypeArgumentKind::Exact,
517                            signature: TypeSignature::Class {
518                                sig: ClassTypeSignature {
519                                    name: "java/lang/String",
520                                    args: SmallVec::new(),
521                                },
522                                suffix: None
523                            }
524                        })),
525                        Some(Box::new(TypeArgument {
526                            kind: TypeArgumentKind::Extends,
527                            signature: TypeSignature::Class {
528                                sig: ClassTypeSignature {
529                                    name: "java/lang/Integer",
530                                    args: SmallVec::new(),
531                                },
532                                suffix: None
533                            }
534                        })),
535                    ]
536                },
537                suffix: None
538            }
539        );
540        validate_rw::<'_, TypeSignature<'_>>(
541            "Ljava/util/Map<Ljava/lang/String;Ljava/lang/Integer;>;",
542        );
543    }
544
545    #[test]
546    fn class_generics_any() {
547        assert_eq!(
548            parse::<'_, TypeSignature<'_>>("Ljava/util/concurrent/Future<*>;").unwrap(),
549            TypeSignature::Class {
550                sig: ClassTypeSignature {
551                    name: "java/util/concurrent/Future",
552                    args: smallvec![None]
553                },
554                suffix: None
555            }
556        );
557        validate_rw::<'_, TypeSignature<'_>>("Ljava/util/concurrent/Future<*>;");
558    }
559
560    #[test]
561    fn class_generics_suffix() {
562        assert_eq!(
563            parse::<'_, TypeSignature<'_>>("LOuter<TT;>.Inner<TU;>;").unwrap(),
564            TypeSignature::Class {
565                sig: ClassTypeSignature {
566                    name: "Outer",
567                    args: smallvec![Some(Box::new(TypeArgument {
568                        kind: TypeArgumentKind::Exact,
569                        signature: TypeSignature::Type("T"),
570                    }))]
571                },
572                suffix: Some(ClassTypeSignature {
573                    name: "Inner",
574                    args: smallvec![Some(Box::new(TypeArgument {
575                        kind: TypeArgumentKind::Exact,
576                        signature: TypeSignature::Type("U"),
577                    }))]
578                },)
579            }
580        );
581        validate_rw::<'_, TypeSignature<'_>>("LOuter<TT;>.Inner<TU;>;");
582    }
583
584    #[test]
585    fn type_sig() {
586        assert_eq!(
587            parse::<'_, TypeSignature<'_>>("TT;").unwrap(),
588            TypeSignature::Type("T")
589        );
590        validate_rw::<'_, TypeSignature<'_>>("TT;");
591    }
592}