jdwp_client/
commands.rs

1use binrw::{BinRead, binrw};
2
3use crate::{ClassStatus, JdwpIdSize, JdwpIdSizes, JdwpString, TypeTag, binrw_enum};
4
5binrw_enum! {
6    #[repr(u16)]
7    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
8    pub enum Command {
9        VirtualMachineVersion =     (1 << 8) | 1,
10        VirtualMachineAllClasses =  (1 << 8) | 3,
11        VirtualMachineIDSizes =     (1 << 8) | 7,
12    }
13}
14
15#[binrw]
16#[brw(big)]
17pub struct CommandPacketHeader {
18    pub length: u32,
19    pub id: u32,
20    pub flags: u8,
21    pub command: Command,
22}
23impl CommandPacketHeader {
24    pub fn get_length() -> usize {
25        return 4 + 4 + 1 + 2;
26    }
27}
28
29#[binrw]
30#[brw(big)]
31pub struct ReplyPacketHeader {
32    pub length: u32,
33    pub id: u32,
34    pub flags: u8,
35    pub error_code: u16,
36}
37impl ReplyPacketHeader {
38    pub fn default() -> Self {
39        ReplyPacketHeader {
40            length: 0,
41            id: 0xFFFFFFFF,
42            flags: 0,
43            error_code: 0,
44        }
45    }
46    pub fn get_length() -> usize {
47        return 4 + 4 + 1 + 2;
48    }
49    pub fn is_success(&self) -> bool {
50        return self.error_code == 0;
51    }
52}
53
54#[derive(Debug)]
55pub struct VariableLengthId {
56    pub value: u64,
57}
58impl BinRead for VariableLengthId {
59    type Args<'a> = JdwpIdSize;
60
61    fn read_options<R: std::io::Read + std::io::Seek>(
62        reader: &mut R,
63        endian: binrw::Endian,
64        args: Self::Args<'_>,
65    ) -> binrw::BinResult<Self> {
66        // TODO: Support non-power-of-2 sizes if needed
67        let val: u64 = match args {
68            1 => u8::read_options(reader, endian, ())? as u64,
69            2 => u16::read_options(reader, endian, ())? as u64,
70            4 => u32::read_options(reader, endian, ())? as u64,
71            8 => u64::read_options(reader, endian, ())?,
72            _ => {
73                return binrw::BinResult::Err(binrw::Error::Custom {
74                    pos: reader.stream_position().unwrap_or(0),
75                    err: Box::new("Unsupported variable size ID"),
76                });
77            }
78        };
79
80        Ok(VariableLengthId { value: val })
81    }
82}
83
84#[binrw]
85#[brw(big)]
86#[derive(Debug)]
87pub struct VersionReply {
88    pub description: JdwpString,
89    pub jdwp_major: i32,
90    pub jdwp_minor: i32,
91    pub vm_version: JdwpString,
92    pub vm_name: JdwpString,
93}
94
95#[binrw]
96#[brw(big)]
97#[derive(Debug)]
98pub struct IdSizesReply {
99    pub field_id_size: i32,
100    pub method_id_size: i32,
101    pub object_id_size: i32,
102    pub reference_type_id_size: i32,
103    pub frame_id_size: i32,
104}
105
106#[derive(Debug)]
107pub struct AllClassesReplyClass {
108    pub ref_type_tag: TypeTag,
109    pub type_id: VariableLengthId,
110    pub signature: JdwpString,
111    pub status: ClassStatus,
112}
113impl BinRead for AllClassesReplyClass {
114    type Args<'a> = JdwpIdSizes;
115
116    fn read_options<R: std::io::Read + std::io::Seek>(
117        reader: &mut R,
118        endian: binrw::Endian,
119        args: Self::Args<'_>,
120    ) -> binrw::BinResult<Self> {
121        Ok(AllClassesReplyClass {
122            ref_type_tag: TypeTag::read_options(reader, endian, ())?,
123            type_id: VariableLengthId::read_options(reader, endian, args.reference_type_id_size)?,
124            signature: JdwpString::read_options(reader, endian, ())?,
125            status: ClassStatus::read_options(reader, endian, ())?,
126        })
127    }
128}
129
130#[derive(Debug)]
131pub struct AllClassesReply {
132    pub classes: Vec<AllClassesReplyClass>,
133}
134impl BinRead for AllClassesReply {
135    type Args<'a> = JdwpIdSizes;
136
137    fn read_options<R: std::io::Read + std::io::Seek>(
138        reader: &mut R,
139        endian: binrw::Endian,
140        args: Self::Args<'_>,
141    ) -> binrw::BinResult<Self> {
142        let classes_length = i32::read_options(reader, endian, ())?;
143        let mut classes = Vec::with_capacity(classes_length as usize);
144        for _ in 0..classes_length {
145            classes.push(AllClassesReplyClass::read_options(reader, endian, args)?);
146        }
147
148        Ok(AllClassesReply { classes })
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use crate::Command;
155    use binrw::BinRead;
156    use std::io::Cursor;
157
158    #[test]
159    fn test_deserialize_vm_version_command() {
160        let data = [1u8, 1u8];
161        let mut cursor = Cursor::new(&data);
162        let value = Command::read_be(&mut cursor).unwrap();
163        assert_eq!(value, Command::VirtualMachineVersion);
164    }
165}