use crate::op::*;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Program {
pub constants: Vec<Const>,
pub functions: Vec<Function>,
pub function_names: IndexMap<String, u32>,
pub module_aliases: IndexMap<String, String>,
pub entry: Option<u32>,
#[serde(default)]
pub record_shapes: Vec<Vec<u32>>,
}
impl Program {
pub fn lookup(&self, name: &str) -> Option<u32> {
self.function_names.get(name).copied()
}
pub fn declared_effects(&self) -> Vec<DeclaredEffect> {
let mut out: Vec<DeclaredEffect> = Vec::new();
for f in &self.functions {
for e in &f.effects {
if !out.iter().any(|x| x == e) {
out.push(e.clone());
}
}
}
out
}
}
pub type BodyHash = [u8; 16];
pub const ZERO_BODY_HASH: BodyHash = [0u8; 16];
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Function {
pub name: String,
pub arity: u16,
pub locals_count: u16,
pub code: Vec<Op>,
#[serde(default)]
pub effects: Vec<DeclaredEffect>,
#[serde(default = "zero_body_hash")]
pub body_hash: BodyHash,
#[serde(default)]
pub refinements: Vec<Option<Refinement>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Refinement {
pub binding: String,
pub predicate: lex_ast::CExpr,
}
fn zero_body_hash() -> BodyHash { ZERO_BODY_HASH }
pub fn compute_body_hash(
arity: u16,
locals_count: u16,
code: &[Op],
record_shapes: &[Vec<u32>],
) -> BodyHash {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(arity.to_le_bytes());
hasher.update(locals_count.to_le_bytes());
hasher.update((code.len() as u64).to_le_bytes());
for op in code {
let bytes = match op {
Op::MakeRecord { shape_idx, .. } => {
let shape = &record_shapes[*shape_idx as usize];
#[derive(Serialize)]
struct LegacyMakeRecord<'a> {
field_name_indices: &'a [u32],
}
#[derive(Serialize)]
enum LegacyOp<'a> {
MakeRecord(LegacyMakeRecord<'a>),
}
serde_json::to_vec(&LegacyOp::MakeRecord(LegacyMakeRecord {
field_name_indices: shape,
}))
.expect("Op serialization must succeed")
}
Op::LoadLocalAddIntConst { local_idx, .. } => {
serde_json::to_vec(&Op::LoadLocal(*local_idx))
.expect("Op serialization must succeed")
}
_ => serde_json::to_vec(op).expect("Op serialization must succeed"),
};
hasher.update((bytes.len() as u64).to_le_bytes());
hasher.update(&bytes);
}
let full = hasher.finalize();
let mut out = [0u8; 16];
out.copy_from_slice(&full[..16]);
out
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DeclaredEffect {
pub kind: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub arg: Option<EffectArg>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum EffectArg {
Str(String),
Int(i64),
Ident(String),
}