wasmi 2.0.0-beta.9

WebAssembly interpreter
Documentation
use super::*;
use crate::{
    Caller,
    Engine,
    ExternRef,
    Func,
    MemoryType,
    Mutability,
    Nullable,
    Ref,
    RefType,
    Store,
    TableType,
    TrapCode,
    Val,
    error::ErrorKind,
    module::InstantiationError,
};

#[test]
#[cfg_attr(not(feature = "wat"), ignore)]
fn instantiate_no_imports() {
    let wasm = r#"
        (module
            (func (export "f") (param i32 i32) (result i32)
                (i32.add (local.get 0) (local.get 1))
            )
        )
    "#;
    let engine = Engine::default();
    let module = Module::new(&engine, wasm).unwrap();
    let mut store = Store::new(&engine, ());
    let instance = Instance::new(&mut store, &module, &[]).unwrap();
    assert!(instance.get_func(&store, "f").is_some());
}

#[test]
#[cfg_attr(not(feature = "wat"), ignore)]
fn instantiate_with_start() {
    let wasm = r#"
        (module
            (func $f)
            (start $f)
        )
    "#;
    let engine = Engine::default();
    let module = Module::new(&engine, wasm).unwrap();
    let mut store = Store::new(&engine, ());
    let _instance = Instance::new(&mut store, &module, &[]).unwrap();
}

#[test]
#[cfg_attr(not(feature = "wat"), ignore)]
fn instantiate_with_trapping_start() {
    let wasm = r#"
        (module
            (func $f
                (unreachable)
            )
            (start $f)
        )
    "#;
    let engine = Engine::default();
    let module = Module::new(&engine, wasm).unwrap();
    let mut store = Store::new(&engine, ());
    let error = Instance::new(&mut store, &module, &[]).unwrap_err();
    assert_eq!(error.as_trap_code(), Some(TrapCode::UnreachableCodeReached));
}

#[test]
#[cfg_attr(not(feature = "wat"), ignore)]
fn instantiate_with_imports_and_start() {
    let wasm = r#"
        (module
            (import "env" "f" (func $f (param i32)))
            (import "env" "t" (table $t 0 funcref))
            (import "env" "m" (memory $m 0))
            (import "env" "g" (global $g (mut i32)))

            (elem declare func $f)

            (func $main
                (global.set $g (i32.const 1))
                (i32.store8 $m (i32.const 0) (i32.const 1))
                (table.set $t (i32.const 0) (ref.func $f))
                (call $f (i32.const 1))
            )
            (start $main)
        )
    "#;
    let engine = Engine::default();
    let module = Module::new(&engine, wasm).unwrap();
    let data: i32 = 0;
    let mut store = Store::new(&engine, data);
    let g = Global::new(&mut store, Val::I32(0), Mutability::Var);
    let m = Memory::new(&mut store, MemoryType::new(1, None)).unwrap();
    let t = Table::new(
        &mut store,
        TableType::new(RefType::Func, 1, None),
        Ref::from(<Nullable<Func>>::Null),
    )
    .unwrap();
    let f = Func::wrap(&mut store, |mut caller: Caller<i32>, a: i32| {
        let data = caller.data_mut();
        *data = a;
    });
    let externals = [
        Extern::from(f),
        Extern::from(t),
        Extern::from(m),
        Extern::from(g),
    ]
    .map(Extern::from);
    let _instance = Instance::new(&mut store, &module, &externals).unwrap();
    assert_eq!(g.get(&store).i32(), Some(1));
    assert_eq!(m.data(&store)[0], 0x01_u8);
    assert!(!t.get(&store, 0).unwrap().unwrap_func().is_null());
    assert_eq!(store.data(), &1);
}

#[test]
#[cfg_attr(not(feature = "wat"), ignore)]
fn instantiate_with_invalid_global_import() {
    let wasm = r#"
        (module
            (import "env" "g" (global $g (mut i32)))
            (func $main
                (global.set $g (i32.const 1))
            )
        )
    "#;
    let engine = Engine::default();
    let module = Module::new(&engine, wasm).unwrap();
    let mut store = Store::new(&engine, ());
    let g = Global::new(&mut store, Val::I64(0), Mutability::Var);
    let externals = [Extern::from(g)].map(Extern::from);
    let error = Instance::new(&mut store, &module, &externals).unwrap_err();
    assert!(matches!(
        error.kind(),
        ErrorKind::Instantiation(InstantiationError::GlobalTypeMismatch { .. })
    ));
}

#[test]
#[cfg_attr(not(feature = "wat"), ignore)]
fn instantiate_with_invalid_memory_import() {
    let wasm = r#"
        (module
            (import "env" "m" (memory $m 2))
            (func
                (i32.store8 $m (i32.const 0) (i32.const 1))
            )
        )
    "#;
    let engine = Engine::default();
    let module = Module::new(&engine, wasm).unwrap();
    let mut store = Store::new(&engine, ());
    let m = Memory::new(&mut store, MemoryType::new(0, Some(1))).unwrap();
    let externals = [Extern::from(m)].map(Extern::from);
    let error = Instance::new(&mut store, &module, &externals).unwrap_err();
    assert!(matches!(
        error.kind(),
        ErrorKind::Instantiation(InstantiationError::MemoryTypeMismatch { .. })
    ));
}

#[test]
#[cfg_attr(not(feature = "wat"), ignore)]
fn instantiate_with_invalid_table_import() {
    let wasm = r#"
        (module
            (import "env" "t" (table $t 0 funcref))
            (elem declare func $f)
            (func $f
                (table.set $t (i32.const 0) (ref.func $f))
            )
        )
    "#;
    let engine = Engine::default();
    let module = Module::new(&engine, wasm).unwrap();
    let mut store = Store::new(&engine, ());
    let t = Table::new(
        &mut store,
        TableType::new(RefType::Extern, 1, None),
        Ref::from(<Nullable<ExternRef>>::Null),
    )
    .unwrap();
    let externals = [Extern::from(t)].map(Extern::from);
    let error = Instance::new(&mut store, &module, &externals).unwrap_err();
    assert!(matches!(
        error.kind(),
        ErrorKind::Instantiation(InstantiationError::TableTypeMismatch { .. })
    ));
}

#[test]
#[cfg_attr(not(feature = "wat"), ignore)]
fn instantiate_with_invalid_func_import() {
    let wasm = r#"
        (module
            (import "env" "f" (func $f (param i32)))
            (elem declare func $f)
            (func
                (call $f (i32.const 1))
            )
        )
    "#;
    let engine = Engine::default();
    let module = Module::new(&engine, wasm).unwrap();
    let data: i64 = 0;
    let mut store = Store::new(&engine, data);
    let f = Func::wrap(&mut store, |mut caller: Caller<i64>, a: i64| {
        let data = caller.data_mut();
        *data = a;
    });
    let externals = [Extern::from(f)].map(Extern::from);
    let error = Instance::new(&mut store, &module, &externals).unwrap_err();
    assert!(matches!(
        error.kind(),
        ErrorKind::Instantiation(InstantiationError::FuncTypeMismatch { .. })
    ));
}

/// Modules without a `data_count` section still get a handle per data segment.
///
/// The [`InstanceLayout`] deliberately reports no data segments for such modules — their
/// addresses are not queryable — so the trailing `handles` buffer holds *more* entries than
/// the layout accounts for. Deriving the buffer length from the layout truncates it.
#[test]
#[cfg_attr(not(feature = "wat"), ignore)]
fn instantiate_data_segments_without_data_count() {
    // Note: `data_count` is only emitted for modules using bulk-memory operators, so this
    //       module (active data segments, no `memory.init`/`data.drop`) has none.
    let wasm = r#"
        (module
            (memory (export "mem") 1)
            (data (i32.const 0) "abc")
            (data (i32.const 8) "de")
        )
    "#;
    let engine = Engine::default();
    let module = Module::new(&engine, wasm).unwrap();
    let mut store = Store::new(&engine, ());
    let instance = Instance::new(&mut store, &module, &[]).unwrap();
    let entity = store.inner.resolve_instance(&instance);
    assert!(entity.layout().data_addr(0).is_none());
    assert_eq!(entity.handles().len(), 3); // 1 memory + 2 data segments
    let memory = instance.get_memory(&store, "mem").unwrap();
    assert_eq!(&memory.data(&store)[..3], b"abc");
    assert_eq!(&memory.data(&store)[8..10], b"de");
}

/// An instance without any handles allocates a header-only [`InstanceEntity`].
#[test]
#[cfg_attr(not(feature = "wat"), ignore)]
fn instantiate_without_handles() {
    let engine = Engine::default();
    let module = Module::new(&engine, "(module)").unwrap();
    let mut store = Store::new(&engine, ());
    let instance = Instance::new(&mut store, &module, &[]).unwrap();
    let entity = store.inner.resolve_instance(&instance);
    assert_eq!(entity.len_handles(), 0);
    assert!(entity.handles().is_empty());
    assert_eq!(instance.exports(&store).count(), 0);
}

/// The handle kind of an instance entry is validated where it is claimed.
///
/// `MemoryAddr(1)` addresses the *function* entry below, since the layout groups are
/// `[memory][global][table][func]…`. Constructing a `HandleAndEntity<Memory>` for it is
/// exactly the mistake the `AnyHandle::assert_kind` guard exists to catch.
///
/// # Note
///
/// Only debug builds store the handle kind, so this can only be tested there.
#[test]
#[cfg(debug_assertions)]
#[cfg_attr(not(feature = "wat"), ignore)]
#[should_panic = "unexpected instance handle kind"]
fn get_entry_of_wrong_handle_kind_panics() {
    let wasm = r#"
        (module
            (memory 1)
            (func)
        )
    "#;
    let engine = Engine::default();
    let module = Module::new(&engine, wasm).unwrap();
    let mut store = Store::new(&engine, ());
    let instance = Instance::new(&mut store, &module, &[]).unwrap();
    let entity = store.inner.resolve_instance(&instance);
    // Sanity check: address 1 really is the function, not a second memory.
    assert!(entity.layout().memory_addr(1).is_none());
    assert_eq!(u32::from(entity.layout().func_addr(0).unwrap()), 1);
    let _ = entity.get_memory(MemoryAddr::from(1));
}