aver-cert 0.1.0

Independent artifact certificate engine and verifier for Aver WebAssembly
Documentation
fn structural_body(
    f: &UserFn,
    box_idx: u32,
    user_idx_set: &std::collections::HashSet<u32>,
    host_roles: &std::collections::HashMap<u32, HostRole>,
) -> Option<StructuralBody> {
    if f.has_loop_or_branch {
        return None;
    }
    let ops = strip_trailing_end(&f.ops);
    if ops
        .iter()
        .any(|op| matches!(op, Op::Other | Op::ReturnCall(_)))
    {
        return None;
    }
    for op in ops {
        if let Op::Call(idx) = op {
            if *idx == f.wasm_idx || user_idx_set.contains(idx) {
                return None;
            }
            if *idx != box_idx && !host_roles.contains_key(idx) {
                return None;
            }
        }
    }
    let normalized_ops = normalize_local_hops(ops);
    let mut pos = 0usize;
    let tree = parse_instr_tree(&normalized_ops, &mut pos, false)?;
    if pos != normalized_ops.len() {
        return None;
    }
    Some(StructuralBody {
        normalized_ops,
        tree,
    })
}

fn parse_instr_tree(ops: &[Op], pos: &mut usize, nested: bool) -> Option<Vec<InstrNode>> {
    let mut out = Vec::new();
    while *pos < ops.len() {
        match &ops[*pos] {
            Op::Else | Op::End if nested => break,
            Op::If => {
                *pos += 1;
                let then_b = parse_instr_tree(ops, pos, true)?;
                if !matches!(ops.get(*pos), Some(Op::Else)) {
                    return None;
                }
                *pos += 1;
                let else_b = parse_instr_tree(ops, pos, true)?;
                if !matches!(ops.get(*pos), Some(Op::End)) {
                    return None;
                }
                *pos += 1;
                out.push(InstrNode::IfElse(then_b, else_b));
            }
            Op::Else | Op::End => return None,
            op => {
                out.push(InstrNode::Op(op.clone()));
                *pos += 1;
            }
        }
    }
    Some(out)
}

fn normalize_local_hops(ops: &[Op]) -> Vec<Op> {
    let mut aliases = std::collections::HashMap::<u32, u32>::new();
    let mut out = Vec::new();
    let mut i = 0usize;
    while i < ops.len() {
        if let (Some(Op::LocalGet(src)), Some(Op::LocalSet(dst))) = (ops.get(i), ops.get(i + 1)) {
            let src = *aliases.get(src).unwrap_or(src);
            aliases.insert(*dst, src);
            i += 2;
            continue;
        }
        let op = match &ops[i] {
            Op::LocalGet(idx) => Op::LocalGet(*aliases.get(idx).unwrap_or(idx)),
            other => other.clone(),
        };
        out.push(op);
        i += 1;
    }

    let mut changed = true;
    while changed {
        changed = false;
        let mut compact = Vec::new();
        let mut j = 0usize;
        while j < out.len() {
            if j + 2 < out.len()
                && matches!(
                    out[j],
                    Op::StructGet(..)
                        | Op::RefCast(..)
                        | Op::I64Const(..)
                        | Op::I32Const(..)
                        | Op::F64Const(..)
                        | Op::RefNull(_)
                        | Op::ArrayNewData { .. }
                )
                && matches!((&out[j + 1], &out[j + 2]), (Op::LocalSet(a), Op::LocalGet(b)) if a == b)
            {
                compact.push(out[j].clone());
                j += 3;
                changed = true;
            } else {
                compact.push(out[j].clone());
                j += 1;
            }
        }
        out = compact;
    }
    out
}

fn flat_ops(nodes: &[InstrNode]) -> Vec<&Op> {
    let mut out = Vec::new();
    collect_flat_ops(nodes, &mut out);
    out
}

fn collect_flat_ops<'a>(nodes: &'a [InstrNode], out: &mut Vec<&'a Op>) {
    for node in nodes {
        match node {
            InstrNode::Op(op) => out.push(op),
            InstrNode::IfElse(then_b, else_b) => {
                collect_flat_ops(then_b, out);
                collect_flat_ops(else_b, out);
            }
        }
    }
}

fn node_ops(nodes: &[InstrNode]) -> Vec<Op> {
    flat_ops(nodes).into_iter().cloned().collect()
}

fn has_branch(nodes: &[InstrNode]) -> bool {
    nodes.iter().any(|node| match node {
        InstrNode::Op(_) => false,
        InstrNode::IfElse(..) => true,
    })
}