use crate::spec::engine::eval::types::*;
fn evaluate_program(
program: &ConformProgram,
runtime: &RuntimeProgram,
file_bytes: &[u8],
file_boundaries: &[u32],
file_ctx: ConformFileContext,
) -> Result<bool, String> {
let mut stack = Vec::with_capacity(32);
for instruction in &program.instructions {
let Some(kind) = instruction.kind() else {
return Err(format!("unknown opcode {}", instruction.opcode));
};
match kind {
ConformOpcode::PushTrue => stack.push(1),
ConformOpcode::PushFalse => stack.push(0),
ConformOpcode::PushImmediate => stack.push(instruction.operand),
ConformOpcode::PushFileSize => stack.push(file_ctx.file_size),
ConformOpcode::PushStringMatched => {
stack.push(u32::from(count(runtime, instruction.operand) != 0));
}
ConformOpcode::PushStringCount => stack.push(count(runtime, instruction.operand)),
ConformOpcode::PushStringOffset => {
let slot = pop(&mut stack)?;
stack.push(position(runtime, instruction.operand, slot));
}
ConformOpcode::PushStringLength => {
let slot = pop(&mut stack)?;
stack.push(length(runtime, instruction.operand, slot));
}
ConformOpcode::PushNumStrings | ConformOpcode::PushEntryCount => {
stack.push(runtime.counts.iter().filter(|&&c| c != 0).count() as u32);
}
ConformOpcode::UniquePatternCount => stack.push(file_ctx.unique_pattern_count),
ConformOpcode::TotalMatchCount => stack.push(file_ctx.total_match_count),
ConformOpcode::PushEntropy => stack.push(file_ctx.entropy_bucket),
ConformOpcode::PushIsPe => stack.push(file_ctx.is_pe),
ConformOpcode::PushIsDll => stack.push(file_ctx.is_dll),
ConformOpcode::PushIs64bit => stack.push(file_ctx.is_64bit),
ConformOpcode::PushHasSignature => stack.push(file_ctx.has_signature),
ConformOpcode::PushMagicU32 => stack.push(file_ctx.magic_u32),
ConformOpcode::PushNumSections => stack.push(file_ctx.num_sections),
ConformOpcode::PushNumImports => stack.push(file_ctx.num_imports),
ConformOpcode::PushEntryPoint => stack.push(file_ctx.entry_point_rva),
ConformOpcode::PushFileAge => stack.push(file_ctx.file_age_seconds),
ConformOpcode::And => binary_bool(&mut stack, |a, b| a != 0 && b != 0)?,
ConformOpcode::Or => binary_bool(&mut stack, |a, b| a != 0 || b != 0)?,
ConformOpcode::Not => {
let value = pop(&mut stack)?;
stack.push(u32::from(value == 0));
}
ConformOpcode::Eq => binary_bool(&mut stack, |a, b| a == b)?,
ConformOpcode::Neq => binary_bool(&mut stack, |a, b| a != b)?,
ConformOpcode::Lt => binary_bool(&mut stack, |a, b| a < b)?,
ConformOpcode::Gt => binary_bool(&mut stack, |a, b| a > b)?,
ConformOpcode::Lte => binary_bool(&mut stack, |a, b| a <= b)?,
ConformOpcode::Gte => binary_bool(&mut stack, |a, b| a >= b)?,
ConformOpcode::Add => binary_value(&mut stack, u32::wrapping_add)?,
ConformOpcode::Sub => binary_value(&mut stack, u32::wrapping_sub)?,
ConformOpcode::Mul => binary_value(&mut stack, u32::wrapping_mul)?,
ConformOpcode::Div => binary_value(&mut stack, |a, b| if b == 0 { 0 } else { a / b })?,
ConformOpcode::Mod => binary_value(&mut stack, |a, b| if b == 0 { 0 } else { a % b })?,
ConformOpcode::BitAnd => binary_value(&mut stack, |a, b| a & b)?,
ConformOpcode::BitOr => binary_value(&mut stack, |a, b| a | b)?,
ConformOpcode::BitXor => binary_value(&mut stack, |a, b| a ^ b)?,
ConformOpcode::Shl => binary_value(&mut stack, |a, b| a << (b & 31))?,
ConformOpcode::Shr => binary_value(&mut stack, |a, b| a >> (b & 31))?,
ConformOpcode::AnyOf => reduce(&mut stack, instruction.operand, |values| {
values.iter().any(|&v| v != 0)
})?,
ConformOpcode::AllOf => reduce(&mut stack, instruction.operand, |values| {
values.iter().all(|&v| v != 0)
})?,
ConformOpcode::CountOf => {
let needed = instruction.operand & 0xFFFF;
let width = instruction.operand >> 16;
reduce(&mut stack, width, |values| {
values.iter().filter(|&&v| v != 0).count() as u32 >= needed
})?;
}
ConformOpcode::StringAt => {
let wanted = pop(&mut stack)?;
stack.push(u32::from(
positions(runtime, instruction.operand).contains(&wanted),
));
}
ConformOpcode::StringIn => {
let hi = pop(&mut stack)?;
let lo = pop(&mut stack)?;
stack.push(u32::from(
positions(runtime, instruction.operand)
.iter()
.any(|&pos| pos >= lo && pos <= hi),
));
}
ConformOpcode::MatchOrder => {
let b = pop(&mut stack)?;
let a = pop(&mut stack)?;
let pa = first_position(runtime, a);
let pb = first_position(runtime, b);
stack.push(u32::from(
pa != ABORT_SENTINEL && pb != ABORT_SENTINEL && pa < pb,
));
}
ConformOpcode::MatchDistance => {
let b = pop(&mut stack)?;
let a = pop(&mut stack)?;
stack.push(match_distance(runtime, a, b));
}
ConformOpcode::MatchBetween => {
let c = pop(&mut stack)?;
let b = pop(&mut stack)?;
let a = pop(&mut stack)?;
let pa = first_position(runtime, a);
let pb = first_position(runtime, b);
let lo = pa.min(pb);
let hi = pa.max(pb);
stack.push(u32::from(
pa != ABORT_SENTINEL
&& pb != ABORT_SENTINEL
&& positions(runtime, c).iter().any(|&p| p >= lo && p <= hi),
));
}
ConformOpcode::SameFile | ConformOpcode::DifferentFile => {
let b = pop(&mut stack)?;
let a = pop(&mut stack)?;
let pa = first_position(runtime, a);
let pb = first_position(runtime, b);
let same = pa != ABORT_SENTINEL
&& pb != ABORT_SENTINEL
&& file_id_at(file_boundaries, pa) == file_id_at(file_boundaries, pb);
stack.push(u32::from(if kind == ConformOpcode::SameFile {
same
} else {
!same && pa != ABORT_SENTINEL && pb != ABORT_SENTINEL
}));
}
ConformOpcode::FileIdOf => {
let pattern = pop(&mut stack)?;
let pos = first_position(runtime, pattern);
stack.push(if pos == ABORT_SENTINEL {
ABORT_SENTINEL
} else {
file_id_at(file_boundaries, pos)
});
}
ConformOpcode::CrossFileChain => {
let c = pop(&mut stack)?;
let b = pop(&mut stack)?;
let a = pop(&mut stack)?;
let pa = first_position(runtime, a);
let pb = first_position(runtime, b);
let pc = first_position(runtime, c);
let fa = file_id_at(file_boundaries, pa);
stack.push(u32::from(
pa != ABORT_SENTINEL
&& pb != ABORT_SENTINEL
&& pc != ABORT_SENTINEL
&& fa == file_id_at(file_boundaries, pc)
&& fa != file_id_at(file_boundaries, pb),
));
}
ConformOpcode::ReadByteAt | ConformOpcode::ByteAt => {
let offset = pop(&mut stack)? as usize;
stack.push(file_bytes.get(offset).copied().unwrap_or(0) as u32);
}
ConformOpcode::Halt => return Ok(stack.last().copied().unwrap_or(0) != 0),
}
}
Ok(false)
}