#![allow(clippy::pattern_type_mismatch)]
use alloc::vec::Vec;
use anyhow::{bail, Result};
use crate::languages::azure_policy::ast::Expr;
use crate::rvm::instructions::PolicyOp;
use crate::rvm::Instruction;
use super::core::Compiler;
impl Compiler {
pub(super) fn compile_arm_template_function(
&mut self,
function_name: &str,
span: &crate::lexer::Span,
args: &[Expr],
) -> Result<Option<u8>> {
let dest = match function_name {
"concat" => {
let mut element_regs = Vec::with_capacity(args.len());
for arg in args {
element_regs.push(self.compile_expr(arg)?);
}
let array_dest = self.alloc_register()?;
let array_params = self.program.instruction_data.add_array_create_params(
crate::rvm::instructions::ArrayCreateParams {
dest: array_dest,
elements: element_regs,
},
);
self.emit(
Instruction::ArrayCreate {
params_index: array_params,
},
span,
);
let delimiter_reg = self.load_literal(crate::Value::from(""), span)?;
self.emit_builtin_call("concat", &[delimiter_reg, array_dest], span)?
}
"if" => {
let [cond_arg, true_arg, false_arg] = args else {
bail!(span.error("if() requires three arguments"));
};
let cond = self.compile_expr(cond_arg)?;
let when_true = self.compile_expr(true_arg)?;
let when_false = self.compile_expr(false_arg)?;
self.emit_builtin_call("azure.policy.if", &[cond, when_true, when_false], span)?
}
"and" => {
let regs = self.compile_call_args(args)?;
self.emit_builtin_call("azure.policy.logic_all", ®s, span)?
}
"not" => {
let [inner_arg] = args else {
bail!(span.error("not() requires one argument"));
};
let inner = self.compile_expr(inner_arg)?;
let dest = self.alloc_register()?;
self.emit(
Instruction::PolicyCondition {
dest,
left: inner,
right: 0,
op: PolicyOp::Not,
},
span,
);
dest
}
"tolower" => {
let regs = self.compile_call_args(args)?;
self.emit_builtin_call("lower", ®s, span)?
}
"toupper" => {
let regs = self.compile_call_args(args)?;
self.emit_builtin_call("upper", ®s, span)?
}
"replace" => {
let regs = self.compile_call_args(args)?;
self.emit_builtin_call("replace", ®s, span)?
}
"substring" => {
let regs = self.compile_call_args(args)?;
self.emit_builtin_call("substring", ®s, span)?
}
"length" => {
let regs = self.compile_call_args(args)?;
self.emit_builtin_call("count", ®s, span)?
}
"add" => self.emit_binary_instruction(args, span, |dest, left, right| {
Instruction::Add { dest, left, right }
})?,
"equals" => self.emit_binary_instruction(args, span, |dest, left, right| {
Instruction::PolicyCondition {
dest,
left,
right,
op: PolicyOp::Equals,
}
})?,
"greaterorequals" => {
self.emit_binary_instruction(args, span, |dest, left, right| {
Instruction::PolicyCondition {
dest,
left,
right,
op: PolicyOp::GreaterOrEquals,
}
})?
}
"lessorequals" => self.emit_binary_instruction(args, span, |dest, left, right| {
Instruction::PolicyCondition {
dest,
left,
right,
op: PolicyOp::LessOrEquals,
}
})?,
"contains" => self.emit_binary_instruction(args, span, |dest, left, right| {
Instruction::PolicyCondition {
dest,
left,
right,
op: PolicyOp::Contains,
}
})?,
"greater" => self.emit_binary_instruction(args, span, |dest, left, right| {
Instruction::PolicyCondition {
dest,
left,
right,
op: PolicyOp::Greater,
}
})?,
"less" => self.emit_binary_instruction(args, span, |dest, left, right| {
Instruction::PolicyCondition {
dest,
left,
right,
op: PolicyOp::Less,
}
})?,
"or" => {
let regs = self.compile_call_args(args)?;
self.emit_builtin_call("azure.policy.logic_any", ®s, span)?
}
"true" => {
if !args.is_empty() {
bail!(span.error("true() takes no arguments"));
}
self.load_literal(crate::Value::Bool(true), span)?
}
"false" => {
if !args.is_empty() {
bail!(span.error("false() takes no arguments"));
}
self.load_literal(crate::Value::Bool(false), span)?
}
"split" => self.emit_builtin_call_from_args("azure.policy.fn.split", args, span)?,
"empty" => self.emit_builtin_call_from_args("azure.policy.fn.empty", args, span)?,
"first" => self.emit_builtin_call_from_args("azure.policy.fn.first", args, span)?,
"last" => self.emit_builtin_call_from_args("azure.policy.fn.last", args, span)?,
"startswith" => {
self.emit_builtin_call_from_args("azure.policy.fn.starts_with", args, span)?
}
"endswith" => {
self.emit_builtin_call_from_args("azure.policy.fn.ends_with", args, span)?
}
"int" => self.emit_builtin_call_from_args("azure.policy.fn.int", args, span)?,
"string" => self.emit_builtin_call_from_args("azure.policy.fn.string", args, span)?,
"bool" => self.emit_builtin_call_from_args("azure.policy.fn.bool", args, span)?,
"padleft" => {
self.emit_builtin_call_from_args("azure.policy.fn.pad_left", args, span)?
}
"iprangecontains" => {
self.emit_builtin_call_from_args("azure.policy.fn.ip_range_contains", args, span)?
}
"createarray" => {
let mut element_regs = Vec::with_capacity(args.len());
for arg in args {
element_regs.push(self.compile_expr(arg)?);
}
let array_dest = self.alloc_register()?;
let array_params = self.program.instruction_data.add_array_create_params(
crate::rvm::instructions::ArrayCreateParams {
dest: array_dest,
elements: element_regs,
},
);
self.emit(
Instruction::ArrayCreate {
params_index: array_params,
},
span,
);
array_dest
}
"indexof" => {
self.emit_builtin_call_from_args("azure.policy.fn.index_of", args, span)?
}
"lastindexof" => {
self.emit_builtin_call_from_args("azure.policy.fn.last_index_of", args, span)?
}
"trim" => self.emit_builtin_call_from_args("azure.policy.fn.trim", args, span)?,
"format" => self.emit_builtin_call_from_args("azure.policy.fn.format", args, span)?,
"base64" => self.emit_builtin_call_from_args("azure.policy.fn.base64", args, span)?,
"base64tostring" => {
self.emit_builtin_call_from_args("azure.policy.fn.base64_to_string", args, span)?
}
"base64tojson" => {
self.emit_builtin_call_from_args("azure.policy.fn.base64_to_json", args, span)?
}
"uri" => self.emit_builtin_call_from_args("azure.policy.fn.uri", args, span)?,
"uricomponent" => {
self.emit_builtin_call_from_args("azure.policy.fn.uri_component", args, span)?
}
"uricomponenttostring" => self.emit_builtin_call_from_args(
"azure.policy.fn.uri_component_to_string",
args,
span,
)?,
"datauri" => {
self.emit_builtin_call_from_args("azure.policy.fn.data_uri", args, span)?
}
"datauritostring" => {
self.emit_builtin_call_from_args("azure.policy.fn.data_uri_to_string", args, span)?
}
"intersection" => {
self.emit_builtin_call_from_args("azure.policy.fn.intersection", args, span)?
}
"union" => self.emit_builtin_call_from_args("azure.policy.fn.union", args, span)?,
"take" => self.emit_builtin_call_from_args("azure.policy.fn.take", args, span)?,
"skip" => self.emit_builtin_call_from_args("azure.policy.fn.skip", args, span)?,
"range" => self.emit_builtin_call_from_args("azure.policy.fn.range", args, span)?,
"array" => self.emit_builtin_call_from_args("azure.policy.fn.array", args, span)?,
"coalesce" => {
self.emit_builtin_call_from_args("azure.policy.fn.coalesce", args, span)?
}
"createobject" => {
self.emit_builtin_call_from_args("azure.policy.fn.create_object", args, span)?
}
"sub" => {
let [left_arg, right_arg] = args else {
bail!(span.error("sub() requires two arguments"));
};
let left = self.compile_expr(left_arg)?;
let right = self.compile_expr(right_arg)?;
let dest = self.alloc_register()?;
self.emit(Instruction::Sub { dest, left, right }, span);
dest
}
"mul" => {
let [left_arg, right_arg] = args else {
bail!(span.error("mul() requires two arguments"));
};
let left = self.compile_expr(left_arg)?;
let right = self.compile_expr(right_arg)?;
let dest = self.alloc_register()?;
self.emit(Instruction::Mul { dest, left, right }, span);
dest
}
"div" => {
let [_, _] = args else {
bail!(span.error("div() requires two arguments"));
};
self.emit_builtin_call_from_args("azure.policy.fn.int_div", args, span)?
}
"mod" => {
let [_, _] = args else {
bail!(span.error("mod() requires two arguments"));
};
self.emit_builtin_call_from_args("azure.policy.fn.int_mod", args, span)?
}
"min" => self.emit_builtin_call_from_args("azure.policy.fn.min", args, span)?,
"max" => self.emit_builtin_call_from_args("azure.policy.fn.max", args, span)?,
"float" => self.emit_builtin_call_from_args("azure.policy.fn.float", args, span)?,
"json" => self.emit_builtin_call_from_args("azure.policy.fn.json", args, span)?,
"join" => self.emit_builtin_call_from_args("azure.policy.fn.join", args, span)?,
"guid" | "uniquestring" => {
bail!(span.error(&alloc::format!(
"unsupported template function '{function_name}' (deployment-template functions are not evaluated for compliance)"
)));
}
"items" => self.emit_builtin_call_from_args("azure.policy.fn.items", args, span)?,
"indexfromend" => {
self.emit_builtin_call_from_args("azure.policy.fn.index_from_end", args, span)?
}
"tryget" => self.emit_builtin_call_from_args("azure.policy.fn.try_get", args, span)?,
"tryindexfromend" => {
self.emit_builtin_call_from_args("azure.policy.fn.try_index_from_end", args, span)?
}
"datetimeadd" => {
self.emit_builtin_call_from_args("azure.policy.fn.date_time_add", args, span)?
}
"datetimefromepoch" => self.emit_builtin_call_from_args(
"azure.policy.fn.date_time_from_epoch",
args,
span,
)?,
"datetimetoepoch" => {
self.emit_builtin_call_from_args("azure.policy.fn.date_time_to_epoch", args, span)?
}
"adddays" => {
self.emit_builtin_call_from_args("azure.policy.fn.add_days", args, span)?
}
_ => return Ok(None),
};
Ok(Some(dest))
}
fn emit_builtin_call_from_args(
&mut self,
name: &str,
args: &[Expr],
span: &crate::lexer::Span,
) -> Result<u8> {
let regs = self.compile_call_args(args)?;
self.emit_builtin_call(name, ®s, span)
}
fn emit_binary_instruction(
&mut self,
args: &[Expr],
span: &crate::lexer::Span,
make_instr: impl FnOnce(u8, u8, u8) -> Instruction,
) -> Result<u8> {
let [left_arg, right_arg] = args else {
bail!(span.error("expected exactly two arguments"));
};
let left = self.compile_expr(left_arg)?;
let right = self.compile_expr(right_arg)?;
let dest = self.alloc_register()?;
self.emit(make_instr(dest, left, right), span);
Ok(dest)
}
}