1use anyhow::{Result, anyhow};
72use rudy_dwarf::{Die, types::DieTypeDefinition};
73use rudy_types::{Layout, Location, StdLayout};
74
75use crate::{DataResolver, Value};
76
77#[derive(Debug, Clone)]
79pub struct SyntheticMethod {
80 pub name: &'static str,
82
83 pub signature: &'static str,
85
86 pub takes_args: bool,
88}
89
90pub fn get_synthetic_methods<L: Location>(type_layout: &Layout<L>) -> Vec<SyntheticMethod> {
92 match type_layout {
93 Layout::Std(std_layout) => match std_layout {
94 StdLayout::Vec(_) => vec![
95 SyntheticMethod {
96 name: "len",
97 signature: "fn len(&self) -> usize",
98 takes_args: false,
99 },
100 SyntheticMethod {
101 name: "capacity",
102 signature: "fn capacity(&self) -> usize",
103 takes_args: false,
104 },
105 SyntheticMethod {
106 name: "is_empty",
107 signature: "fn is_empty(&self) -> bool",
108 takes_args: false,
109 },
110 ],
111 StdLayout::String(_) => vec![
112 SyntheticMethod {
113 name: "len",
114 signature: "fn len(&self) -> usize",
115 takes_args: false,
116 },
117 SyntheticMethod {
118 name: "is_empty",
119 signature: "fn is_empty(&self) -> bool",
120 takes_args: false,
121 },
122 ],
123 StdLayout::Option(_) => vec![
124 SyntheticMethod {
125 name: "is_some",
126 signature: "fn is_some(&self) -> bool",
127 takes_args: false,
128 },
129 SyntheticMethod {
130 name: "is_none",
131 signature: "fn is_none(&self) -> bool",
132 takes_args: false,
133 },
134 ],
135 StdLayout::Result(_) => vec![
136 SyntheticMethod {
137 name: "is_ok",
138 signature: "fn is_ok(&self) -> bool",
139 takes_args: false,
140 },
141 SyntheticMethod {
142 name: "is_err",
143 signature: "fn is_err(&self) -> bool",
144 takes_args: false,
145 },
146 ],
147 StdLayout::Map(_) => vec![
148 ],
151 _ => vec![],
152 },
153 Layout::Primitive(prim_layout) => {
154 use rudy_types::PrimitiveLayout;
155 match prim_layout {
156 PrimitiveLayout::Slice(_) | PrimitiveLayout::StrSlice(_) => vec![
157 SyntheticMethod {
158 name: "len",
159 signature: "fn len(&self) -> usize",
160 takes_args: false,
161 },
162 SyntheticMethod {
163 name: "is_empty",
164 signature: "fn is_empty(&self) -> bool",
165 takes_args: false,
166 },
167 ],
168 PrimitiveLayout::Array(_) => vec![SyntheticMethod {
169 name: "len",
170 signature: "fn len(&self) -> usize",
171 takes_args: false,
172 }],
173 _ => vec![],
174 }
175 }
176 _ => vec![],
177 }
178}
179
180pub fn evaluate_synthetic_method(
182 address: u64,
183 def: &DieTypeDefinition,
184 method: &str,
185 _args: &[Value], resolver: &dyn DataResolver,
187) -> Result<Value> {
188 match def.layout.as_ref() {
189 Layout::Std(std_layout) => match std_layout {
190 StdLayout::Vec(vec_layout) => {
191 evaluate_vec_method(address, vec_layout, method, resolver)
192 }
193 StdLayout::String(string_layout) => {
194 evaluate_string_method(address, string_layout, method, resolver)
195 }
196 StdLayout::Option(option_layout) => {
197 evaluate_option_method(address, option_layout, method, resolver)
198 }
199 StdLayout::Result(result_layout) => {
200 evaluate_result_method(address, result_layout, method, resolver)
201 }
202 StdLayout::Map(map_layout) => {
203 evaluate_map_method(address, map_layout, method, resolver)
204 }
205 _ => Err(anyhow!(
206 "No synthetic method '{}' for type {}",
207 method,
208 def.display_name()
209 )),
210 },
211 Layout::Primitive(prim_layout) => {
212 use rudy_types::PrimitiveLayout;
213 match prim_layout {
214 PrimitiveLayout::Slice(slice_layout) => {
215 evaluate_slice_method(address, slice_layout, method, resolver)
216 }
217 PrimitiveLayout::StrSlice(_) => {
218 evaluate_str_slice_method(address, method, resolver)
219 }
220 PrimitiveLayout::Array(array_layout) => evaluate_array_method(array_layout, method),
221 _ => Err(anyhow!(
222 "No synthetic method '{}' for type {}",
223 method,
224 def.display_name()
225 )),
226 }
227 }
228 _ => Err(anyhow!(
229 "No synthetic methods for type {}",
230 def.display_name()
231 )),
232 }
233}
234
235fn evaluate_vec_method(
236 address: u64,
237 vec_layout: &rudy_types::VecLayout<Die>,
238 method: &str,
239 resolver: &dyn DataResolver,
240) -> Result<Value> {
241 match method {
242 "len" => {
243 let len_address = address + vec_layout.length_offset as u64;
244 let len_bytes = resolver.read_memory(len_address, std::mem::size_of::<usize>())?;
245 let len = usize_from_bytes(&len_bytes)?;
246 Ok(Value::Scalar {
247 ty: "usize".to_string(),
248 value: len.to_string(),
249 })
250 }
251 "capacity" => {
252 let cap_offset = vec_layout.capacity_offset;
255 let cap_address = address + cap_offset as u64;
256 let cap_bytes = resolver.read_memory(cap_address, std::mem::size_of::<usize>())?;
257 let cap = usize_from_bytes(&cap_bytes)?;
258 Ok(Value::Scalar {
259 ty: "usize".to_string(),
260 value: cap.to_string(),
261 })
262 }
263 "is_empty" => {
264 let len_address = address + vec_layout.length_offset as u64;
265 let len_bytes = resolver.read_memory(len_address, std::mem::size_of::<usize>())?;
266 let len = usize_from_bytes(&len_bytes)?;
267 Ok(Value::Scalar {
268 ty: "bool".to_string(),
269 value: (len == 0).to_string(),
270 })
271 }
272 _ => Err(anyhow!("Unknown synthetic method '{}' for Vec", method)),
273 }
274}
275
276fn evaluate_string_method(
277 address: u64,
278 string_layout: &rudy_types::StringLayout<Die>,
279 method: &str,
280 resolver: &dyn DataResolver,
281) -> Result<Value> {
282 let vec_layout = &string_layout.0;
284 match method {
285 "len" => evaluate_vec_method(address, vec_layout, "len", resolver),
286 "is_empty" => evaluate_vec_method(address, vec_layout, "is_empty", resolver),
287 "capacity" => evaluate_vec_method(address, vec_layout, "capacity", resolver),
288 _ => Err(anyhow!("Unknown synthetic method '{}' for String", method)),
289 }
290}
291
292fn evaluate_option_method(
293 address: u64,
294 option_layout: &rudy_types::OptionLayout<Die>,
295 method: &str,
296 resolver: &dyn DataResolver,
297) -> Result<Value> {
298 let discriminant_bytes = resolver.read_memory(
300 address + option_layout.discriminant.offset as u64,
301 option_layout.discriminant.size(),
302 )?;
303
304 let discriminant_value = match discriminant_bytes.len() {
305 1 => discriminant_bytes[0] as u64,
306 2 => u16::from_le_bytes(discriminant_bytes.try_into().unwrap()) as u64,
307 4 => u32::from_le_bytes(discriminant_bytes.try_into().unwrap()) as u64,
308 8 => u64::from_le_bytes(discriminant_bytes.try_into().unwrap()),
309 _ => return Err(anyhow!("Unexpected discriminant size")),
310 };
311
312 match method {
315 "is_some" => Ok(Value::Scalar {
316 ty: "bool".to_string(),
317 value: (discriminant_value != 0).to_string(),
318 }),
319 "is_none" => Ok(Value::Scalar {
320 ty: "bool".to_string(),
321 value: (discriminant_value == 0).to_string(),
322 }),
323 _ => Err(anyhow!("Unknown synthetic method '{}' for Option", method)),
324 }
325}
326
327fn evaluate_result_method(
328 address: u64,
329 result_layout: &rudy_types::ResultLayout<Die>,
330 method: &str,
331 resolver: &dyn DataResolver,
332) -> Result<Value> {
333 let discriminant_bytes = resolver.read_memory(
335 address + result_layout.discriminant.offset as u64,
336 result_layout.discriminant.size(),
337 )?;
338
339 let discriminant_value = match discriminant_bytes.len() {
340 1 => discriminant_bytes[0] as u64,
341 2 => u16::from_le_bytes(discriminant_bytes.clone().try_into().unwrap()) as u64,
342 4 => u32::from_le_bytes(discriminant_bytes.clone().try_into().unwrap()) as u64,
343 8 => u64::from_le_bytes(discriminant_bytes.clone().try_into().unwrap()),
344 _ => return Err(anyhow!("Unexpected discriminant size")),
345 };
346
347 tracing::debug!(
349 "Result discriminant bytes: {:?}, value: {:#x}, at offset {}",
350 discriminant_bytes,
351 discriminant_value,
352 result_layout.discriminant.offset
353 );
354
355 match method {
360 "is_ok" => {
361 let is_ok = match discriminant_bytes.len() {
363 8 => (discriminant_value & 0x8000000000000000) == 0,
364 4 => (discriminant_value & 0x80000000) == 0,
365 _ => discriminant_value == 0,
366 };
367 Ok(Value::Scalar {
368 ty: "bool".to_string(),
369 value: is_ok.to_string(),
370 })
371 }
372 "is_err" => {
373 let is_err = match discriminant_bytes.len() {
375 8 => (discriminant_value & 0x8000000000000000) != 0,
376 4 => (discriminant_value & 0x80000000) != 0,
377 _ => discriminant_value != 0,
378 };
379 Ok(Value::Scalar {
380 ty: "bool".to_string(),
381 value: is_err.to_string(),
382 })
383 }
384 _ => Err(anyhow!("Unknown synthetic method '{}' for Result", method)),
385 }
386}
387
388fn evaluate_map_method(
389 _address: u64,
390 _map_layout: &rudy_types::MapLayout<Die>,
391 method: &str,
392 _resolver: &dyn DataResolver,
393) -> Result<Value> {
394 match method {
397 "len" | "is_empty" => Err(anyhow!(
398 "HashMap/BTreeMap synthetic methods not yet implemented"
399 )),
400 _ => Err(anyhow!("Unknown synthetic method '{}' for Map", method)),
401 }
402}
403
404fn evaluate_slice_method(
405 address: u64,
406 _slice_layout: &rudy_types::SliceLayout<Die>,
407 method: &str,
408 resolver: &dyn DataResolver,
409) -> Result<Value> {
410 match method {
411 "len" => {
412 let len_address = address + std::mem::size_of::<usize>() as u64;
415 let len_bytes = resolver.read_memory(len_address, std::mem::size_of::<usize>())?;
416 let len = usize_from_bytes(&len_bytes)?;
417 Ok(Value::Scalar {
418 ty: "usize".to_string(),
419 value: len.to_string(),
420 })
421 }
422 "is_empty" => {
423 let len_address = address + std::mem::size_of::<usize>() as u64;
424 let len_bytes = resolver.read_memory(len_address, std::mem::size_of::<usize>())?;
425 let len = usize_from_bytes(&len_bytes)?;
426 Ok(Value::Scalar {
427 ty: "bool".to_string(),
428 value: (len == 0).to_string(),
429 })
430 }
431 _ => Err(anyhow!("Unknown synthetic method '{}' for slice", method)),
432 }
433}
434
435fn evaluate_str_slice_method(
436 address: u64,
437 method: &str,
438 resolver: &dyn DataResolver,
439) -> Result<Value> {
440 match method {
442 "len" => {
443 let len_address = address + std::mem::size_of::<usize>() as u64;
445 let len_bytes = resolver.read_memory(len_address, std::mem::size_of::<usize>())?;
446 let len = usize_from_bytes(&len_bytes)?;
447 Ok(Value::Scalar {
448 ty: "usize".to_string(),
449 value: len.to_string(),
450 })
451 }
452 "is_empty" => {
453 let len_address = address + std::mem::size_of::<usize>() as u64;
454 let len_bytes = resolver.read_memory(len_address, std::mem::size_of::<usize>())?;
455 let len = usize_from_bytes(&len_bytes)?;
456 Ok(Value::Scalar {
457 ty: "bool".to_string(),
458 value: (len == 0).to_string(),
459 })
460 }
461 _ => Err(anyhow!("Unknown synthetic method '{}' for &str", method)),
462 }
463}
464
465fn evaluate_array_method(
466 array_layout: &rudy_types::ArrayLayout<Die>,
467 method: &str,
468) -> Result<Value> {
469 match method {
470 "len" => Ok(Value::Scalar {
471 ty: "usize".to_string(),
472 value: array_layout.length.to_string(),
473 }),
474 _ => Err(anyhow!("Unknown synthetic method '{}' for array", method)),
475 }
476}
477
478fn usize_from_bytes(bytes: &[u8]) -> Result<usize> {
479 if bytes.len() == 8 {
480 Ok(u64::from_le_bytes(bytes.try_into().unwrap()) as usize)
481 } else if bytes.len() == 4 {
482 Ok(u32::from_le_bytes(bytes.try_into().unwrap()) as usize)
483 } else {
484 Err(anyhow!("Unexpected size for usize: {} bytes", bytes.len()))
485 }
486}