Skip to content

Commit 56e942e

Browse files
authoredApr 12, 2021
Merge cd68644 into 8cb1234
2 parents 8cb1234 + cd68644 commit 56e942e

19 files changed

+1398
-1277
lines changed
 

‎Cargo.lock

+24
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎rust/candid/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ pretty = "0.10.0"
3535
serde = { version = "1.0.118", features = ["derive"] }
3636
serde_bytes = "0.11"
3737
thiserror = "1.0.20"
38+
anyhow = "1.0"
39+
binread = { version = "2.0", features = ["debug_template"] }
3840

3941
arbitrary = { version = "0.4.7", optional = true }
4042
serde_dhall = { version = "0.10.0", optional = true }

‎rust/candid/src/binary_parser.rs

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

‎rust/candid/src/bindings/candid.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ pub fn pp_ty(ty: &Type) -> RcDoc {
115115
}
116116
}
117117
Knot(ref id) => RcDoc::text(format!("{}", id)),
118-
Unknown => unreachable!(),
118+
Unknown => str("unknown"),
119119
}
120120
}
121121

0 commit comments

Comments
 (0)