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)istrue(identical bit patterns).F64(0.0) == F64(-0.0)isfalse(distinct bit patterns, and distinguishable at runtime via1.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>
impl<T: Target> ConstValue<T>
Sourcepub const fn is_integer(&self) -> bool
pub const fn is_integer(&self) -> bool
Returns true if this is an integer constant (signed or unsigned).
Sourcepub const fn is_unsigned(&self) -> bool
pub const fn is_unsigned(&self) -> bool
Returns true if this is an unsigned integer constant.
Sourcepub const fn is_string_like(&self) -> bool
pub const fn is_string_like(&self) -> bool
Returns true if this value is a string (String or DecryptedString).
Sourcepub const fn as_u64(&self) -> Option<u64>
pub const fn as_u64(&self) -> Option<u64>
Returns the constant as a u64 if applicable (for unsigned operations).
Sourcepub const fn as_u32(&self) -> Option<u32>
pub const fn as_u32(&self) -> Option<u32>
Returns the constant as a u32 if applicable (for unsigned operations).
Sourcepub const fn as_f32(&self) -> Option<f32>
pub const fn as_f32(&self) -> Option<f32>
Returns the constant as an f32 if it’s stored as F32.
Sourcepub const fn as_f64(&self) -> Option<f64>
pub const fn as_f64(&self) -> Option<f64>
Returns the constant as an f64 if it’s stored as F64.
Sourcepub fn as_decrypted_string(&self) -> Option<&str>
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.
Sourcepub const fn is_zero(&self) -> bool
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.
Sourcepub const fn is_one(&self) -> bool
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.
Sourcepub const fn is_minus_one(&self) -> bool
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.
Sourcepub const fn is_all_ones(&self) -> bool
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.
Sourcepub fn integer_of_same_type(&self, value: i64) -> Option<Self>
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);Sourcepub const fn zero_of_same_type(&self) -> Self
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.
Sourcefn zip_lanes(
a: &[Self],
b: &[Self],
f: impl Fn(&Self, &Self) -> Option<Self>,
) -> Option<Self>
fn zip_lanes( a: &[Self], b: &[Self], f: impl Fn(&Self, &Self) -> Option<Self>, ) -> Option<Self>
Sourcefn map_lanes(a: &[Self], f: impl Fn(&Self) -> Option<Self>) -> Option<Self>
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.
Sourcepub fn negate(&self, ptr_size: PointerSize) -> Option<Self>
pub fn negate(&self, ptr_size: PointerSize) -> Option<Self>
Attempts to negate this constant.
Sourcepub fn bitwise_not(&self, ptr_size: PointerSize) -> Option<Self>
pub fn bitwise_not(&self, ptr_size: PointerSize) -> Option<Self>
Attempts to perform bitwise NOT on this constant.
Sourcepub fn bitwise_and(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
pub fn bitwise_and(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
Attempts to perform bitwise AND on two constants.
Sourcepub fn bitwise_or(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
pub fn bitwise_or(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
Attempts to perform bitwise OR on two constants.
Sourcepub fn bitwise_xor(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
pub fn bitwise_xor(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
Attempts to perform bitwise XOR on two constants.
Sourcefn integer_bits(&self, ptr_size: PointerSize) -> Option<u32>
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.
Sourcefn shift_distance(amount: &Self, bits: u32) -> Option<u32>
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.
Sourcepub fn shl(&self, amount: &Self, ptr_size: PointerSize) -> Option<Self>
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.
Sourcepub fn shr(
&self,
amount: &Self,
unsigned: bool,
ptr_size: PointerSize,
) -> Option<Self>
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.
Sourcepub fn add(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
pub fn add(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
Attempts to add two constants.
Sourcepub fn sub(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
pub fn sub(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
Attempts to subtract two constants.
Sourcepub fn mul(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
pub fn mul(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
Attempts to multiply two constants.
Sourcepub fn add_checked(
&self,
other: &Self,
unsigned: bool,
ptr_size: PointerSize,
) -> Option<Self>
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.
Sourcepub fn sub_checked(
&self,
other: &Self,
unsigned: bool,
ptr_size: PointerSize,
) -> Option<Self>
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.
Sourcepub fn mul_checked(
&self,
other: &Self,
unsigned: bool,
ptr_size: PointerSize,
) -> Option<Self>
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.
Sourcepub fn div(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
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.
Sourcepub fn rem(&self, other: &Self, ptr_size: PointerSize) -> Option<Self>
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.
Sourcepub fn ceq(&self, other: &Self) -> Option<Self>
pub fn ceq(&self, other: &Self) -> Option<Self>
Attempts to compare two constants for equality.
Sourcepub fn clt(&self, other: &Self) -> Option<Self>
pub fn clt(&self, other: &Self) -> Option<Self>
Attempts to compare two constants for less-than (signed).
Sourcepub fn clt_un(&self, other: &Self) -> Option<Self>
pub fn clt_un(&self, other: &Self) -> Option<Self>
Attempts to compare two constants for less-than (unsigned).
Sourcepub fn cgt(&self, other: &Self) -> Option<Self>
pub fn cgt(&self, other: &Self) -> Option<Self>
Attempts to compare two constants for greater-than (signed).
Sourcepub fn cgt_un(&self, other: &Self) -> Option<Self>
pub fn cgt_un(&self, other: &Self) -> Option<Self>
Attempts to compare two constants for greater-than (unsigned).
Sourcepub fn mask_native(self, ptr_size: PointerSize) -> Self
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.
Sourcepub fn to_bytes(
&self,
endianness: Endianness,
ptr_size: PointerSize,
) -> Option<Vec<u8>>
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);Sourcepub fn from_bytes(bytes: &[u8], endianness: Endianness) -> Option<Self>
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:
| Length | Variant |
|---|---|
| 1 | U8 |
| 2 | U16 |
| 4 | U32 |
| 8 | U64 |
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>
impl<T: Target> ConstValue<T>
Trait Implementations§
Source§impl<T: Clone + Target> Clone for ConstValue<T>
impl<T: Clone + Target> Clone for ConstValue<T>
Source§fn clone(&self) -> ConstValue<T>
fn clone(&self) -> ConstValue<T>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl<'de, T: Target> Deserialize<'de> for ConstValue<T>
impl<'de, T: Target> Deserialize<'de> for ConstValue<T>
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl<T: Target> Display for ConstValue<T>
impl<T: Target> Display for ConstValue<T>
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>
impl<T: Target> From<ConstValue<T>> for SymbolicExpr<T>
Source§fn from(value: ConstValue<T>) -> Self
fn from(value: ConstValue<T>) -> Self
Source§impl<T: Target> Hash for ConstValue<T>
impl<T: Target> Hash for ConstValue<T>
Source§impl<T: Target> PartialEq for ConstValue<T>
impl<T: Target> PartialEq for ConstValue<T>
Source§fn eq(&self, other: &Self) -> bool
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.
Auto Trait Implementations§
impl<T> Freeze for ConstValue<T>
impl<T> RefUnwindSafe for ConstValue<T>where
<T as Target>::TypeRef: RefUnwindSafe,
<T as Target>::MethodRef: RefUnwindSafe,
<T as Target>::FieldRef: RefUnwindSafe,
impl<T> Send for ConstValue<T>
impl<T> Sync for ConstValue<T>
impl<T> Unpin for ConstValue<T>
impl<T> UnsafeUnpin for ConstValue<T>where
<T as Target>::TypeRef: UnsafeUnpin,
<T as Target>::MethodRef: UnsafeUnpin,
<T as Target>::FieldRef: UnsafeUnpin,
impl<T> UnwindSafe for ConstValue<T>where
<T as Target>::TypeRef: UnwindSafe,
<T as Target>::MethodRef: UnwindSafe,
<T as Target>::FieldRef: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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