mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
95bffcb5f9
Add typed metadata support to RVM programs so that language frontends can store language identity and arbitrary annotations alongside the compiled bytecode. Program metadata: - Add `language` field to identify the source language (e.g. "rego", "azure_policy") so the VM can adjust semantics at runtime - Add `annotations` map (BTreeMap<String, MetadataValue>) for frontend-specific key-value metadata - Add MetadataValue enum with String, Bool, Integer, Float, Array, and Object variants, plus full serde support - Add to_value() conversion for runtime access from VM instructions - Add has_host_await flag with recompute_host_await_presence() Serialization: - Bump binary format version from 5 to 6 - Add JSON serialization for the new metadata fields Assembly listing: - Display language and annotations in the program header Compiler: - Track has_host_await during Rego compilation
190 lines
5.7 KiB
Rust
190 lines
5.7 KiB
Rust
#![allow(
|
|
missing_debug_implementations,
|
|
clippy::missing_const_for_fn,
|
|
clippy::option_if_let_else,
|
|
clippy::if_then_some_else_none,
|
|
clippy::unused_self
|
|
)] // compiler internals do not require Debug
|
|
|
|
mod comprehensions;
|
|
mod core;
|
|
mod destructuring;
|
|
mod error;
|
|
mod expressions;
|
|
mod function_calls;
|
|
mod loops;
|
|
mod program;
|
|
mod queries;
|
|
mod references;
|
|
mod rules;
|
|
|
|
pub use error::{CompilerError, Result, SpannedCompilerError};
|
|
|
|
use crate::ast::ExprRef;
|
|
use crate::lexer::Span;
|
|
use crate::rvm::program::{Program, RuleType, SpanInfo};
|
|
use crate::CompiledPolicy;
|
|
use crate::Value;
|
|
use alloc::collections::{BTreeMap, BTreeSet};
|
|
use alloc::string::String;
|
|
use alloc::vec;
|
|
use alloc::vec::Vec;
|
|
use indexmap::IndexMap;
|
|
|
|
pub type Register = u8;
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
struct Scope {
|
|
bound_vars: BTreeMap<String, Register>,
|
|
unbound_vars: BTreeSet<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum ComprehensionType {
|
|
Array,
|
|
Object,
|
|
Set,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum ContextType {
|
|
Comprehension(ComprehensionType),
|
|
Rule(RuleType),
|
|
Every,
|
|
}
|
|
|
|
/// Compilation context for handling different types of rule bodies and comprehensions
|
|
#[derive(Debug, Clone)]
|
|
pub struct CompilationContext {
|
|
pub(super) context_type: ContextType,
|
|
pub(super) dest_register: Register,
|
|
pub(super) key_expr: Option<ExprRef>,
|
|
pub(super) value_expr: Option<ExprRef>,
|
|
pub(super) span: Span,
|
|
pub(super) key_value_loops_hoisted: bool,
|
|
}
|
|
|
|
/// Entry in the rule compilation worklist that tracks both rule path and full call stack for recursion detection
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct WorklistEntry {
|
|
/// Rule path to be compiled (e.g., "data.package.rule")
|
|
pub rule_path: String,
|
|
/// Call stack of rule indices leading to this rule (empty for entry point)
|
|
pub call_stack: Vec<u16>,
|
|
}
|
|
|
|
impl WorklistEntry {
|
|
pub fn new(rule_path: String, call_stack: Vec<u16>) -> Self {
|
|
Self {
|
|
rule_path,
|
|
call_stack,
|
|
}
|
|
}
|
|
|
|
pub fn entry_point(rule_path: String) -> Self {
|
|
Self {
|
|
rule_path,
|
|
call_stack: Vec::new(),
|
|
}
|
|
}
|
|
|
|
/// Create a new entry by extending the call stack with the caller's rule index
|
|
pub fn with_caller(
|
|
rule_path: String,
|
|
current_call_stack: &[u16],
|
|
caller_rule_index: u16,
|
|
) -> Self {
|
|
let mut new_call_stack = current_call_stack.to_vec();
|
|
new_call_stack.push(caller_rule_index);
|
|
Self {
|
|
rule_path,
|
|
call_stack: new_call_stack,
|
|
}
|
|
}
|
|
|
|
/// Check if this entry would create a recursive call
|
|
pub fn would_create_recursion(&self, target_rule_index: u16) -> bool {
|
|
self.call_stack.contains(&target_rule_index)
|
|
}
|
|
}
|
|
|
|
pub struct Compiler<'a> {
|
|
program: Program,
|
|
spans: Vec<SpanInfo>,
|
|
register_counter: Register,
|
|
scopes: Vec<Scope>,
|
|
policy: &'a CompiledPolicy,
|
|
current_package: String,
|
|
current_module_index: u32,
|
|
rule_index_map: BTreeMap<String, u16>,
|
|
rule_worklist: Vec<WorklistEntry>,
|
|
rule_definitions: Vec<Vec<Vec<u32>>>,
|
|
rule_definition_function_params: Vec<Vec<Option<Vec<String>>>>,
|
|
rule_definition_destructuring_patterns: Vec<Vec<Option<u32>>>,
|
|
/// Per-rule, per-definition: the static value produced by this definition,
|
|
/// or `None` if the value is dynamic or differs across else-branches.
|
|
/// Used to compute `RuleInfo::early_exit_on_first_success`.
|
|
rule_definition_static_values: Vec<Vec<Option<Value>>>,
|
|
rule_types: Vec<RuleType>,
|
|
rule_function_param_count: Vec<Option<usize>>,
|
|
rule_result_registers: Vec<u8>,
|
|
rule_num_registers: Vec<u8>,
|
|
context_stack: Vec<CompilationContext>,
|
|
loop_expr_register_map: BTreeMap<ExprRef, Register>,
|
|
source_to_index: BTreeMap<String, usize>,
|
|
builtin_index_map: BTreeMap<String, u16>,
|
|
current_input_register: Option<Register>,
|
|
current_data_register: Option<Register>,
|
|
current_rule_path: String,
|
|
current_call_stack: Vec<u16>,
|
|
entry_points: IndexMap<String, usize>,
|
|
soft_assert_mode: bool,
|
|
}
|
|
|
|
impl<'a> Compiler<'a> {
|
|
pub fn with_policy(policy: &'a CompiledPolicy) -> Self {
|
|
let mut program = Program::new();
|
|
program.rego_v0 = policy.is_rego_v0();
|
|
Self {
|
|
program,
|
|
spans: Vec::new(),
|
|
register_counter: 1,
|
|
scopes: vec![Scope::default()],
|
|
policy,
|
|
current_package: String::new(),
|
|
current_module_index: 0,
|
|
rule_index_map: BTreeMap::new(),
|
|
rule_worklist: Vec::new(),
|
|
rule_definitions: Vec::new(),
|
|
rule_definition_function_params: Vec::new(),
|
|
rule_definition_destructuring_patterns: Vec::new(),
|
|
rule_definition_static_values: Vec::new(),
|
|
rule_types: Vec::new(),
|
|
rule_function_param_count: Vec::new(),
|
|
rule_result_registers: Vec::new(),
|
|
rule_num_registers: Vec::new(),
|
|
context_stack: vec![],
|
|
loop_expr_register_map: BTreeMap::new(),
|
|
source_to_index: BTreeMap::new(),
|
|
builtin_index_map: BTreeMap::new(),
|
|
current_input_register: None,
|
|
current_data_register: None,
|
|
current_rule_path: String::new(),
|
|
current_call_stack: Vec::new(),
|
|
entry_points: IndexMap::new(),
|
|
soft_assert_mode: false,
|
|
}
|
|
}
|
|
|
|
pub(super) fn with_soft_assert_mode<F, R>(&mut self, enabled: bool, f: F) -> R
|
|
where
|
|
F: FnOnce(&mut Self) -> R,
|
|
{
|
|
let previous = self.soft_assert_mode;
|
|
self.soft_assert_mode = enabled;
|
|
let result = f(self);
|
|
self.soft_assert_mode = previous;
|
|
result
|
|
}
|
|
}
|