diff --git a/src/bytecode.rs b/src/bytecode.rs index b9d67e1..b89aaf8 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -7,6 +7,7 @@ use crate::constant_pool::{ use crate::constant_pool::{ ConstantPoolEntry, ConstantPoolEntryTypes, InvokeDynamic, Loadable, MemberRef, }; +use crate::descriptor::ReferenceType; use crate::{read_u1, read_u2, read_u4, CafeRc, ParseError}; pub type JumpOffset = i32; @@ -43,7 +44,7 @@ pub enum Opcode<'a> { Aastore, AconstNull, Aload(u16), // both wide and narrow - Anewarray(Cow<'a, str>), + Anewarray(ReferenceType<'a>), Areturn, Arraylength, Astore(u16), // both wide and narrow @@ -54,7 +55,7 @@ pub enum Opcode<'a> { Breakpoint, Caload, Castore, - Checkcast(Cow<'a, str>), + Checkcast(ReferenceType<'a>), D2f, D2i, D2l, @@ -141,7 +142,7 @@ pub enum Opcode<'a> { Impdep2, Imul, Ineg, - Instanceof(Cow<'a, str>), + Instanceof(ReferenceType<'a>), Invokedynamic(InvokeDynamic<'a>), Invokeinterface(MemberRef<'a>, u8), Invokespecial(MemberRef<'a>), @@ -186,7 +187,7 @@ pub enum Opcode<'a> { Lxor, Monitorenter, Monitorexit, - Multianewarray(Cow<'a, str>, u8), + Multianewarray(ReferenceType<'a>, u8), New(Cow<'a, str>), Newarray(PrimitiveArrayType), Nop, @@ -632,11 +633,20 @@ fn read_opcodes<'a>( }; Opcode::Newarray(primitive_type) } - 0xbd => Opcode::Anewarray(read_cp_classinfo(code, &mut ix, pool)?), + 0xbd => Opcode::Anewarray({ + let ty = read_cp_classinfo(code, &mut ix, pool)?; + ReferenceType::parse(&ty)? + }), 0xbe => Opcode::Arraylength, 0xbf => Opcode::Athrow, - 0xc0 => Opcode::Checkcast(read_cp_classinfo(code, &mut ix, pool)?), - 0xc1 => Opcode::Instanceof(read_cp_classinfo(code, &mut ix, pool)?), + 0xc0 => Opcode::Checkcast({ + let ty = read_cp_classinfo(code, &mut ix, pool)?; + ReferenceType::parse(&ty)? + }), + 0xc1 => Opcode::Instanceof({ + let ty = read_cp_classinfo(code, &mut ix, pool)?; + ReferenceType::parse(&ty)? + }), 0xc2 => Opcode::Monitorenter, 0xc3 => Opcode::Monitorexit, 0xc4 => { @@ -662,7 +672,7 @@ fn read_opcodes<'a>( } } 0xc5 => Opcode::Multianewarray( - read_cp_classinfo(code, &mut ix, pool)?, + ReferenceType::parse(&read_cp_classinfo(code, &mut ix, pool)?)?, read_u1(code, &mut ix)?, ), 0xc6 => Opcode::Ifnull((read_u2(code, &mut ix)? as i16).into()), diff --git a/src/descriptor.rs b/src/descriptor.rs index 0a31f23..3e4cab1 100644 --- a/src/descriptor.rs +++ b/src/descriptor.rs @@ -80,6 +80,58 @@ pub enum FieldType<'a> { Array { dimensions: usize, ty: Ty<'a> }, } +/// FieldType as described in section 4.3.2 of the [JVM 18 specification](https://docs.oracle.com/javase/specs/jvms/se18/html/jvms-4.html#jvms-4.3.2) +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum ReferenceType<'a> { + Object(Cow<'a, str>), + Array { dimensions: usize, ty: Ty<'a> }, +} + +impl<'a> ReferenceType<'a> { + pub(crate) fn parse(chars: &Cow<'a, str>) -> Result { + let mut chars_idx = chars.char_indices(); + Self::parse_from_chars_idx(chars, &mut chars_idx) + } + + fn parse_from_chars_idx( + chars: &Cow<'a, str>, + chars_idx: &mut CharIndices, + ) -> Result { + let mut array_depth = 0; + + while let Some(ch) = chars_idx.next().map(|(_, ch)| ch) { + match ch { + '[' => { + array_depth += 1; + + // A field descriptor representing an array type is valid only if it represents a type with 255 or fewer dimensions. + // see: https://docs.oracle.com/javase/specs/jvms/se18/html/jvms-4.html#jvms-4.3.2 + if array_depth > 255 { + fail!("Array exceeds 255 dimensions"); + } + } + 'L' if array_depth != 0 => { + return Ok(ReferenceType::Array { + dimensions: array_depth, + ty: Ty::Object(parse_object(chars, chars_idx)?), + }); + } + ch => { + return Ok(if array_depth == 0 { + ReferenceType::Object(chars.clone()) + } else { + ReferenceType::Array { + dimensions: array_depth, + ty: Ty::Base(BaseType::parse(ch)?), + } + }) + } + }; + } + fail!("no ReferenceType found") + } +} + impl<'a> FieldType<'a> { pub(crate) fn parse(chars: &Cow<'a, str>) -> Result { let mut chars_idx = chars.char_indices(); diff --git a/tests/parse/clazz/L.clazz b/tests/parse/clazz/L.clazz new file mode 100644 index 0000000..690d9a9 Binary files /dev/null and b/tests/parse/clazz/L.clazz differ diff --git a/tests/parse/clazz/Test.clazz b/tests/parse/clazz/Test.clazz new file mode 100644 index 0000000..e12a405 Binary files /dev/null and b/tests/parse/clazz/Test.clazz differ