1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
/// Methods for loading a wasm binary module into a more convenient form.
use std::rc::Rc;
use parity_wasm::elements;
use types::*;
/// Takes a slice of `Local`'s (local variable *specifications*),
/// and creates a vec of their types.
///
/// Slightly trickier than just a map+collect.
fn types_from_locals(locals: &[elements::Local]) -> Vec<elements::ValueType> {
// This looks like it should just be a map and collect but actually isn't,
// 'cause we need to iterate the inner loop. We could make it a map but it's
// trickier and not worth the bother.
let num_local_slots = locals.iter().map(|x| x.count() as usize).sum();
let mut v = Vec::with_capacity(num_local_slots);
for local in locals {
for _i in 0..local.count() {
let t = local.value_type();
v.push(t);
}
}
v
}
/// A loaded wasm module
#[derive(Debug, Clone)]
pub struct LoadedModule {
/// Module name. Not technically necessary, but handy.
pub name: String,
/// Function type vector
pub types: Vec<FuncType>,
/// Function value vector
pub funcs: Vec<Func>,
/// Index of start function, if any.
pub start: Option<FuncIdx>,
/// wasm 1.0 defines only a single table.
/// Even if we import some other table we can only
/// do it if there's not an existing one, I think.
pub tables: Option<Table>,
/// Initializer code for table
/// `(offset, values)`
pub table_initializers: Vec<(ConstExpr, Vec<FuncIdx>)>,
/// wasm 1.0 defines only a single memory.
pub mem: Option<Memory>,
/// Initializer code for data segments.
pub mem_initializers: Vec<(ConstExpr, Vec<u8>)>,
/// Global values.
pub globals: Vec<(Global, ConstExpr)>,
/// Exported values
pub exported_functions: Vec<Export<FuncIdx>>,
pub exported_tables: Option<Export<()>>,
pub exported_memories: Option<Export<()>>,
pub exported_globals: Vec<Export<GlobalIdx>>,
/// Imported values
pub imported_functions: Vec<Import<TypeIdx>>,
pub imported_tables: Option<Import<elements::TableType>>,
pub imported_memories: Option<Import<elements::MemoryType>>,
pub imported_globals: Vec<Import<elements::GlobalType>>,
}
/// A wrapper type that assures at compile-time that
/// a module has been validated.
#[derive(Debug, Clone)]
pub struct ValidatedModule(LoadedModule);
impl ValidatedModule {
/// Extracts the actual `LoadedModule` from the `ValidatedModule`
pub(crate) fn into_inner(self) -> LoadedModule {
self.0
}
/// Borrows the actual `LoadedModule` from the `ValidatedModule`
pub fn borrow_inner(&self) -> &LoadedModule {
&self.0
}
}
impl LoadedModule {
/// Instantiates and initializes a new module from the `parity_wasm` module type.
/// This basically goes from a representation very close to the raw webassembly
/// binary format to a representation more convenient to be loaded into the interpreter's
/// runtime data.
///
/// Does NOT validate the module or run the start function though!
pub fn new(name: &str, module: elements::Module) -> Result<Self, Error> {
if module.version() != 1 {
return Err(Error::VersionMismatch {
module: name.to_owned(),
expected: 1,
got: module.version()
});
}
let mut m = Self {
name: name.to_owned(),
types: vec![],
funcs: vec![],
start: None,
tables: None,
table_initializers: vec![],
mem: None,
mem_initializers: vec![],
globals: vec![],
imported_functions: vec![],
imported_tables: None,
imported_memories: None,
imported_globals: vec![],
exported_functions: vec![],
exported_tables: None,
exported_memories: None,
exported_globals: vec![],
};
// Allocate types
if let Some(types) = module.type_section() {
let functypes = types.types().iter().map(From::from);
m.types.extend(functypes);
}
// Allocate functions
//
// In `wat` a function's type index is declared
// along with the function, but in the binary `wasm`
// it's in its own section; the `code` section
// contains the code and the `function` section
// contains signature indices. We join them back
// together here.
match (module.code_section(), module.function_section()) {
(Some(code), Some(functions)) => {
assert_eq!(code.bodies().len(), functions.entries().len());
// Evade double-borrow of m here.
let types = &m.types;
let converted_funcs =
code.bodies().iter().zip(functions.entries()).map(|(c, f)| {
// Make sure the function signature is a valid type.
let type_idx = f.type_ref() as usize;
assert!(
type_idx < types.len(),
"Function refers to a type signature that does not exist!"
);
Func {
typeidx: TypeIdx(type_idx),
locals: types_from_locals(c.locals()),
body: FuncBody::Opcodes(c.code().elements().to_owned()),
}
});
m.funcs.extend(converted_funcs);
}
(None, None) => (),
_ => {
panic!("Code section exists but type section does not, or vice versa!");
}
}
// Allocate tables
if let Some(table) = module.table_section() {
println!("Table: {:?}", table);
// currently we can only have one table section with
// 0 or 1 elements in it, so.
assert!(
table.entries().len() < 2,
"More than one table entry, should never happen!"
);
if let Some(table) = table.entries().iter().next() {
// TODO: As far as I can tell, the memory's minimum size is never used?
let _min = table.limits().initial();
let max = table.limits().maximum();
// TODO: It's apparently valid for a memory to have no max size?
let mut t = Table::new();
if let Some(max) = max {
t.fill(max);
}
m.tables = Some(t);
if let Some(elements) = module.elements_section() {
for segment in elements.entries() {
let table_idx = segment.index();
assert_eq!(
table_idx,
0,
"Had an Elements segment that referred to table != 0!"
);
let offset_code =
ConstExpr::try_from(segment.offset().code()).expect("TODO");
let members: Vec<FuncIdx> = segment
.members()
.iter()
.map(|x| FuncIdx(*x as usize))
.collect();
m.table_initializers.push((offset_code, members));
}
}
}
}
// Allocate memories
if let Some(memory) = module.memory_section() {
// currently we can only have one memory section with
// 0 or 1 elements in it, so.
assert!(
memory.entries().len() < 2,
"More than one memory entry, should never happen!"
);
if let Some(memory) = memory.entries().iter().next() {
// TODO: As far as I can tell, the memory's minimum size is never used?
let _min = memory.limits().initial();
let max = memory.limits().maximum();
// TODO: It's apparently valid for a memory to have no max size?
let mut mem = Memory::new(max);
if let Some(data) = module.data_section() {
for segment in data.entries() {
let mem_idx = segment.index();
assert_eq!(
mem_idx,
0,
"Had a Data segment that referred to memory != 0!"
);
let offset_code =
ConstExpr::try_from(segment.offset().code()).expect("TODO");
let members = segment.value().to_owned();
m.mem_initializers.push((offset_code, members));
}
}
m.mem = Some(mem);
}
}
// Load globals
if let Some(globals) = module.global_section() {
let global_iter = globals.entries().iter().map(|global| {
let global_type = global.global_type().content_type();
let mutability = global.global_type().is_mutable();
let init_code = ConstExpr::try_from(global.init_expr().code()).expect("TODO");
let global = Global {
variable_type: global_type,
mutable: mutability,
value: Value::default_from_type(global_type),
};
(global, init_code)
});
m.globals.extend(global_iter);
}
// Load imports
if let Some(imports) = module.import_section() {
for entry in imports.entries() {
let module_name = entry.module().to_owned();
let field_name = entry.field().to_owned();
match *entry.external() {
elements::External::Function(i) => {
m.imported_functions.push(Import {
module_name,
field_name,
value: TypeIdx(i as usize),
});
}
elements::External::Table(i) => {
m.imported_tables = Some(Import {
module_name,
field_name,
value: i,
});
}
elements::External::Memory(i) => {
m.imported_memories = Some(Import {
module_name,
field_name,
value: i,
});
}
elements::External::Global(i) => {
m.imported_globals.push(Import {
module_name,
field_name,
value: i,
});
}
}
}
}
// Load exports
if let Some(exports) = module.export_section() {
for entry in exports.entries() {
let name = entry.field().to_owned();
match *entry.internal() {
elements::Internal::Function(i) => m.exported_functions.push(Export {
name,
value: FuncIdx(i as usize),
}),
elements::Internal::Table(i) => {
m.exported_tables = Some(Export { name, value: () })
}
elements::Internal::Memory(i) => {
m.exported_memories = Some(Export { name, value: () })
}
elements::Internal::Global(i) => m.exported_globals.push(Export {
name,
value: GlobalIdx(i as usize),
}),
}
}
}
// Check for start section
//println!("Module start section: {:?}", module.start_section());
if let Some(start) = module.start_section() {
let start = start as usize;
// Ensure start function is in bounds.
let max_start_idx = m.funcs.len() + m.imported_functions.len();
if start >= max_start_idx {
let message = format!(
"unknown function for start: {}, max start index: {}",
start, max_start_idx
);
let e = Error::Invalid {
module: name.to_owned(),
reason: message,
};
return Err(e);
}
// Ensure start function signature is correct.
// Remember, the indices of imported functions go before
// the indices of locally defined functions, so we have
// to figure out whether we're indexing into the functions vec
// or the imported functions vec.
// Might want to combine those?
let startfunc_typeidx = if start < m.imported_functions.len() {
m.imported_functions[start].value
} else {
m.funcs[start - m.imported_functions.len()].typeidx
};
let startfunc_type = &m.types[startfunc_typeidx.0];
let valid_startfunc_type = &FuncType {
params: vec![],
return_type: None,
};
if startfunc_type != valid_startfunc_type {
let message = format!(
"Invalid start function type: {:?}, should take nothing and return nothing",
startfunc_type
);
let e = Error::Invalid {
module: name.to_owned(),
reason: message,
};
return Err(e);
}
m.start = Some(FuncIdx(start));
}
Ok(m)
}
/// Adds a host function, plus an export for it. Not really ideal, but what can one do
/// when parity-wasm doesn't handle them either? Hmmm.
pub fn add_host_func<T>(&mut self, export_name: &str, func: T, params: &FuncType)
where
T: Fn(&mut Vec<Value>) + 'static,
{
// Add parameters to the type list if necessary
let type_idx = if let Some(i) = self.types.iter().position(|t| t == params) {
i
} else {
self.types.push(params.clone());
self.types.len() - 1
};
// Create and add function
let f = Func {
typeidx: TypeIdx(type_idx),
locals: vec![],
body: FuncBody::HostFunction(Rc::new(func)),
};
self.funcs.push(f);
let func_idx = self.funcs.len() - 1;
// Add export for function.
self.exported_functions.push(Export {
name: export_name.to_owned(),
value: FuncIdx(func_idx),
})
}
/// Validates the module: makes sure types are correct,
/// all the indices into various parts of the module are valid, etc.
///
/// TODO: Currently does nothing
pub fn validate(self) -> ValidatedModule {
ValidatedModule(self)
}
}