Skip to main content

ConstValue

Enum ConstValue 

Source
pub enum ConstValue<T: Target> {
Show 22 variants I8(i8), I16(i16), I32(i32), I64(i64), U8(u8), U16(u16), U32(u32), U64(u64), NativeInt(i64), NativeUInt(u64), F32(f32), F64(f64), Vector(Box<[ConstValue<T>]>), String(u32), DecryptedString(Box<str>), Null, True, False, Type(T::TypeRef), MethodHandle(T::MethodRef), FieldHandle(T::FieldRef), DecryptedArray(Box<DecryptedArrayData<T>>),
}
Expand description

Compile-time constant values tracked through SSA analysis.

Represents all constant forms that can appear in the IR: numeric literals, string references, null, booleans, and runtime handles. Supports constant folding for arithmetic, bitwise, comparison, and conversion operations.

Generic over T: Target so metadata handles (type refs, method refs, field refs) carry the host’s reference types rather than generic placeholders.

§Equality

PartialEq/Eq/Hash model structural constant identity: “are these the same constant?”, not “would these compare equal at runtime?”. Floating-point arms are therefore compared and hashed bitwise, which is what makes Eq sound (NaN is reflexive here) and lets constants be used as hash-map keys — global value numbering keys on exactly this.

Two consequences follow, both deliberate:

  • F64(NAN) == F64(NAN) is true (identical bit patterns).
  • F64(0.0) == F64(-0.0) is false (distinct bit patterns, and distinguishable at runtime via 1.0 / x).

IEEE-754 semantic comparison is a separate operation: use Self::ceq and the c*_un family, which implement CIL/IEEE ordering and unordered semantics.

Variants§

§

I8(i8)

Signed 8-bit integer constant (CIL: int8).

§

I16(i16)

Signed 16-bit integer constant (CIL: int16).

§

I32(i32)

Signed 32-bit integer constant (CIL: int32).

§

I64(i64)

Signed 64-bit integer constant (CIL: int64).

§

U8(u8)

Unsigned 8-bit integer constant (CIL: uint8).

§

U16(u16)

Unsigned 16-bit integer constant (CIL: uint16).

§

U32(u32)

Unsigned 32-bit integer constant (CIL: uint32).

§

U64(u64)

Unsigned 64-bit integer constant (CIL: uint64).

§

NativeInt(i64)

Native-size signed integer constant. Width depends on the target pointer size (32-bit or 64-bit). Automatically masked to pointer width by mask_native().

§

NativeUInt(u64)

Native-size unsigned integer constant. Width depends on the target pointer size.

§

F32(f32)

32-bit IEEE 754 floating point constant.

§

F64(f64)

64-bit IEEE 754 floating point constant.

§

Vector(Box<[ConstValue<T>]>)

Vector constant with one constant per lane.

Boxed so the rarely-used vector arm does not widen every scalar ConstValue.

§

String(u32)

String constant referenced by its metadata token (index into #US heap). The actual string content is resolved during code generation.

§

DecryptedString(Box<str>)

Decrypted string with inline content (not just a heap index). Used by deobfuscation passes that decrypt strings at analysis time. Contains the actual string bytes for codegen to emit. Boxed to keep the common scalar ConstValue variants compact.

§

Null

Null reference constant.

§

True

Boolean true constant.

§

False

Boolean false constant.

§

Type(T::TypeRef)

Runtime type handle (result of typeof() or ldtoken).

§

MethodHandle(T::MethodRef)

Runtime method handle (result of ldtoken for a method).

§

FieldHandle(T::FieldRef)

Runtime field handle (result of ldtoken for a field).

§

DecryptedArray(Box<DecryptedArrayData<T>>)

Decrypted array data from deobfuscation.

Stores the raw bytes and element type of an array that was decrypted at analysis time. Code generation emits newarr + element stores to reconstruct the array in the output. The payload is boxed (see DecryptedArrayData) to keep ConstValue small.

Implementations§

Source§

impl<T: Target> ConstValue<T>

Source

pub const fn is_null(&self) -> bool

Returns true if this is the null constant.

Source

pub const fn is_bool(&self) -> bool

Returns true if this is a boolean constant.

Source

pub const fn is_integer(&self) -> bool

Returns true if this is an integer constant (signed or unsigned).

Source

pub const fn is_signed(&self) -> bool

Returns true if this is a signed integer constant.

Source

pub const fn is_unsigned(&self) -> bool

Returns true if this is an unsigned integer constant.

Source

pub const fn is_float(&self) -> bool

Returns true if this is a floating-point constant.

Source

pub const fn is_vector(&self) -> bool

Returns true if this is a vector constant.

Source

pub const fn is_string_like(&self) -> bool

Returns true if this value is a string (String or DecryptedString).

Source

pub const fn as_i32(&self) -> Option<i32>

Returns the constant as an i32 if applicable.

Source

pub const fn as_i64(&self) -> Option<i64>

Returns the constant as an i64 if applicable.

Source

pub const fn as_u64(&self) -> Option<u64>

Returns the constant as a u64 if applicable (for unsigned operations).

Source

pub const fn as_u32(&self) -> Option<u32>

Returns the constant as a u32 if applicable (for unsigned operations).

Source

pub const fn as_f32(&self) -> Option<f32>

Returns the constant as an f32 if it’s stored as F32.

Source

pub const fn as_f64(&self) -> Option<f64>

Returns the constant as an f64 if it’s stored as F64.

Source

pub const fn as_bool(&self) -> Option<bool>

Returns the constant as a bool if applicable.

Source

pub const fn from_bool(value: bool) -> Self

Creates a boolean constant from a bool value.

Source

pub fn as_decrypted_string(&self) -> Option<&str>

Returns the string content if this is a DecryptedString.

String variants are heap references and do not carry inline content.

Source

pub const fn is_zero(&self) -> bool

Returns true if this constant represents zero.

This includes all numeric zero values and False. Useful for opaque predicate detection where x ^ x, x - x, x * 0, etc. produce zero.

Source

pub const fn is_one(&self) -> bool

Returns true if this constant represents one.

This includes all numeric one values and True. Useful for identity operations and opaque predicate detection.

Source

pub const fn is_minus_one(&self) -> bool

Returns true if this constant represents negative one (-1).

This is useful for detecting x | -1 = -1 patterns in opaque predicates.

Source

pub const fn is_all_ones(&self) -> bool

Returns true if this constant has all bits set (e.g., -1 for signed, MAX for unsigned).

This is useful for detecting x & -1 = x and x | -1 = -1 patterns.

Source

pub fn integer_of_same_type(&self, value: i64) -> Option<Self>

Returns value as a constant of this constant’s own integer type.

Returns None when self is not an integer, or when value does not fit the target variant. Use this when a rewrite must materialise a new constant alongside an existing one — a mask, a shift amount — so the replacement keeps the width the surrounding IR declares. Hard-coding Self::I32 there silently narrows the type, and mixed-width folds then sign-extend the narrowed operand rather than using the value meant.

§Examples
use analyssa::{ir::ConstValue, testing::MockTarget};

let wide: ConstValue<MockTarget> = ConstValue::I64(1 << 32);
assert_eq!(wide.integer_of_same_type(255), Some(ConstValue::I64(255)));

// A value that does not fit the variant is rejected rather than wrapped.
let narrow: ConstValue<MockTarget> = ConstValue::I8(1);
assert_eq!(narrow.integer_of_same_type(1 << 20), None);
Source

pub const fn zero_of_same_type(&self) -> Self

Returns a zero constant of the same type as this constant.

Useful for algebraic simplifications like x * 0 = 0 where the result should preserve the type of the operands.

Source

fn zip_lanes( a: &[Self], b: &[Self], f: impl Fn(&Self, &Self) -> Option<Self>, ) -> Option<Self>

Folds a binary operation lane-wise over two equal-length vector constants, returning None if the lane counts differ or any lane fails to fold. This lifts every scalar fold method (Self::add, Self::mul, …) to SIMD vector constants with no per-op code.

Source

fn map_lanes(a: &[Self], f: impl Fn(&Self) -> Option<Self>) -> Option<Self>

Folds a unary operation lane-wise over a vector constant, returning None if any lane fails to fold.

Source

pub fn negate(&self, ptr_size: PointerSize) -> Option<Self>

Attempts to negate this constant.

Source

pub fn bitwise_not(&self, ptr_size: PointerSize) -> Option<Self>

Attempts to perform bitwise NOT on this constant.

Source

pub fn bitwise_and(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>

Attempts to perform bitwise AND on two constants.

Source

pub fn bitwise_or(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>

Attempts to perform bitwise OR on two constants.

Source

pub fn bitwise_xor(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>

Attempts to perform bitwise XOR on two constants.

Source

fn integer_bits(&self, ptr_size: PointerSize) -> Option<u32>

Returns this constant’s bit width, for the integer arms.

NativeInt/NativeUInt report the target’s pointer width; everything non-integral reports None.

Source

fn shift_distance(amount: &Self, bits: u32) -> Option<u32>

Returns the shift distance if it is representable and in range for a value of bits width, or None to decline the fold.

SsaOp::Shl/Shr document only dest = value << amount and define no out-of-range behaviour, and real ISAs disagree about it — x86 masks the count by 31 (63 with REX.W) regardless of the destination’s sub-register width, while AArch64 and RISC-V mask by the register width. Rust’s wrapping_shl masks by the value’s own type, so an I8 arm masked by 7: shl bl, 8 is 0 on hardware, but folding U8(0x81) << 8 returned U8(0x81) — a third semantics that matches neither.

Declining to fold is sound under every convention, so an out-of-range or negative count is simply not folded.

Source

pub fn shl(&self, amount: &Self, ptr_size: PointerSize) -> Option<Self>

Attempts to shift left.

Returns None when the shift distance is negative or at least the value’s own width — see shift_distance for why declining is the only convention-independent answer.

Source

pub fn shr( &self, amount: &Self, unsigned: bool, ptr_size: PointerSize, ) -> Option<Self>

Attempts to shift right (arithmetic for signed, logical for unsigned).

Returns None when the shift distance is negative or at least the value’s own width — see shift_distance.

Source

pub fn add(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>

Attempts to add two constants.

Source

pub fn sub(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>

Attempts to subtract two constants.

Source

pub fn mul(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>

Attempts to multiply two constants.

Source

pub fn add_checked( &self, other: &Self, unsigned: bool, ptr_size: PointerSize, ) -> Option<Self>

Attempts to add two constants with overflow checking.

Returns None if the addition would overflow. When unsigned is true, operands are treated as unsigned for overflow detection.

Source

pub fn sub_checked( &self, other: &Self, unsigned: bool, ptr_size: PointerSize, ) -> Option<Self>

Attempts to subtract two constants with overflow checking.

Returns None if the subtraction would overflow. When unsigned is true, operands are treated as unsigned for overflow detection.

Source

pub fn mul_checked( &self, other: &Self, unsigned: bool, ptr_size: PointerSize, ) -> Option<Self>

Attempts to multiply two constants with overflow checking.

Returns None if the multiplication would overflow. When unsigned is true, operands are treated as unsigned for overflow detection.

Source

pub fn div(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>

Attempts to divide two constants. Uses checked_div/checked_rem so MIN/-1 overflows fold to None rather than wrapping silently.

Source

pub fn rem(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>

Attempts to compute remainder (modulo) of two constants. Uses checked_rem so MIN%-1 overflows fold to None.

Source

pub fn ceq(&self, other: &Self) -> Option<Self>

Attempts to compare two constants for equality.

Source

pub fn clt(&self, other: &Self) -> Option<Self>

Attempts to compare two constants for less-than (signed).

Source

pub fn clt_un(&self, other: &Self) -> Option<Self>

Attempts to compare two constants for less-than (unsigned).

Source

pub fn cgt(&self, other: &Self) -> Option<Self>

Attempts to compare two constants for greater-than (signed).

Source

pub fn cgt_un(&self, other: &Self) -> Option<Self>

Attempts to compare two constants for greater-than (unsigned).

Source

pub fn mask_native(self, ptr_size: PointerSize) -> Self

Masks a ConstValue to the target pointer width.

For NativeInt, sign-extends from 32-bit on Bit32. For NativeUInt, zero-extends from 32-bit on Bit32. All other variants are returned unchanged.

Source

pub fn to_bytes( &self, endianness: Endianness, ptr_size: PointerSize, ) -> Option<Vec<u8>>

Serializes an integer constant to bytes in the given endianness.

Returns None for non-integer variants (floats, strings, handles, etc.).

§Examples
use analyssa::{ir::value::ConstValue, target::Endianness, MockTarget, PointerSize};

let val = ConstValue::<MockTarget>::U32(0x01020304);
let le = val.to_bytes(Endianness::Little, PointerSize::Bit64).unwrap();
let be = val.to_bytes(Endianness::Big, PointerSize::Bit64).unwrap();
assert_eq!(le, vec![0x04, 0x03, 0x02, 0x01]);
assert_eq!(be, vec![0x01, 0x02, 0x03, 0x04]);

// A native-width value follows the target's pointer size, not `u64`.
let native = ConstValue::<MockTarget>::NativeUInt(0x0102_0304);
assert_eq!(native.to_bytes(Endianness::Little, PointerSize::Bit32).unwrap().len(), 4);
assert_eq!(native.to_bytes(Endianness::Little, PointerSize::Bit64).unwrap().len(), 8);
Source

pub fn from_bytes(bytes: &[u8], endianness: Endianness) -> Option<Self>

Deserializes a byte slice into an unsigned integer constant.

The byte slice is interpreted as an unsigned integer in the given endianness. The variant is chosen based on the slice length:

LengthVariant
1U8
2U16
4U32
8U64

Returns None for other slice lengths.

§Examples
use analyssa::{ir::value::ConstValue, target::Endianness, MockTarget};

let be_bytes = vec![0x01, 0x02, 0x03, 0x04];
let val = ConstValue::<MockTarget>::from_bytes(&be_bytes, Endianness::Big);
assert_eq!(val, Some(ConstValue::U32(0x01020304)));
Source§

impl<T: Target> ConstValue<T>

Source

pub fn convert_to( &self, target: &T::Type, unsigned_source: bool, ptr_bytes: u32, ) -> Option<Self>

Converts this constant to a different type. Forwards to Target::convert_const; non-CIL hosts that don’t override the hook always observe None.

Source

pub fn convert_to_checked( &self, target: &T::Type, unsigned_source: bool, ptr_bytes: u32, ) -> Option<Self>

Converts this constant with overflow checking. Forwards to Target::convert_const_checked.

Trait Implementations§

Source§

impl<T: Clone + Target> Clone for ConstValue<T>
where T::TypeRef: Clone, T::MethodRef: Clone, T::FieldRef: Clone,

Source§

fn clone(&self) -> ConstValue<T>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Debug + Target> Debug for ConstValue<T>
where T::TypeRef: Debug, T::MethodRef: Debug, T::FieldRef: Debug,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de, T: Target> Deserialize<'de> for ConstValue<T>
where T::TypeRef: Deserialize<'de>, T::MethodRef: Deserialize<'de>, T::FieldRef: Deserialize<'de>,

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl<T: Target> Display for ConstValue<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: Target> Eq for ConstValue<T>

Structural identity is reflexive: the float arms compare bitwise, so NaN == NaN holds here even though IEEE-754 says otherwise.

Source§

impl<T: Target> From<ConstValue<T>> for SymbolicExpr<T>

Source§

fn from(value: ConstValue<T>) -> Self

Converts to this type from the input type.
Source§

impl<T: Target> Hash for ConstValue<T>

Source§

fn hash<H: Hasher>(&self, state: &mut H)

Hashes the constant consistently with PartialEq: the arm followed by its payload, with floats hashed by their bit pattern.

1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<T: Target> PartialEq for ConstValue<T>

Source§

fn eq(&self, other: &Self) -> bool

Compares two constants for structural identity.

Constants of different arms are never equal, even when they denote the same mathematical value (I32(4) != I64(4)) — the arm is part of the constant’s identity. Floats compare bitwise; see the type-level documentation for why.

1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl<T: Target> Serialize for ConstValue<T>

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl<T> Freeze for ConstValue<T>
where <T as Target>::TypeRef: Freeze, <T as Target>::MethodRef: Freeze, <T as Target>::FieldRef: Freeze,

§

impl<T> RefUnwindSafe for ConstValue<T>

§

impl<T> Send for ConstValue<T>
where <T as Target>::TypeRef: Send, <T as Target>::MethodRef: Send, <T as Target>::FieldRef: Send,

§

impl<T> Sync for ConstValue<T>
where <T as Target>::TypeRef: Sync, <T as Target>::MethodRef: Sync, <T as Target>::FieldRef: Sync,

§

impl<T> Unpin for ConstValue<T>
where <T as Target>::TypeRef: Unpin, <T as Target>::MethodRef: Unpin, <T as Target>::FieldRef: Unpin,

§

impl<T> UnsafeUnpin for ConstValue<T>

§

impl<T> UnwindSafe for ConstValue<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.