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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
//! Unstable non-standard Wasmer-specific API that contains more WASI
//! API.

use super::super::{
    externals::wasm_extern_t, module::wasm_module_t, types::wasm_name_t, wasi::wasi_env_t,
};

/// Unstable non-standard type wrapping `wasm_extern_t` with the
/// addition of two `wasm_name_t` respectively for the module name and
/// the name of the extern (very likely to be an import). This
/// non-standard type is used by the unstable non-standard
/// `wasi_get_unordered_imports` function.
///
/// The `module`, `name` and `extern` fields are all owned by this type.
#[allow(non_camel_case_types)]
#[derive(Clone)]
pub struct wasmer_named_extern_t {
    module: wasm_name_t,
    name: wasm_name_t,
    r#extern: Box<wasm_extern_t>,
}

wasm_declare_boxed_vec!(named_extern, wasmer);

/// So. Let's explain a dirty hack. `cbindgen` reads the code and
/// collects symbols. What symbols do we need? None of the one
/// declared in `wasm.h`, but for non-standard API, we need to collect
/// all of them. The problem is that `wasmer_named_extern_t` is the only
/// non-standard type where extra symbols are generated by a macro
/// (`wasm_declare_boxed_vec!`). If we want those macro-generated
/// symbols to be collected by `cbindgen`, we need to _expand_ the
/// crate (i.e. running something like `rustc -- -Zunstable-options
/// --pretty=expanded`). Expanding code is unstable and available only
/// on nightly compiler. We _don't want_ to use a nightly compiler
/// only for that. So how can we help `cbindgen` to _see_ those
/// symbols?
///
/// First solution: We write the C code directly in a file, which is
/// then included in the generated header file with the `cbindgen`
/// API. Problem, it's super easy to get it outdated, and it makes the
/// build process more complex.
///
/// Second solution: We write those symbols in a custom module, that
/// is just here for `cbindgen`, never used by our Rust code
/// (otherwise it's duplicated code), with no particular
/// implementation.
///
/// And that's why we have the following `cbindgen_hack`
/// module.
///
/// But this module must not be compiled by `rustc`. How to force
/// `rustc` to ignore a module? With conditional compilation. Because
/// `cbindgen` does not support conditional compilation, it will
/// always _ignore_ the `#[cfg]` attribute, and will always read the
/// content of the module.
///
/// Sorry.
#[doc(hidden)]
#[cfg(__cbindgen_hack__ = "yes")]
mod __cbindgen_hack__ {
    use super::*;

    #[repr(C)]
    pub struct wasmer_named_extern_vec_t {
        pub size: usize,
        pub data: *mut *mut wasmer_named_extern_t,
    }

    #[no_mangle]
    pub unsafe extern "C" fn wasmer_named_extern_vec_new(
        out: *mut wasmer_named_extern_vec_t,
        length: usize,
        init: *const *mut wasmer_named_extern_t,
    ) {
        unimplemented!()
    }

    #[no_mangle]
    pub unsafe extern "C" fn wasmer_named_extern_vec_new_uninitialized(
        out: *mut wasmer_named_extern_vec_t,
        length: usize,
    ) {
        unimplemented!()
    }

    #[no_mangle]
    pub unsafe extern "C" fn wasmer_named_extern_vec_copy(
        out_ptr: &mut wasmer_named_extern_vec_t,
        in_ptr: &wasmer_named_extern_vec_t,
    ) {
        unimplemented!()
    }

    #[no_mangle]
    pub unsafe extern "C" fn wasmer_named_extern_vec_delete(
        ptr: Option<&mut wasmer_named_extern_vec_t>,
    ) {
        unimplemented!()
    }

    #[no_mangle]
    pub unsafe extern "C" fn wasmer_named_extern_vec_new_empty(
        out: *mut wasmer_named_extern_vec_t,
    ) {
        unimplemented!()
    }
}

/// Non-standard function to get the module name of a
/// `wasmer_named_extern_t`.
///
/// The returned value isn't owned by the caller.
#[no_mangle]
pub extern "C" fn wasmer_named_extern_module(
    named_extern: Option<&wasmer_named_extern_t>,
) -> Option<&wasm_name_t> {
    Some(&named_extern?.module)
}

/// Non-standard function to get the name of a `wasmer_named_extern_t`.
///
/// The returned value isn't owned by the caller.
#[no_mangle]
pub extern "C" fn wasmer_named_extern_name(
    named_extern: Option<&wasmer_named_extern_t>,
) -> Option<&wasm_name_t> {
    Some(&named_extern?.name)
}

/// Non-standard function to get the wrapped extern of a
/// `wasmer_named_extern_t`.
///
/// The returned value isn't owned by the caller.
#[no_mangle]
pub extern "C" fn wasmer_named_extern_unwrap(
    named_extern: Option<&wasmer_named_extern_t>,
) -> Option<&wasm_extern_t> {
    Some(named_extern?.r#extern.as_ref())
}

/// Non-standard function to get the imports needed for the WASI
/// implementation with no particular order. Each import has its
/// associated module name and name, so that it can be re-order later
/// based on the `wasm_module_t` requirements.
#[no_mangle]
pub unsafe extern "C" fn wasi_get_unordered_imports(
    wasi_env: Option<&mut wasi_env_t>,
    module: Option<&wasm_module_t>,
    imports: &mut wasmer_named_extern_vec_t,
) -> bool {
    wasi_get_unordered_imports_inner(wasi_env, module, imports).is_some()
}

unsafe fn wasi_get_unordered_imports_inner(
    wasi_env: Option<&mut wasi_env_t>,
    module: Option<&wasm_module_t>,
    imports: &mut wasmer_named_extern_vec_t,
) -> Option<()> {
    let wasi_env = wasi_env?;
    let store = &mut wasi_env.store;
    let mut store_mut = store.store_mut();
    let module = module?;

    let import_object = c_try!(wasi_env.inner.import_object(&mut store_mut, &module.inner));

    imports.set_buffer(
        import_object
            .into_iter()
            .map(move |((module, name), extern_)| {
                let module = module.into();
                let name = name.into();

                Some(Box::new(wasmer_named_extern_t {
                    module,
                    name,
                    r#extern: Box::new(wasm_extern_t::new(store.clone(), extern_)),
                }))
            })
            .collect::<Vec<_>>(),
    );

    Some(())
}