mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat(rvm): new instructions and loop semantics for Azure Policy support (#659)
The Rego VM was designed around Rego's semantics, but Azure Policy needs a few things Rego doesn't: host-supplied context alongside input/data, undefined-to-null coercion for missing fields, skip-undefined collection behavior for wildcard aliases, and non-vacuous iteration over non-array values. This commit adds five new instructions to bridge those gaps: LoadContext / LoadMetadata — give programs access to host-supplied evaluation context and cached program metadata at runtime. ArrayPushDefined — like ArrayPush but silently drops undefined values, so wildcard alias collection (field[*].property) excludes absent nested properties instead of leaking undefined entries into the array. ReturnUndefinedIfNotTrue — early return with Undefined when a guard condition isn't satisfied, without tripping a VM assertion failure. This models "condition doesn't match" cleanly. CoalesceUndefinedToNull — turns Undefined into Null in-place so that downstream builtins see null rather than short-circuiting on undefined. The loop engine also gains an Azure Policy mode: when the source language is "azure_policy", an Every loop over a non-array value (scalars, null, objects) iterates once over a virtual Null element instead of being vacuously true. This matches how field[*] behaves on non-array fields in Azure Policy — the condition body runs once against Null, which typically evaluates to false. On the plumbing side: the VM gets a context field with set_context(), metadata is cached as a Value on program load, and map_limit_error is inlined into memory_check since it had only one call site. Four new YAML test suites (~880 lines) cover the new instructions and context/metadata loading, along with instruction parser, display, and assembly listing support for everything added here.
This commit is contained in:
committed by
GitHub
parent
8f740e2f6f
commit
e5ac9a2734
@@ -72,6 +72,14 @@ impl RegoVM {
|
||||
self.set_register(dest, self.input.clone())?;
|
||||
Ok(InstructionOutcome::Continue)
|
||||
}
|
||||
LoadContext { dest } => {
|
||||
self.set_register(dest, self.context.clone())?;
|
||||
Ok(InstructionOutcome::Continue)
|
||||
}
|
||||
LoadMetadata { dest } => {
|
||||
self.set_register(dest, self.metadata_value.clone())?;
|
||||
Ok(InstructionOutcome::Continue)
|
||||
}
|
||||
Move { dest, src } => {
|
||||
let value = self.get_register(src)?.clone();
|
||||
self.set_register(dest, value)?;
|
||||
@@ -351,6 +359,21 @@ impl RegoVM {
|
||||
self.handle_condition(passed)?;
|
||||
Ok(InstructionOutcome::Continue)
|
||||
}
|
||||
ReturnUndefinedIfNotTrue { condition } => {
|
||||
let value = self.get_register(condition)?;
|
||||
if matches!(value, Value::Bool(true)) {
|
||||
Ok(InstructionOutcome::Continue)
|
||||
} else {
|
||||
Ok(InstructionOutcome::Return(Value::Undefined))
|
||||
}
|
||||
}
|
||||
CoalesceUndefinedToNull { register } => {
|
||||
let value = self.get_register(register)?;
|
||||
if matches!(value, Value::Undefined) {
|
||||
self.set_register(register, Value::Null)?;
|
||||
}
|
||||
Ok(InstructionOutcome::Continue)
|
||||
}
|
||||
other => self.execute_call_instruction(program, other),
|
||||
}
|
||||
}
|
||||
@@ -585,6 +608,33 @@ impl RegoVM {
|
||||
}
|
||||
Ok(InstructionOutcome::Continue)
|
||||
}
|
||||
ArrayPushDefined { arr, value } => {
|
||||
// Skip undefined values — matches Azure Policy's
|
||||
// `field('alias[*].property')` collection semantics where
|
||||
// absent nested properties are excluded from the collected
|
||||
// array.
|
||||
if self.get_register(value)? == &Value::Undefined {
|
||||
return Ok(InstructionOutcome::Continue);
|
||||
}
|
||||
|
||||
let value_to_push = self.get_register(value)?.clone();
|
||||
|
||||
let mut arr_value = self.take_register(arr)?;
|
||||
|
||||
if let Ok(arr_mut) = arr_value.as_array_mut() {
|
||||
arr_mut.push(value_to_push);
|
||||
self.set_register(arr, arr_value)?;
|
||||
} else {
|
||||
let offending = arr_value.clone();
|
||||
self.set_register(arr, arr_value)?;
|
||||
return Err(VmError::RegisterNotArray {
|
||||
register: arr,
|
||||
value: offending,
|
||||
pc: self.pc,
|
||||
});
|
||||
}
|
||||
Ok(InstructionOutcome::Continue)
|
||||
}
|
||||
ArrayCreate { params_index } => {
|
||||
if let Some(params) = program
|
||||
.instruction_data
|
||||
|
||||
Reference in New Issue
Block a user