Files
regorus/tests/rvm/vm/suites/array_push_defined.yaml
Anand Krishnamoorthi e5ac9a2734 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.
2026-04-06 15:40:41 -05:00

93 lines
3.1 KiB
YAML

# ArrayPushDefined instruction test suite
#
# Like ArrayPush but silently skips Undefined values. Used by the Azure Policy
# compiler when collecting `field('alias[*].property')` results: absent nested
# properties produce Undefined and should be excluded from the collected array.
#
# Semantics:
# - If value register is Undefined → no-op (skip).
# - If value register is any other value (including Null) → push to array.
# - If arr register is not an array → error.
cases:
# =========================================================================
# Undefined values are skipped
# =========================================================================
- note: push_undefined_is_skipped
description: Pushing an undefined value leaves the array unchanged
literals: []
instructions:
- "ArrayNew { dest: 0 }"
# Register 1 is implicitly Undefined
- "ArrayPushDefined { arr: 0, value: 1 }"
- "Return { value: 0 }"
want_result: []
- note: push_undefined_among_defined
description: Only defined values are collected; undefined ones are silently dropped
literals:
- 10
- 20
instructions:
- "ArrayNew { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "ArrayPushDefined { arr: 0, value: 1 }"
# Register 2 is Undefined — should be skipped
- "ArrayPushDefined { arr: 0, value: 2 }"
- "Load { dest: 3, literal_idx: 1 }"
- "ArrayPushDefined { arr: 0, value: 3 }"
- "Return { value: 0 }"
want_result: [10, 20]
# =========================================================================
# Null and other values are kept
# =========================================================================
- note: push_null_is_kept
description: Null is not undefined — it is pushed to the array
literals: []
instructions:
- "ArrayNew { dest: 0 }"
- "LoadNull { dest: 1 }"
- "ArrayPushDefined { arr: 0, value: 1 }"
- "Return { value: 0 }"
want_result: [null]
- note: push_bool_is_kept
description: Boolean value is pushed normally
literals:
- true
instructions:
- "ArrayNew { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "ArrayPushDefined { arr: 0, value: 1 }"
- "Return { value: 0 }"
want_result: [true]
- note: push_string_is_kept
description: String value is pushed normally
literals:
- "hello"
instructions:
- "ArrayNew { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "ArrayPushDefined { arr: 0, value: 1 }"
- "Return { value: 0 }"
want_result: ["hello"]
# =========================================================================
# Non-array target is an error
# =========================================================================
- note: push_to_non_array_errors
description: ArrayPushDefined on a non-array register produces an error
literals:
- 42
- 1
instructions:
- "Load { dest: 0, literal_idx: 0 }"
- "Load { dest: 1, literal_idx: 1 }"
- "ArrayPushDefined { arr: 0, value: 1 }"
want_error: "Register 0 does not contain an array"