use std::borrow::Cow;
use jstd::Identifier;
use crate::{
context::{Context, Shared},
error::Result,
space::{Space, SpaceId, SpaceRef},
value::{
Value, ValueId,
util::{
base_ref::{BaseRef, WithCtx, WithShared},
named::{Named, Renameable, update_context_name},
},
},
};
pub mod register;
#[derive(Identifier)]
pub struct VarnodeId(usize);
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Varnode<'str> {
name: Option<Cow<'str, str>>,
label: Option<u32>,
address: i64,
size: usize,
space: SpaceId,
}
impl<'str> Varnode<'str> {
pub fn size_bytes(&self) -> usize {
self.size
}
fn new(base: i64, size: usize, space: SpaceId) -> Self {
Self {
name: None,
label: None,
address: base,
size,
space,
}
}
pub fn make<'ctx>(
ctx: &'ctx mut Context<'str>,
base: i64,
size: usize,
space: SpaceId,
) -> VarnodeMutRef<'str, 'ctx> {
let id = ctx
.shared
.values
.varnodes
.push(Varnode::new(base, size, space));
VarnodeMutRef::from_id(ctx, id)
}
pub fn from_id<'ctx>(
src: impl crate::value::util::base_ref::AsShared<'ctx, 'str>,
id: VarnodeId,
) -> VarnodeRef<'str, 'ctx> {
VarnodeRef::from_id(src, id)
}
pub fn from_id_mut<'ctx>(
ctx: &'ctx mut Context<'str>,
id: VarnodeId,
) -> VarnodeMutRef<'str, 'ctx> {
VarnodeMutRef::from_id(ctx, id)
}
}
impl<'s, 'ctx: 's, 'str: 'ctx, Ctx> BaseRef<Ctx, VarnodeId>
where
Self: WithShared<'s, 'ctx, 'str>,
{
fn inner(&'s self) -> &'ctx Varnode<'str> {
&self.shared().values.varnodes[self.id]
}
fn fmt(&'s self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(name) = self.name() {
write!(f, "{}", name)
} else if let Some(label) = self.inner().label {
write!(f, "v{label}")
} else {
write!(f, "[{}]:{} {}", *self.space(), self.size(), self.address())
}
}
pub fn space(&'s self) -> SpaceRef<'ctx> {
Space::from_id(self.shared(), self.inner().space)
}
pub fn address(&'s self) -> i64 {
self.inner().address
}
pub fn size(&'s self) -> usize {
self.inner().size
}
pub fn name(&'s self) -> Option<&'ctx str> {
self.inner().name.as_deref()
}
pub fn label(&'s self) -> Option<u32> {
self.inner().label
}
}
pub type VarnodeRef<'str, 'ctx> = BaseRef<&'ctx Shared<'str>, VarnodeId>;
impl<'s, 'ctx: 's, 'str: 'ctx> WithShared<'s, 'ctx, 'str> for VarnodeRef<'str, 'ctx> {
fn shared(&'s self) -> &'ctx Shared<'str> {
self.ctx
}
}
impl Named for VarnodeRef<'_, '_> {
fn name(&self) -> Option<&str> {
self.name()
}
}
impl std::fmt::Display for VarnodeRef<'_, '_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt(f)
}
}
impl<'str, 'ctx> Value<'str, 'ctx> for VarnodeRef<'str, 'ctx> {
fn id(&self) -> ValueId {
self.id()
}
fn size(&self) -> usize {
self.size()
}
}
pub type VarnodeMutRef<'str, 'ctx> = BaseRef<&'ctx mut Context<'str>, VarnodeId>;
impl<'str, 'ctx> VarnodeMutRef<'str, 'ctx> {
fn inner_mut(&mut self) -> &mut Varnode<'str> {
&mut self.ctx.shared.values.varnodes[self.id]
}
pub fn set_label(&mut self, label: u32) {
self.inner_mut().label = Some(label);
}
}
impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 's, 'str> for VarnodeMutRef<'str, 'ctx> {
fn ctx(&'s self) -> &'s Context<'str> {
self.ctx
}
}
impl<'s, 'ctx: 's, 'str: 'ctx> WithShared<'s, 's, 'str> for VarnodeMutRef<'str, 'ctx> {
fn shared(&'s self) -> &'s Shared<'str> {
&self.ctx.shared
}
}
impl Named for VarnodeMutRef<'_, '_> {
fn name(&self) -> Option<&str> {
self.name()
}
}
impl std::fmt::Display for VarnodeMutRef<'_, '_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt(f)
}
}
impl<'str, 'ctx> Value<'str, 'ctx> for VarnodeMutRef<'str, 'ctx> {
fn id(&self) -> ValueId {
self.id()
}
fn size(&self) -> usize {
self.size()
}
}
impl<'str, 'ctx> Renameable<'str, 'ctx> for VarnodeMutRef<'str, 'ctx> {
fn rename(&mut self, name: Cow<'str, str>) -> Result<()> {
let id = self.id.into();
let old_name = self.inner_mut().name.take();
update_context_name(id, self.ctx, name.clone(), old_name.as_deref())?;
self.ctx.shared.values.varnodes[self.id].name = Some(name);
Ok(())
}
}
#[cfg(test)]
mod tests {
use wazabin_qcode_macro::qcode;
use crate::{
context::Context,
value::{ModuleView, TempRef},
};
#[test]
fn body_local_temp_name() {
let mut ctx = Context::new();
qcode!(ctx, "<block> varnode i64 ptr; goto <0x1001>;");
let temp = TempRef::new(ModuleView::new(&ctx), ptr);
assert_eq!(temp.name(), Some("ptr"));
assert_eq!(temp.space().name(), Some("ptr"));
}
}