1use serde::{Deserialize, Serialize};
6
7pub type ObjectId = u64;
9pub type ThreadId = ObjectId;
10pub type ThreadGroupId = ObjectId;
11pub type StringId = ObjectId;
12pub type ClassLoaderId = ObjectId;
13pub type ClassObjectId = ObjectId;
14pub type ArrayId = ObjectId;
15
16pub type ReferenceTypeId = u64;
17pub type ClassId = ReferenceTypeId;
18pub type InterfaceId = ReferenceTypeId;
19pub type ArrayTypeId = ReferenceTypeId;
20
21pub type MethodId = u64;
22pub type FieldId = u64;
23pub type FrameId = u64;
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct Location {
28 pub type_tag: u8, pub class_id: ReferenceTypeId,
30 pub method_id: MethodId,
31 pub index: u64, }
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36#[repr(u32)]
37pub enum ThreadStatus {
38 Zombie = 0,
39 Running = 1,
40 Sleeping = 2,
41 Monitor = 3,
42 Wait = 4,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
47#[repr(u32)]
48pub enum SuspendStatus {
49 Running = 0,
50 Suspended = 1,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55#[repr(u8)]
56pub enum TypeTag {
57 Array = 91, Byte = 66, Char = 67, Object = 76, Float = 70, Double = 68, Int = 73, Long = 74, Short = 83, Void = 86, Boolean = 90, String = 115, Thread = 116, ThreadGroup = 103, ClassLoader = 108, ClassObject = 99, }
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct Value {
78 pub tag: u8,
79 pub data: ValueData,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83#[serde(untagged)]
84pub enum ValueData {
85 Byte(i8),
86 Char(u16),
87 Float(f32),
88 Double(f64),
89 Int(i32),
90 Long(i64),
91 Short(i16),
92 Boolean(bool),
93 Object(ObjectId),
94 Void,
95}
96
97impl ValueData {
98 #[must_use]
112 pub fn format_primitive(&self) -> Option<String> {
113 Some(match self {
114 Self::Byte(v) => format!("(byte) {v}"),
115 Self::Char(v) => format_char(*v),
116 Self::Float(v) => format!("(float) {v}"),
117 Self::Double(v) => format!("(double) {v}"),
118 Self::Int(v) => format!("(int) {v}"),
119 Self::Long(v) => format!("(long) {v}"),
120 Self::Short(v) => format!("(short) {v}"),
121 Self::Boolean(v) => format!("(boolean) {v}"),
122 Self::Void => "(void)".to_string(),
123 Self::Object(_) => return None,
124 })
125 }
126}
127
128fn format_char(unit: u16) -> String {
139 char::from_u32(u32::from(unit)).map_or_else(
141 || format!("(char) '\\u{unit:04X}' (unpaired surrogate, not a character)"),
142 |c| format!("(char) '{c}'"),
143 )
144}
145
146impl Value {
147 #[must_use]
152 pub fn format(&self) -> String {
153 match &self.data {
154 ValueData::Object(0) => "(object) null".to_string(),
155 ValueData::Object(id) => format!("(object) @{id:x}"),
156 primitive => primitive.format_primitive().unwrap_or_default(),
157 }
158 }
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct Variable {
164 pub code_index: u64,
165 pub name: String,
166 pub signature: String,
167 pub generic_signature: Option<String>,
175 pub length: u32,
176 pub slot: u32,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct FrameInfo {
182 pub frame_id: FrameId,
183 pub location: Location,
184}
185
186#[cfg(test)]
187mod tests {
188 use super::{Value, ValueData};
189
190 #[test]
197 fn an_unpaired_surrogate_is_rendered_apart_from_a_real_question_mark() {
198 let surrogate = ValueData::Char(0xD800).format_primitive().expect("a char is a primitive");
199 let question = ValueData::Char(u16::from(b'?')).format_primitive().expect("so is this one");
200
201 assert_eq!(question, "(char) '?'", "a real question mark still renders as itself");
202 assert_ne!(surrogate, question, "the two must not be the same bytes: {surrogate}");
203 assert!(surrogate.contains("\\uD800"), "the code unit itself is shown: {surrogate}");
204 assert!(surrogate.contains("unpaired surrogate"), "and what it is: {surrogate}");
205
206 let low = ValueData::Char(0xDFFF).format_primitive().expect("still a char");
209 assert!(low.contains("\\uDFFF"), "the low half of the range is covered too: {low}");
210 assert!(low.contains("unpaired surrogate"), "{low}");
211
212 assert_eq!(ValueData::Char(0xD7FF).format_primitive().unwrap(), "(char) '\u{d7ff}'");
214 assert_eq!(ValueData::Char(0xE000).format_primitive().unwrap(), "(char) '\u{e000}'");
215 }
216
217 #[test]
222 fn only_a_reference_declines_to_render_without_asking_the_debuggee() {
223 assert_eq!(ValueData::Int(-2_147_483_648).format_primitive().unwrap(), "(int) -2147483648");
224 assert_eq!(ValueData::Boolean(true).format_primitive().unwrap(), "(boolean) true");
225 assert_eq!(ValueData::Byte(-7).format_primitive().unwrap(), "(byte) -7");
226 assert_eq!(ValueData::Short(-300).format_primitive().unwrap(), "(short) -300");
227 assert_eq!(ValueData::Long(9_000_000_000).format_primitive().unwrap(), "(long) 9000000000");
228 assert_eq!(ValueData::Float(1.5).format_primitive().unwrap(), "(float) 1.5");
229 assert_eq!(ValueData::Double(-2.25).format_primitive().unwrap(), "(double) -2.25");
230
231 assert!(
232 ValueData::Object(0x2b).format_primitive().is_none(),
233 "a reference needs a round trip to describe, and this crate is not the side that pays it"
234 );
235 assert_eq!(Value { tag: 76, data: ValueData::Object(0x2b) }.format(), "(object) @2b");
237 assert_eq!(Value { tag: 76, data: ValueData::Object(0) }.format(), "(object) null");
238 assert_eq!(Value { tag: 67, data: ValueData::Char(0xD800) }.format(), surrogate_rendering());
240 }
241
242 fn surrogate_rendering() -> String {
243 ValueData::Char(0xD800).format_primitive().expect("a char is a primitive")
244 }
245}