Skip to content

Commit 95bffcb

Browse files
authored
feat(rvm): extend program metadata and bump serialization to v6 (microsoft#654)
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
1 parent db8a9ab commit 95bffcb

9 files changed

Lines changed: 488 additions & 26 deletions

File tree

src/languages/rego/compiler/core.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ impl<'a> Compiler<'a> {
259259
}
260260

261261
pub fn emit_instruction(&mut self, instruction: Instruction, span: &Span) {
262+
if matches!(instruction, Instruction::HostAwait { .. }) {
263+
self.program.has_host_await = true;
264+
}
262265
self.program.instructions.push(instruction);
263266

264267
let source_path = span.source.get_path().to_string();

src/languages/rego/compiler/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ pub use error::{CompilerError, Result, SpannedCompilerError};
2323
use crate::ast::ExprRef;
2424
use crate::lexer::Span;
2525
use crate::rvm::program::{Program, RuleType, SpanInfo};
26-
use crate::value::Value;
2726
use crate::CompiledPolicy;
27+
use crate::Value;
2828
use alloc::collections::{BTreeMap, BTreeSet};
2929
use alloc::string::String;
3030
use alloc::vec;

src/rvm/program/core.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use anyhow::Result as AnyResult;
88
use indexmap::IndexMap;
99
use serde::{Deserialize, Serialize};
1010

11-
use super::types::{BuiltinInfo, ProgramMetadata, RuleInfo, SourceFile, SpanInfo};
11+
use super::metadata::ProgramMetadata;
12+
use super::types::{BuiltinInfo, RuleInfo, SourceFile, SpanInfo};
1213
use crate::builtins::BuiltinFcn;
1314
use crate::rvm::instructions::InstructionData;
1415
use crate::rvm::Instruction;
@@ -70,6 +71,11 @@ pub struct Program {
7071
/// Flag indicating that VirtualDataDocumentLookup instruction was used and runtime recursion checking is needed
7172
pub needs_runtime_recursion_check: bool,
7273

74+
/// Flag indicating whether this program contains any HostAwait instruction.
75+
/// Clients can use this to decide whether suspendable execution mode is required.
76+
#[serde(default)]
77+
pub has_host_await: bool,
78+
7379
/// Flag indicating that recompilation is needed due to partial deserialization failure
7480
/// This is set to true when the artifact section was successfully read but the extensible
7581
/// section failed to deserialize (e.g., due to version incompatibility)
@@ -85,7 +91,7 @@ pub struct Program {
8591

8692
impl Program {
8793
/// Current serialization format version
88-
pub const SERIALIZATION_VERSION: u32 = 5;
94+
pub const SERIALIZATION_VERSION: u32 = 6;
8995
/// Magic bytes to identify Regorus program files
9096
pub const MAGIC: [u8; 4] = *b"REGO";
9197
/// Maximum instructions supported (matches u16 jump targets)
@@ -122,10 +128,13 @@ impl Program {
122128
compiled_at: "unknown".to_string(),
123129
source_info: "unknown".to_string(),
124130
optimization_level: 0,
131+
language: String::new(),
132+
annotations: alloc::collections::BTreeMap::new(),
125133
},
126134
rule_tree: Value::new_object(),
127135
resolved_builtins: Vec::new(),
128136
needs_runtime_recursion_check: false,
137+
has_host_await: false,
129138
needs_recompilation: false,
130139
rego_v0: false, // Default to Rego v1
131140
}
@@ -330,10 +339,21 @@ impl Program {
330339

331340
/// Add instruction with optional span
332341
pub fn add_instruction(&mut self, instruction: Instruction, span: Option<SpanInfo>) {
342+
if matches!(instruction, Instruction::HostAwait { .. }) {
343+
self.has_host_await = true;
344+
}
333345
self.instructions.push(instruction);
334346
self.instruction_spans.push(span);
335347
}
336348

349+
/// Recompute whether HostAwait is present by scanning instructions.
350+
pub fn recompute_host_await_presence(&mut self) {
351+
self.has_host_await = self
352+
.instructions
353+
.iter()
354+
.any(|instruction| matches!(instruction, Instruction::HostAwait { .. }));
355+
}
356+
337357
/// Add literal value and return its index
338358
pub fn add_literal(&mut self, value: Value) -> usize {
339359
for (i, existing) in self.literals.iter().enumerate() {
@@ -408,6 +428,11 @@ impl Program {
408428
pub const fn is_fully_functional(&self) -> bool {
409429
!self.needs_recompilation
410430
}
431+
432+
/// Check whether HostAwait instructions are present in this program.
433+
pub const fn has_host_await(&self) -> bool {
434+
self.has_host_await
435+
}
411436
}
412437

413438
impl Default for Program {

src/rvm/program/listing.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,19 @@ pub fn generate_assembly_listing(program: &Program, config: &AssemblyListingConf
133133
program.metadata.optimization_level
134134
),
135135
);
136+
if !program.metadata.language.is_empty() {
137+
push_line(
138+
&mut output,
139+
format_args!("; language: {}", program.metadata.language),
140+
);
141+
}
142+
if !program.metadata.annotations.is_empty() {
143+
push_line(&mut output, format_args!("; annotations:"));
144+
for (key, value) in &program.metadata.annotations {
145+
let json = serde_json::to_string(value).unwrap_or_else(|_| "<invalid>".to_string());
146+
push_line(&mut output, format_args!("; {}: {}", key, json));
147+
}
148+
}
136149
}
137150

138151
push_line(&mut output, format_args!(";"));

0 commit comments

Comments
 (0)