mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Merge the three separate Assert* instructions (AssertNot, AssertCondition,
AssertNotUndefined) into a single `Guard { register, mode }` instruction
with a GuardMode enum. This cuts duplicated match arms across display,
listing, parser, dispatch, and all compiler emit sites.
Drop the unnecessary `#[repr(C)]` from the Instruction enum. It was never
exposed across FFI, so the C-compatible 4-byte discriminant was pure waste.
Without it Rust picks a 1-byte discriminant, shrinking every instruction
from 8 bytes to 6. A new `instruction_size` unit test locks this at 6.
While touching these files, also clean up several long-standing issues:
- Deduplicate the iteration-state setup in loops.rs by extracting a shared
resolve_iteration_state() helper -- the stack-based and stackless paths
had near-identical 40-line blocks.
- Collapse the ExitWithSuccess / ExitWithFailure match arms into one.
- In rules.rs, stop cloning Arc<Program> just to borrow a RuleInfo -- clone
the small RuleInfo struct directly and extract a get_rule_info() helper.
- Move the memory check into dispatch (runs per instruction) and remove the
now-dead enforce_memory_check() entry-point calls.
- Apply map_or_else style throughout listing.rs for consistency.
124 lines
4.2 KiB
Rust
124 lines
4.2 KiB
Rust
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
// `pattern_type_mismatch` requires explicit `&`+`ref` patterns for tuple matches
|
|
// on (&Value, &Value), which conflicts with `needless_borrowed_reference`.
|
|
// Disable both to keep patterns consistent within this file.
|
|
#![allow(clippy::pattern_type_mismatch, clippy::needless_borrowed_reference)]
|
|
|
|
use alloc::collections::BTreeSet;
|
|
|
|
use crate::number::Number;
|
|
use crate::value::Value;
|
|
|
|
use super::errors::{Result, VmError};
|
|
use super::machine::RegoVM;
|
|
|
|
impl RegoVM {
|
|
/// Add two values using interpreter's arithmetic logic
|
|
pub(super) fn add_values(&self, a: &Value, b: &Value) -> Result<Value> {
|
|
match (a, b) {
|
|
(&Value::Number(ref x), &Value::Number(ref y)) => Ok(Value::from(x.add(y)?)),
|
|
_ => Err(VmError::InvalidAddition {
|
|
left: a.clone(),
|
|
right: b.clone(),
|
|
pc: self.pc,
|
|
}),
|
|
}
|
|
}
|
|
|
|
/// Subtract two values using interpreter's arithmetic logic
|
|
pub(super) fn sub_values(&self, a: &Value, b: &Value) -> Result<Value> {
|
|
match (a, b) {
|
|
(&Value::Number(ref x), &Value::Number(ref y)) => Ok(Value::from(x.sub(y)?)),
|
|
(&Value::Set(ref left), &Value::Set(ref right)) => {
|
|
let diff: BTreeSet<Value> = left.difference(right).cloned().collect();
|
|
Ok(Value::from_set(diff))
|
|
}
|
|
_ => Err(VmError::InvalidSubtraction {
|
|
left: a.clone(),
|
|
right: b.clone(),
|
|
pc: self.pc,
|
|
}),
|
|
}
|
|
}
|
|
|
|
/// Multiply two values using interpreter's arithmetic logic
|
|
pub(super) fn mul_values(&self, a: &Value, b: &Value) -> Result<Value> {
|
|
match (a, b) {
|
|
(&Value::Number(ref x), &Value::Number(ref y)) => Ok(Value::from(x.mul(y)?)),
|
|
_ => Err(VmError::InvalidMultiplication {
|
|
left: a.clone(),
|
|
right: b.clone(),
|
|
pc: self.pc,
|
|
}),
|
|
}
|
|
}
|
|
|
|
/// Divide two values using interpreter's arithmetic logic
|
|
pub(super) fn div_values(&self, a: &Value, b: &Value) -> Result<Value> {
|
|
match (a, b) {
|
|
(&Value::Number(ref x), &Value::Number(ref y)) => {
|
|
if *y == Number::from(0_u64) {
|
|
if self.strict_builtin_errors {
|
|
return Err(VmError::InvalidDivision {
|
|
left: a.clone(),
|
|
right: b.clone(),
|
|
pc: self.pc,
|
|
});
|
|
}
|
|
return Ok(Value::Undefined);
|
|
}
|
|
|
|
Ok(Value::from(x.clone().divide(y)?))
|
|
}
|
|
_ => Err(VmError::InvalidDivision {
|
|
left: a.clone(),
|
|
right: b.clone(),
|
|
pc: self.pc,
|
|
}),
|
|
}
|
|
}
|
|
|
|
/// Modulo two values using interpreter's arithmetic logic
|
|
pub(super) fn mod_values(&self, a: &Value, b: &Value) -> Result<Value> {
|
|
match (a, b) {
|
|
(&Value::Number(ref x), &Value::Number(ref y)) => {
|
|
if *y == Number::from(0_u64) {
|
|
if self.strict_builtin_errors {
|
|
return Err(VmError::InvalidModulo {
|
|
left: a.clone(),
|
|
right: b.clone(),
|
|
pc: self.pc,
|
|
});
|
|
}
|
|
return Ok(Value::Undefined);
|
|
}
|
|
|
|
if !x.is_integer() || !y.is_integer() {
|
|
return Err(VmError::ModuloOnFloat {
|
|
left: a.clone(),
|
|
right: b.clone(),
|
|
pc: self.pc,
|
|
});
|
|
}
|
|
|
|
Ok(Value::from(x.clone().modulo(y)?))
|
|
}
|
|
_ => Err(VmError::InvalidModulo {
|
|
left: a.clone(),
|
|
right: b.clone(),
|
|
pc: self.pc,
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub(super) const fn to_bool(&self, value: &Value) -> Option<bool> {
|
|
match *value {
|
|
Value::Bool(b) => Some(b),
|
|
Value::Null if !self.strict_builtin_errors => Some(true),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|