wasmer_wasix/syscalls/wasix/
reflect_signature.rs

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
use crate::{state::FunctionLookupError, WasiEnv, WasiError};
use tracing::{instrument, trace};
use wasmer::{FunctionEnvMut, MemorySize, Type, WasmPtr, WasmSlice};
use wasmer_wasix_types::wasi::{Bool, Errno, ReflectionResult, WasmValueType};

fn serialize_types(types: &[Type]) -> Result<Vec<WasmValueType>, Errno> {
    types
        .iter()
        .map(|t| {
            WasmValueType::try_from(*t).map_err(|_| {
                trace!("Cannot convert type {} to WasmValueType", t);
                Errno::Inval
            })
        })
        .collect::<Result<Vec<_>, _>>()
}

/// Provides information about a function's signature.
///
/// ### Errors
///
/// Besides the standard error codes, `reflect_signature` may set `errno` to the
/// following values:
///
/// - `Errno::Inval`: The function pointer is not valid, i.e. it does not point to a
/// function in the indirect function table or the function has a unsupported
/// signature. The sizes in the result are undefined in this case.
/// - `Errno::Overflow`: The argument_types and result_types buffers were not big enough
/// to hold the signature. They will be left unchanged. The reflection result
/// will be valid.
#[instrument(
    level = "trace",
    skip_all,
    fields(
        %function_id,
        argument_types_ptr = argument_types.offset().into(),
        argument_types_len,
        result_types_ptr = result_types.offset().into(),
        result_types_len,
        result = result.offset().into()),
    ret
)]
pub fn reflect_signature<M: MemorySize>(
    mut ctx: FunctionEnvMut<'_, WasiEnv>,
    function_id: u32,
    argument_types: WasmPtr<WasmValueType, M>,
    argument_types_len: u16,
    result_types: WasmPtr<WasmValueType, M>,
    result_types_len: u16,
    result: WasmPtr<ReflectionResult, M>,
) -> Result<Errno, WasiError> {
    let (env, mut store) = ctx.data_and_store_mut();

    let function_lookup_result = env
        .inner()
        .indirect_function_table_lookup(&mut store, function_id);

    let memory = unsafe { env.memory_view(&store) };
    let signature_info = result.deref(&memory);

    // Look up the function in the indirect function table
    let function = match function_lookup_result {
        Ok(f) => f,
        Err(e) => {
            let cacheable = match e {
                FunctionLookupError::Empty(_) => {
                    if env.inner().is_closure(function_id) {
                        Bool::False
                    } else {
                        Bool::True
                    }
                }
                FunctionLookupError::OutOfBounds(_) => Bool::False,
                _ => Bool::True,
            };
            trace!(
                "Failed to look up function in indirect function table: {}",
                e
            );
            wasi_try_mem_ok!(signature_info.write(ReflectionResult {
                cacheable,
                arguments: 0,
                results: 0,
            }));
            return Ok(e.into());
        }
    };

    let is_closure = env.inner().is_closure(function_id);
    let cacheable = if is_closure { Bool::False } else { Bool::True };

    let function_type = function.ty(&store);
    let arguments = function_type.params();
    let results = function_type.results();

    wasi_try_mem_ok!(signature_info.write(ReflectionResult {
        cacheable,
        arguments: arguments.len() as u16,
        results: results.len() as u16,
    }));

    if (arguments.len() as u16 > argument_types_len) {
        trace!(
            "Provided arguments buffer is too small {}/{}",
            argument_types_len,
            arguments.len()
        );
        return Ok(Errno::Overflow);
    }
    if (results.len() as u16 > result_types_len) {
        trace!(
            "Provided results buffer is too small {}/{}",
            result_types_len,
            results.len()
        );
        return Ok(Errno::Overflow);
    }

    let serialized_argument_types = wasi_try_ok!(serialize_types(function_type.params()));
    let serialized_result_types = wasi_try_ok!(serialize_types(function_type.results()));

    let mut argument_types_slice = wasi_try_mem_ok!(WasmSlice::<WasmValueType>::new(
        &memory,
        argument_types.offset().into(),
        arguments.len() as u64
    ));
    let mut result_types_slice = wasi_try_mem_ok!(WasmSlice::<WasmValueType>::new(
        &memory,
        result_types.offset().into(),
        results.len() as u64
    ));

    wasi_try_mem_ok!(argument_types_slice.write_slice(&serialized_argument_types));
    wasi_try_mem_ok!(result_types_slice.write_slice(&serialized_result_types));

    Ok(Errno::Success)
}