Files
regorus/src/rvm/tests/test_utils.rs
Anand Krishnamoorthi 49bd3c22f3 feat!: add Rego Virtual Machine (RVM) implementation (#495)
* feat!: add Rego Virtual Machine (RVM) implementation

This commit introduces a register-based virtual machine for executing Rego
policies with bytecode-style instructions. Unlike the existing tree-walking
interpreter, the RVM compiles policies into instruction sequences that operate
on virtual registers, offering better performance and optimization potential.

Core Components:

Instruction Set Architecture:
- Define instruction types for data operations, control flow, and builtins
- Implement instruction parameter encoding and display formatting
- Add instruction parser with comprehensive test coverage

Virtual Machine Engine:
- Register-based execution model with program counter management
- Loop execution supporting iterators, comprehensions, and quantifiers
- Function call handling with argument evaluation and context management
- Rule evaluation with default value resolution and virtual data support
- Arithmetic and comparison operation implementations

Program Representation:
- Program listing builder with instruction sequencing
- Rule tree construction for organizing policy rules
- Binary and JSON serialization for compiled programs
- Recompilation support for program modification

Testing Infrastructure:
- Extensive YAML test suites covering all VM features
- Rust unit tests for VM execution and instruction parsing
- Test suites for loops, comprehensions, builtins, and control flow

BREAKING CHANGE: Introduces new VM execution path alongside interpreter

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>

* docs: add detailed RVM architecture references

Introduce architecture.md explaining program artifacts, serialization, and runtime subsystems.
Document the full opcode catalog in instruction-set.md, including operands, parameter tables, and outcomes.
Walk through execution flow, stacks, and operational guidance in vm-runtime.md, tying the runtime to the new architecture docs.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>

---------

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2025-11-14 11:43:19 -06:00

105 lines
3.8 KiB
Rust

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//! Test utility functions for RVM serialization
use crate::rvm::program::{binaries_to_values, BinaryValue, Program};
use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
use bincode::config::standard;
use bincode::serde::decode_from_slice;
/// Test utility function for round-trip serialization
/// Serializes program, deserializes it, and serializes again to check for consistency
pub fn test_round_trip_serialization(program: &Program) -> Result<(), String> {
// First serialization
let serialized1 = program.serialize_binary()?;
// Basic validation: ensure literal section decodes cleanly under the current format.
if serialized1.len() >= 8 && serialized1.starts_with(&Program::MAGIC) {
let version = u32::from_le_bytes([
serialized1[4],
serialized1[5],
serialized1[6],
serialized1[7],
]);
if version == 2 && serialized1.len() >= 25 {
let entry_points_len = u32::from_le_bytes([
serialized1[8],
serialized1[9],
serialized1[10],
serialized1[11],
]) as usize;
let sources_len = u32::from_le_bytes([
serialized1[12],
serialized1[13],
serialized1[14],
serialized1[15],
]) as usize;
let literals_len = u32::from_le_bytes([
serialized1[16],
serialized1[17],
serialized1[18],
serialized1[19],
]) as usize;
let entry_points_start = 25;
let sources_start = entry_points_start + entry_points_len;
let literals_start = sources_start + sources_len;
let rule_tree_start = literals_start + literals_len;
if literals_len > 0 && serialized1.len() >= rule_tree_start {
match decode_from_slice::<Vec<BinaryValue>, _>(
&serialized1[literals_start..rule_tree_start],
standard(),
) {
Ok((decoded_literals, _)) => {
if binaries_to_values(decoded_literals).is_err() {
return Err(
"Failed to convert literal table from binary representation".into(),
);
}
}
Err(err) => {
return Err(format!(
"Failed to decode literal table with bincode: {}",
err
));
}
}
}
}
}
// Deserialize
let deserialized = match Program::deserialize_binary(&serialized1)? {
crate::rvm::program::DeserializationResult::Complete(program) => program,
crate::rvm::program::DeserializationResult::Partial(program) => {
let info = format!(
"Deserialization resulted in partial program during round-trip test \
(instructions={}, literals={}, needs_recompilation={})",
program.instructions.len(),
program.literals.len(),
program.needs_recompilation()
);
return Err(info);
}
};
// Second serialization
let serialized2 = deserialized.serialize_binary()?;
// Compare the two serialized versions
if serialized1 == serialized2 {
Ok(())
} else {
Err(format!(
"Round-trip serialization failed: serialized data differs. \
First serialization: {} bytes, Second: {} bytes",
serialized1.len(),
serialized2.len()
))
}
}