vihaco_module/
program_metadata.rs1use crate::module::{FunctionInfo, LabelInfo, LocalModule, NoInfo, SourceSymbolInfo};
17use vihaco_abi::{Type, Value};
18
19#[derive(Debug, Clone, PartialEq, vihaco_parser_derive::Parse)]
22#[syntax_class(metadata, head = "metadata", open = "{", close = "}")]
23pub struct ProgramMetadata<V = Value, Ty = Type, Info = NoInfo> {
24 pub functions: Vec<FunctionInfo<Ty>>,
25 pub labels: Vec<LabelInfo>,
26 pub constants: Vec<V>,
27 pub strings: Vec<String>,
28 pub main_function: Option<u32>,
29 pub source_symbols: Vec<SourceSymbolInfo>,
30 pub extra: Info,
31}
32
33fn check_index(index: u32, len: usize, what: &str) -> eyre::Result<()> {
34 eyre::ensure!(
35 (index as usize) < len,
36 "{what}: index {index} is outside table of length {len}"
37 );
38 Ok(())
39}
40
41impl<V, Ty, Info> ProgramMetadata<V, Ty, Info> {
42 pub fn map_extra<NewInfo>(
44 self,
45 map: impl FnOnce(Info) -> NewInfo,
46 ) -> ProgramMetadata<V, Ty, NewInfo> {
47 ProgramMetadata {
48 functions: self.functions,
49 labels: self.labels,
50 constants: self.constants,
51 strings: self.strings,
52 main_function: self.main_function,
53 source_symbols: self.source_symbols,
54 extra: map(self.extra),
55 }
56 }
57
58 pub fn validate(&self, instruction_count: usize) -> eyre::Result<()> {
60 eyre::ensure!(
61 u32::try_from(instruction_count).is_ok(),
62 "instruction stream exceeds u32 address space"
63 );
64 if let Some(main) = self.main_function {
65 check_index(main, self.functions.len(), "metadata.main_function")?;
66 }
67 for (index, function) in self.functions.iter().enumerate() {
68 let path = format!("metadata.functions[{index}]");
69 check_index(function.name, self.strings.len(), &format!("{path}.name"))?;
70 check_index(function.file, self.strings.len(), &format!("{path}.file"))?;
71 eyre::ensure!(
72 function.start_address <= function.end_address
73 && function.end_address as usize <= instruction_count,
74 "{path}: invalid instruction range {}..{} for {instruction_count} instructions",
75 function.start_address,
76 function.end_address
77 );
78 eyre::ensure!(
79 function.local_count as usize >= function.signature.params.len(),
80 "{path}.local_count: fewer slots than parameters"
81 );
82 for parameter in &function.signature.params {
83 check_index(
84 parameter.name,
85 self.strings.len(),
86 &format!("{path}.signature.params.name"),
87 )?;
88 }
89 }
90 for (index, label) in self.labels.iter().enumerate() {
91 check_index(
92 label.name,
93 self.strings.len(),
94 &format!("metadata.labels[{index}].name"),
95 )?;
96 eyre::ensure!(
97 label.address as usize <= instruction_count,
98 "metadata.labels[{index}].address: outside instruction stream"
99 );
100 }
101 let mut symbols = std::collections::BTreeSet::new();
102 for symbol in &self.source_symbols {
103 eyre::ensure!(
104 symbols.insert(symbol.index),
105 "metadata.source_symbols: duplicate index {}",
106 symbol.index
107 );
108 }
109 Ok(())
110 }
111}
112
113impl<Ty, Info> ProgramMetadata<Value, Ty, Info> {
114 pub fn into_module<I>(self, code: Vec<I>) -> eyre::Result<LocalModule<I, Value, Ty, Info>> {
117 self.validate(code.len())?;
118 for (index, constant) in self.constants.iter().enumerate() {
119 match constant {
120 Value::String(id) => check_index(
121 *id,
122 self.strings.len(),
123 &format!("metadata.constants[{index}] string"),
124 )?,
125 Value::FunctionRef(id) => check_index(
126 *id,
127 self.functions.len(),
128 &format!("metadata.constants[{index}] function"),
129 )?,
130 _ => {}
131 }
132 }
133 Ok(LocalModule {
134 code,
135 functions: self.functions,
136 labels: self.labels,
137 constants: self.constants,
138 strings: self.strings,
139 main_function: self.main_function,
140 source_symbols: self.source_symbols,
141 extra: self.extra,
142 })
143 }
144}