1mod argument_get;
2mod arrays;
3mod byte_arrays;
4mod call_core;
5mod dup;
6pub(self) mod lilo;
7mod numbers;
8mod push;
9mod records;
10mod strings;
11mod swap2;
12
13use crate::errors::{
14 InstructionError, InstructionErrorKind, InstructionResult, WasmValueNativeCastError,
15};
16use crate::interpreter::wasm;
17use crate::IType;
18use crate::IValue;
19use crate::NEVec;
20
21pub(crate) use argument_get::argument_get;
22pub(crate) use arrays::*;
23pub(crate) use byte_arrays::*;
24pub(crate) use call_core::call_core;
25pub(crate) use dup::dup;
26pub(crate) use numbers::*;
27pub(crate) use push::*;
28pub(crate) use records::*;
29pub(crate) use strings::*;
30pub(crate) use swap2::swap2;
31
32use fluence_it_types::NativeType;
33use serde::Deserialize;
34use serde::Serialize;
35
36use std::convert::TryFrom;
37
38pub(self) const ALLOCATE_FUNC_INDEX: u32 = 0;
39
40#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
42pub enum Instruction {
43 ArgumentGet {
45 index: u32,
47 },
48
49 CallCore {
51 function_index: u32,
53 },
54
55 BoolFromI32,
57
58 S8FromI32,
60
61 S8FromI64,
63
64 S16FromI32,
66
67 S16FromI64,
69
70 S32FromI32,
72
73 S32FromI64,
75
76 S64FromI32,
78
79 S64FromI64,
81
82 I32FromBool,
84
85 I32FromS8,
87
88 I32FromS16,
90
91 I32FromS32,
93
94 I32FromS64,
96
97 I64FromS8,
99
100 I64FromS16,
102
103 I64FromS32,
105
106 I64FromS64,
108
109 U8FromI32,
111
112 U8FromI64,
114
115 U16FromI32,
117
118 U16FromI64,
120
121 U32FromI32,
123
124 U32FromI64,
126
127 U64FromI32,
129
130 U64FromI64,
132
133 I32FromU8,
135
136 I32FromU16,
138
139 I32FromU32,
141
142 I32FromU64,
144
145 I64FromU8,
147
148 I64FromU16,
150
151 I64FromU32,
153
154 I64FromU64,
156
157 StringLiftMemory,
159
160 StringLowerMemory,
162
163 ByteArraySize,
165
166 ByteArrayLiftMemory,
168
169 ByteArrayLowerMemory,
171
172 StringSize,
174
175 ArrayLiftMemory {
177 value_type: IType,
179 },
180
181 ArrayLowerMemory {
183 value_type: IType,
185 },
186
187 RecordLiftMemory {
189 record_type_id: u32,
191 },
192
193 RecordLowerMemory {
195 record_type_id: u32,
197 },
198
199 PushI32 {
201 value: i32,
203 },
204
205 PushI64 {
207 value: i64,
209 },
210
211 Dup,
213
214 Swap2,
216}
217
218pub(crate) fn to_native<'a, T>(wit_value: IValue, instruction: Instruction) -> InstructionResult<T>
221where
222 T: NativeType + TryFrom<IValue, Error = WasmValueNativeCastError>,
223{
224 T::try_from(wit_value).map_err(|error| {
225 InstructionError::from_error_kind(instruction, InstructionErrorKind::ToNative(error))
226 })
227}
228
229pub(crate) fn check_function_signature<
230 'instance,
231 Instance,
232 Export,
233 LocalImport,
234 Memory,
235 MemoryView,
236 Store,
237>(
238 instance: &'instance Instance,
239 local_import: &LocalImport,
240 values: &[IValue],
241) -> Result<(), InstructionErrorKind>
242where
243 Export: wasm::structures::Export + 'instance,
244 LocalImport: wasm::structures::LocalImport<Store> + 'instance,
245 Memory: wasm::structures::Memory<MemoryView, Store> + 'instance,
246 MemoryView: wasm::structures::MemoryView<Store>,
247 Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
248 Store: wasm::structures::Store,
249{
250 let func_inputs = local_import.arguments();
251
252 for (func_input_arg, value) in func_inputs.iter().zip(values.iter()) {
253 is_value_compatible_to_type(instance, &func_input_arg.ty, value)?;
254 }
255
256 Ok(())
257}
258
259pub(crate) fn is_value_compatible_to_type<
261 'instance,
262 Instance,
263 Export,
264 LocalImport,
265 Memory,
266 MemoryView,
267 Store,
268>(
269 instance: &'instance Instance,
270 interface_type: &IType,
271 interface_value: &IValue,
272) -> Result<(), InstructionErrorKind>
273where
274 Export: wasm::structures::Export + 'instance,
275 LocalImport: wasm::structures::LocalImport<Store> + 'instance,
276 Memory: wasm::structures::Memory<MemoryView, Store> + 'instance,
277 MemoryView: wasm::structures::MemoryView<Store>,
278 Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
279 Store: wasm::structures::Store,
280{
281 match (&interface_type, interface_value) {
282 (IType::Boolean, IValue::Boolean(_)) => Ok(()),
283 (IType::S8, IValue::S8(_)) => Ok(()),
284 (IType::S16, IValue::S16(_)) => Ok(()),
285 (IType::S32, IValue::S32(_)) => Ok(()),
286 (IType::S64, IValue::S64(_)) => Ok(()),
287 (IType::U8, IValue::U8(_)) => Ok(()),
288 (IType::U16, IValue::U16(_)) => Ok(()),
289 (IType::U32, IValue::U32(_)) => Ok(()),
290 (IType::U64, IValue::U64(_)) => Ok(()),
291 (IType::I32, IValue::I32(_)) => Ok(()),
292 (IType::I64, IValue::I64(_)) => Ok(()),
293 (IType::F32, IValue::F32(_)) => Ok(()),
294 (IType::F64, IValue::F64(_)) => Ok(()),
295 (IType::String, IValue::String(_)) => Ok(()),
296 (IType::ByteArray, IValue::ByteArray(_)) => Ok(()),
297 (IType::Array(ty), IValue::Array(values)) => {
298 for value in values {
299 is_value_compatible_to_type(instance, ty, value)?
300 }
301
302 Ok(())
303 }
304 (IType::ByteArray, IValue::Array(values)) => {
305 for value in values {
306 is_value_compatible_to_type(instance, &IType::U8, value)?
307 }
308
309 Ok(())
310 }
311 (IType::Array(ty), IValue::ByteArray(_)) => {
312 if ty.as_ref() == &IType::U8 {
313 return Ok(());
314 }
315
316 Err(InstructionErrorKind::InvalidValueOnTheStack {
317 expected_type: interface_type.clone(),
318 received_value: interface_value.clone(),
319 })
320 }
321 (IType::Record(ref record_type_id), IValue::Record(record_fields)) => {
322 is_record_fields_compatible_to_type(instance, *record_type_id, record_fields)?;
323
324 Ok(())
325 }
326 _ => Err(InstructionErrorKind::InvalidValueOnTheStack {
327 expected_type: interface_type.clone(),
328 received_value: interface_value.clone(),
329 }),
330 }
331}
332
333pub(crate) fn is_record_fields_compatible_to_type<
334 'instance,
335 Instance,
336 Export,
337 LocalImport,
338 Memory,
339 MemoryView,
340 Store,
341>(
342 instance: &'instance Instance,
343 record_type_id: u64,
344 record_fields: &[IValue],
345) -> Result<(), InstructionErrorKind>
346where
347 Export: wasm::structures::Export + 'instance,
348 LocalImport: wasm::structures::LocalImport<Store> + 'instance,
349 Memory: wasm::structures::Memory<MemoryView, Store> + 'instance,
350 MemoryView: wasm::structures::MemoryView<Store>,
351 Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
352 Store: wasm::structures::Store,
353{
354 let record_type = instance
355 .wit_record_by_id(record_type_id)
356 .ok_or(InstructionErrorKind::RecordTypeByNameIsMissing { record_type_id })?;
357
358 if record_fields.len() != record_type.fields.len() {
359 return Err(InstructionErrorKind::InvalidValueOnTheStack {
360 expected_type: IType::Record(record_type_id),
361 received_value: IValue::Record(NEVec::new(record_fields.to_vec()).unwrap()),
363 });
364 }
365
366 for (record_type_field, record_value_field) in
367 record_type.fields.iter().zip(record_fields.iter())
368 {
369 is_value_compatible_to_type(instance, &record_type_field.ty, record_value_field)?;
370 }
371
372 Ok(())
373}
374
375#[cfg(test)]
376pub(crate) mod tests {
377 use crate::{ast::*, interpreter::wasm, types::*, values::*};
378 use std::{cell::Cell, collections::HashMap, convert::TryInto, ops::Deref, rc::Rc};
379
380 pub(crate) struct Export {
381 pub(crate) inputs: Vec<IType>,
382 pub(crate) outputs: Vec<IType>,
383 pub(crate) function: fn(arguments: &[IValue]) -> Result<Vec<IValue>, ()>,
384 }
385
386 impl wasm::structures::Export for Export {
387 fn inputs_cardinality(&self) -> usize {
388 self.inputs.len() as usize
389 }
390
391 fn outputs_cardinality(&self) -> usize {
392 self.outputs.len()
393 }
394
395 fn arguments(&self) -> &[IType] {
396 &self.inputs
397 }
398
399 fn outputs(&self) -> &[IType] {
400 &self.outputs
401 }
402
403 fn call(&self, arguments: &[IValue]) -> Result<Vec<IValue>, ()> {
404 (self.function)(arguments)
405 }
406 }
407
408 pub(crate) struct LocalImport {
409 pub(crate) inputs: Vec<IType>,
410 pub(crate) outputs: Vec<IType>,
411 pub(crate) function: fn(arguments: &[IValue]) -> Result<Vec<IValue>, ()>,
412 }
413
414 impl wasm::structures::LocalImport for LocalImport {
415 fn inputs_cardinality(&self) -> usize {
416 self.inputs.len()
417 }
418
419 fn outputs_cardinality(&self) -> usize {
420 self.outputs.len()
421 }
422
423 fn arguments(&self) -> &[IType] {
424 &self.inputs
425 }
426
427 fn outputs(&self) -> &[IType] {
428 &self.outputs
429 }
430
431 fn call(&self, arguments: &[IValue]) -> Result<Vec<IValue>, ()> {
432 (self.function)(arguments)
433 }
434 }
435
436 #[derive(Default, Clone)]
437 pub(crate) struct MemoryView(Rc<Vec<Cell<u8>>>);
438
439 impl wasm::structures::MemoryView for MemoryView {}
440
441 impl Deref for MemoryView {
442 type Target = [Cell<u8>];
443
444 fn deref(&self) -> &Self::Target {
445 self.0.as_slice()
446 }
447 }
448
449 #[derive(Default)]
450 pub(crate) struct Memory {
451 pub(crate) view: MemoryView,
452 }
453
454 impl Memory {
455 pub(crate) fn new(data: Vec<Cell<u8>>) -> Self {
456 Self {
457 view: MemoryView(Rc::new(data)),
458 }
459 }
460 }
461
462 impl wasm::structures::Memory<MemoryView, Store> for Memory {
463 fn view(&self) -> MemoryView {
464 self.view.clone()
465 }
466 }
467
468 #[derive(Default)]
469 pub(crate) struct Instance {
470 pub(crate) exports: HashMap<String, Export>,
471 pub(crate) locals_or_imports: HashMap<usize, LocalImport>,
472 pub(crate) memory: Memory,
473 pub(crate) wit_types: Vec<Type>,
474 }
475
476 impl Instance {
477 pub(crate) fn new() -> Self {
478 Self {
479 exports: {
480 let mut hashmap = HashMap::new();
481 hashmap.insert(
482 "sum".into(),
483 Export {
484 inputs: vec![IType::I32, IType::I32],
485 outputs: vec![IType::I32],
486 function: |arguments: &[IValue]| {
487 let a: i32 = (&arguments[0]).try_into().unwrap();
488 let b: i32 = (&arguments[1]).try_into().unwrap();
489
490 Ok(vec![IValue::I32(a + b)])
491 },
492 },
493 );
494
495 hashmap
496 },
497 locals_or_imports: {
498 let mut hashmap = HashMap::new();
499 hashmap.insert(
501 42,
502 LocalImport {
503 inputs: vec![IType::I32, IType::I32],
504 outputs: vec![IType::I32],
505 function: |arguments: &[IValue]| {
506 let a: i32 = (&arguments[0]).try_into().unwrap();
507 let b: i32 = (&arguments[1]).try_into().unwrap();
508
509 Ok(vec![IValue::I32(a * b)])
510 },
511 },
512 );
513 hashmap.insert(
515 43,
516 LocalImport {
517 inputs: vec![IType::I32],
518 outputs: vec![IType::I32],
519 function: |arguments: &[IValue]| {
520 let _size: i32 = (&arguments[0]).try_into().unwrap();
521
522 Ok(vec![IValue::I32(0)])
523 },
524 },
525 );
526
527 hashmap
528 },
529 memory: Memory::new(vec![Cell::new(0); 128]),
530 wit_types: vec![Type::Record(RecordType {
531 name: String::from("RecordType0"),
532 fields: vec1![
533 RecordFieldType {
534 name: String::from("field_0"),
535 ty: IType::I32,
536 },
537 RecordFieldType {
538 name: String::from("field_1"),
539 ty: IType::Record(RecordType {
540 name: String::from("RecordType1"),
541 fields: vec1![
542 RecordFieldType {
543 name: String::from("field_0"),
544 ty: IType::String,
545 },
546 RecordFieldType {
547 name: String::from("field1"),
548 ty: IType::F32
549 }
550 ],
551 }),
552 },
553 RecordFieldType {
554 name: String::from("field_2"),
555 ty: IType::I64,
556 }
557 ],
558 })],
559 }
560 }
561 }
562
563 impl wasm::structures::Instance<Export, LocalImport, Memory, MemoryView> for Instance {
564 fn export(&self, export_name: &str) -> Option<&Export> {
565 self.exports.get(export_name)
566 }
567
568 fn local_or_import<I: wasm::structures::TypedIndex + wasm::structures::LocalImportIndex>(
569 &mut self,
570 index: I,
571 ) -> Option<&LocalImport> {
572 self.locals_or_imports.get(&index.index())
573 }
574
575 fn memory(&self, _index: usize) -> Option<&Memory> {
576 Some(&self.memory)
577 }
578
579 fn wit_type_by_id(&self, index: u32) -> Option<&Type> {
580 self.wit_types.get(index as usize)
581 }
582
583 fn wit_record_by_id(&self, index: u64) -> Option<&RecordType> {
584 self.wit_types.get(index as _)
585 }
586 }
587}