move-neovm 0.1.0

Move bytecode to NeoVM translator (experimental)
Documentation
use super::lowering::{SCRATCH_KEY_OFFSET, SCRATCH_VALUE_OFFSET};
use crate::bytecode::{AbilitySet, MoveModule, MoveOpcode, StructDef, TypeTag};
use anyhow::{anyhow, bail, Result};
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use wasm_encoder::{Function, Instruction};

pub(super) fn struct_hash(name: &str) -> u64 {
    let mut hasher = std::collections::hash_map::DefaultHasher::new();
    name.hash(&mut hasher);
    hasher.finish()
}

pub(super) fn build_struct_lookup(structs: &[StructDef]) -> HashMap<String, AbilitySet> {
    let mut map = HashMap::new();
    for s in structs {
        map.insert(s.name.clone(), s.abilities);
    }
    map
}

pub(super) fn ensure_has_key(def: &StructDef) -> Result<()> {
    if !def.abilities.key {
        bail!(
            "struct {} does not have the 'key' ability required for global operations",
            def.name
        );
    }
    Ok(())
}

pub(super) fn ensure_copy_allowed(
    tag: &TypeTag,
    lookup: &HashMap<String, AbilitySet>,
) -> Result<()> {
    if let TypeTag::Struct(name) = tag {
        if let Some(abilities) = lookup.get(name) {
            if !abilities.copy {
                bail!("copy of resource {} is not allowed", name);
            }
        }
    }
    Ok(())
}

pub(super) fn struct_for_index(module: &MoveModule, idx: u16) -> Result<&StructDef> {
    module
        .structs
        .get(idx as usize)
        .ok_or_else(|| anyhow!("struct index {} out of range", idx))
}

pub(super) fn write_resource_key(func: &mut Function, def: &StructDef, addr_local: u32) {
    let hash = struct_hash(&def.name);
    func.instruction(&Instruction::I32Const(SCRATCH_KEY_OFFSET));
    func.instruction(&Instruction::I64Const(hash as i64));
    func.instruction(&Instruction::I64Store(wasm_encoder::MemArg {
        offset: 0,
        align: 3,
        memory_index: 0,
    }));

    func.instruction(&Instruction::I32Const(SCRATCH_KEY_OFFSET + 8));
    func.instruction(&Instruction::LocalGet(addr_local));
    func.instruction(&Instruction::I64Store(wasm_encoder::MemArg {
        offset: 0,
        align: 3,
        memory_index: 0,
    }));
}

pub(super) fn write_resource_value(func: &mut Function, value_local: u32) {
    func.instruction(&Instruction::I32Const(SCRATCH_VALUE_OFFSET));
    func.instruction(&Instruction::LocalGet(value_local));
    func.instruction(&Instruction::I64Store(wasm_encoder::MemArg {
        offset: 0,
        align: 3,
        memory_index: 0,
    }));
}

pub(super) fn is_resource_opcode(op: &MoveOpcode) -> bool {
    matches!(
        op,
        MoveOpcode::MoveTo(_)
            | MoveOpcode::MoveFrom(_)
            | MoveOpcode::Exists(_)
            | MoveOpcode::BorrowGlobal(_)
            | MoveOpcode::MutBorrowGlobal(_)
    )
}