1use crate::types::internal::{Field, Function, Label, Type, TypeInner};
2use crate::types::{FuncMode, TypeEnv};
3use anyhow::{anyhow, Context, Result};
4use binread::io::{Read, Seek};
5use binread::{BinRead, BinResult, Error as BError, ReadOptions};
6use std::convert::TryInto;
7
8const MAX_TYPE_TABLE_LEN: u64 = 10_000; fn read_leb<R: Read + Seek>(reader: &mut R, ro: &ReadOptions, (): ()) -> BinResult<u64> {
11 let pos = reader.stream_position()?;
12 leb128::read::unsigned(reader).map_err(|_| BError::Custom {
13 pos,
14 err: Box::new(ro.variable_name.unwrap_or("Invalid leb128")),
15 })
16}
17fn read_sleb<R: Read + Seek>(reader: &mut R, ro: &ReadOptions, (): ()) -> BinResult<i64> {
18 let pos = reader.stream_position()?;
19 leb128::read::signed(reader).map_err(|_| BError::Custom {
20 pos,
21 err: Box::new(ro.variable_name.unwrap_or("Invalid sleb128")),
22 })
23}
24
25#[derive(BinRead, Debug)]
26#[br(magic = b"DIDL")]
27#[br(import(max_type_len: Option<usize>))]
28pub struct Header {
29 #[br(args(max_type_len))]
30 table: Table,
31 #[br(parse_with = read_leb)]
32 len: u64,
33 #[br(count = len)]
34 args: Vec<IndexType>,
35}
36
37#[derive(BinRead, Debug)]
38#[br(import(max_type_len: Option<usize>))]
39struct Table {
40 #[br(parse_with = read_leb)]
41 #[br(assert(len <= max_type_len.unwrap_or(MAX_TYPE_TABLE_LEN as usize) as u64, "type table size exceeded"))]
42 len: u64,
43 #[br(count = len)]
44 table: Vec<ConsType>,
45}
46#[derive(BinRead, Debug)]
47enum ConsType {
48 #[br(magic = 0x6eu8)]
49 Opt(Box<IndexType>),
50 #[br(magic = 0x6du8)]
51 Vec(Box<IndexType>),
52 #[br(magic = 0x6cu8)]
53 Record(Fields),
54 #[br(magic = 0x6bu8)]
55 Variant(Fields),
56 #[br(magic = 0x6au8)]
57 Func(FuncType),
58 #[br(magic = 0x69u8)]
59 Service(ServType),
60 Future(FutureType),
61}
62#[derive(BinRead, Debug)]
63struct IndexType {
64 #[br(parse_with = read_sleb, assert(index >= -17 || index == -24, "unknown opcode {}", index))]
65 index: i64,
66}
67#[derive(BinRead, Debug)]
68struct Fields {
69 #[br(parse_with = read_leb, try_map = |x:u64| x.try_into().map_err(|_| "field length out of 32-bit range"))]
70 len: u32,
71 #[br(count = len)]
72 inner: Vec<FieldType>,
73}
74#[derive(BinRead, Debug)]
75struct FieldType {
76 #[br(parse_with = read_leb, try_map = |x:u64| x.try_into().map_err(|_| "field id out of 32-bit range"))]
77 id: u32,
78 index: IndexType,
79}
80#[derive(BinRead, Debug)]
81struct FuncType {
82 #[br(parse_with = read_leb)]
83 arg_len: u64,
84 #[br(count = arg_len)]
85 args: Vec<IndexType>,
86 #[br(parse_with = read_leb)]
87 ret_len: u64,
88 #[br(count = ret_len)]
89 rets: Vec<IndexType>,
90 #[br(assert(ann_len <= 1u8, "function annotation length should be at most 1"))]
91 ann_len: u8,
92 #[br(count = ann_len)]
93 ann: Vec<Mode>,
94}
95#[derive(BinRead, Debug)]
96struct ServType {
97 #[br(parse_with = read_leb)]
98 len: u64,
99 #[br(count = len)]
100 meths: Vec<Meths>,
101}
102#[derive(BinRead, Debug)]
103struct FutureType {
104 #[br(parse_with = read_sleb, assert(opcode < -24, "{} is not a valid future type", opcode))]
105 opcode: i64,
106 #[br(parse_with = read_leb)]
107 len: u64,
108 #[br(count = len)]
109 blob: Vec<u8>,
110}
111#[derive(BinRead, Debug)]
112struct Meths {
113 #[br(parse_with = read_leb)]
114 len: u64,
115 #[br(count = len, try_map = |x:Vec<u8>| String::from_utf8(x).map_err(|_| "invalid utf8"))]
116 name: String,
117 ty: IndexType,
118}
119#[derive(BinRead, Debug)]
120struct Mode {
121 #[br(try_map = |x:u8| match x { 1u8 => Ok(FuncMode::Query), | 2u8 => Ok(FuncMode::Oneway), | 3u8 => Ok(FuncMode::CompositeQuery), | _ => Err("Unknown annotation") })]
122 inner: FuncMode,
123}
124
125#[derive(BinRead)]
126pub struct BoolValue(
127 #[br(try_map = |x:u8| match x { 0u8 => Ok(false), | 1u8 => Ok(true), | _ => Err("Expect 00 or 01") } )]
128 pub bool,
129);
130#[derive(BinRead)]
131pub struct Len(
132 #[br(parse_with = read_leb, try_map = |x:u64| x.try_into().map_err(|_| "length out of usize range"))]
133 pub usize,
134);
135#[derive(BinRead)]
136pub struct PrincipalBytes {
137 #[br(assert(flag == 1u8, "Opaque reference not supported"))]
138 pub flag: u8,
139 #[br(parse_with = read_leb, assert(len <= 29, "Principal is longer than 29 bytes"))]
140 pub len: u64,
141 #[br(count = len)]
142 pub inner: Vec<u8>,
143}
144
145fn index_to_var(ind: i64) -> String {
146 format!("table{ind}")
147}
148impl IndexType {
149 fn to_type(&self, len: u64) -> Result<Type> {
150 Ok(match self.index {
151 v if v >= 0 => {
152 if v >= len as i64 {
153 return Err(anyhow!("type index {} out of range", v));
154 }
155 TypeInner::Var(index_to_var(v))
156 }
157 -1 => TypeInner::Null,
158 -2 => TypeInner::Bool,
159 -3 => TypeInner::Nat,
160 -4 => TypeInner::Int,
161 -5 => TypeInner::Nat8,
162 -6 => TypeInner::Nat16,
163 -7 => TypeInner::Nat32,
164 -8 => TypeInner::Nat64,
165 -9 => TypeInner::Int8,
166 -10 => TypeInner::Int16,
167 -11 => TypeInner::Int32,
168 -12 => TypeInner::Int64,
169 -13 => TypeInner::Float32,
170 -14 => TypeInner::Float64,
171 -15 => TypeInner::Text,
172 -16 => TypeInner::Reserved,
173 -17 => TypeInner::Empty,
174 -24 => TypeInner::Principal,
175 _ => unreachable!(),
176 }
177 .into())
178 }
179}
180impl ConsType {
181 fn to_type(&self, len: u64) -> Result<Type> {
182 Ok(match &self {
183 ConsType::Opt(ref ind) => TypeInner::Opt(ind.to_type(len)?),
184 ConsType::Vec(ref ind) => TypeInner::Vec(ind.to_type(len)?),
185 ConsType::Record(fs) | ConsType::Variant(fs) => {
186 let mut res = Vec::new();
187 let mut prev = None;
188 for f in &fs.inner {
189 if let Some(prev) = prev {
190 if prev >= f.id {
191 return Err(anyhow!("field id {} collision or not sorted", f.id));
192 }
193 }
194 prev = Some(f.id);
195 let field = Field {
196 id: Label::Id(f.id).into(),
197 ty: f.index.to_type(len)?,
198 };
199 res.push(field);
200 }
201 if matches!(&self, ConsType::Record(_)) {
202 TypeInner::Record(res)
203 } else {
204 TypeInner::Variant(res)
205 }
206 }
207 ConsType::Func(f) => {
208 let mut args = Vec::new();
209 let mut rets = Vec::new();
210 for arg in &f.args {
211 args.push(arg.to_type(len)?);
212 }
213 for ret in &f.rets {
214 rets.push(ret.to_type(len)?);
215 }
216 TypeInner::Func(Function {
217 modes: f.ann.iter().map(|x| x.inner.clone()).collect(),
218 args,
219 rets,
220 })
221 }
222 ConsType::Service(serv) => {
223 let mut res = Vec::new();
224 let mut prev = None;
225 for m in &serv.meths {
226 if let Some(prev) = prev {
227 if prev >= &m.name {
228 return Err(anyhow!("method name {} duplicate or not sorted", m.name));
229 }
230 }
231 prev = Some(&m.name);
232 res.push((m.name.clone(), m.ty.to_type(len)?));
233 }
234 TypeInner::Service(res)
235 }
236 ConsType::Future(_) => TypeInner::Future,
237 }
238 .into())
239 }
240}
241impl Table {
242 fn to_env(&self, len: u64) -> Result<TypeEnv> {
243 use std::collections::BTreeMap;
244 let mut env = BTreeMap::new();
245 for (i, t) in self.table.iter().enumerate() {
246 let ty = t
247 .to_type(len)
248 .with_context(|| format!("Invalid table entry {i}: {t:?}"))?;
249 env.insert(index_to_var(i as i64), ty);
250 }
251 for t in env.values() {
253 if let TypeInner::Service(ms) = t.as_ref() {
254 for (name, ty) in ms {
255 if let TypeInner::Var(id) = ty.as_ref() {
256 if matches!(env.get(id).map(|t| t.as_ref()), Some(TypeInner::Func(_))) {
257 continue;
258 }
259 }
260 return Err(anyhow!("Method {name} has a non-function type {ty}"));
261 }
262 }
263 }
264 Ok(TypeEnv(env))
265 }
266}
267impl Header {
268 pub fn to_types(&self) -> Result<(TypeEnv, Vec<Type>)> {
269 let len = self.table.len;
270 let mut env = self.table.to_env(len)?;
271 env.replace_empty()?;
272 let mut args = Vec::new();
273 for (i, t) in self.args.iter().enumerate() {
274 args.push(
275 t.to_type(len)
276 .with_context(|| format!("Invalid argument entry {i}: {t:?}"))?,
277 );
278 }
279 Ok((env, args))
280 }
281}